/** * A JSON value carried from a module's server runtime to its browser UI. * * Realtime state is deliberately data, not transport vocabulary. A module does not open a socket, * choose an origin, attach credentials, or know how its host keeps a subscription alive. The host * owns all of that and gives the module only typed snapshots and updates. */ export type ModuleUiRealtimeJsonValue = | boolean | number | string | null | readonly ModuleUiRealtimeJsonValue[] | { readonly [key: string]: ModuleUiRealtimeJsonValue }; /** Connection state useful for rendering a small, honest status indicator. */ export type ModuleUiRealtimeStatus = "connecting" | "connected" | "reconnecting" | "closed"; /** Stable, browser-safe failures. A host never exposes a caught platform or network error. */ export type ModuleUiRealtimeErrorCode = "REALTIME_CONNECTION_FAILED" | "REALTIME_MESSAGE_INVALID"; export interface ModuleUiRealtimeError { readonly code: ModuleUiRealtimeErrorCode; readonly message: string; } /** Callbacks for one module-local channel. Snapshot and update are distinct on purpose: a UI can * replace all derived state when it (re)connects, then apply later authoritative states normally. */ export interface ModuleUiRealtimeHandlers { readonly onSnapshot?: (state: State, sequence: number) => void; readonly onUpdate?: (state: State, sequence: number) => void; readonly onStatus?: (status: ModuleUiRealtimeStatus) => void; readonly onError?: (error: ModuleUiRealtimeError) => void; } /** One live subscription. Closing is synchronous and idempotent. */ export interface ModuleUiRealtimeSubscription { close(): void; } /** * A module UI's access to its own runtime-published state. * * `channel` is local to the module and uses lowercase kebab-case. The host supplies the module id, * endpoint, origin, credentials, reconnection policy, and concrete transport. Modules can neither * subscribe to another module nor supply a bearer token through this API. * * State must be JSON-compatible. The generic is intentionally unconstrained so a normal named * interface can describe a payload without needing an index signature; the runtime wire boundary * still validates that every delivered payload is JSON. */ export interface ModuleUiRealtimeClient { subscribe( channel: string, handlers: ModuleUiRealtimeHandlers, ): ModuleUiRealtimeSubscription; }