2026-07-30 09:21:55 +08:00
|
|
|
import type { BackendSnapshot } from "../lib/api";
|
|
|
|
|
import type { ConnectionState, View } from "../lib/model";
|
|
|
|
|
|
|
|
|
|
export const SDK_CHANNEL = "wasmeld-console";
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Compatibility version for the same-origin Host/Page document protocol.
|
|
|
|
|
*
|
|
|
|
|
* Additive optional messages may keep the version. Removing a field or
|
|
|
|
|
* changing its meaning requires a version bump and an atomic Host/Page
|
|
|
|
|
* release, which is naturally provided by the embedded Console binary.
|
|
|
|
|
*/
|
|
|
|
|
export const SDK_VERSION = 1;
|
|
|
|
|
|
|
|
|
|
export type RuntimeAction = "start" | "stop" | "restart";
|
|
|
|
|
export type ServiceAction = "start" | "stop" | "restart";
|
2026-07-30 09:43:08 +08:00
|
|
|
export type PageLifecyclePhase = "activate" | "deactivate" | "dispose";
|
2026-07-30 09:21:55 +08:00
|
|
|
|
|
|
|
|
export type HostSharedState = {
|
|
|
|
|
// This is a serializable snapshot, never a Solid signal crossing realms.
|
|
|
|
|
view: View;
|
|
|
|
|
snapshot: BackendSnapshot | null;
|
|
|
|
|
connection: ConnectionState;
|
|
|
|
|
query: string;
|
|
|
|
|
apiBase: string;
|
|
|
|
|
runtimeAction: RuntimeAction | null;
|
|
|
|
|
serviceAction: string | null;
|
|
|
|
|
deploymentAction: string | null;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export type PageCommand =
|
|
|
|
|
| { type: "refresh" }
|
|
|
|
|
| { type: "navigate"; view: View }
|
|
|
|
|
| { type: "open-register" }
|
|
|
|
|
| { type: "open-wit-publish" }
|
|
|
|
|
| { type: "open-invoke"; serviceKey: string }
|
|
|
|
|
| { type: "open-activate"; serviceKey: string }
|
|
|
|
|
| { type: "runtime-action"; action: RuntimeAction }
|
|
|
|
|
| { type: "service-action"; serviceKey: string; action: ServiceAction }
|
|
|
|
|
| { type: "save-api-base"; value: string };
|
|
|
|
|
|
|
|
|
|
export type PageMessage =
|
|
|
|
|
| {
|
|
|
|
|
channel: typeof SDK_CHANNEL;
|
|
|
|
|
version: typeof SDK_VERSION;
|
|
|
|
|
source: "page";
|
|
|
|
|
type: "ready";
|
|
|
|
|
view: View;
|
|
|
|
|
}
|
|
|
|
|
| {
|
|
|
|
|
channel: typeof SDK_CHANNEL;
|
|
|
|
|
version: typeof SDK_VERSION;
|
|
|
|
|
source: "page";
|
|
|
|
|
type: "command";
|
|
|
|
|
view: View;
|
|
|
|
|
command: PageCommand;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export type HostMessage =
|
|
|
|
|
| {
|
|
|
|
|
channel: typeof SDK_CHANNEL;
|
|
|
|
|
version: typeof SDK_VERSION;
|
|
|
|
|
source: "host";
|
|
|
|
|
type: "state";
|
|
|
|
|
state: HostSharedState;
|
|
|
|
|
}
|
|
|
|
|
| {
|
|
|
|
|
channel: typeof SDK_CHANNEL;
|
|
|
|
|
version: typeof SDK_VERSION;
|
|
|
|
|
source: "host";
|
2026-07-30 09:43:08 +08:00
|
|
|
type: "lifecycle";
|
|
|
|
|
phase: PageLifecyclePhase;
|
2026-07-30 09:21:55 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export function isPageMessage(value: unknown): value is PageMessage {
|
|
|
|
|
if (!isRecord(value)) return false;
|
|
|
|
|
return (
|
|
|
|
|
value.channel === SDK_CHANNEL &&
|
|
|
|
|
value.version === SDK_VERSION &&
|
|
|
|
|
value.source === "page" &&
|
|
|
|
|
(value.type === "ready" || value.type === "command")
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function isHostMessage(value: unknown): value is HostMessage {
|
|
|
|
|
if (!isRecord(value)) return false;
|
2026-07-30 09:43:08 +08:00
|
|
|
if (value.channel !== SDK_CHANNEL || value.version !== SDK_VERSION || value.source !== "host") {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
if (value.type === "state") return isRecord(value.state);
|
|
|
|
|
return value.type === "lifecycle" && isPageLifecyclePhase(value.phase);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function isPageLifecyclePhase(value: unknown): value is PageLifecyclePhase {
|
|
|
|
|
return value === "activate" || value === "deactivate" || value === "dispose";
|
2026-07-30 09:21:55 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
|
|
|
return typeof value === "object" && value !== null;
|
|
|
|
|
}
|