commit 2cdd578848e9c6021689b9687355af9e0f6c8d69 Author: Timothy J. Aveni Date: Sun May 24 15:34:50 2026 -0700 Initial commit germanium cutoff diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..025dd8c --- /dev/null +++ b/.editorconfig @@ -0,0 +1,8 @@ +root = true + +[*] +charset = utf-8 +end_of_line = lf +indent_size = 2 +indent_style = space +insert_final_newline = true diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..af3ad12 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,4 @@ +/.yarn/** linguist-vendored +/.yarn/releases/* binary +/.yarn/plugins/**/* binary +/.pnp.* binary linguist-generated diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..7b6c345 --- /dev/null +++ b/.gitignore @@ -0,0 +1,19 @@ +.yarn/* +!.yarn/patches +!.yarn/plugins +!.yarn/releases +!.yarn/sdks +!.yarn/versions + +# Whether you use PnP or not, the node_modules folder is often used to store +# build artifacts that should be gitignored +node_modules + +# Swap the comments on the following lines if you wish to use zero-installs +# In that case, don't forget to run `yarn config set enableGlobalCache false`! +# Documentation here: https://yarnpkg.com/features/caching#zero-installs + +#!.yarn/cache +.pnp.* + +*.swp diff --git a/dist/src/browserSurface.d.ts b/dist/src/browserSurface.d.ts new file mode 100644 index 0000000..4e43960 --- /dev/null +++ b/dist/src/browserSurface.d.ts @@ -0,0 +1,122 @@ +import z, { type ZodType } from "zod"; +import { type JsonValue } from "./browserSurfaceShared.js"; +import { type CreatePackageOptions, type EventSchemaMap, type PackageFunctionSchema, type PackageFunctions, type PackageContext, type PackageSchema, type SchemaAnnotation, type ValueSchemaMap } from "./index.js"; +export { jsonValueSchema, type JsonValue } from "./browserSurfaceShared.js"; +export { createEmbeddedSurfaceRef, createEmbeddedSurfaceRefSchema, embeddedSurfaceRefSchema, getEmbeddedSurfaceId, getEmbeddedSurfacePackageName, type EmbeddedSurfaceRef, } from "./browserSurfaceShared.js"; +export type { SubscriptionHandle } from "./index.js"; +export declare const stateContextResultSchema: z.ZodObject<{ + stateContextId: z.ZodString; +}, z.z.core.$strip>; +export declare const createBrowserSurfaceAnnotation: ({ surfaceId, namespaceProp, surfaces, capabilities, aspectRatioHint, propsFunction, }: { + surfaceId: string; + namespaceProp?: string; + surfaces?: string[]; + capabilities?: string[]; + aspectRatioHint?: string; + propsFunction?: string; +}) => SchemaAnnotation; +export declare const createBrowserSurfacePropsUpdateFunctionSchema: ({ propsSchema, namespaceProp, description, inputDescription, outputDescription, }: { + propsSchema: PropsSchema; + namespaceProp?: string; + description: string; + inputDescription?: string; + outputDescription?: string; +}) => PackageFunctionSchema; +export declare const createBrowserSurfacePackageSchema: = Record, Events extends EventSchemaMap = Record, Values extends ValueSchemaMap = Record>({ description, majorVersion, surfaceId, propsSchema, namespaceProp, surfaces, capabilities, aspectRatioHint, propsFunctionDescription, propsInputDescription, propsOutputDescription, annotations, functions, events, values, }: { + description: string; + majorVersion?: number; + surfaceId: string; + propsSchema: PropsSchema; + namespaceProp?: string; + surfaces?: string[]; + capabilities?: string[]; + aspectRatioHint?: string; + propsFunctionDescription: string; + propsInputDescription?: string; + propsOutputDescription?: string; + annotations?: SchemaAnnotation[]; + functions?: ExtraFunctions; + events?: Events; + values?: Values; +}) => PackageSchema<{ + propsUpdate: PackageFunctionSchema; +} & ExtraFunctions, Events, Values>; +type EventSink = { + emit: (data: T) => Promise; +}; +export type EventHub> = { + subscribe: (eventName: K, sink: EventSink) => () => void; + emit: (eventName: K, payload: Events[K], onError?: (error: unknown, eventName: K) => void) => void; +}; +export declare const createEventHub: >() => EventHub; +type ProducerHandlers = { + onHostAction?: (hostSessionId: string, action: JsonValue) => void | Promise; + onHostDetached?: (hostSessionId: string) => void | Promise; + onHostError?: (hostSessionId: string, error: { + message: string; + stack?: string; + }) => void | Promise; +}; +export declare class BrowserSurfaceRelayProducerClient { + #private; + readonly relayUrl: string; + constructor(relayUrl?: string); + connect(): Promise; + close(): void; + attach(stateContextId: string, surfaceId: string, handlers?: ProducerHandlers): Promise; + publishProps(stateContextId: string, surfaceId: string, props: unknown): Promise; + publishMessage(stateContextId: string, surfaceId: string, payload: unknown): Promise; + detach(stateContextId: string, surfaceId: string): Promise; +} +type DependencyClient = ReturnType; +type RelaySchema = Parameters[0]; +type BrowserSurfaceInstanceInternal = { + ctx: PackageContext; + stateContextId: string; + relay: DependencyClient; + producer: BrowserSurfaceRelayProducerClient; + state: State; + publishQueue: Promise; + publish: () => void; + publishMessage: (payload: unknown) => Promise; +}; +export type BrowserSurfaceInstance = Omit, "publishQueue">; +export type BrowserSurfaceHostError = { + message: string; + stack?: string; +}; +export type BrowserSurfaceControllerOptions = { + relaySchema: RelaySchema; + surfaceId: string; + bundleDir: string; + entryPoint: string; + propsSchema: ZodType; + actionSchema: ZodType; + logPrefix: string; + createState: (instance: BrowserSurfaceInstance) => State | Promise; + buildProps: (instance: BrowserSurfaceInstance) => unknown; + applyProps: (instance: BrowserSurfaceInstance, props: Props) => void | Promise; + onHostAction?: (instance: BrowserSurfaceInstance, hostSessionId: string, action: Action) => void | Promise; + onHostDetached?: (instance: BrowserSurfaceInstance, hostSessionId: string) => void | Promise; + onHostError?: (instance: BrowserSurfaceInstance, hostSessionId: string, error: BrowserSurfaceHostError) => void | Promise; + onAfterCreate?: (instance: BrowserSurfaceInstance) => void | Promise; + onBeforeDestroy?: (instance: BrowserSurfaceInstance) => void | Promise; +}; +type BrowserSurfaceController = { + onContextOpen: (ctx: Context) => void | Promise; + onContextClose: (ctx: Context) => void | Promise; + onDestroy: () => void | Promise; + propsUpdate: (ctx: Context, nextProps: unknown) => Promise; +}; +export declare const createBrowserSurfacePackage: ({ schema, surface, functions, events, values, onCreate, onContextOpen, onContextClose, onDestroy, }: Omit, "functions"> & { + surface: BrowserSurfaceController; + functions?: Omit, "propsUpdate">; +}) => {}; +export declare const createBrowserSurfaceController: ({ relaySchema, surfaceId, bundleDir, entryPoint, propsSchema, actionSchema, logPrefix, createState, buildProps, applyProps, onHostAction, onHostDetached, onHostError, onAfterCreate, onBeforeDestroy, }: BrowserSurfaceControllerOptions) => { + getOrCreate: (ctx: PackageContext) => Promise>; + onContextOpen: (ctx: PackageContext) => Promise; + onContextClose: (ctx: PackageContext) => Promise; + onDestroy: () => Promise; + propsUpdate: (ctx: PackageContext, nextProps: unknown) => Promise; +}; +//# sourceMappingURL=browserSurface.d.ts.map \ No newline at end of file diff --git a/dist/src/browserSurface.d.ts.map b/dist/src/browserSurface.d.ts.map new file mode 100644 index 0000000..4a1839d --- /dev/null +++ b/dist/src/browserSurface.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"browserSurface.d.ts","sourceRoot":"","sources":["../../src/browserSurface.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/dist/src/browserSurface.js b/dist/src/browserSurface.js new file mode 100644 index 0000000..38eb7d2 --- /dev/null +++ b/dist/src/browserSurface.js @@ -0,0 +1,403 @@ +import WebSocket from "ws"; +import z, {} from "zod"; +import { createEmbeddedSurfaceRef, createEmbeddedSurfaceRefSchema, embeddedSurfaceRefSchema, getEmbeddedSurfaceId, getEmbeddedSurfacePackageName, jsonValueSchema, } from "./browserSurfaceShared.js"; +import { createPackage, reportPackageRuntimeError, } from "./index.js"; +export { jsonValueSchema } from "./browserSurfaceShared.js"; +export { createEmbeddedSurfaceRef, createEmbeddedSurfaceRefSchema, embeddedSurfaceRefSchema, getEmbeddedSurfaceId, getEmbeddedSurfacePackageName, } from "./browserSurfaceShared.js"; +export const stateContextResultSchema = z.object({ + stateContextId: z.string().min(1), +}); +export const createBrowserSurfaceAnnotation = ({ surfaceId, namespaceProp = "quixosKey", surfaces = ["desktop"], capabilities = ["mouse", "keyboard"], aspectRatioHint = "16:10", propsFunction = "propsUpdate", }) => ({ + type: "quixos.ui.surface/v1", + surfaceId, + runtime: "browser-module", + transport: "relay", + propsFunction, + namespaceProp, + surfaces, + capabilities, + aspectRatioHint, +}); +export const createBrowserSurfacePropsUpdateFunctionSchema = ({ propsSchema, namespaceProp = "quixosKey", description, inputDescription, outputDescription, }) => ({ + description, + annotations: [ + { + type: "quixos.ui.browser-surface-props/v1", + namespaceProp, + semantics: "replace", + transport: "relay", + }, + ], + inputSchema: (inputDescription + ? z.object({ props: propsSchema }).describe(inputDescription) + : z.object({ props: propsSchema })), + outputSchema: (outputDescription + ? stateContextResultSchema.describe(outputDescription) + : stateContextResultSchema), +}); +export const createBrowserSurfacePackageSchema = ({ description, majorVersion = 1, surfaceId, propsSchema, namespaceProp = "quixosKey", surfaces = ["desktop"], capabilities = ["mouse", "keyboard"], aspectRatioHint = "16:10", propsFunctionDescription, propsInputDescription, propsOutputDescription, annotations = [], functions, events, values, }) => ({ + schemaVersion: 1, + majorVersion, + description, + annotations: [ + createBrowserSurfaceAnnotation({ + surfaceId, + namespaceProp, + surfaces, + capabilities, + aspectRatioHint, + propsFunction: "propsUpdate", + }), + ...annotations, + ], + functions: { + ...(functions ?? {}), + propsUpdate: createBrowserSurfacePropsUpdateFunctionSchema({ + propsSchema, + namespaceProp, + description: propsFunctionDescription, + inputDescription: propsInputDescription, + outputDescription: propsOutputDescription, + }), + }, + events: (events ?? {}), + values: (values ?? {}), +}); +export const createEventHub = () => { + const sinks = new Map(); + return { + subscribe: (eventName, sink) => { + let eventSinks = sinks.get(eventName); + if (!eventSinks) { + eventSinks = new Set(); + sinks.set(eventName, eventSinks); + } + eventSinks.add(sink); + return () => { + eventSinks?.delete(sink); + }; + }, + emit: (eventName, payload, onError) => { + const eventSinks = sinks.get(eventName); + if (!eventSinks) { + return; + } + for (const sink of eventSinks) { + void sink + .emit(payload) + .catch((error) => onError?.(error, eventName)); + } + }, + }; +}; +const DEFAULT_SURFACE_RELAY_URL = "ws://127.0.0.1:6247"; +const producerMessageSchema = z.discriminatedUnion("type", [ + z.object({ + type: z.literal("browser-surface-host-action"), + stateContextId: z.string().min(1), + surfaceId: z.string().min(1), + hostSessionId: z.string().min(1), + action: jsonValueSchema, + }), + z.object({ + type: z.literal("browser-surface-host-detached"), + stateContextId: z.string().min(1), + surfaceId: z.string().min(1), + hostSessionId: z.string().min(1), + }), + z.object({ + type: z.literal("browser-surface-host-error"), + stateContextId: z.string().min(1), + surfaceId: z.string().min(1), + hostSessionId: z.string().min(1), + message: z.string().min(1), + stack: z.string().min(1).optional(), + }), + z.object({ + type: z.literal("error"), + message: z.string(), + }), +]); +const surfaceKey = (stateContextId, surfaceId) => JSON.stringify([stateContextId, surfaceId]); +const getErrorMessage = (error) => error instanceof Error ? error.message : String(error); +const parseProducerMessage = (value) => { + const parsed = producerMessageSchema.safeParse(value); + return parsed.success ? parsed.data : null; +}; +export class BrowserSurfaceRelayProducerClient { + relayUrl; + #socket = null; + #ready = null; + #handlers = new Map(); + constructor(relayUrl = DEFAULT_SURFACE_RELAY_URL) { + this.relayUrl = relayUrl; + } + async connect() { + if (this.#ready) { + return await this.#ready; + } + this.#ready = new Promise((resolve, reject) => { + const socket = new WebSocket(this.relayUrl); + this.#socket = socket; + let opened = false; + const fail = (error) => { + const message = new Error(getErrorMessage(error)); + this.#socket = null; + this.#ready = null; + if (!opened) { + reject(message); + return; + } + console.error(`[browser-surface] relay websocket disconnected: ${message.message}`); + }; + socket.on("open", () => { + opened = true; + resolve(); + }); + socket.on("message", (rawData) => { + let parsedJson; + try { + parsedJson = JSON.parse(String(rawData)); + } + catch { + return; + } + const message = parseProducerMessage(parsedJson); + if (!message) { + return; + } + if (message.type === "error") { + console.error(`[browser-surface] relay error: ${message.message}`); + return; + } + const handlers = this.#handlers.get(surfaceKey(message.stateContextId, message.surfaceId)); + if (!handlers) { + return; + } + if (message.type === "browser-surface-host-action") { + void handlers.onHostAction?.(message.hostSessionId, message.action); + return; + } + if (message.type === "browser-surface-host-error") { + void handlers.onHostError?.(message.hostSessionId, { + message: message.message, + stack: message.stack, + }); + return; + } + void handlers.onHostDetached?.(message.hostSessionId); + }); + socket.on("error", () => { + fail(new Error("Surface relay websocket error")); + }); + socket.on("close", () => { + fail(new Error("Surface relay websocket closed")); + }); + }); + return await this.#ready; + } + close() { + this.#socket?.close(); + this.#socket = null; + this.#ready = null; + this.#handlers.clear(); + } + async #send(message) { + await this.connect(); + const socket = this.#socket; + if (!socket) { + throw new Error("Surface relay websocket is not connected"); + } + socket.send(JSON.stringify(message)); + } + async attach(stateContextId, surfaceId, handlers = {}) { + this.#handlers.set(surfaceKey(stateContextId, surfaceId), handlers); + await this.#send({ + type: "browser-surface-producer-attach", + stateContextId, + surfaceId, + }); + } + async publishProps(stateContextId, surfaceId, props) { + await this.#send({ + type: "browser-surface-props", + stateContextId, + surfaceId, + props, + }); + } + async publishMessage(stateContextId, surfaceId, payload) { + await this.#send({ + type: "browser-surface-message", + stateContextId, + surfaceId, + payload, + }); + } + async detach(stateContextId, surfaceId) { + this.#handlers.delete(surfaceKey(stateContextId, surfaceId)); + try { + await this.#send({ + type: "browser-surface-producer-detach", + stateContextId, + surfaceId, + }); + } + catch { + // ignore shutdown races + } + } +} +export const createBrowserSurfacePackage = ({ schema, surface, functions, events, values, onCreate, onContextOpen, onContextClose, onDestroy, }) => createPackage({ + schema, + onCreate, + onContextOpen: async (ctx) => { + await surface.onContextOpen(ctx); + await onContextOpen?.(ctx); + }, + onContextClose: async (ctx) => { + try { + await onContextClose?.(ctx); + } + finally { + await surface.onContextClose(ctx); + } + }, + onDestroy: async () => { + try { + await onDestroy?.(); + } + finally { + await surface.onDestroy(); + } + }, + functions: { + ...(functions ?? {}), + propsUpdate: async (ctx, params) => stateContextResultSchema.parse({ + stateContextId: await surface.propsUpdate(ctx, params.props), + }), + }, + events: events, + values: values, +}); +const toPublicInstance = (instance) => instance; +export const createBrowserSurfaceController = ({ relaySchema, surfaceId, bundleDir, entryPoint, propsSchema, actionSchema, logPrefix, createState, buildProps, applyProps, onHostAction, onHostDetached, onHostError, onAfterCreate, onBeforeDestroy, }) => { + const renderStates = new Map(); + const pendingRenderStates = new Map(); + const queuePublish = (instance) => { + instance.publishQueue = instance.publishQueue + .then(async () => { + await instance.producer.publishProps(instance.stateContextId, surfaceId, buildProps(toPublicInstance(instance))); + }) + .catch((error) => { + console.error(`${logPrefix} failed to publish surface props for ${instance.stateContextId}`, error); + }); + }; + const createRenderState = async (ctx) => { + const relay = ctx.usePackage(relaySchema); + const registration = (await relay.functions.registerBrowserSurface({ + stateContextId: ctx.stateContext, + surfaceId, + bundleDir, + entryPoint, + })); + const producer = new BrowserSurfaceRelayProducerClient(registration.relayUrl); + const instance = { + ctx, + stateContextId: ctx.stateContext, + relay, + producer, + state: undefined, + publishQueue: Promise.resolve(), + publish: () => { + queuePublish(instance); + }, + publishMessage: async (payload) => { + await producer.publishMessage(ctx.stateContext, surfaceId, payload); + }, + }; + instance.state = await createState(toPublicInstance(instance)); + renderStates.set(ctx.stateContext, instance); + await producer.attach(ctx.stateContext, surfaceId, { + onHostAction: async (hostSessionId, action) => { + if (!onHostAction) { + return; + } + const parsed = actionSchema.parse(action); + await onHostAction(toPublicInstance(instance), hostSessionId, parsed); + }, + onHostDetached: async (hostSessionId) => { + await onHostDetached?.(toPublicInstance(instance), hostSessionId); + }, + onHostError: async (hostSessionId, error) => { + await reportPackageRuntimeError({ + phase: "browser-surface", + error: new Error(error.message), + stateContextId: instance.stateContextId, + surfaceId, + hostSessionId, + stack: error.stack, + }); + await onHostError?.(toPublicInstance(instance), hostSessionId, error); + }, + }); + await onAfterCreate?.(toPublicInstance(instance)); + instance.publish(); + return instance; + }; + const getOrCreate = async (ctx) => { + const existing = renderStates.get(ctx.stateContext); + if (existing) { + return existing; + } + const pending = pendingRenderStates.get(ctx.stateContext); + if (pending) { + return await pending; + } + const creation = createRenderState(ctx); + pendingRenderStates.set(ctx.stateContext, creation); + try { + return await creation; + } + finally { + pendingRenderStates.delete(ctx.stateContext); + } + }; + const destroyState = async (stateContextId) => { + const instance = renderStates.get(stateContextId); + if (!instance) { + return; + } + renderStates.delete(stateContextId); + pendingRenderStates.delete(stateContextId); + await instance.publishQueue.catch(() => { }); + await onBeforeDestroy?.(toPublicInstance(instance)); + await instance.producer.detach(stateContextId, surfaceId); + instance.producer.close(); + await instance.relay.functions.unregisterBrowserSurface({ stateContextId, surfaceId }); + }; + return { + getOrCreate: async (ctx) => toPublicInstance(await getOrCreate(ctx)), + onContextOpen: async (ctx) => { + await getOrCreate(ctx); + }, + onContextClose: async (ctx) => { + await destroyState(ctx.stateContext); + }, + onDestroy: async () => { + const activeStateContexts = [...renderStates.keys()]; + pendingRenderStates.clear(); + for (const stateContextId of activeStateContexts) { + await destroyState(stateContextId); + } + }, + propsUpdate: async (ctx, nextProps) => { + const parsedProps = propsSchema.parse(nextProps); + const instance = await getOrCreate(ctx); + await applyProps(toPublicInstance(instance), parsedProps); + instance.publish(); + return ctx.stateContext; + }, + }; +}; +//# sourceMappingURL=browserSurface.js.map \ No newline at end of file diff --git a/dist/src/browserSurface.js.map b/dist/src/browserSurface.js.map new file mode 100644 index 0000000..c7eef3e --- /dev/null +++ b/dist/src/browserSurface.js.map @@ -0,0 +1 @@ +{"version":3,"file":"browserSurface.js","sourceRoot":"","sources":["../../src/browserSurface.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/dist/src/browserSurfaceEmbedded.d.ts b/dist/src/browserSurfaceEmbedded.d.ts new file mode 100644 index 0000000..ad40df8 --- /dev/null +++ b/dist/src/browserSurfaceEmbedded.d.ts @@ -0,0 +1,33 @@ +import { type CSSProperties, type ReactNode } from "react"; +import type { PackageSchema } from "./index.js"; +import { type EmbeddedSurfaceRef } from "./browserSurfaceShared.js"; +type BrowserSurfaceSchemaLike = PackageSchema & { + __quixos?: { + name?: string | null; + flakeRef?: string | null; + }; +}; +type EmbeddedSurfaceHostProps = { + surface: EmbeddedSurfaceRef; + relayUrl?: string; + className?: string; + style?: CSSProperties; + fallback?: ReactNode; + onError?: (error: Error) => void; +}; +export type EmbeddedSurfaceComponentProps = { + stateContextId: string; + surfaceId?: SurfaceId; + relayUrl?: string; + className?: string; + style?: CSSProperties; + fallback?: ReactNode; + onError?: (error: Error) => void; +}; +export declare const EmbeddedSurface: ({ surface, relayUrl, className, style, fallback, onError, }: EmbeddedSurfaceHostProps) => import("react/jsx-runtime").JSX.Element; +export declare const createEmbeddedSurfaceComponent: (target: Target, options?: { + surfaceId?: SurfaceId; + relayUrl?: string; +}) => ({ stateContextId, surfaceId, relayUrl, className, style, fallback, onError, }: EmbeddedSurfaceComponentProps) => import("react/jsx-runtime").JSX.Element; +export {}; +//# sourceMappingURL=browserSurfaceEmbedded.d.ts.map \ No newline at end of file diff --git a/dist/src/browserSurfaceEmbedded.d.ts.map b/dist/src/browserSurfaceEmbedded.d.ts.map new file mode 100644 index 0000000..ef4bf59 --- /dev/null +++ b/dist/src/browserSurfaceEmbedded.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"browserSurfaceEmbedded.d.ts","sourceRoot":"","sources":["../../src/browserSurfaceEmbedded.tsx"],"names":[],"mappings":"AAAA,OAAO,EAML,KAAK,aAAa,EAClB,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;AACf,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAChD,OAAO,EAGL,KAAK,kBAAkB,EACxB,MAAM,2BAA2B,CAAC;AAMnC,KAAK,wBAAwB,GAAG,aAAa,GAAG;IAC9C,QAAQ,CAAC,EAAE;QACT,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QACrB,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;KAC1B,CAAC;CACH,CAAC;AA6HF,KAAK,wBAAwB,GAAG;IAC9B,OAAO,EAAE,kBAAkB,CAAC;IAC5B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,aAAa,CAAC;IACtB,QAAQ,CAAC,EAAE,SAAS,CAAC;IACrB,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;CAClC,CAAC;AAEF,MAAM,MAAM,6BAA6B,CAAC,SAAS,SAAS,MAAM,GAAG,MAAM,IAAI;IAC7E,cAAc,EAAE,MAAM,CAAC;IACvB,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,aAAa,CAAC;IACtB,QAAQ,CAAC,EAAE,SAAS,CAAC;IACrB,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;CAClC,CAAC;AAmUF,eAAO,MAAM,eAAe,GAAI,6DAO7B,wBAAwB,4CA6L1B,CAAC;AAEF,eAAO,MAAM,8BAA8B,GACzC,MAAM,SAAS,MAAM,GAAG,wBAAwB,EAChD,SAAS,SAAS,MAAM,GAAG,MAAM,EAEjC,QAAQ,MAAM,EACd,UAAU;IACR,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,MAOO,+EAQH,6BAA6B,CAAC,SAAS,CAAC,4CAa9C,CAAC"} \ No newline at end of file diff --git a/dist/src/browserSurfaceEmbedded.js b/dist/src/browserSurfaceEmbedded.js new file mode 100644 index 0000000..c22b79d --- /dev/null +++ b/dist/src/browserSurfaceEmbedded.js @@ -0,0 +1,444 @@ +import { Fragment as _Fragment, jsx as _jsx } from "react/jsx-runtime"; +import { useEffect, useLayoutEffect, useMemo, useRef, useState, } from "react"; +import { createEmbeddedSurfaceRef, getEmbeddedSurfaceId, } from "./browserSurfaceShared.js"; +import { jsonValueSchema, } from "./browserSurfaceShared.js"; +import z from "zod"; +const LOCAL_HOSTNAMES = new Set(["localhost", "127.0.0.1", "::1", "[::1]"]); +const RELAY_LOCAL_PORT = "6247"; +const RELAY_PROXY_PATH = "/relay/"; +const relayServerMessageSchema = z.discriminatedUnion("type", [ + z.object({ + type: z.literal("browser-surface-bootstrap"), + stateContextId: z.string().min(1), + surfaceId: z.string().min(1), + bundleUrl: z.string().url(), + initialProps: jsonValueSchema, + }), + z.object({ + type: z.literal("browser-surface-props"), + stateContextId: z.string().min(1), + surfaceId: z.string().min(1), + props: jsonValueSchema, + }), + z.object({ + type: z.literal("browser-surface-message"), + stateContextId: z.string().min(1), + surfaceId: z.string().min(1), + payload: jsonValueSchema, + }), + z.object({ + type: z.literal("browser-surface-closed"), + stateContextId: z.string().min(1), + surfaceId: z.string().min(1), + }), + z.object({ + type: z.literal("error"), + message: z.string(), + }), +]); +const isLocalHostname = (hostname) => LOCAL_HOSTNAMES.has(hostname); +const urlForCurrentHostAndPort = (locationLike = window.location, port, { ws = false } = {}) => { + const url = new URL(locationLike.href); + url.protocol = ws + ? url.protocol === "https:" + ? "wss:" + : "ws:" + : url.protocol === "https:" + ? "https:" + : "http:"; + url.port = port; + url.pathname = "/"; + url.search = ""; + url.hash = ""; + return url.toString(); +}; +const urlForCurrentOriginAndPath = (locationLike = window.location, path, { ws = false } = {}) => { + const url = new URL(locationLike.href); + url.protocol = ws + ? url.protocol === "https:" + ? "wss:" + : "ws:" + : url.protocol === "https:" + ? "https:" + : "http:"; + url.port = ""; + url.pathname = path; + url.search = ""; + url.hash = ""; + return url.toString(); +}; +const defaultRelayUrlForBrowser = (locationLike = window.location) => { + if (isLocalHostname(locationLike.hostname)) { + return urlForCurrentHostAndPort(locationLike, RELAY_LOCAL_PORT, { + ws: true, + }); + } + return urlForCurrentOriginAndPath(locationLike, RELAY_PROXY_PATH, { + ws: true, + }); +}; +const getErrorMessage = (error) => error instanceof Error ? error.message : String(error); +const toError = (error) => error instanceof Error ? error : new Error(String(error)); +const getErrorDetails = (error) => { + if (error instanceof Error) { + return { + message: error.message || "Unknown error", + stack: error.stack, + }; + } + return { + message: String(error), + stack: undefined, + }; +}; +const parseServerMessage = (value) => { + const parsed = relayServerMessageSchema.safeParse(value); + return parsed.success ? parsed.data : null; +}; +const normalizeSurfaceModule = (module) => { + if (typeof module === "object" && + module !== null && + typeof module.mount === "function") { + return module; + } + throw new Error("Surface bundle does not export a mount(...) function"); +}; +const attachmentKey = (stateContextId, surfaceId, hostSessionId) => JSON.stringify([stateContextId, surfaceId, hostSessionId]); +const sharedBrowserSurfaceRelayClients = new Map(); +const createBrowserSurfaceHostSessionId = () => crypto.randomUUID(); +class BrowserSurfaceRelayClient { + relayUrl; + #socket = null; + #ready = null; + #attachments = new Map(); + constructor(relayUrl) { + this.relayUrl = relayUrl; + } + async connect() { + if (this.#ready) { + return await this.#ready; + } + this.#ready = new Promise((resolve, reject) => { + const socket = new WebSocket(this.relayUrl); + this.#socket = socket; + let opened = false; + const fail = (error) => { + const message = toError(error); + this.#socket = null; + this.#ready = null; + if (!opened) { + reject(message); + } + }; + socket.addEventListener("open", () => { + opened = true; + resolve(); + }); + socket.addEventListener("message", (event) => { + let parsedJson; + try { + parsedJson = JSON.parse(String(event.data)); + } + catch { + return; + } + const message = parseServerMessage(parsedJson); + if (!message) { + return; + } + if (message.type === "error") { + console.error(`[browser-surface-relay] ${message.message}`); + return; + } + const attachment = this.#findAttachment(message.stateContextId, message.surfaceId); + if (!attachment) { + return; + } + void attachment.handler(message); + }); + socket.addEventListener("error", () => { + fail(new Error("Browser surface relay socket error")); + }); + socket.addEventListener("close", () => { + fail(new Error("Browser surface relay socket closed")); + }); + }); + return await this.#ready; + } + close() { + this.#socket?.close(); + this.#socket = null; + this.#ready = null; + this.#attachments.clear(); + } + async #send(message) { + await this.connect(); + const socket = this.#socket; + if (!socket) { + throw new Error("Browser surface relay socket is not connected"); + } + socket.send(JSON.stringify(message)); + } + #findAttachment(stateContextId, surfaceId) { + for (const attachment of this.#attachments.values()) { + if (attachment.stateContextId === stateContextId && + attachment.surfaceId === surfaceId) { + return attachment; + } + } + return null; + } + async attach(stateContextId, surfaceId, hostSessionId, handler) { + const key = attachmentKey(stateContextId, surfaceId, hostSessionId); + const existing = this.#findAttachment(stateContextId, surfaceId); + if (existing) { + for (const [existingKey, attachment] of this.#attachments.entries()) { + if (attachment === existing) { + this.#attachments.delete(existingKey); + break; + } + } + try { + await this.#send({ + type: "browser-surface-host-detach", + stateContextId, + surfaceId, + hostSessionId: existing.hostSessionId, + }); + } + catch { + // Ignore replacement races. + } + } + this.#attachments.set(key, { + stateContextId, + surfaceId, + hostSessionId, + handler, + }); + await this.#send({ + type: "browser-surface-host-attach", + stateContextId, + surfaceId, + hostSessionId, + }); + return async () => { + this.#attachments.delete(key); + try { + await this.#send({ + type: "browser-surface-host-detach", + stateContextId, + surfaceId, + hostSessionId, + }); + } + catch { + // Ignore detach races during teardown. + } + }; + } + async sendAction(stateContextId, surfaceId, hostSessionId, action) { + await this.#send({ + type: "browser-surface-host-action", + stateContextId, + surfaceId, + hostSessionId, + action, + }); + } + async sendError(stateContextId, surfaceId, hostSessionId, error) { + await this.#send({ + type: "browser-surface-host-error", + stateContextId, + surfaceId, + hostSessionId, + message: error.message, + ...(error.stack ? { stack: error.stack } : {}), + }); + } +} +const acquireSharedBrowserSurfaceRelayClient = (relayUrl) => { + const existing = sharedBrowserSurfaceRelayClients.get(relayUrl); + if (existing) { + existing.refCount += 1; + return existing.client; + } + const client = new BrowserSurfaceRelayClient(relayUrl); + sharedBrowserSurfaceRelayClients.set(relayUrl, { + client, + refCount: 1, + }); + return client; +}; +const releaseSharedBrowserSurfaceRelayClient = (relayUrl) => { + const entry = sharedBrowserSurfaceRelayClients.get(relayUrl); + if (!entry) { + return; + } + entry.refCount -= 1; + if (entry.refCount > 0) { + return; + } + entry.client.close(); + sharedBrowserSurfaceRelayClients.delete(relayUrl); +}; +export const EmbeddedSurface = ({ surface, relayUrl, className, style, fallback = null, onError, }) => { + const hostRef = useRef(null); + const unsubscribeRef = useRef(null); + const mountedHandleRef = useRef(null); + const loadedBundleUrlRef = useRef(null); + const propListenersRef = useRef(new Set()); + const messageListenersRef = useRef(new Set()); + const hostSessionIdRef = useRef(""); + const [error, setError] = useState(null); + const resolvedRelayUrl = useMemo(() => relayUrl ?? defaultRelayUrlForBrowser(), [relayUrl]); + const relayClient = useMemo(() => acquireSharedBrowserSurfaceRelayClient(resolvedRelayUrl), [resolvedRelayUrl]); + const clearMountedSurface = () => { + try { + mountedHandleRef.current?.unmount(); + } + catch { + // Ignore teardown races during remount. + } + mountedHandleRef.current = null; + loadedBundleUrlRef.current = null; + propListenersRef.current.clear(); + messageListenersRef.current.clear(); + }; + const reportSurfaceError = (errorValue) => { + const error = toError(errorValue); + setError(error); + onError?.(error); + const details = getErrorDetails(error); + if (!hostSessionIdRef.current) { + return; + } + void relayClient.sendError(surface.stateContextId, surface.surfaceId, hostSessionIdRef.current, details).catch((reportingError) => { + console.error("[embedded-surface] failed to report surface error", reportingError); + }); + }; + const dispatchAction = (action) => { + if (!hostSessionIdRef.current) { + return; + } + void relayClient + .sendAction(surface.stateContextId, surface.surfaceId, hostSessionIdRef.current, action) + .catch((dispatchError) => { + reportSurfaceError(dispatchError); + }); + }; + const mountSurface = async (bundleUrl, initialProps) => { + const host = hostRef.current; + if (!host) { + throw new Error("Embedded surface host container is not mounted"); + } + if (loadedBundleUrlRef.current !== bundleUrl) { + clearMountedSurface(); + const imported = await import(/* @vite-ignore */ bundleUrl); + const surfaceModule = normalizeSurfaceModule(imported); + const mounted = await surfaceModule.mount({ + container: host, + initialProps, + onProps: (listener) => { + propListenersRef.current.add(listener); + return () => { + propListenersRef.current.delete(listener); + }; + }, + onMessage: (listener) => { + messageListenersRef.current.add(listener); + return () => { + messageListenersRef.current.delete(listener); + }; + }, + dispatch: dispatchAction, + reportError: reportSurfaceError, + }); + mountedHandleRef.current = { + unmount: mounted && typeof mounted.unmount === "function" + ? () => mounted.unmount?.() + : () => { }, + }; + loadedBundleUrlRef.current = bundleUrl; + return; + } + for (const listener of propListenersRef.current) { + listener(initialProps); + } + }; + const handleRelayMessage = async (message) => { + switch (message.type) { + case "browser-surface-bootstrap": + await mountSurface(message.bundleUrl, message.initialProps); + return; + case "browser-surface-props": + for (const listener of propListenersRef.current) { + listener(message.props); + } + return; + case "browser-surface-message": + for (const listener of messageListenersRef.current) { + listener(message.payload); + } + return; + case "browser-surface-closed": + clearMountedSurface(); + return; + case "error": + reportSurfaceError(new Error(message.message)); + return; + } + }; + useLayoutEffect(() => { + setError(null); + clearMountedSurface(); + let cancelled = false; + const hostSessionId = createBrowserSurfaceHostSessionId(); + hostSessionIdRef.current = hostSessionId; + void relayClient + .attach(surface.stateContextId, surface.surfaceId, hostSessionId, async (message) => { + try { + await handleRelayMessage(message); + } + catch (messageError) { + reportSurfaceError(messageError); + } + }) + .then(async (unsubscribe) => { + if (cancelled) { + await unsubscribe(); + return; + } + unsubscribeRef.current = unsubscribe; + }) + .catch((attachError) => { + if (!cancelled) { + reportSurfaceError(attachError); + } + }); + return () => { + cancelled = true; + void unsubscribeRef.current?.(); + unsubscribeRef.current = null; + hostSessionIdRef.current = ""; + clearMountedSurface(); + }; + }, [relayClient, surface.packageName, surface.stateContextId, surface.surfaceId]); + useEffect(() => { + return () => { + releaseSharedBrowserSurfaceRelayClient(resolvedRelayUrl); + }; + }, [relayClient, resolvedRelayUrl]); + if (error) { + return _jsx(_Fragment, { children: fallback }); + } + return _jsx("div", { ref: hostRef, className: className, style: style }); +}; +export const createEmbeddedSurfaceComponent = (target, options) => { + const defaultSurfaceId = getEmbeddedSurfaceId(target, options?.surfaceId); + return ({ stateContextId, surfaceId, relayUrl, className, style, fallback, onError, }) => (_jsx(EmbeddedSurface, { surface: createEmbeddedSurfaceRef(target, { + stateContextId, + surfaceId: surfaceId ?? defaultSurfaceId, + }), relayUrl: relayUrl ?? options?.relayUrl, className: className, style: style, fallback: fallback, onError: onError })); +}; +//# sourceMappingURL=browserSurfaceEmbedded.js.map \ No newline at end of file diff --git a/dist/src/browserSurfaceEmbedded.js.map b/dist/src/browserSurfaceEmbedded.js.map new file mode 100644 index 0000000..46fb94a --- /dev/null +++ b/dist/src/browserSurfaceEmbedded.js.map @@ -0,0 +1 @@ +{"version":3,"file":"browserSurfaceEmbedded.js","sourceRoot":"","sources":["../../src/browserSurfaceEmbedded.tsx"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/dist/src/browserSurfaceReact.d.ts b/dist/src/browserSurfaceReact.d.ts new file mode 100644 index 0000000..6934bf1 --- /dev/null +++ b/dist/src/browserSurfaceReact.d.ts @@ -0,0 +1,34 @@ +import { type ComponentType } from "react"; +import type { ZodType } from "zod"; +export { EmbeddedSurface, createEmbeddedSurfaceComponent, type EmbeddedSurfaceComponentProps, } from "./browserSurfaceEmbedded.js"; +export type BrowserSurfaceMountApi = { + container: HTMLElement; + initialProps: unknown; + onProps: (listener: (props: unknown) => void) => () => void; + onMessage: (listener: (message: unknown) => void) => () => void; + dispatch: (action: unknown) => void; + reportError?: (error: unknown) => void; +}; +export type BrowserSurfaceViewProps = { + props: Props; + dispatch: (action: Action) => void; +}; +type CreateReactSurfaceMountOptions = { + Component: ComponentType>; + parseProps: (value: unknown) => Props; + normalizeAction: (action: Action) => Action; + onMessage?: (message: unknown) => void; +}; +type CreateReactBrowserSurfaceMountOptions = { + Component: ComponentType>; + propsSchema: ZodType; + actionSchema: ZodType; + onMessage?: (message: unknown) => void; +}; +export declare const createReactSurfaceMount: ({ Component, parseProps, normalizeAction, onMessage, }: CreateReactSurfaceMountOptions) => ({ container, initialProps, onProps, onMessage: subscribeMessages, dispatch, reportError, }: BrowserSurfaceMountApi) => { + unmount: () => void; +}; +export declare const createReactBrowserSurfaceMount: ({ Component, propsSchema, actionSchema, onMessage, }: CreateReactBrowserSurfaceMountOptions) => ({ container, initialProps, onProps, onMessage: subscribeMessages, dispatch, reportError, }: BrowserSurfaceMountApi) => { + unmount: () => void; +}; +//# sourceMappingURL=browserSurfaceReact.d.ts.map \ No newline at end of file diff --git a/dist/src/browserSurfaceReact.d.ts.map b/dist/src/browserSurfaceReact.d.ts.map new file mode 100644 index 0000000..88dd80d --- /dev/null +++ b/dist/src/browserSurfaceReact.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"browserSurfaceReact.d.ts","sourceRoot":"","sources":["../../src/browserSurfaceReact.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAa,KAAK,aAAa,EAAkC,MAAM,OAAO,CAAC;AAEtF,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,KAAK,CAAC;AACnC,OAAO,EACL,eAAe,EACf,8BAA8B,EAC9B,KAAK,6BAA6B,GACnC,MAAM,6BAA6B,CAAC;AAErC,MAAM,MAAM,sBAAsB,GAAG;IACnC,SAAS,EAAE,WAAW,CAAC;IACvB,YAAY,EAAE,OAAO,CAAC;IACtB,OAAO,EAAE,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,KAAK,MAAM,IAAI,CAAC;IAC5D,SAAS,EAAE,CAAC,QAAQ,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,KAAK,MAAM,IAAI,CAAC;IAChE,QAAQ,EAAE,CAAC,MAAM,EAAE,OAAO,KAAK,IAAI,CAAC;IACpC,WAAW,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;CACxC,CAAC;AAEF,MAAM,MAAM,uBAAuB,CAAC,KAAK,EAAE,MAAM,IAAI;IACnD,KAAK,EAAE,KAAK,CAAC;IACb,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;CACpC,CAAC;AAEF,KAAK,8BAA8B,CAAC,KAAK,EAAE,MAAM,IAAI;IACnD,SAAS,EAAE,aAAa,CAAC,uBAAuB,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC,CAAC;IACjE,UAAU,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,KAAK,CAAC;IACtC,eAAe,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,MAAM,CAAC;IAC5C,SAAS,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,CAAC;CACxC,CAAC;AAEF,KAAK,qCAAqC,CAAC,KAAK,EAAE,MAAM,IAAI;IAC1D,SAAS,EAAE,aAAa,CAAC,uBAAuB,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC,CAAC;IACjE,WAAW,EAAE,OAAO,CAAC,KAAK,CAAC,CAAC;IAC5B,YAAY,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC;IAC9B,SAAS,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,CAAC;CACxC,CAAC;AA0CF,eAAO,MAAM,uBAAuB,GAAI,KAAK,EAAE,MAAM,EAAE,wDAKpD,8BAA8B,CAAC,KAAK,EAAE,MAAM,CAAC,MACtC,4FAOL,sBAAsB;;CA2E1B,CAAC;AAEF,eAAO,MAAM,8BAA8B,GAAI,KAAK,EAAE,MAAM,EAAE,sDAK3D,qCAAqC,CAAC,KAAK,EAAE,MAAM,CAAC,kGAlFlD,sBAAsB;;CAwFvB,CAAC"} \ No newline at end of file diff --git a/dist/src/browserSurfaceReact.js b/dist/src/browserSurfaceReact.js new file mode 100644 index 0000000..1652fe5 --- /dev/null +++ b/dist/src/browserSurfaceReact.js @@ -0,0 +1,94 @@ +import { jsx as _jsx } from "react/jsx-runtime"; +import { Component } from "react"; +import { createRoot } from "react-dom/client"; +export { EmbeddedSurface, createEmbeddedSurfaceComponent, } from "./browserSurfaceEmbedded.js"; +class SurfaceErrorBoundary extends Component { + state = { hasError: false }; + static getDerivedStateFromError() { + return { hasError: true }; + } + componentDidCatch(error, info) { + this.props.onError(error, info); + } + componentDidUpdate(prevProps) { + if (this.state.hasError && prevProps.resetToken !== this.props.resetToken) { + this.setState({ hasError: false }); + } + } + render() { + if (this.state.hasError) { + return null; + } + return this.props.children; + } +} +const surfaceRoots = new WeakMap(); +export const createReactSurfaceMount = ({ Component, parseProps, normalizeAction, onMessage, }) => { + return ({ container, initialProps, onProps, onMessage: subscribeMessages, dispatch, reportError, }) => { + const root = surfaceRoots.get(container) ?? + (() => { + const createdRoot = createRoot(container); + surfaceRoots.set(container, createdRoot); + return createdRoot; + })(); + let currentProps = parseProps(initialProps); + let propsVersion = 0; + const reportSurfaceError = (error, componentStack) => { + if (!(error instanceof Error)) { + reportError?.(error); + return; + } + if (componentStack && componentStack.trim().length > 0) { + const errorWithComponentStack = new Error(error.message); + errorWithComponentStack.name = error.name; + errorWithComponentStack.stack = [ + error.stack ?? `${error.name}: ${error.message}`, + "", + "Component stack:", + componentStack.trim(), + ].join("\n"); + reportError?.(errorWithComponentStack); + return; + } + reportError?.(error); + }; + const render = () => { + root.render(_jsx(SurfaceErrorBoundary, { resetToken: propsVersion, onError: (error, info) => { + reportSurfaceError(error, info.componentStack ?? undefined); + }, children: _jsx(Component, { props: currentProps, dispatch: (action) => { + dispatch(normalizeAction(action)); + } }) })); + }; + render(); + const unsubscribeProps = onProps((nextProps) => { + try { + currentProps = parseProps(nextProps); + propsVersion += 1; + render(); + } + catch (error) { + reportSurfaceError(error); + } + }); + const unsubscribeMessages = subscribeMessages((message) => { + onMessage?.(message); + }); + return { + unmount: () => { + unsubscribeProps(); + unsubscribeMessages(); + root.unmount(); + if (surfaceRoots.get(container) === root) { + surfaceRoots.delete(container); + } + }, + }; + }; +}; +export const createReactBrowserSurfaceMount = ({ Component, propsSchema, actionSchema, onMessage, }) => createReactSurfaceMount({ + Component, + parseProps: (value) => propsSchema.parse(value), + normalizeAction: (action) => actionSchema.parse(action), + onMessage, +}); +//# sourceMappingURL=browserSurfaceReact.js.map \ No newline at end of file diff --git a/dist/src/browserSurfaceReact.js.map b/dist/src/browserSurfaceReact.js.map new file mode 100644 index 0000000..fa7c5aa --- /dev/null +++ b/dist/src/browserSurfaceReact.js.map @@ -0,0 +1 @@ +{"version":3,"file":"browserSurfaceReact.js","sourceRoot":"","sources":["../../src/browserSurfaceReact.tsx"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/dist/src/browserSurfaceShared.d.ts b/dist/src/browserSurfaceShared.d.ts new file mode 100644 index 0000000..49e6d14 --- /dev/null +++ b/dist/src/browserSurfaceShared.d.ts @@ -0,0 +1,41 @@ +import type { PackageSchema } from "./index.js"; +import z from "zod"; +export type JsonValue = null | boolean | number | string | JsonValue[] | { + [key: string]: JsonValue; +}; +export declare const jsonValueSchema: z.ZodType; +type BrowserSurfaceSchemaLike = PackageSchema & { + __quixos?: { + name?: string | null; + flakeRef?: string | null; + }; +}; +type BrowserSurfaceAnnotation = { + type: "quixos.ui.surface/v1"; + surfaceId: string; +}; +export type EmbeddedSurfaceRef = { + packageName: PackageName; + stateContextId: string; + surfaceId: SurfaceId; +}; +export declare const embeddedSurfaceRefSchema: z.ZodObject<{ + packageName: z.ZodString; + stateContextId: z.ZodString; + surfaceId: z.ZodDefault; +}, z.z.core.$strip>; +export declare const createEmbeddedSurfaceRefSchema: (target: string | BrowserSurfaceSchemaLike, options?: { + surfaceId?: SurfaceId; +}) => z.ZodObject<{ + packageName: z.ZodLiteral; + stateContextId: z.ZodString; + surfaceId: z.ZodDefault; +}, z.z.core.$strip>; +export declare const createEmbeddedSurfaceRef: (target: string | BrowserSurfaceSchemaLike, options: { + stateContextId: string; + surfaceId?: SurfaceId; +}) => EmbeddedSurfaceRef; +export declare const getEmbeddedSurfacePackageName: (target: string | BrowserSurfaceSchemaLike) => string; +export declare const getEmbeddedSurfaceId: (target: string | BrowserSurfaceSchemaLike, surfaceId?: string) => string; +export type { BrowserSurfaceAnnotation }; +//# sourceMappingURL=browserSurfaceShared.d.ts.map \ No newline at end of file diff --git a/dist/src/browserSurfaceShared.d.ts.map b/dist/src/browserSurfaceShared.d.ts.map new file mode 100644 index 0000000..cc5a727 --- /dev/null +++ b/dist/src/browserSurfaceShared.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"browserSurfaceShared.d.ts","sourceRoot":"","sources":["../../src/browserSurfaceShared.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAChD,OAAO,CAAC,MAAM,KAAK,CAAC;AAEpB,MAAM,MAAM,SAAS,GACjB,IAAI,GACJ,OAAO,GACP,MAAM,GACN,MAAM,GACN,SAAS,EAAE,GACX;IAAE,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,CAAA;CAAE,CAAC;AAEjC,eAAO,MAAM,eAAe,EAAE,CAAC,CAAC,OAAO,CAAC,SAAS,CAShD,CAAC;AAEF,KAAK,wBAAwB,GAAG,aAAa,GAAG;IAC9C,QAAQ,CAAC,EAAE;QACT,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QACrB,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;KAC1B,CAAC;CACH,CAAC;AAEF,KAAK,wBAAwB,GAAG;IAC9B,IAAI,EAAE,sBAAsB,CAAC;IAC7B,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC;AA6EF,MAAM,MAAM,kBAAkB,CAC5B,WAAW,SAAS,MAAM,GAAG,MAAM,EACnC,SAAS,SAAS,MAAM,GAAG,MAAM,IAC/B;IACF,WAAW,EAAE,WAAW,CAAC;IACzB,cAAc,EAAE,MAAM,CAAC;IACvB,SAAS,EAAE,SAAS,CAAC;CACtB,CAAC;AAEF,eAAO,MAAM,wBAAwB;;;;mBAInC,CAAC;AAEH,eAAO,MAAM,8BAA8B,GACzC,WAAW,SAAS,MAAM,GAAG,MAAM,EACnC,SAAS,SAAS,MAAM,GAAG,MAAM,EAEjC,QAAQ,MAAM,GAAG,wBAAwB,EACzC,UAAU;IACR,SAAS,CAAC,EAAE,SAAS,CAAC;CACvB;;;;mBAYF,CAAC;AAEF,eAAO,MAAM,wBAAwB,GACnC,WAAW,SAAS,MAAM,GAAG,MAAM,EACnC,SAAS,SAAS,MAAM,GAAG,MAAM,EAEjC,QAAQ,MAAM,GAAG,wBAAwB,EACzC,SAAS;IACP,cAAc,EAAE,MAAM,CAAC;IACvB,SAAS,CAAC,EAAE,SAAS,CAAC;CACvB,KACA,kBAAkB,CAAC,WAAW,EAAE,SAAS,CAQ3C,CAAC;AAEF,eAAO,MAAM,6BAA6B,GACxC,QAAQ,MAAM,GAAG,wBAAwB,WACZ,CAAC;AAEhC,eAAO,MAAM,oBAAoB,GAC/B,QAAQ,MAAM,GAAG,wBAAwB,EACzC,YAAY,MAAM,WACoB,CAAC;AAEzC,YAAY,EAAE,wBAAwB,EAAE,CAAC"} \ No newline at end of file diff --git a/dist/src/browserSurfaceShared.js b/dist/src/browserSurfaceShared.js new file mode 100644 index 0000000..7053dbe --- /dev/null +++ b/dist/src/browserSurfaceShared.js @@ -0,0 +1,94 @@ +import z from "zod"; +export const jsonValueSchema = z.lazy(() => z.union([ + z.string(), + z.number(), + z.boolean(), + z.null(), + z.array(jsonValueSchema), + z.record(z.string(), jsonValueSchema), +])); +const browserSurfaceAnnotationSchema = z.object({ + type: z.literal("quixos.ui.surface/v1"), + surfaceId: z.string().min(1), +}); +const normalizePackageName = (value) => { + const normalized = value.startsWith("@quixos-package-schemas/") + ? value.slice("@quixos-package-schemas/".length) + : value; + return normalized.endsWith(".git") + ? normalized.slice(0, -".git".length) + : normalized; +}; +const packageNameFromFlakeRef = (flakeRef) => { + const withoutQuery = flakeRef.split("?")[0] ?? flakeRef; + const withoutGitPrefix = withoutQuery.startsWith("git+") + ? withoutQuery.slice(4) + : withoutQuery; + try { + const parsed = new URL(withoutGitPrefix); + const segments = parsed.pathname.split("/").filter(Boolean); + const packageName = segments.at(-1); + if (packageName) { + return normalizePackageName(packageName); + } + } + catch { + // Fall through to a simple path split for non-URL flake refs. + } + const segments = withoutGitPrefix.split("/").filter(Boolean); + const packageName = segments.at(-1); + if (!packageName) { + throw new Error(`Unable to determine package name for ${flakeRef}`); + } + return normalizePackageName(packageName); +}; +const resolveSchemaPackageName = (schema) => { + if (typeof schema.__quixos?.name === "string" && schema.__quixos.name.length > 0) { + return normalizePackageName(schema.__quixos.name); + } + if (typeof schema.__quixos?.flakeRef === "string" && + schema.__quixos.flakeRef.length > 0) { + return packageNameFromFlakeRef(schema.__quixos.flakeRef); + } + throw new Error("Package schema is missing __quixos metadata. Rebuild the schema with quixos helpers."); +}; +const findDefaultSurfaceId = (schema) => { + const annotations = Array.isArray(schema.annotations) ? schema.annotations : []; + for (const annotation of annotations) { + const parsed = browserSurfaceAnnotationSchema.safeParse(annotation); + if (parsed.success) { + return parsed.data.surfaceId; + } + } + return "desktop"; +}; +const resolvePackageName = (target) => typeof target === "string" + ? normalizePackageName(target) + : resolveSchemaPackageName(target); +const resolveSurfaceId = (target, surfaceId) => surfaceId ?? (typeof target === "string" ? "desktop" : findDefaultSurfaceId(target)); +export const embeddedSurfaceRefSchema = z.object({ + packageName: z.string().min(1), + stateContextId: z.string().min(1), + surfaceId: z.string().min(1).default("desktop"), +}); +export const createEmbeddedSurfaceRefSchema = (target, options) => { + const packageName = resolvePackageName(target); + const surfaceId = resolveSurfaceId(target, options?.surfaceId); + return z.object({ + packageName: z.literal(packageName), + stateContextId: z.string().min(1), + surfaceId: z.string().min(1).default(surfaceId), + }); +}; +export const createEmbeddedSurfaceRef = (target, options) => { + const packageName = resolvePackageName(target); + const surfaceId = resolveSurfaceId(target, options.surfaceId); + return { + packageName, + stateContextId: options.stateContextId, + surfaceId, + }; +}; +export const getEmbeddedSurfacePackageName = (target) => resolvePackageName(target); +export const getEmbeddedSurfaceId = (target, surfaceId) => resolveSurfaceId(target, surfaceId); +//# sourceMappingURL=browserSurfaceShared.js.map \ No newline at end of file diff --git a/dist/src/browserSurfaceShared.js.map b/dist/src/browserSurfaceShared.js.map new file mode 100644 index 0000000..94e0164 --- /dev/null +++ b/dist/src/browserSurfaceShared.js.map @@ -0,0 +1 @@ +{"version":3,"file":"browserSurfaceShared.js","sourceRoot":"","sources":["../../src/browserSurfaceShared.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/dist/src/index.d.ts b/dist/src/index.d.ts new file mode 100644 index 0000000..5891a7a --- /dev/null +++ b/dist/src/index.d.ts @@ -0,0 +1,178 @@ +import z, { type ZodTypeAny, type output as ZodOutput } from "zod"; +import { type RuntimeErrorRequestParams } from "./rpc.js"; +export * from "./rpc.js"; +export type PayloadMode = "snapshot" | "delta"; +export type SchemaAnnotation = { + type: string; + [key: string]: unknown; +}; +export type PackageFunctionSchema = { + description: string; + annotations?: SchemaAnnotation[]; + inputSchema: ZodTypeAny; + outputSchema: ZodTypeAny; +}; +type StaticResourceSchema = { + description: string; + annotations?: SchemaAnnotation[]; + dataSchema: ZodTypeAny; + history?: boolean; + payloadMode?: PayloadMode; +}; +type ParameterizedResourceSchema = StaticResourceSchema & { + paramsSchema: ZodTypeAny; +}; +export type StaticEventSchema = StaticResourceSchema; +export type ParameterizedEventSchema = ParameterizedResourceSchema; +export type EventSchema = StaticEventSchema | ParameterizedEventSchema; +export type EventSchemaMap = Record; +export type StaticValueSchema = StaticResourceSchema; +export type ParameterizedValueSchema = ParameterizedResourceSchema; +export type ValueSchema = StaticValueSchema | ParameterizedValueSchema; +export type ValueSchemaMap = Record; +export type PackageSchema = Record, Events extends EventSchemaMap = EventSchemaMap, Values extends ValueSchemaMap = ValueSchemaMap> = { + schemaVersion: 1; + majorVersion: number; + description: string; + annotations?: SchemaAnnotation[]; + functions: Functions; + events: Events; + values: Values; +}; +export declare const QUIXOS_MEDIA_JSON_SCHEMA_KEY = "x-quixos-media"; +export type QuixosDataUrlAnnotation = { + transport: "data-url"; + mimeType: string; + extension?: string; +}; +export declare const annotateDataUrlSchema: (schema: Schema, params: { + mimeType: string; + extension?: string; +}) => Schema; +export declare const quixosMedia: { + dataUrlString: (params: { + mimeType: string; + extension?: string; + }) => z.ZodString; + annotateDataUrl: (schema: Schema, params: { + mimeType: string; + extension?: string; + }) => Schema; +}; +type FunctionInput = ZodOutput; +type FunctionOutput = ZodOutput; +type SchemaParams = Schema extends { + paramsSchema: infer ParamsSchema extends ZodTypeAny; +} ? ZodOutput : undefined; +type EventParams = SchemaParams; +type EventData = ZodOutput; +type ValueParams = SchemaParams; +type ValueData = ZodOutput; +export type SubscriptionOptions = { + signal?: AbortSignal; + subscriptionNamespace?: string; +}; +export type SubscriptionHandle = { + unsubscribe: () => Promise; +}; +export type UsePackageOptions = { + contextNamespace?: string; + stateContextId?: string; +}; +type ScopedResource = T & { + withStateContext: (stateContextId: string) => T; + withContextNamespace: (contextNamespace?: string) => T; +}; +type EventConsumer = (data: EventData) => void | Promise; +type ValueConsumer = (data: ValueData) => void | Promise; +type StaticEventClientBase = { + consume: (handler: EventConsumer, options?: SubscriptionOptions) => Promise; +}; +type ParameterizedEventClientBase = { + consume: (params: EventParams, handler: EventConsumer, options?: SubscriptionOptions) => Promise; +}; +export type EventClient = Schema extends ParameterizedEventSchema ? ScopedResource> : ScopedResource>; +export type EventClients = { + [Key in keyof Schema]: EventClient; +}; +type StaticValueClientBase = { + get: () => Promise>; + watch: (handler: ValueConsumer, options?: SubscriptionOptions) => Promise; +}; +type ParameterizedValueClientBase = { + get: (params: ValueParams) => Promise>; + watch: (params: ValueParams, handler: ValueConsumer, options?: SubscriptionOptions) => Promise; +}; +export type ValueClient = Schema extends ParameterizedValueSchema ? ScopedResource> : ScopedResource>; +export type ValueClients = { + [Key in keyof Schema]: ValueClient; +}; +export type PackageContext = { + stateContext: string; + stateDirectory: string; + usePackage: (schema: Schema, options?: UsePackageOptions) => PackageClient; +}; +export type PackageFunction = (ctx: Context, params: FunctionInput) => FunctionOutput | Promise>; +export type PackageFunctions, Context = any> = { + [Key in keyof Functions]: PackageFunction; +}; +export type EventSink = { + subscriptionId: string; + emit: (data: EventData) => Promise; +}; +type EventHandler = { + subscribe: (ctx: Context, params: EventParams, sink: EventSink) => void | (() => void | Promise) | Promise void | Promise)>; +}; +export type PackageEvents = { + [Key in keyof Events]: EventHandler; +}; +export type ValueSink = { + subscriptionId: string; + set: (data: ValueData) => Promise; +}; +type ValueHandler = { + get: (ctx: Context, params: ValueParams) => ValueData | Promise>; + watch?: (ctx: Context, params: ValueParams, sink: ValueSink) => void | (() => void | Promise) | Promise void | Promise)>; +}; +export type PackageValues = { + [Key in keyof Values]: ValueHandler; +}; +export type PackageFunctionCaller = ScopedResource<(params: FunctionInput) => Promise>>; +type PackageFunctionCallers> = { + [Key in keyof Functions]: PackageFunctionCaller; +}; +export type PackageClient = { + functions: PackageFunctionCallers; + events: EventClients; + values: ValueClients; + withStateContext: (stateContextId: string) => PackageClient; + withContextNamespace: (contextNamespace?: string) => PackageClient; +}; +export type CreatePackageOptions = { + schema: Schema; + functions: PackageFunctions; + events: PackageEvents; + values: PackageValues; + onCreate?: () => void | Promise; + onContextOpen?: (ctx: Context) => void | Promise; + onContextClose?: (ctx: Context) => void | Promise; + onDestroy?: () => void | Promise; +}; +type LifecycleManager = {}; +export type RuntimeErrorPhase = RuntimeErrorRequestParams["phase"]; +export type RuntimeErrorReportParams = { + phase: RuntimeErrorPhase; + error: unknown; + stateContextId?: string; + functionName?: string; + resourceKind?: "event" | "value"; + resourceName?: string; + surfaceId?: string; + hostSessionId?: string; + stack?: string; +}; +export declare const reportPackageRuntimeError: (params: RuntimeErrorReportParams) => Promise; +export declare const runWithStateContext: (stateContextId: string, fn: () => T | Promise) => Promise; +export declare const publishToSubscription: (subscriptionId: string, data: unknown) => Promise; +export declare const createPackage: (options: CreatePackageOptions) => LifecycleManager; +//# sourceMappingURL=index.d.ts.map \ No newline at end of file diff --git a/dist/src/index.d.ts.map b/dist/src/index.d.ts.map new file mode 100644 index 0000000..1476533 --- /dev/null +++ b/dist/src/index.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/dist/src/index.js b/dist/src/index.js new file mode 100644 index 0000000..dcc97eb --- /dev/null +++ b/dist/src/index.js @@ -0,0 +1,1154 @@ +import { AsyncLocalStorage } from "node:async_hooks"; +import crypto from "node:crypto"; +import fs from "node:fs"; +import os from "node:os"; +import path from "node:path"; +import WebSocket from "ws"; +import z, {} from "zod"; +import { BootRequestParamsSchema, CallRequestParamsSchema, ContextCloseRequestParamsSchema, ContextOpenRequestParamsSchema, EventSubscribeRequestParamsSchema, EventUnsubscribeRequestParamsSchema, ServerMessageSchema, TargetContextOpenRequestParamsSchema, ValueGetRequestParamsSchema, ValueUnwatchRequestParamsSchema, ValueWatchRequestParamsSchema, parseJson, sendClientMessage, } from "./rpc.js"; +export * from "./rpc.js"; +export const QUIXOS_MEDIA_JSON_SCHEMA_KEY = "x-quixos-media"; +const readSchemaMeta = (schema) => { + try { + const meta = schema.meta(); + if (meta && typeof meta === "object") { + return { ...meta }; + } + } + catch { + // ignore + } + return {}; +}; +export const annotateDataUrlSchema = (schema, params) => schema.meta({ + ...readSchemaMeta(schema), + [QUIXOS_MEDIA_JSON_SCHEMA_KEY]: { + transport: "data-url", + mimeType: params.mimeType, + ...(params.extension ? { extension: params.extension } : {}), + }, +}); +export const quixosMedia = { + dataUrlString: (params) => annotateDataUrlSchema(z.string(), params), + annotateDataUrl: annotateDataUrlSchema, +}; +const pendingRequests = new Map(); +const clientSubscriptions = new Map(); +const serverSubscriptions = new Map(); +const runtimeContextStorage = new AsyncLocalStorage(); +const openedRuntimeStateContexts = new Set(); +const DEFAULT_REQUEST_TIMEOUT_MS = 60_000; +const PACKAGE_BOOT_TIMEOUT_MS = 300_000; +const TARGET_CONTEXT_OPEN_TIMEOUT_MS = 300_000; +let socketPromise = null; +let socketResolve = null; +let socketRef = null; +let socketAuthenticated = false; +let packageOptions = null; +let runtimeExitTimer = null; +let runtimeExitRequested = false; +const clearRuntimeExitTimer = () => { + if (runtimeExitTimer) { + clearTimeout(runtimeExitTimer); + runtimeExitTimer = null; + } +}; +const scheduleRuntimeExit = (code, reason) => { + if (runtimeExitRequested) { + return; + } + runtimeExitRequested = true; + clearRuntimeExitTimer(); + runtimeExitTimer = setTimeout(() => { + console.warn(`[quixos-runtime] exiting (${reason})`); + process.exit(code); + }, 100); +}; +const closeServerSubscription = async (subscriptionId) => { + const subscription = serverSubscriptions.get(subscriptionId); + if (!subscription) { + return; + } + serverSubscriptions.delete(subscriptionId); + try { + await subscription.closeRemote(); + } + catch (error) { + await reportPackageRuntimeError({ + phase: subscription.kind === "event" ? "event-cleanup" : "value-cleanup", + error, + stateContextId: subscription.stateContextId, + resourceKind: subscription.kind, + resourceName: subscription.resourceName, + }); + throw error; + } +}; +const normalizeError = (error) => error instanceof Error ? error : new Error(String(error)); +const createRuntimeErrorPayload = (params) => { + const normalized = normalizeError(params.error); + return { + phase: params.phase, + stateContextId: params.stateContextId, + functionName: params.functionName, + resourceKind: params.resourceKind, + resourceName: params.resourceName, + surfaceId: params.surfaceId, + hostSessionId: params.hostSessionId, + message: normalized.message || "Unknown error", + stack: params.stack ?? normalized.stack, + }; +}; +export const reportPackageRuntimeError = async (params) => { + try { + await sendRequest("runtime-error", createRuntimeErrorPayload(params), 5_000); + } + catch { + // Best-effort reporting only. + } +}; +const resetSocket = () => { + const shouldExitForSocketClose = socketAuthenticated && + packageOptions !== null && + Boolean(process.env.QUIXOS_ORCH_SECRET); + socketRef = null; + socketPromise = null; + socketResolve = null; + socketAuthenticated = false; + for (const pending of pendingRequests.values()) { + clearTimeout(pending.timeout); + pending.reject(new Error("Socket closed")); + } + pendingRequests.clear(); + for (const subscription of clientSubscriptions.values()) { + subscription.closeLocal(new Error("Socket closed")); + } + clientSubscriptions.clear(); + const activeServerSubscriptionIds = [...serverSubscriptions.keys()]; + for (const subscriptionId of activeServerSubscriptionIds) { + void closeServerSubscription(subscriptionId).catch(() => { }); + } + if (shouldExitForSocketClose) { + scheduleRuntimeExit(0, "orchestrator-socket-closed"); + } +}; +const getCurrentStateContextId = () => runtimeContextStorage.getStore()?.stateContext; +const getStateDirectoryRoot = () => { + const xdgDataHome = process.env.XDG_DATA_HOME; + if (xdgDataHome && xdgDataHome.length > 0) { + return path.join(xdgDataHome, "quixos", "state-contexts"); + } + return path.join(os.homedir(), ".local", "share", "quixos", "state-contexts"); +}; +const sanitizePackageNameForPath = (packageName) => encodeURIComponent(packageName); +const resolveCurrentPackageName = () => { + const meta = packageOptions?.schema?.__quixos; + if (meta) { + return resolveTargetPackageName(meta); + } + const packageRef = process.env.QUIXOS_PACKAGE_REF; + if (packageRef) { + return normalizePackageName(packageNameFromFlakeRef(packageRef)); + } + throw new Error("Unable to determine current package name for runtime context."); +}; +const getStateDirectoryForContext = (packageName, stateContextId) => path.join(getStateDirectoryRoot(), sanitizePackageNameForPath(packageName), stateContextId); +const withRuntimeContext = async (ctx, fn) => await runtimeContextStorage.run(ctx, fn); +export const runWithStateContext = async (stateContextId, fn) => await withRuntimeContext(createRuntimeContext(stateContextId), fn); +function createRuntimeContext(stateContextId) { + const packageName = resolveCurrentPackageName(); + const stateDirectory = getStateDirectoryForContext(packageName, stateContextId); + fs.mkdirSync(stateDirectory, { recursive: true }); + return { + stateContext: stateContextId, + stateDirectory, + usePackage: (schema, options = {}) => createPackageClient(schema, { + ...options, + callerStateContextId: stateContextId, + }), + }; +} +const closeRuntimeContext = async (stateContextId) => { + const hasOpenedContext = openedRuntimeStateContexts.has(stateContextId); + const activeServerSubscriptionIds = [...serverSubscriptions.entries()] + .filter(([, subscription]) => subscription.stateContextId === stateContextId) + .map(([subscriptionId]) => subscriptionId); + if (!hasOpenedContext && activeServerSubscriptionIds.length === 0) { + return; + } + for (const subscriptionId of activeServerSubscriptionIds) { + await closeServerSubscription(subscriptionId); + } + if (!hasOpenedContext) { + return; + } + const ctx = createRuntimeContext(stateContextId); + try { + await withRuntimeContext(ctx, async () => { + await packageOptions?.onContextClose?.(ctx); + }); + } + finally { + openedRuntimeStateContexts.delete(stateContextId); + } +}; +const hasParamsSchema = (schema) => "paramsSchema" in schema; +const parseResourceParams = (resourceKind, resourceName, schema, params) => { + if (!hasParamsSchema(schema)) { + if (params !== undefined) { + throw new Error(`${resourceKind} ${resourceName} does not take params`); + } + return undefined; + } + const parsed = schema.paramsSchema.safeParse(params); + if (!parsed.success) { + throw new Error(`Invalid params for ${resourceKind} ${resourceName}`); + } + return parsed.data; +}; +const parseResourceData = (resourceKind, resourceName, schema, data) => { + const parsed = schema.dataSchema.safeParse(data); + if (!parsed.success) { + throw new Error(`Invalid data for ${resourceKind} ${resourceName}`); + } + return parsed.data; +}; +const normalizePackageName = (value) => { + const normalized = value.startsWith("@quixos-package-schemas/") + ? value.slice("@quixos-package-schemas/".length) + : value; + return normalized.endsWith(".git") + ? normalized.slice(0, -".git".length) + : normalized; +}; +const packageNameFromFlakeRef = (flakeRef) => { + const withoutQuery = flakeRef.split("?")[0] ?? flakeRef; + const withoutGitPrefix = withoutQuery.startsWith("git+") + ? withoutQuery.slice(4) + : withoutQuery; + try { + const parsed = new URL(withoutGitPrefix); + const segments = parsed.pathname.split("/").filter(Boolean); + const packageName = segments.at(-1); + if (packageName) { + return normalizePackageName(packageName); + } + } + catch { + // Fall through to a simple path split for non-URL flake refs. + } + const segments = withoutGitPrefix.split("/").filter(Boolean); + const packageName = segments.at(-1); + if (!packageName) { + throw new Error(`Unable to determine package name for ${flakeRef}`); + } + return normalizePackageName(packageName); +}; +const resolveTargetPackageName = (meta) => { + if (meta.name) { + return normalizePackageName(meta.name); + } + if (meta.flakeRef) { + return packageNameFromFlakeRef(meta.flakeRef); + } + throw new Error("Package schema is missing package name metadata."); +}; +const sendErrorResponse = (socket, requestId, error) => { + const normalized = normalizeError(error); + sendClientMessage(socket, { + type: "response", + requestId, + ok: false, + error: normalized.message || "Unknown error", + }); +}; +const reportAndSendErrorResponse = async (socket, requestId, params) => { + void reportPackageRuntimeError(params); + sendErrorResponse(socket, requestId, params.error); +}; +const handleRequest = async (socket, message) => { + if (!packageOptions) { + sendClientMessage(socket, { + type: "response", + requestId: message.requestId, + ok: false, + error: "Package not initialized", + }); + return; + } + if (message.method === "stop") { + try { + const activeStateContextIds = [...openedRuntimeStateContexts]; + for (const stateContextId of activeStateContextIds) { + await closeRuntimeContext(stateContextId); + } + await packageOptions.onDestroy?.(); + sendClientMessage(socket, { + type: "response", + requestId: message.requestId, + ok: true, + }); + scheduleRuntimeExit(0, "stop-request"); + } + catch (error) { + await reportAndSendErrorResponse(socket, message.requestId, { + phase: "on-destroy", + error, + }); + } + return; + } + if (message.method === "context-open") { + const parsedParams = ContextOpenRequestParamsSchema.safeParse(message.params); + if (!parsedParams.success) { + sendClientMessage(socket, { + type: "response", + requestId: message.requestId, + ok: false, + error: "Invalid context open params", + }); + return; + } + const ctx = createRuntimeContext(parsedParams.data.stateContextId); + try { + await withRuntimeContext(ctx, async () => { + await packageOptions?.onContextOpen?.(ctx); + }); + openedRuntimeStateContexts.add(ctx.stateContext); + sendClientMessage(socket, { + type: "response", + requestId: message.requestId, + ok: true, + }); + } + catch (error) { + await reportAndSendErrorResponse(socket, message.requestId, { + phase: "on-context-open", + error, + stateContextId: ctx.stateContext, + }); + } + return; + } + if (message.method === "context-close") { + const parsedParams = ContextCloseRequestParamsSchema.safeParse(message.params); + if (!parsedParams.success) { + sendClientMessage(socket, { + type: "response", + requestId: message.requestId, + ok: false, + error: "Invalid context close params", + }); + return; + } + try { + await closeRuntimeContext(parsedParams.data.stateContextId); + sendClientMessage(socket, { + type: "response", + requestId: message.requestId, + ok: true, + }); + } + catch (error) { + await reportAndSendErrorResponse(socket, message.requestId, { + phase: "on-context-close", + error, + stateContextId: parsedParams.data.stateContextId, + }); + } + return; + } + if (message.method === "call") { + const parsedParams = CallRequestParamsSchema.safeParse(message.params); + if (!parsedParams.success) { + sendClientMessage(socket, { + type: "response", + requestId: message.requestId, + ok: false, + error: "Invalid call params", + }); + return; + } + const { functionName, params, stateContextId } = parsedParams.data; + if (!stateContextId) { + sendClientMessage(socket, { + type: "response", + requestId: message.requestId, + ok: false, + error: "Missing state context", + }); + return; + } + const fn = packageOptions.functions?.[functionName]; + if (!fn) { + sendClientMessage(socket, { + type: "response", + requestId: message.requestId, + ok: false, + error: `Unknown function: ${functionName}`, + }); + return; + } + const ctx = createRuntimeContext(stateContextId); + try { + const result = await withRuntimeContext(ctx, async () => await fn(ctx, params)); + sendClientMessage(socket, { + type: "response", + requestId: message.requestId, + ok: true, + result, + }); + } + catch (error) { + await reportAndSendErrorResponse(socket, message.requestId, { + phase: "function", + error, + stateContextId, + functionName, + }); + } + return; + } + if (message.method === "boot") { + const parsedParams = BootRequestParamsSchema.safeParse(message.params); + if (!parsedParams.success) { + sendClientMessage(socket, { + type: "response", + requestId: message.requestId, + ok: false, + error: "Invalid boot params", + }); + return; + } + sendClientMessage(socket, { + type: "response", + requestId: message.requestId, + ok: true, + }); + return; + } + if (message.method === "event-subscribe") { + const parsedParams = EventSubscribeRequestParamsSchema.safeParse(message.params); + if (!parsedParams.success) { + sendClientMessage(socket, { + type: "response", + requestId: message.requestId, + ok: false, + error: "Invalid event subscribe params", + }); + return; + } + const { eventName, params, stateContextId } = parsedParams.data; + if (!stateContextId) { + sendClientMessage(socket, { + type: "response", + requestId: message.requestId, + ok: false, + error: "Missing state context", + }); + return; + } + const eventSchema = packageOptions.schema.events?.[eventName]; + const eventHandler = packageOptions.events?.[eventName]; + if (!eventSchema || !eventHandler) { + sendClientMessage(socket, { + type: "response", + requestId: message.requestId, + ok: false, + error: `Unknown event: ${eventName}`, + }); + return; + } + const ctx = createRuntimeContext(stateContextId); + const subscriptionId = crypto.randomUUID(); + try { + const parsedResourceParams = parseResourceParams("event", eventName, eventSchema, params); + const cleanup = await withRuntimeContext(ctx, async () => await eventHandler.subscribe(ctx, parsedResourceParams, { + subscriptionId, + emit: async (data) => { + const parsedData = parseResourceData("event", eventName, eventSchema, data); + await publishToSubscription(subscriptionId, parsedData); + }, + })); + serverSubscriptions.set(subscriptionId, { + stateContextId: ctx.stateContext, + kind: "event", + resourceName: eventName, + closeRemote: async () => { + if (typeof cleanup === "function") { + await runWithStateContext(ctx.stateContext, cleanup); + } + }, + }); + sendClientMessage(socket, { + type: "response", + requestId: message.requestId, + ok: true, + result: { + subscriptionId, + }, + }); + } + catch (error) { + await reportAndSendErrorResponse(socket, message.requestId, { + phase: "event-subscribe", + error, + stateContextId, + resourceKind: "event", + resourceName: eventName, + }); + } + return; + } + if (message.method === "event-unsubscribe") { + const parsedParams = EventUnsubscribeRequestParamsSchema.safeParse(message.params); + if (!parsedParams.success) { + sendClientMessage(socket, { + type: "response", + requestId: message.requestId, + ok: false, + error: "Invalid event unsubscribe params", + }); + return; + } + try { + await closeServerSubscription(parsedParams.data.subscriptionId); + sendClientMessage(socket, { + type: "response", + requestId: message.requestId, + ok: true, + }); + } + catch (error) { + sendErrorResponse(socket, message.requestId, error); + } + return; + } + if (message.method === "value-get") { + const parsedParams = ValueGetRequestParamsSchema.safeParse(message.params); + if (!parsedParams.success) { + sendClientMessage(socket, { + type: "response", + requestId: message.requestId, + ok: false, + error: "Invalid value get params", + }); + return; + } + const { valueName, params, stateContextId } = parsedParams.data; + if (!stateContextId) { + sendClientMessage(socket, { + type: "response", + requestId: message.requestId, + ok: false, + error: "Missing state context", + }); + return; + } + const valueSchema = packageOptions.schema.values?.[valueName]; + const valueHandler = packageOptions.values?.[valueName]; + if (!valueSchema || !valueHandler) { + sendClientMessage(socket, { + type: "response", + requestId: message.requestId, + ok: false, + error: `Unknown value: ${valueName}`, + }); + return; + } + const ctx = createRuntimeContext(stateContextId); + try { + const parsedResourceParams = parseResourceParams("value", valueName, valueSchema, params); + const result = await withRuntimeContext(ctx, async () => await valueHandler.get(ctx, parsedResourceParams)); + const parsedResult = parseResourceData("value", valueName, valueSchema, result); + sendClientMessage(socket, { + type: "response", + requestId: message.requestId, + ok: true, + result: parsedResult, + }); + } + catch (error) { + await reportAndSendErrorResponse(socket, message.requestId, { + phase: "value-get", + error, + stateContextId, + resourceKind: "value", + resourceName: valueName, + }); + } + return; + } + if (message.method === "value-watch") { + const parsedParams = ValueWatchRequestParamsSchema.safeParse(message.params); + if (!parsedParams.success) { + sendClientMessage(socket, { + type: "response", + requestId: message.requestId, + ok: false, + error: "Invalid value watch params", + }); + return; + } + const { valueName, params, stateContextId } = parsedParams.data; + if (!stateContextId) { + sendClientMessage(socket, { + type: "response", + requestId: message.requestId, + ok: false, + error: "Missing state context", + }); + return; + } + const valueSchema = packageOptions.schema.values?.[valueName]; + const valueHandler = packageOptions.values?.[valueName]; + if (!valueSchema || !valueHandler?.watch) { + sendClientMessage(socket, { + type: "response", + requestId: message.requestId, + ok: false, + error: `Unknown watchable value: ${valueName}`, + }); + return; + } + const ctx = createRuntimeContext(stateContextId); + const subscriptionId = crypto.randomUUID(); + try { + const parsedResourceParams = parseResourceParams("value", valueName, valueSchema, params); + const cleanup = await withRuntimeContext(ctx, async () => await valueHandler.watch?.(ctx, parsedResourceParams, { + subscriptionId, + set: async (data) => { + const parsedData = parseResourceData("value", valueName, valueSchema, data); + await publishToSubscription(subscriptionId, parsedData); + }, + })); + serverSubscriptions.set(subscriptionId, { + stateContextId: ctx.stateContext, + kind: "value", + resourceName: valueName, + closeRemote: async () => { + if (typeof cleanup === "function") { + await runWithStateContext(ctx.stateContext, cleanup); + } + }, + }); + sendClientMessage(socket, { + type: "response", + requestId: message.requestId, + ok: true, + result: { + subscriptionId, + }, + }); + } + catch (error) { + await reportAndSendErrorResponse(socket, message.requestId, { + phase: "value-watch", + error, + stateContextId, + resourceKind: "value", + resourceName: valueName, + }); + } + return; + } + if (message.method === "value-unwatch") { + const parsedParams = ValueUnwatchRequestParamsSchema.safeParse(message.params); + if (!parsedParams.success) { + sendClientMessage(socket, { + type: "response", + requestId: message.requestId, + ok: false, + error: "Invalid value unwatch params", + }); + return; + } + try { + await closeServerSubscription(parsedParams.data.subscriptionId); + sendClientMessage(socket, { + type: "response", + requestId: message.requestId, + ok: true, + }); + } + catch (error) { + sendErrorResponse(socket, message.requestId, error); + } + return; + } + sendClientMessage(socket, { + type: "response", + requestId: message.requestId, + ok: false, + error: "Unknown method", + }); +}; +const handleMessage = (socket, data) => { + const parsed = parseJson(data.toString()); + if (!parsed.ok) { + return; + } + const messageResult = ServerMessageSchema.safeParse(parsed.value); + if (!messageResult.success) { + return; + } + const message = messageResult.data; + if (message.type === "auth-ack") { + socketAuthenticated = true; + runtimeExitRequested = false; + clearRuntimeExitTimer(); + if (socketResolve) { + socketResolve(socket); + socketResolve = null; + } + return; + } + if (message.type === "subscription-data") { + const subscription = clientSubscriptions.get(message.subscriptionId); + if (!subscription) { + return; + } + subscription.deliver(message.data); + return; + } + if (message.type === "response") { + const pending = pendingRequests.get(message.requestId); + if (!pending) { + return; + } + pendingRequests.delete(message.requestId); + clearTimeout(pending.timeout); + if (message.ok === false) { + pending.reject(new Error(message.error ?? "Unknown error")); + } + else { + pending.resolve(message.result); + } + return; + } + if (message.type === "request") { + void handleRequest(socket, message); + } +}; +const ensureSocket = async (requireAuth) => { + if (requireAuth && !process.env.QUIXOS_ORCH_SECRET) { + throw new Error("QUIXOS_ORCH_SECRET is not set; cannot use orchestrator"); + } + if (!socketPromise) { + socketPromise = new Promise((resolve) => { + socketResolve = resolve; + }); + const socket = new WebSocket("ws://127.0.0.1:6245"); + socketRef = socket; + socket.on("open", () => { + const secret = process.env.QUIXOS_ORCH_SECRET; + if (secret) { + sendClientMessage(socket, { + type: "auth", + secret, + pid: process.pid, + }); + } + else if (socketResolve) { + socketResolve(socket); + socketResolve = null; + } + }); + socket.on("message", (eventData) => handleMessage(socket, eventData)); + socket.on("close", resetSocket); + socket.on("error", resetSocket); + } + const socket = await socketPromise; + if (requireAuth && !socketAuthenticated) { + return await socketPromise; + } + return socket; +}; +const sendRequest = async (method, params, timeoutMs) => { + const socket = await ensureSocket(true); + const requestId = crypto.randomUUID(); + const effectiveTimeoutMs = timeoutMs ?? + (method === "boot" + ? PACKAGE_BOOT_TIMEOUT_MS + : method === "target-context-open" || method === "context-open" + ? TARGET_CONTEXT_OPEN_TIMEOUT_MS + : DEFAULT_REQUEST_TIMEOUT_MS); + return await new Promise((resolve, reject) => { + const timeout = setTimeout(() => { + pendingRequests.delete(requestId); + reject(new Error("Request timed out")); + }, effectiveTimeoutMs); + pendingRequests.set(requestId, { resolve, reject, timeout }); + sendClientMessage(socket, { + type: "request", + requestId, + method, + params, + }); + }); +}; +export const publishToSubscription = async (subscriptionId, data) => { + await sendRequest("subscription-publish", { + subscriptionId, + data, + }); +}; +const createPackageClient = (schema, baseOptions = {}) => { + const meta = schema.__quixos; + if (!meta?.flakeRef) { + throw new Error("Package schema is missing __quixos metadata. Rebuild the schema with quixos helpers."); + } + const targetPackageName = resolveTargetPackageName(meta); + let bootError = null; + let bootPromise = null; + const ensureBooted = async () => { + if (!bootPromise) { + bootPromise = (async () => { + try { + await sendRequest("boot", { + target: meta.flakeRef, + }); + } + catch (error) { + bootError = error instanceof Error ? error : new Error(String(error)); + } + })(); + } + await bootPromise; + if (bootError) { + throw bootError; + } + }; + const createClient = (scope) => { + let readyError = null; + let readyPromise = null; + const resolveRouting = (callerStateContextId) => ({ + callerStateContextId: scope.callerStateContextId ?? callerStateContextId, + targetStateContextId: scope.stateContextId, + contextNamespace: scope.stateContextId ? undefined : scope.contextNamespace, + }); + const missingStateContextError = () => new Error(`No active state context for package ${targetPackageName}. Move this call into \`onContextOpen(ctx)\` or another handler with \`ctx.usePackage(...)\`, or pass \`stateContextId\` explicitly.`); + const ensurePackageReady = async (callerStateContextId) => { + if (!readyPromise) { + readyPromise = (async () => { + try { + await ensureBooted(); + const routing = resolveRouting(callerStateContextId); + if (!routing.callerStateContextId && !routing.targetStateContextId) { + return; + } + await sendRequest("target-context-open", { + target: meta.flakeRef, + targetPackageName, + callerStateContextId: routing.callerStateContextId, + stateContextId: routing.targetStateContextId, + contextNamespace: routing.contextNamespace, + }); + } + catch (error) { + readyError = + error instanceof Error ? error : new Error(String(error)); + } + })(); + } + await readyPromise; + if (readyError) { + throw readyError; + } + }; + const scopedResourceMethods = (lookup) => ({ + withStateContext: (stateContextId) => lookup(createClient({ + ...scope, + stateContextId, + })), + withContextNamespace: (contextNamespace) => lookup(createClient({ + ...scope, + contextNamespace, + })), + }); + const createManagedSubscription = (subscriptionId, resourceKind, resourceName, schemaType, handler, unsubscribeMethod, subscriberStateContextId, options) => { + let closed = false; + let abortHandler = null; + let deliveryChain = Promise.resolve(); + if (clientSubscriptions.has(subscriptionId)) { + throw new Error(`Subscription ${subscriptionId} is already attached. Pass \`subscriptionNamespace\` to create a distinct logical subscription.`); + } + const closeLocal = (_error) => { + if (closed) { + return; + } + closed = true; + clientSubscriptions.delete(subscriptionId); + if (options?.signal && abortHandler) { + options.signal.removeEventListener("abort", abortHandler); + } + }; + const sendUnsubscribe = async () => { + try { + await sendRequest(unsubscribeMethod, { + subscriptionId, + }); + } + catch { + // Ignore unsubscribe errors; local close already happened. + } + }; + const unsubscribe = async () => { + if (closed) { + return; + } + closeLocal(); + await sendUnsubscribe(); + }; + const fail = (error) => { + closeLocal(error); + void sendUnsubscribe(); + }; + const deliver = (data) => { + if (closed) { + return; + } + let parsedData; + try { + parsedData = parseResourceData(resourceKind, resourceName, schemaType, data); + } + catch (error) { + fail(error instanceof Error ? error : new Error(String(error))); + return; + } + deliveryChain = deliveryChain + .then(async () => { + if (closed) { + return; + } + const invoke = async () => { + await handler(parsedData); + }; + if (subscriberStateContextId) { + await runWithStateContext(subscriberStateContextId, invoke); + return; + } + await invoke(); + }) + .catch((error) => { + fail(error instanceof Error ? error : new Error(String(error))); + }); + }; + clientSubscriptions.set(subscriptionId, { + deliver, + closeLocal, + }); + if (options?.signal) { + abortHandler = () => { + void unsubscribe(); + }; + if (options.signal.aborted) { + void unsubscribe(); + } + else { + options.signal.addEventListener("abort", abortHandler); + } + } + return { + unsubscribe, + }; + }; + const consumeEvent = async (eventName, eventSchema, params, handler, options) => { + const callerStateContextId = getCurrentStateContextId(); + await ensurePackageReady(callerStateContextId); + const routing = resolveRouting(callerStateContextId); + if (!routing.callerStateContextId && !routing.targetStateContextId) { + throw missingStateContextError(); + } + const result = await sendRequest("event-subscribe", { + target: meta.flakeRef, + targetPackageName, + eventName, + params, + callerStateContextId: routing.callerStateContextId, + stateContextId: routing.targetStateContextId, + contextNamespace: routing.contextNamespace, + subscriptionNamespace: options?.subscriptionNamespace, + }); + const subscriptionId = result + ?.subscriptionId; + if (!subscriptionId) { + throw new Error(`Subscription failed for event ${eventName}`); + } + const handle = createManagedSubscription(subscriptionId, "event", eventName, eventSchema, handler, "event-unsubscribe", callerStateContextId, options); + try { + await sendRequest("subscription-ready", { + subscriptionId, + }); + } + catch (error) { + await handle.unsubscribe(); + throw error; + } + return handle; + }; + const getValue = async (valueName, valueSchema, params) => { + const callerStateContextId = getCurrentStateContextId(); + await ensurePackageReady(callerStateContextId); + const routing = resolveRouting(callerStateContextId); + if (!routing.callerStateContextId && !routing.targetStateContextId) { + throw missingStateContextError(); + } + const result = await sendRequest("value-get", { + target: meta.flakeRef, + targetPackageName, + valueName, + params, + callerStateContextId: routing.callerStateContextId, + stateContextId: routing.targetStateContextId, + contextNamespace: routing.contextNamespace, + }); + return parseResourceData("value", valueName, valueSchema, result); + }; + const watchValue = async (valueName, valueSchema, params, handler, options) => { + const callerStateContextId = getCurrentStateContextId(); + await ensurePackageReady(callerStateContextId); + const routing = resolveRouting(callerStateContextId); + if (!routing.callerStateContextId && !routing.targetStateContextId) { + throw missingStateContextError(); + } + const result = await sendRequest("value-watch", { + target: meta.flakeRef, + targetPackageName, + valueName, + params, + callerStateContextId: routing.callerStateContextId, + stateContextId: routing.targetStateContextId, + contextNamespace: routing.contextNamespace, + subscriptionNamespace: options?.subscriptionNamespace, + }); + const subscriptionId = result + ?.subscriptionId; + if (!subscriptionId) { + throw new Error(`Watch failed for value ${valueName}`); + } + const handle = createManagedSubscription(subscriptionId, "value", valueName, valueSchema, handler, "value-unwatch", callerStateContextId, options); + try { + await sendRequest("subscription-ready", { + subscriptionId, + }); + } + catch (error) { + await handle.unsubscribe(); + throw error; + } + return handle; + }; + const functions = new Proxy({}, { + get: (_target, prop) => { + if (typeof prop !== "string") { + return undefined; + } + const call = async (params) => { + const callerStateContextId = getCurrentStateContextId(); + await ensurePackageReady(callerStateContextId); + const routing = resolveRouting(callerStateContextId); + if (!routing.callerStateContextId && !routing.targetStateContextId) { + throw missingStateContextError(); + } + return await sendRequest("call", { + target: meta.flakeRef, + targetPackageName, + functionName: prop, + params, + callerStateContextId: routing.callerStateContextId, + stateContextId: routing.targetStateContextId, + contextNamespace: routing.contextNamespace, + }); + }; + return Object.assign(call, scopedResourceMethods((client) => client.functions[prop])); + }, + }); + const events = {}; + const eventEntries = Object.entries((schema.events ?? {})); + for (const [eventName, eventSchema] of eventEntries) { + if (hasParamsSchema(eventSchema)) { + events[eventName] = Object.assign({ + consume: async (params, handler, options) => { + const parsedParams = eventSchema.paramsSchema.safeParse(params); + if (!parsedParams.success) { + throw new Error(`Invalid params for event ${eventName}`); + } + return await consumeEvent(eventName, eventSchema, parsedParams.data, handler, options); + }, + }, scopedResourceMethods((client) => client.events[eventName])); + } + else { + events[eventName] = Object.assign({ + consume: async (handler, options) => await consumeEvent(eventName, eventSchema, undefined, handler, options), + }, scopedResourceMethods((client) => client.events[eventName])); + } + } + const values = {}; + const valueEntries = Object.entries((schema.values ?? {})); + for (const [valueName, valueSchema] of valueEntries) { + if (hasParamsSchema(valueSchema)) { + values[valueName] = Object.assign({ + get: async (params) => { + const parsedParams = valueSchema.paramsSchema.safeParse(params); + if (!parsedParams.success) { + throw new Error(`Invalid params for value ${valueName}`); + } + return await getValue(valueName, valueSchema, parsedParams.data); + }, + watch: async (params, handler, options) => { + const parsedParams = valueSchema.paramsSchema.safeParse(params); + if (!parsedParams.success) { + throw new Error(`Invalid params for value ${valueName}`); + } + return await watchValue(valueName, valueSchema, parsedParams.data, handler, options); + }, + }, scopedResourceMethods((client) => client.values[valueName])); + } + else { + values[valueName] = Object.assign({ + get: async () => await getValue(valueName, valueSchema, undefined), + watch: async (handler, options) => await watchValue(valueName, valueSchema, undefined, handler, options), + }, scopedResourceMethods((client) => client.values[valueName])); + } + } + const eagerCallerStateContextId = scope.callerStateContextId ?? getCurrentStateContextId(); + if (eagerCallerStateContextId || scope.stateContextId) { + void ensurePackageReady(eagerCallerStateContextId).catch((error) => { + console.warn(`Failed to eagerly open child context for ${targetPackageName}: ${error instanceof Error ? error.message : String(error)}`); + }); + } + return { + functions, + events, + values, + withStateContext: (stateContextId) => createClient({ + ...scope, + stateContextId, + }), + withContextNamespace: (contextNamespace) => createClient({ + ...scope, + contextNamespace, + }), + }; + }; + return createClient(baseOptions); +}; +export const createPackage = (options) => { + packageOptions = options; + void ensureSocket(false); + void (async () => { + try { + await options.onCreate?.(); + } + catch (error) { + await reportPackageRuntimeError({ + phase: "on-create", + error, + }); + console.error("[quixos-runtime] onCreate failed:", normalizeError(error).stack ?? normalizeError(error).message); + } + })(); + return {}; +}; +//# sourceMappingURL=index.js.map \ No newline at end of file diff --git a/dist/src/index.js.map b/dist/src/index.js.map new file mode 100644 index 0000000..f8a3994 --- /dev/null +++ b/dist/src/index.js.map @@ -0,0 +1 @@ +{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/dist/src/rpc.d.ts b/dist/src/rpc.d.ts new file mode 100644 index 0000000..2dde93b --- /dev/null +++ b/dist/src/rpc.d.ts @@ -0,0 +1,280 @@ +import z from "zod"; +export declare const AuthMessageSchema: z.ZodObject<{ + type: z.ZodLiteral<"auth">; + secret: z.ZodString; + pid: z.ZodOptional; +}, z.z.core.$strip>; +export declare const AuthAckMessageSchema: z.ZodObject<{ + type: z.ZodLiteral<"auth-ack">; +}, z.z.core.$strip>; +export declare const RpcMethodSchema: z.ZodEnum<{ + stop: "stop"; + call: "call"; + boot: "boot"; + "target-context-open": "target-context-open"; + "runtime-error": "runtime-error"; + "context-open": "context-open"; + "context-close": "context-close"; + "event-subscribe": "event-subscribe"; + "subscription-ready": "subscription-ready"; + "event-unsubscribe": "event-unsubscribe"; + "value-get": "value-get"; + "value-watch": "value-watch"; + "value-unwatch": "value-unwatch"; + "subscription-publish": "subscription-publish"; +}>; +export declare const RequestMessageSchema: z.ZodObject<{ + type: z.ZodLiteral<"request">; + requestId: z.ZodString; + method: z.ZodEnum<{ + stop: "stop"; + call: "call"; + boot: "boot"; + "target-context-open": "target-context-open"; + "runtime-error": "runtime-error"; + "context-open": "context-open"; + "context-close": "context-close"; + "event-subscribe": "event-subscribe"; + "subscription-ready": "subscription-ready"; + "event-unsubscribe": "event-unsubscribe"; + "value-get": "value-get"; + "value-watch": "value-watch"; + "value-unwatch": "value-unwatch"; + "subscription-publish": "subscription-publish"; + }>; + params: z.ZodRecord; +}, z.z.core.$strip>; +export declare const CallRequestParamsSchema: z.ZodObject<{ + functionName: z.ZodString; + params: z.ZodUnknown; + target: z.ZodOptional; + targetPackageName: z.ZodOptional; + callerStateContextId: z.ZodOptional; + stateContextId: z.ZodOptional; + contextNamespace: z.ZodOptional; +}, z.z.core.$strip>; +export declare const BootRequestParamsSchema: z.ZodObject<{ + target: z.ZodString; +}, z.z.core.$strip>; +export declare const TargetContextOpenRequestParamsSchema: z.ZodObject<{ + target: z.ZodString; + targetPackageName: z.ZodOptional; + callerStateContextId: z.ZodOptional; + stateContextId: z.ZodOptional; + contextNamespace: z.ZodOptional; +}, z.z.core.$strip>; +export declare const RuntimeErrorRequestParamsSchema: z.ZodObject<{ + phase: z.ZodEnum<{ + function: "function"; + "event-subscribe": "event-subscribe"; + "value-get": "value-get"; + "value-watch": "value-watch"; + "subscription-publish": "subscription-publish"; + "on-create": "on-create"; + "on-destroy": "on-destroy"; + "on-context-open": "on-context-open"; + "on-context-close": "on-context-close"; + "browser-surface": "browser-surface"; + "event-cleanup": "event-cleanup"; + "value-cleanup": "value-cleanup"; + internal: "internal"; + }>; + stateContextId: z.ZodOptional; + functionName: z.ZodOptional; + resourceKind: z.ZodOptional>; + resourceName: z.ZodOptional; + surfaceId: z.ZodOptional; + hostSessionId: z.ZodOptional; + message: z.ZodString; + stack: z.ZodOptional; +}, z.z.core.$strip>; +export declare const ContextOpenRequestParamsSchema: z.ZodObject<{ + stateContextId: z.ZodString; +}, z.z.core.$strip>; +export declare const ContextCloseRequestParamsSchema: z.ZodObject<{ + stateContextId: z.ZodString; +}, z.z.core.$strip>; +export declare const EventSubscribeRequestParamsSchema: z.ZodObject<{ + eventName: z.ZodString; + params: z.ZodOptional; + subscriptionNamespace: z.ZodOptional; + target: z.ZodOptional; + targetPackageName: z.ZodOptional; + callerStateContextId: z.ZodOptional; + stateContextId: z.ZodOptional; + contextNamespace: z.ZodOptional; +}, z.z.core.$strip>; +export declare const EventUnsubscribeRequestParamsSchema: z.ZodObject<{ + subscriptionId: z.ZodString; +}, z.z.core.$strip>; +export declare const SubscriptionReadyRequestParamsSchema: z.ZodObject<{ + subscriptionId: z.ZodString; +}, z.z.core.$strip>; +export declare const ValueGetRequestParamsSchema: z.ZodObject<{ + valueName: z.ZodString; + params: z.ZodOptional; + target: z.ZodOptional; + targetPackageName: z.ZodOptional; + callerStateContextId: z.ZodOptional; + stateContextId: z.ZodOptional; + contextNamespace: z.ZodOptional; +}, z.z.core.$strip>; +export declare const ValueWatchRequestParamsSchema: z.ZodObject<{ + valueName: z.ZodString; + params: z.ZodOptional; + subscriptionNamespace: z.ZodOptional; + target: z.ZodOptional; + targetPackageName: z.ZodOptional; + callerStateContextId: z.ZodOptional; + stateContextId: z.ZodOptional; + contextNamespace: z.ZodOptional; +}, z.z.core.$strip>; +export declare const ValueUnwatchRequestParamsSchema: z.ZodObject<{ + subscriptionId: z.ZodString; +}, z.z.core.$strip>; +export declare const SubscriptionPublishRequestParamsSchema: z.ZodObject<{ + subscriptionId: z.ZodString; + data: z.ZodUnknown; +}, z.z.core.$strip>; +export declare const SubscriptionResponseSchema: z.ZodObject<{ + subscriptionId: z.ZodString; +}, z.z.core.$strip>; +export declare const SubscriptionDataMessageSchema: z.ZodObject<{ + type: z.ZodLiteral<"subscription-data">; + subscriptionId: z.ZodString; + data: z.ZodUnknown; +}, z.z.core.$strip>; +export declare const ResponseOkMessageSchema: z.ZodObject<{ + type: z.ZodLiteral<"response">; + requestId: z.ZodString; + ok: z.ZodLiteral; + result: z.ZodOptional; +}, z.z.core.$strip>; +export declare const ResponseErrorMessageSchema: z.ZodObject<{ + type: z.ZodLiteral<"response">; + requestId: z.ZodString; + ok: z.ZodLiteral; + error: z.ZodString; +}, z.z.core.$strip>; +export declare const ResponseMessageSchema: z.ZodUnion; + requestId: z.ZodString; + ok: z.ZodLiteral; + result: z.ZodOptional; +}, z.z.core.$strip>, z.ZodObject<{ + type: z.ZodLiteral<"response">; + requestId: z.ZodString; + ok: z.ZodLiteral; + error: z.ZodString; +}, z.z.core.$strip>]>; +export declare const ServerMessageSchema: z.ZodUnion; +}, z.z.core.$strip>, z.ZodObject<{ + type: z.ZodLiteral<"request">; + requestId: z.ZodString; + method: z.ZodEnum<{ + stop: "stop"; + call: "call"; + boot: "boot"; + "target-context-open": "target-context-open"; + "runtime-error": "runtime-error"; + "context-open": "context-open"; + "context-close": "context-close"; + "event-subscribe": "event-subscribe"; + "subscription-ready": "subscription-ready"; + "event-unsubscribe": "event-unsubscribe"; + "value-get": "value-get"; + "value-watch": "value-watch"; + "value-unwatch": "value-unwatch"; + "subscription-publish": "subscription-publish"; + }>; + params: z.ZodRecord; +}, z.z.core.$strip>, z.ZodUnion; + requestId: z.ZodString; + ok: z.ZodLiteral; + result: z.ZodOptional; +}, z.z.core.$strip>, z.ZodObject<{ + type: z.ZodLiteral<"response">; + requestId: z.ZodString; + ok: z.ZodLiteral; + error: z.ZodString; +}, z.z.core.$strip>]>, z.ZodObject<{ + type: z.ZodLiteral<"subscription-data">; + subscriptionId: z.ZodString; + data: z.ZodUnknown; +}, z.z.core.$strip>]>; +export declare const ClientMessageSchema: z.ZodUnion; + secret: z.ZodString; + pid: z.ZodOptional; +}, z.z.core.$strip>, z.ZodUnion; + requestId: z.ZodString; + ok: z.ZodLiteral; + result: z.ZodOptional; +}, z.z.core.$strip>, z.ZodObject<{ + type: z.ZodLiteral<"response">; + requestId: z.ZodString; + ok: z.ZodLiteral; + error: z.ZodString; +}, z.z.core.$strip>]>, z.ZodObject<{ + type: z.ZodLiteral<"request">; + requestId: z.ZodString; + method: z.ZodEnum<{ + stop: "stop"; + call: "call"; + boot: "boot"; + "target-context-open": "target-context-open"; + "runtime-error": "runtime-error"; + "context-open": "context-open"; + "context-close": "context-close"; + "event-subscribe": "event-subscribe"; + "subscription-ready": "subscription-ready"; + "event-unsubscribe": "event-unsubscribe"; + "value-get": "value-get"; + "value-watch": "value-watch"; + "value-unwatch": "value-unwatch"; + "subscription-publish": "subscription-publish"; + }>; + params: z.ZodRecord; +}, z.z.core.$strip>]>; +export type AuthMessage = z.infer; +export type AuthAckMessage = z.infer; +export type RequestMessage = z.infer; +export type CallRequestParams = z.infer; +export type BootRequestParams = z.infer; +export type TargetContextOpenRequestParams = z.infer; +export type RuntimeErrorRequestParams = z.infer; +export type ContextOpenRequestParams = z.infer; +export type ContextCloseRequestParams = z.infer; +export type EventSubscribeRequestParams = z.infer; +export type EventUnsubscribeRequestParams = z.infer; +export type SubscriptionReadyRequestParams = z.infer; +export type ValueGetRequestParams = z.infer; +export type ValueWatchRequestParams = z.infer; +export type ValueUnwatchRequestParams = z.infer; +export type SubscriptionPublishRequestParams = z.infer; +export type SubscriptionResponse = z.infer; +export type SubscriptionDataMessage = z.infer; +export type ResponseMessage = z.infer; +export type ClientMessage = z.infer; +export type ServerMessage = z.infer; +type SocketLike = { + send: (data: string) => void; +}; +export declare const sendMessage: (socket: SocketLike, message: Message) => void; +export declare const sendClientMessage: (socket: SocketLike, message: ClientMessage) => void; +export declare const sendServerMessage: (socket: SocketLike, message: ServerMessage) => void; +export declare const parseJson: (value: string) => { + ok: true; + value: any; +} | { + ok: false; + value?: undefined; +}; +export {}; +//# sourceMappingURL=rpc.d.ts.map \ No newline at end of file diff --git a/dist/src/rpc.d.ts.map b/dist/src/rpc.d.ts.map new file mode 100644 index 0000000..c9f2f8a --- /dev/null +++ b/dist/src/rpc.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"rpc.d.ts","sourceRoot":"","sources":["../../src/rpc.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/dist/src/rpc.js b/dist/src/rpc.js new file mode 100644 index 0000000..56abea3 --- /dev/null +++ b/dist/src/rpc.js @@ -0,0 +1,165 @@ +import z from "zod"; +export const AuthMessageSchema = z.object({ + type: z.literal("auth"), + secret: z.string().min(1), + pid: z.number().int().nonnegative().optional(), +}); +export const AuthAckMessageSchema = z.object({ + type: z.literal("auth-ack"), +}); +export const RpcMethodSchema = z.enum([ + "stop", + "call", + "boot", + "target-context-open", + "runtime-error", + "context-open", + "context-close", + "event-subscribe", + "subscription-ready", + "event-unsubscribe", + "value-get", + "value-watch", + "value-unwatch", + "subscription-publish", +]); +export const RequestMessageSchema = z.object({ + type: z.literal("request"), + requestId: z.string().min(1), + method: RpcMethodSchema, + params: z.record(z.string(), z.unknown()), +}); +const RoutedStateContextFields = { + target: z.string().min(1).optional(), + targetPackageName: z.string().min(1).optional(), + callerStateContextId: z.string().min(1).optional(), + stateContextId: z.string().min(1).optional(), + contextNamespace: z.string().min(1).optional(), +}; +export const CallRequestParamsSchema = z.object({ + ...RoutedStateContextFields, + functionName: z.string().min(1), + params: z.unknown(), +}); +export const BootRequestParamsSchema = z.object({ + target: z.string().min(1), +}); +export const TargetContextOpenRequestParamsSchema = z.object({ + ...RoutedStateContextFields, + target: z.string().min(1), + targetPackageName: z.string().min(1).optional(), +}); +export const RuntimeErrorRequestParamsSchema = z.object({ + phase: z.enum([ + "on-create", + "on-destroy", + "on-context-open", + "on-context-close", + "browser-surface", + "function", + "event-subscribe", + "event-cleanup", + "value-get", + "value-watch", + "value-cleanup", + "subscription-publish", + "internal", + ]), + stateContextId: z.string().min(1).optional(), + functionName: z.string().min(1).optional(), + resourceKind: z.enum(["event", "value"]).optional(), + resourceName: z.string().min(1).optional(), + surfaceId: z.string().min(1).optional(), + hostSessionId: z.string().min(1).optional(), + message: z.string().min(1), + stack: z.string().min(1).optional(), +}); +export const ContextOpenRequestParamsSchema = z.object({ + stateContextId: z.string().min(1), +}); +export const ContextCloseRequestParamsSchema = z.object({ + stateContextId: z.string().min(1), +}); +export const EventSubscribeRequestParamsSchema = z.object({ + ...RoutedStateContextFields, + eventName: z.string().min(1), + params: z.unknown().optional(), + subscriptionNamespace: z.string().min(1).optional(), +}); +export const EventUnsubscribeRequestParamsSchema = z.object({ + subscriptionId: z.string().min(1), +}); +export const SubscriptionReadyRequestParamsSchema = z.object({ + subscriptionId: z.string().min(1), +}); +export const ValueGetRequestParamsSchema = z.object({ + ...RoutedStateContextFields, + valueName: z.string().min(1), + params: z.unknown().optional(), +}); +export const ValueWatchRequestParamsSchema = z.object({ + ...RoutedStateContextFields, + valueName: z.string().min(1), + params: z.unknown().optional(), + subscriptionNamespace: z.string().min(1).optional(), +}); +export const ValueUnwatchRequestParamsSchema = z.object({ + subscriptionId: z.string().min(1), +}); +export const SubscriptionPublishRequestParamsSchema = z.object({ + subscriptionId: z.string().min(1), + data: z.unknown(), +}); +export const SubscriptionResponseSchema = z.object({ + subscriptionId: z.string().min(1), +}); +export const SubscriptionDataMessageSchema = z.object({ + type: z.literal("subscription-data"), + subscriptionId: z.string().min(1), + data: z.unknown(), +}); +export const ResponseOkMessageSchema = z.object({ + type: z.literal("response"), + requestId: z.string().min(1), + ok: z.literal(true), + result: z.unknown().optional(), +}); +export const ResponseErrorMessageSchema = z.object({ + type: z.literal("response"), + requestId: z.string().min(1), + ok: z.literal(false), + error: z.string(), +}); +export const ResponseMessageSchema = z.union([ + ResponseOkMessageSchema, + ResponseErrorMessageSchema, +]); +export const ServerMessageSchema = z.union([ + AuthAckMessageSchema, + RequestMessageSchema, + ResponseMessageSchema, + SubscriptionDataMessageSchema, +]); +export const ClientMessageSchema = z.union([ + AuthMessageSchema, + ResponseMessageSchema, + RequestMessageSchema, +]); +export const sendMessage = (socket, message) => { + socket.send(JSON.stringify(message)); +}; +export const sendClientMessage = (socket, message) => { + sendMessage(socket, message); +}; +export const sendServerMessage = (socket, message) => { + sendMessage(socket, message); +}; +export const parseJson = (value) => { + try { + return { ok: true, value: JSON.parse(value) }; + } + catch { + return { ok: false }; + } +}; +//# sourceMappingURL=rpc.js.map \ No newline at end of file diff --git a/dist/src/rpc.js.map b/dist/src/rpc.js.map new file mode 100644 index 0000000..a03897b --- /dev/null +++ b/dist/src/rpc.js.map @@ -0,0 +1 @@ +{"version":3,"file":"rpc.js","sourceRoot":"","sources":["../../src/rpc.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/package.json b/package.json new file mode 100644 index 0000000..b43d652 --- /dev/null +++ b/package.json @@ -0,0 +1,42 @@ +{ + "name": "@quixos/package-runtime", + "version": "1.0.0", + "author": "Timothy J. Aveni (https://timothyaveni.com/)", + "description": "", + "packageManager": "yarn@4.12.0", + "main": "dist/src/index.js", + "types": "dist/src/index.d.ts", + "exports": { + ".": { + "types": "./dist/src/index.d.ts", + "default": "./dist/src/index.js" + }, + "./browser-surface": { + "types": "./dist/src/browserSurface.d.ts", + "default": "./dist/src/browserSurface.js" + }, + "./browser-surface/shared": { + "types": "./dist/src/browserSurfaceShared.d.ts", + "default": "./dist/src/browserSurfaceShared.js" + }, + "./browser-surface/react": { + "types": "./dist/src/browserSurfaceReact.d.ts", + "default": "./dist/src/browserSurfaceReact.js" + } + }, + "type": "module", + "scripts": { + "build": "tsc -p tsconfig.json" + }, + "dependencies": { + "@types/node": "^24", + "@types/react": "^18.3.12", + "@types/react-dom": "^18.3.1", + "@types/ws": "^8.18.1", + "react": "^18.3.1", + "react-dom": "^18.3.1", + "typescript": "^5.9.3", + "ws": "^8.18.3", + "zod": "^4.3.6" + } +} diff --git a/src/browserSurface.ts b/src/browserSurface.ts new file mode 100644 index 0000000..2562808 --- /dev/null +++ b/src/browserSurface.ts @@ -0,0 +1,761 @@ +import WebSocket from "ws"; +import z, { type ZodType } from "zod"; +import { + createEmbeddedSurfaceRef, + createEmbeddedSurfaceRefSchema, + embeddedSurfaceRefSchema, + getEmbeddedSurfaceId, + getEmbeddedSurfacePackageName, + jsonValueSchema, + type JsonValue, +} from "./browserSurfaceShared.js"; +import { + createPackage, + reportPackageRuntimeError, + type CreatePackageOptions, + type EventSchemaMap, + type PackageEvents, + type PackageFunctionSchema, + type PackageFunctions, + type PackageContext, + type PackageSchema, + type PackageValues, + type SchemaAnnotation, + type ValueSchemaMap, +} from "./index.js"; +export { jsonValueSchema, type JsonValue } from "./browserSurfaceShared.js"; +export { + createEmbeddedSurfaceRef, + createEmbeddedSurfaceRefSchema, + embeddedSurfaceRefSchema, + getEmbeddedSurfaceId, + getEmbeddedSurfacePackageName, + type EmbeddedSurfaceRef, +} from "./browserSurfaceShared.js"; +export type { SubscriptionHandle } from "./index.js"; + +export const stateContextResultSchema = z.object({ + stateContextId: z.string().min(1), +}); + +export const createBrowserSurfaceAnnotation = ({ + surfaceId, + namespaceProp = "quixosKey", + surfaces = ["desktop"], + capabilities = ["mouse", "keyboard"], + aspectRatioHint = "16:10", + propsFunction = "propsUpdate", +}: { + surfaceId: string; + namespaceProp?: string; + surfaces?: string[]; + capabilities?: string[]; + aspectRatioHint?: string; + propsFunction?: string; +}): SchemaAnnotation => ({ + type: "quixos.ui.surface/v1", + surfaceId, + runtime: "browser-module", + transport: "relay", + propsFunction, + namespaceProp, + surfaces, + capabilities, + aspectRatioHint, +}); + +export const createBrowserSurfacePropsUpdateFunctionSchema = < + PropsSchema extends z.ZodTypeAny, +>({ + propsSchema, + namespaceProp = "quixosKey", + description, + inputDescription, + outputDescription, +}: { + propsSchema: PropsSchema; + namespaceProp?: string; + description: string; + inputDescription?: string; + outputDescription?: string; +}): PackageFunctionSchema => ({ + description, + annotations: [ + { + type: "quixos.ui.browser-surface-props/v1", + namespaceProp, + semantics: "replace", + transport: "relay", + }, + ], + inputSchema: (inputDescription + ? z.object({ props: propsSchema }).describe(inputDescription) + : z.object({ props: propsSchema })) as z.ZodTypeAny, + outputSchema: (outputDescription + ? stateContextResultSchema.describe(outputDescription) + : stateContextResultSchema) as z.ZodTypeAny, +}); + +export const createBrowserSurfacePackageSchema = < + PropsSchema extends z.ZodTypeAny, + ExtraFunctions extends Record = Record< + never, + never + >, + Events extends EventSchemaMap = Record, + Values extends ValueSchemaMap = Record, +>({ + description, + majorVersion = 1, + surfaceId, + propsSchema, + namespaceProp = "quixosKey", + surfaces = ["desktop"], + capabilities = ["mouse", "keyboard"], + aspectRatioHint = "16:10", + propsFunctionDescription, + propsInputDescription, + propsOutputDescription, + annotations = [], + functions, + events, + values, +}: { + description: string; + majorVersion?: number; + surfaceId: string; + propsSchema: PropsSchema; + namespaceProp?: string; + surfaces?: string[]; + capabilities?: string[]; + aspectRatioHint?: string; + propsFunctionDescription: string; + propsInputDescription?: string; + propsOutputDescription?: string; + annotations?: SchemaAnnotation[]; + functions?: ExtraFunctions; + events?: Events; + values?: Values; +}): PackageSchema< + { propsUpdate: PackageFunctionSchema } & ExtraFunctions, + Events, + Values +> => ({ + schemaVersion: 1, + majorVersion, + description, + annotations: [ + createBrowserSurfaceAnnotation({ + surfaceId, + namespaceProp, + surfaces, + capabilities, + aspectRatioHint, + propsFunction: "propsUpdate", + }), + ...annotations, + ], + functions: { + ...(functions ?? ({} as ExtraFunctions)), + propsUpdate: createBrowserSurfacePropsUpdateFunctionSchema({ + propsSchema, + namespaceProp, + description: propsFunctionDescription, + inputDescription: propsInputDescription, + outputDescription: propsOutputDescription, + }), + }, + events: (events ?? {}) as Events, + values: (values ?? {}) as Values, +}); + +type EventSink = { + emit: (data: T) => Promise; +}; + +export type EventHub> = { + subscribe: ( + eventName: K, + sink: EventSink, + ) => () => void; + emit: ( + eventName: K, + payload: Events[K], + onError?: (error: unknown, eventName: K) => void, + ) => void; +}; + +export const createEventHub = < + Events extends Record, +>(): EventHub => { + const sinks = new Map>>(); + + return { + subscribe: (eventName, sink) => { + let eventSinks = sinks.get(eventName); + if (!eventSinks) { + eventSinks = new Set(); + sinks.set(eventName, eventSinks as Set>); + } + eventSinks.add(sink as EventSink); + return () => { + eventSinks?.delete(sink as EventSink); + }; + }, + emit: (eventName, payload, onError) => { + const eventSinks = sinks.get(eventName); + if (!eventSinks) { + return; + } + for (const sink of eventSinks) { + void sink + .emit(payload as Events[keyof Events]) + .catch((error) => onError?.(error, eventName)); + } + }, + }; +}; + +const DEFAULT_SURFACE_RELAY_URL = "ws://127.0.0.1:6247"; + +const producerMessageSchema = z.discriminatedUnion("type", [ + z.object({ + type: z.literal("browser-surface-host-action"), + stateContextId: z.string().min(1), + surfaceId: z.string().min(1), + hostSessionId: z.string().min(1), + action: jsonValueSchema, + }), + z.object({ + type: z.literal("browser-surface-host-detached"), + stateContextId: z.string().min(1), + surfaceId: z.string().min(1), + hostSessionId: z.string().min(1), + }), + z.object({ + type: z.literal("browser-surface-host-error"), + stateContextId: z.string().min(1), + surfaceId: z.string().min(1), + hostSessionId: z.string().min(1), + message: z.string().min(1), + stack: z.string().min(1).optional(), + }), + z.object({ + type: z.literal("error"), + message: z.string(), + }), +]); + +type ProducerMessage = + | { + type: "browser-surface-host-action"; + stateContextId: string; + surfaceId: string; + hostSessionId: string; + action: JsonValue; + } + | { + type: "browser-surface-host-detached"; + stateContextId: string; + surfaceId: string; + hostSessionId: string; + } + | { + type: "browser-surface-host-error"; + stateContextId: string; + surfaceId: string; + hostSessionId: string; + message: string; + stack?: string; + } + | { + type: "error"; + message: string; + }; + +type ProducerClientMessage = + | { + type: "browser-surface-producer-attach"; + stateContextId: string; + surfaceId: string; + } + | { + type: "browser-surface-producer-detach"; + stateContextId: string; + surfaceId: string; + } + | { + type: "browser-surface-props"; + stateContextId: string; + surfaceId: string; + props: unknown; + } + | { + type: "browser-surface-message"; + stateContextId: string; + surfaceId: string; + payload: unknown; + }; + +type ProducerHandlers = { + onHostAction?: (hostSessionId: string, action: JsonValue) => void | Promise; + onHostDetached?: (hostSessionId: string) => void | Promise; + onHostError?: ( + hostSessionId: string, + error: { message: string; stack?: string }, + ) => void | Promise; +}; + +const surfaceKey = (stateContextId: string, surfaceId: string) => + JSON.stringify([stateContextId, surfaceId]); + +const getErrorMessage = (error: unknown) => + error instanceof Error ? error.message : String(error); + +const parseProducerMessage = (value: unknown): ProducerMessage | null => { + const parsed = producerMessageSchema.safeParse(value); + return parsed.success ? (parsed.data as ProducerMessage) : null; +}; + +export class BrowserSurfaceRelayProducerClient { + readonly relayUrl: string; + + #socket: WebSocket | null = null; + #ready: Promise | null = null; + #handlers = new Map(); + + constructor(relayUrl = DEFAULT_SURFACE_RELAY_URL) { + this.relayUrl = relayUrl; + } + + async connect() { + if (this.#ready) { + return await this.#ready; + } + + this.#ready = new Promise((resolve, reject) => { + const socket = new WebSocket(this.relayUrl); + this.#socket = socket; + let opened = false; + + const fail = (error: unknown) => { + const message = new Error(getErrorMessage(error)); + this.#socket = null; + this.#ready = null; + if (!opened) { + reject(message); + return; + } + console.error(`[browser-surface] relay websocket disconnected: ${message.message}`); + }; + + socket.on("open", () => { + opened = true; + resolve(); + }); + + socket.on("message", (rawData) => { + let parsedJson: unknown; + try { + parsedJson = JSON.parse(String(rawData)); + } catch { + return; + } + const message = parseProducerMessage(parsedJson); + if (!message) { + return; + } + if (message.type === "error") { + console.error(`[browser-surface] relay error: ${message.message}`); + return; + } + const handlers = this.#handlers.get( + surfaceKey(message.stateContextId, message.surfaceId), + ); + if (!handlers) { + return; + } + if (message.type === "browser-surface-host-action") { + void handlers.onHostAction?.(message.hostSessionId, message.action); + return; + } + if (message.type === "browser-surface-host-error") { + void handlers.onHostError?.(message.hostSessionId, { + message: message.message, + stack: message.stack, + }); + return; + } + void handlers.onHostDetached?.(message.hostSessionId); + }); + + socket.on("error", () => { + fail(new Error("Surface relay websocket error")); + }); + socket.on("close", () => { + fail(new Error("Surface relay websocket closed")); + }); + }); + + return await this.#ready; + } + + close() { + this.#socket?.close(); + this.#socket = null; + this.#ready = null; + this.#handlers.clear(); + } + + async #send(message: ProducerClientMessage) { + await this.connect(); + const socket = this.#socket; + if (!socket) { + throw new Error("Surface relay websocket is not connected"); + } + socket.send(JSON.stringify(message)); + } + + async attach( + stateContextId: string, + surfaceId: string, + handlers: ProducerHandlers = {}, + ) { + this.#handlers.set(surfaceKey(stateContextId, surfaceId), handlers); + await this.#send({ + type: "browser-surface-producer-attach", + stateContextId, + surfaceId, + }); + } + + async publishProps(stateContextId: string, surfaceId: string, props: unknown) { + await this.#send({ + type: "browser-surface-props", + stateContextId, + surfaceId, + props, + }); + } + + async publishMessage(stateContextId: string, surfaceId: string, payload: unknown) { + await this.#send({ + type: "browser-surface-message", + stateContextId, + surfaceId, + payload, + }); + } + + async detach(stateContextId: string, surfaceId: string) { + this.#handlers.delete(surfaceKey(stateContextId, surfaceId)); + try { + await this.#send({ + type: "browser-surface-producer-detach", + stateContextId, + surfaceId, + }); + } catch { + // ignore shutdown races + } + } +} + +type DependencyClient = ReturnType; +type RelaySchema = Parameters[0]; + +type BrowserSurfaceInstanceInternal = { + ctx: PackageContext; + stateContextId: string; + relay: DependencyClient; + producer: BrowserSurfaceRelayProducerClient; + state: State; + publishQueue: Promise; + publish: () => void; + publishMessage: (payload: unknown) => Promise; +}; + +export type BrowserSurfaceInstance = Omit< + BrowserSurfaceInstanceInternal, + "publishQueue" +>; + +type RelayRegistration = { + relayUrl: string; +}; + +export type BrowserSurfaceHostError = { + message: string; + stack?: string; +}; + +export type BrowserSurfaceControllerOptions = { + relaySchema: RelaySchema; + surfaceId: string; + bundleDir: string; + entryPoint: string; + propsSchema: ZodType; + actionSchema: ZodType; + logPrefix: string; + createState: ( + instance: BrowserSurfaceInstance, + ) => State | Promise; + buildProps: (instance: BrowserSurfaceInstance) => unknown; + applyProps: ( + instance: BrowserSurfaceInstance, + props: Props, + ) => void | Promise; + onHostAction?: ( + instance: BrowserSurfaceInstance, + hostSessionId: string, + action: Action, + ) => void | Promise; + onHostDetached?: ( + instance: BrowserSurfaceInstance, + hostSessionId: string, + ) => void | Promise; + onHostError?: ( + instance: BrowserSurfaceInstance, + hostSessionId: string, + error: BrowserSurfaceHostError, + ) => void | Promise; + onAfterCreate?: ( + instance: BrowserSurfaceInstance, + ) => void | Promise; + onBeforeDestroy?: ( + instance: BrowserSurfaceInstance, + ) => void | Promise; +}; + +type BrowserSurfaceController = { + onContextOpen: (ctx: Context) => void | Promise; + onContextClose: (ctx: Context) => void | Promise; + onDestroy: () => void | Promise; + propsUpdate: (ctx: Context, nextProps: unknown) => Promise; +}; + +export const createBrowserSurfacePackage = < + Schema extends PackageSchema, + Context extends PackageContext = PackageContext, +>({ + schema, + surface, + functions, + events, + values, + onCreate, + onContextOpen, + onContextClose, + onDestroy, +}: Omit, "functions"> & { + surface: BrowserSurfaceController; + functions?: Omit, "propsUpdate">; +}) => + createPackage({ + schema, + onCreate, + onContextOpen: async (ctx) => { + await surface.onContextOpen(ctx); + await onContextOpen?.(ctx); + }, + onContextClose: async (ctx) => { + try { + await onContextClose?.(ctx); + } finally { + await surface.onContextClose(ctx); + } + }, + onDestroy: async () => { + try { + await onDestroy?.(); + } finally { + await surface.onDestroy(); + } + }, + functions: { + ...(functions ?? ({} as Omit, "propsUpdate">)), + propsUpdate: async (ctx: Context, params: unknown) => + stateContextResultSchema.parse({ + stateContextId: await surface.propsUpdate( + ctx, + (params as { props: unknown }).props, + ), + }), + } as PackageFunctions, + events: events as PackageEvents, + values: values as PackageValues, + }); + +const toPublicInstance = ( + instance: BrowserSurfaceInstanceInternal, +): BrowserSurfaceInstance => instance; + +export const createBrowserSurfaceController = ({ + relaySchema, + surfaceId, + bundleDir, + entryPoint, + propsSchema, + actionSchema, + logPrefix, + createState, + buildProps, + applyProps, + onHostAction, + onHostDetached, + onHostError, + onAfterCreate, + onBeforeDestroy, +}: BrowserSurfaceControllerOptions) => { + const renderStates = new Map>(); + const pendingRenderStates = new Map< + string, + Promise> + >(); + + const queuePublish = (instance: BrowserSurfaceInstanceInternal) => { + instance.publishQueue = instance.publishQueue + .then(async () => { + await instance.producer.publishProps( + instance.stateContextId, + surfaceId, + buildProps(toPublicInstance(instance)), + ); + }) + .catch((error) => { + console.error( + `${logPrefix} failed to publish surface props for ${instance.stateContextId}`, + error, + ); + }); + }; + + const createRenderState = async (ctx: PackageContext) => { + const relay = ctx.usePackage(relaySchema); + const registration = (await ( + relay.functions.registerBrowserSurface as (params: { + stateContextId: string; + surfaceId: string; + bundleDir: string; + entryPoint: string; + }) => Promise + )({ + stateContextId: ctx.stateContext, + surfaceId, + bundleDir, + entryPoint, + })) as RelayRegistration; + const producer = new BrowserSurfaceRelayProducerClient(registration.relayUrl); + + const instance: BrowserSurfaceInstanceInternal = { + ctx, + stateContextId: ctx.stateContext, + relay, + producer, + state: undefined as State, + publishQueue: Promise.resolve(), + publish: () => { + queuePublish(instance); + }, + publishMessage: async (payload: unknown) => { + await producer.publishMessage(ctx.stateContext, surfaceId, payload); + }, + }; + + instance.state = await createState(toPublicInstance(instance)); + renderStates.set(ctx.stateContext, instance); + + await producer.attach(ctx.stateContext, surfaceId, { + onHostAction: async (hostSessionId, action) => { + if (!onHostAction) { + return; + } + const parsed = actionSchema.parse(action); + await onHostAction(toPublicInstance(instance), hostSessionId, parsed); + }, + onHostDetached: async (hostSessionId) => { + await onHostDetached?.(toPublicInstance(instance), hostSessionId); + }, + onHostError: async (hostSessionId, error) => { + await reportPackageRuntimeError({ + phase: "browser-surface", + error: new Error(error.message), + stateContextId: instance.stateContextId, + surfaceId, + hostSessionId, + stack: error.stack, + }); + await onHostError?.(toPublicInstance(instance), hostSessionId, error); + }, + }); + + await onAfterCreate?.(toPublicInstance(instance)); + instance.publish(); + return instance; + }; + + const getOrCreate = async (ctx: PackageContext) => { + const existing = renderStates.get(ctx.stateContext); + if (existing) { + return existing; + } + + const pending = pendingRenderStates.get(ctx.stateContext); + if (pending) { + return await pending; + } + + const creation = createRenderState(ctx); + pendingRenderStates.set(ctx.stateContext, creation); + try { + return await creation; + } finally { + pendingRenderStates.delete(ctx.stateContext); + } + }; + + const destroyState = async (stateContextId: string) => { + const instance = renderStates.get(stateContextId); + if (!instance) { + return; + } + + renderStates.delete(stateContextId); + pendingRenderStates.delete(stateContextId); + await instance.publishQueue.catch(() => {}); + await onBeforeDestroy?.(toPublicInstance(instance)); + await instance.producer.detach(stateContextId, surfaceId); + instance.producer.close(); + await ( + instance.relay.functions.unregisterBrowserSurface as (params: { + stateContextId: string; + surfaceId: string; + }) => Promise + )({ stateContextId, surfaceId }); + }; + + return { + getOrCreate: async (ctx: PackageContext) => + toPublicInstance(await getOrCreate(ctx)), + onContextOpen: async (ctx: PackageContext) => { + await getOrCreate(ctx); + }, + onContextClose: async (ctx: PackageContext) => { + await destroyState(ctx.stateContext); + }, + onDestroy: async () => { + const activeStateContexts = [...renderStates.keys()]; + pendingRenderStates.clear(); + for (const stateContextId of activeStateContexts) { + await destroyState(stateContextId); + } + }, + propsUpdate: async (ctx: PackageContext, nextProps: unknown) => { + const parsedProps = propsSchema.parse(nextProps); + const instance = await getOrCreate(ctx); + await applyProps(toPublicInstance(instance), parsedProps); + instance.publish(); + return ctx.stateContext; + }, + }; +}; diff --git a/src/browserSurfaceEmbedded.tsx b/src/browserSurfaceEmbedded.tsx new file mode 100644 index 0000000..a46094f --- /dev/null +++ b/src/browserSurfaceEmbedded.tsx @@ -0,0 +1,725 @@ +import { + useEffect, + useLayoutEffect, + useMemo, + useRef, + useState, + type CSSProperties, + type ReactNode, +} from "react"; +import type { PackageSchema } from "./index.js"; +import { + createEmbeddedSurfaceRef, + getEmbeddedSurfaceId, + type EmbeddedSurfaceRef, +} from "./browserSurfaceShared.js"; +import { + jsonValueSchema, +} from "./browserSurfaceShared.js"; +import z from "zod"; + +type BrowserSurfaceSchemaLike = PackageSchema & { + __quixos?: { + name?: string | null; + flakeRef?: string | null; + }; +}; + +type SurfaceModuleHandle = { + unmount?: () => void; +} | void; + +type SurfaceMountApi = { + container: HTMLElement; + initialProps: unknown; + onProps: (listener: (props: unknown) => void) => () => void; + onMessage: (listener: (message: unknown) => void) => () => void; + dispatch: (action: unknown) => void; + reportError?: (error: unknown) => void; +}; + +type SurfaceModule = { + mount: (api: SurfaceMountApi) => SurfaceModuleHandle | Promise; +}; + +const LOCAL_HOSTNAMES = new Set(["localhost", "127.0.0.1", "::1", "[::1]"]); +const RELAY_LOCAL_PORT = "6247"; +const RELAY_PROXY_PATH = "/relay/"; + +const relayServerMessageSchema = z.discriminatedUnion("type", [ + z.object({ + type: z.literal("browser-surface-bootstrap"), + stateContextId: z.string().min(1), + surfaceId: z.string().min(1), + bundleUrl: z.string().url(), + initialProps: jsonValueSchema, + }), + z.object({ + type: z.literal("browser-surface-props"), + stateContextId: z.string().min(1), + surfaceId: z.string().min(1), + props: jsonValueSchema, + }), + z.object({ + type: z.literal("browser-surface-message"), + stateContextId: z.string().min(1), + surfaceId: z.string().min(1), + payload: jsonValueSchema, + }), + z.object({ + type: z.literal("browser-surface-closed"), + stateContextId: z.string().min(1), + surfaceId: z.string().min(1), + }), + z.object({ + type: z.literal("error"), + message: z.string(), + }), +]); + +type BrowserSurfaceRelayServerMessage = + | { + type: "browser-surface-bootstrap"; + stateContextId: string; + surfaceId: string; + bundleUrl: string; + initialProps: unknown; + } + | { + type: "browser-surface-props"; + stateContextId: string; + surfaceId: string; + props: unknown; + } + | { + type: "browser-surface-message"; + stateContextId: string; + surfaceId: string; + payload: unknown; + } + | { + type: "browser-surface-closed"; + stateContextId: string; + surfaceId: string; + } + | { + type: "error"; + message: string; + }; + +type BrowserSurfaceRelayClientMessage = + | { + type: "browser-surface-host-attach"; + stateContextId: string; + surfaceId: string; + hostSessionId: string; + } + | { + type: "browser-surface-host-detach"; + stateContextId: string; + surfaceId: string; + hostSessionId: string; + } + | { + type: "browser-surface-host-action"; + stateContextId: string; + surfaceId: string; + hostSessionId: string; + action: unknown; + } + | { + type: "browser-surface-host-error"; + stateContextId: string; + surfaceId: string; + hostSessionId: string; + message: string; + stack?: string; + }; + +type HostAttachment = { + stateContextId: string; + surfaceId: string; + hostSessionId: string; + handler: (message: BrowserSurfaceRelayServerMessage) => void | Promise; +}; + +type SharedBrowserSurfaceRelayClientEntry = { + client: BrowserSurfaceRelayClient; + refCount: number; +}; + +type EmbeddedSurfaceHostProps = { + surface: EmbeddedSurfaceRef; + relayUrl?: string; + className?: string; + style?: CSSProperties; + fallback?: ReactNode; + onError?: (error: Error) => void; +}; + +export type EmbeddedSurfaceComponentProps = { + stateContextId: string; + surfaceId?: SurfaceId; + relayUrl?: string; + className?: string; + style?: CSSProperties; + fallback?: ReactNode; + onError?: (error: Error) => void; +}; + +const isLocalHostname = (hostname: string) => LOCAL_HOSTNAMES.has(hostname); + +const urlForCurrentHostAndPort = ( + locationLike: { href: string } = window.location, + port: string, + { ws = false }: { ws?: boolean } = {}, +) => { + const url = new URL(locationLike.href); + url.protocol = ws + ? url.protocol === "https:" + ? "wss:" + : "ws:" + : url.protocol === "https:" + ? "https:" + : "http:"; + url.port = port; + url.pathname = "/"; + url.search = ""; + url.hash = ""; + return url.toString(); +}; + +const urlForCurrentOriginAndPath = ( + locationLike: { href: string } = window.location, + path: string, + { ws = false }: { ws?: boolean } = {}, +) => { + const url = new URL(locationLike.href); + url.protocol = ws + ? url.protocol === "https:" + ? "wss:" + : "ws:" + : url.protocol === "https:" + ? "https:" + : "http:"; + url.port = ""; + url.pathname = path; + url.search = ""; + url.hash = ""; + return url.toString(); +}; + +const defaultRelayUrlForBrowser = ( + locationLike: Pick = window.location, +) => { + if (isLocalHostname(locationLike.hostname)) { + return urlForCurrentHostAndPort(locationLike, RELAY_LOCAL_PORT, { + ws: true, + }); + } + return urlForCurrentOriginAndPath(locationLike, RELAY_PROXY_PATH, { + ws: true, + }); +}; + +const getErrorMessage = (error: unknown) => + error instanceof Error ? error.message : String(error); + +const toError = (error: unknown) => + error instanceof Error ? error : new Error(String(error)); + +const getErrorDetails = (error: unknown) => { + if (error instanceof Error) { + return { + message: error.message || "Unknown error", + stack: error.stack, + }; + } + return { + message: String(error), + stack: undefined, + }; +}; + +const parseServerMessage = ( + value: unknown, +): BrowserSurfaceRelayServerMessage | null => { + const parsed = relayServerMessageSchema.safeParse(value); + return parsed.success ? (parsed.data as BrowserSurfaceRelayServerMessage) : null; +}; + +const normalizeSurfaceModule = (module: unknown): SurfaceModule => { + if ( + typeof module === "object" && + module !== null && + typeof (module as { mount?: unknown }).mount === "function" + ) { + return module as SurfaceModule; + } + throw new Error("Surface bundle does not export a mount(...) function"); +}; + +const attachmentKey = ( + stateContextId: string, + surfaceId: string, + hostSessionId: string, +) => JSON.stringify([stateContextId, surfaceId, hostSessionId]); + +const sharedBrowserSurfaceRelayClients = new Map< + string, + SharedBrowserSurfaceRelayClientEntry +>(); + +const createBrowserSurfaceHostSessionId = () => crypto.randomUUID(); + +class BrowserSurfaceRelayClient { + readonly relayUrl: string; + + #socket: WebSocket | null = null; + #ready: Promise | null = null; + #attachments = new Map(); + + constructor(relayUrl: string) { + this.relayUrl = relayUrl; + } + + async connect() { + if (this.#ready) { + return await this.#ready; + } + + this.#ready = new Promise((resolve, reject) => { + const socket = new WebSocket(this.relayUrl); + this.#socket = socket; + let opened = false; + + const fail = (error: unknown) => { + const message = toError(error); + this.#socket = null; + this.#ready = null; + if (!opened) { + reject(message); + } + }; + + socket.addEventListener("open", () => { + opened = true; + resolve(); + }); + + socket.addEventListener("message", (event) => { + let parsedJson: unknown; + try { + parsedJson = JSON.parse(String(event.data)); + } catch { + return; + } + const message = parseServerMessage(parsedJson); + if (!message) { + return; + } + if (message.type === "error") { + console.error(`[browser-surface-relay] ${message.message}`); + return; + } + const attachment = this.#findAttachment( + message.stateContextId, + message.surfaceId, + ); + if (!attachment) { + return; + } + void attachment.handler(message); + }); + + socket.addEventListener("error", () => { + fail(new Error("Browser surface relay socket error")); + }); + socket.addEventListener("close", () => { + fail(new Error("Browser surface relay socket closed")); + }); + }); + + return await this.#ready; + } + + close() { + this.#socket?.close(); + this.#socket = null; + this.#ready = null; + this.#attachments.clear(); + } + + async #send(message: BrowserSurfaceRelayClientMessage) { + await this.connect(); + const socket = this.#socket; + if (!socket) { + throw new Error("Browser surface relay socket is not connected"); + } + socket.send(JSON.stringify(message)); + } + + #findAttachment(stateContextId: string, surfaceId: string) { + for (const attachment of this.#attachments.values()) { + if ( + attachment.stateContextId === stateContextId && + attachment.surfaceId === surfaceId + ) { + return attachment; + } + } + return null; + } + + async attach( + stateContextId: string, + surfaceId: string, + hostSessionId: string, + handler: (message: BrowserSurfaceRelayServerMessage) => void | Promise, + ) { + const key = attachmentKey(stateContextId, surfaceId, hostSessionId); + const existing = this.#findAttachment(stateContextId, surfaceId); + if (existing) { + for (const [existingKey, attachment] of this.#attachments.entries()) { + if (attachment === existing) { + this.#attachments.delete(existingKey); + break; + } + } + try { + await this.#send({ + type: "browser-surface-host-detach", + stateContextId, + surfaceId, + hostSessionId: existing.hostSessionId, + }); + } catch { + // Ignore replacement races. + } + } + + this.#attachments.set(key, { + stateContextId, + surfaceId, + hostSessionId, + handler, + }); + await this.#send({ + type: "browser-surface-host-attach", + stateContextId, + surfaceId, + hostSessionId, + }); + return async () => { + this.#attachments.delete(key); + try { + await this.#send({ + type: "browser-surface-host-detach", + stateContextId, + surfaceId, + hostSessionId, + }); + } catch { + // Ignore detach races during teardown. + } + }; + } + + async sendAction( + stateContextId: string, + surfaceId: string, + hostSessionId: string, + action: unknown, + ) { + await this.#send({ + type: "browser-surface-host-action", + stateContextId, + surfaceId, + hostSessionId, + action, + }); + } + + async sendError( + stateContextId: string, + surfaceId: string, + hostSessionId: string, + error: { + message: string; + stack?: string; + }, + ) { + await this.#send({ + type: "browser-surface-host-error", + stateContextId, + surfaceId, + hostSessionId, + message: error.message, + ...(error.stack ? { stack: error.stack } : {}), + }); + } +} + +const acquireSharedBrowserSurfaceRelayClient = (relayUrl: string) => { + const existing = sharedBrowserSurfaceRelayClients.get(relayUrl); + if (existing) { + existing.refCount += 1; + return existing.client; + } + + const client = new BrowserSurfaceRelayClient(relayUrl); + sharedBrowserSurfaceRelayClients.set(relayUrl, { + client, + refCount: 1, + }); + return client; +}; + +const releaseSharedBrowserSurfaceRelayClient = (relayUrl: string) => { + const entry = sharedBrowserSurfaceRelayClients.get(relayUrl); + if (!entry) { + return; + } + entry.refCount -= 1; + if (entry.refCount > 0) { + return; + } + entry.client.close(); + sharedBrowserSurfaceRelayClients.delete(relayUrl); +}; + +export const EmbeddedSurface = ({ + surface, + relayUrl, + className, + style, + fallback = null, + onError, +}: EmbeddedSurfaceHostProps) => { + const hostRef = useRef(null); + const unsubscribeRef = useRef Promise)>(null); + const mountedHandleRef = useRef<{ unmount: () => void } | null>(null); + const loadedBundleUrlRef = useRef(null); + const propListenersRef = useRef(new Set<(props: unknown) => void>()); + const messageListenersRef = useRef(new Set<(message: unknown) => void>()); + const hostSessionIdRef = useRef(""); + const [error, setError] = useState(null); + + const resolvedRelayUrl = useMemo( + () => relayUrl ?? defaultRelayUrlForBrowser(), + [relayUrl], + ); + const relayClient = useMemo( + () => acquireSharedBrowserSurfaceRelayClient(resolvedRelayUrl), + [resolvedRelayUrl], + ); + + const clearMountedSurface = () => { + try { + mountedHandleRef.current?.unmount(); + } catch { + // Ignore teardown races during remount. + } + mountedHandleRef.current = null; + loadedBundleUrlRef.current = null; + propListenersRef.current.clear(); + messageListenersRef.current.clear(); + }; + + const reportSurfaceError = (errorValue: unknown) => { + const error = toError(errorValue); + setError(error); + onError?.(error); + + const details = getErrorDetails(error); + if (!hostSessionIdRef.current) { + return; + } + void relayClient.sendError( + surface.stateContextId, + surface.surfaceId, + hostSessionIdRef.current, + details, + ).catch((reportingError) => { + console.error( + "[embedded-surface] failed to report surface error", + reportingError, + ); + }); + }; + + const dispatchAction = (action: unknown) => { + if (!hostSessionIdRef.current) { + return; + } + void relayClient + .sendAction( + surface.stateContextId, + surface.surfaceId, + hostSessionIdRef.current, + action, + ) + .catch((dispatchError) => { + reportSurfaceError(dispatchError); + }); + }; + + const mountSurface = async (bundleUrl: string, initialProps: unknown) => { + const host = hostRef.current; + if (!host) { + throw new Error("Embedded surface host container is not mounted"); + } + + if (loadedBundleUrlRef.current !== bundleUrl) { + clearMountedSurface(); + const imported = await import(/* @vite-ignore */ bundleUrl); + const surfaceModule = normalizeSurfaceModule(imported); + const mounted = await surfaceModule.mount({ + container: host, + initialProps, + onProps: (listener) => { + propListenersRef.current.add(listener); + return () => { + propListenersRef.current.delete(listener); + }; + }, + onMessage: (listener) => { + messageListenersRef.current.add(listener); + return () => { + messageListenersRef.current.delete(listener); + }; + }, + dispatch: dispatchAction, + reportError: reportSurfaceError, + }); + mountedHandleRef.current = { + unmount: + mounted && typeof mounted.unmount === "function" + ? () => mounted.unmount?.() + : () => {}, + }; + loadedBundleUrlRef.current = bundleUrl; + return; + } + + for (const listener of propListenersRef.current) { + listener(initialProps); + } + }; + + const handleRelayMessage = async (message: BrowserSurfaceRelayServerMessage) => { + switch (message.type) { + case "browser-surface-bootstrap": + await mountSurface(message.bundleUrl, message.initialProps); + return; + case "browser-surface-props": + for (const listener of propListenersRef.current) { + listener(message.props); + } + return; + case "browser-surface-message": + for (const listener of messageListenersRef.current) { + listener(message.payload); + } + return; + case "browser-surface-closed": + clearMountedSurface(); + return; + case "error": + reportSurfaceError(new Error(message.message)); + return; + } + }; + + useLayoutEffect(() => { + setError(null); + clearMountedSurface(); + let cancelled = false; + const hostSessionId = createBrowserSurfaceHostSessionId(); + hostSessionIdRef.current = hostSessionId; + + void relayClient + .attach( + surface.stateContextId, + surface.surfaceId, + hostSessionId, + async (message) => { + try { + await handleRelayMessage(message); + } catch (messageError) { + reportSurfaceError(messageError); + } + }, + ) + .then(async (unsubscribe) => { + if (cancelled) { + await unsubscribe(); + return; + } + unsubscribeRef.current = unsubscribe; + }) + .catch((attachError) => { + if (!cancelled) { + reportSurfaceError(attachError); + } + }); + + return () => { + cancelled = true; + void unsubscribeRef.current?.(); + unsubscribeRef.current = null; + hostSessionIdRef.current = ""; + clearMountedSurface(); + }; + }, [relayClient, surface.packageName, surface.stateContextId, surface.surfaceId]); + + useEffect(() => { + return () => { + releaseSharedBrowserSurfaceRelayClient(resolvedRelayUrl); + }; + }, [relayClient, resolvedRelayUrl]); + + if (error) { + return <>{fallback}; + } + + return
; +}; + +export const createEmbeddedSurfaceComponent = < + Target extends string | BrowserSurfaceSchemaLike, + SurfaceId extends string = string, +>( + target: Target, + options?: { + surfaceId?: SurfaceId; + relayUrl?: string; + }, +) => { + const defaultSurfaceId = getEmbeddedSurfaceId( + target, + options?.surfaceId, + ) as SurfaceId; + + return ({ + stateContextId, + surfaceId, + relayUrl, + className, + style, + fallback, + onError, + }: EmbeddedSurfaceComponentProps) => ( + + ); +}; diff --git a/src/browserSurfaceReact.tsx b/src/browserSurfaceReact.tsx new file mode 100644 index 0000000..2de8640 --- /dev/null +++ b/src/browserSurfaceReact.tsx @@ -0,0 +1,179 @@ +import { Component, type ComponentType, type ErrorInfo, type ReactNode } from "react"; +import { createRoot, type Root } from "react-dom/client"; +import type { ZodType } from "zod"; +export { + EmbeddedSurface, + createEmbeddedSurfaceComponent, + type EmbeddedSurfaceComponentProps, +} from "./browserSurfaceEmbedded.js"; + +export type BrowserSurfaceMountApi = { + container: HTMLElement; + initialProps: unknown; + onProps: (listener: (props: unknown) => void) => () => void; + onMessage: (listener: (message: unknown) => void) => () => void; + dispatch: (action: unknown) => void; + reportError?: (error: unknown) => void; +}; + +export type BrowserSurfaceViewProps = { + props: Props; + dispatch: (action: Action) => void; +}; + +type CreateReactSurfaceMountOptions = { + Component: ComponentType>; + parseProps: (value: unknown) => Props; + normalizeAction: (action: Action) => Action; + onMessage?: (message: unknown) => void; +}; + +type CreateReactBrowserSurfaceMountOptions = { + Component: ComponentType>; + propsSchema: ZodType; + actionSchema: ZodType; + onMessage?: (message: unknown) => void; +}; + +type SurfaceErrorBoundaryProps = { + children: ReactNode; + onError: (error: Error, info: ErrorInfo) => void; + resetToken: number; +}; + +type SurfaceErrorBoundaryState = { + hasError: boolean; +}; + +class SurfaceErrorBoundary extends Component< + SurfaceErrorBoundaryProps, + SurfaceErrorBoundaryState +> { + state: SurfaceErrorBoundaryState = { hasError: false }; + + static getDerivedStateFromError() { + return { hasError: true }; + } + + componentDidCatch(error: Error, info: ErrorInfo) { + this.props.onError(error, info); + } + + componentDidUpdate(prevProps: SurfaceErrorBoundaryProps) { + if (this.state.hasError && prevProps.resetToken !== this.props.resetToken) { + this.setState({ hasError: false }); + } + } + + render() { + if (this.state.hasError) { + return null; + } + return this.props.children; + } +} + +const surfaceRoots = new WeakMap(); + +export const createReactSurfaceMount = ({ + Component, + parseProps, + normalizeAction, + onMessage, +}: CreateReactSurfaceMountOptions) => { + return ({ + container, + initialProps, + onProps, + onMessage: subscribeMessages, + dispatch, + reportError, + }: BrowserSurfaceMountApi) => { + const root = + surfaceRoots.get(container) ?? + (() => { + const createdRoot = createRoot(container); + surfaceRoots.set(container, createdRoot); + return createdRoot; + })(); + let currentProps = parseProps(initialProps); + let propsVersion = 0; + + const reportSurfaceError = (error: unknown, componentStack?: string) => { + if (!(error instanceof Error)) { + reportError?.(error); + return; + } + if (componentStack && componentStack.trim().length > 0) { + const errorWithComponentStack = new Error(error.message); + errorWithComponentStack.name = error.name; + errorWithComponentStack.stack = [ + error.stack ?? `${error.name}: ${error.message}`, + "", + "Component stack:", + componentStack.trim(), + ].join("\n"); + reportError?.(errorWithComponentStack); + return; + } + reportError?.(error); + }; + + const render = () => { + root.render( + { + reportSurfaceError(error, info.componentStack ?? undefined); + }} + > + { + dispatch(normalizeAction(action)); + }} + /> + , + ); + }; + + render(); + + const unsubscribeProps = onProps((nextProps) => { + try { + currentProps = parseProps(nextProps); + propsVersion += 1; + render(); + } catch (error) { + reportSurfaceError(error); + } + }); + const unsubscribeMessages = subscribeMessages((message) => { + onMessage?.(message); + }); + + return { + unmount: () => { + unsubscribeProps(); + unsubscribeMessages(); + root.unmount(); + if (surfaceRoots.get(container) === root) { + surfaceRoots.delete(container); + } + }, + }; + }; +}; + +export const createReactBrowserSurfaceMount = ({ + Component, + propsSchema, + actionSchema, + onMessage, +}: CreateReactBrowserSurfaceMountOptions) => + createReactSurfaceMount({ + Component, + parseProps: (value) => propsSchema.parse(value), + normalizeAction: (action) => actionSchema.parse(action), + onMessage, + }); diff --git a/src/browserSurfaceShared.ts b/src/browserSurfaceShared.ts new file mode 100644 index 0000000..dc10c04 --- /dev/null +++ b/src/browserSurfaceShared.ts @@ -0,0 +1,174 @@ +import type { PackageSchema } from "./index.js"; +import z from "zod"; + +export type JsonValue = + | null + | boolean + | number + | string + | JsonValue[] + | { [key: string]: JsonValue }; + +export const jsonValueSchema: z.ZodType = z.lazy(() => + z.union([ + z.string(), + z.number(), + z.boolean(), + z.null(), + z.array(jsonValueSchema), + z.record(z.string(), jsonValueSchema), + ]), +); + +type BrowserSurfaceSchemaLike = PackageSchema & { + __quixos?: { + name?: string | null; + flakeRef?: string | null; + }; +}; + +type BrowserSurfaceAnnotation = { + type: "quixos.ui.surface/v1"; + surfaceId: string; +}; + +const browserSurfaceAnnotationSchema = z.object({ + type: z.literal("quixos.ui.surface/v1"), + surfaceId: z.string().min(1), +}); + +const normalizePackageName = (value: string) => { + const normalized = value.startsWith("@quixos-package-schemas/") + ? value.slice("@quixos-package-schemas/".length) + : value; + return normalized.endsWith(".git") + ? normalized.slice(0, -".git".length) + : normalized; +}; + +const packageNameFromFlakeRef = (flakeRef: string) => { + const withoutQuery = flakeRef.split("?")[0] ?? flakeRef; + const withoutGitPrefix = withoutQuery.startsWith("git+") + ? withoutQuery.slice(4) + : withoutQuery; + + try { + const parsed = new URL(withoutGitPrefix); + const segments = parsed.pathname.split("/").filter(Boolean); + const packageName = segments.at(-1); + if (packageName) { + return normalizePackageName(packageName); + } + } catch { + // Fall through to a simple path split for non-URL flake refs. + } + + const segments = withoutGitPrefix.split("/").filter(Boolean); + const packageName = segments.at(-1); + if (!packageName) { + throw new Error(`Unable to determine package name for ${flakeRef}`); + } + return normalizePackageName(packageName); +}; + +const resolveSchemaPackageName = (schema: BrowserSurfaceSchemaLike) => { + if (typeof schema.__quixos?.name === "string" && schema.__quixos.name.length > 0) { + return normalizePackageName(schema.__quixos.name); + } + if ( + typeof schema.__quixos?.flakeRef === "string" && + schema.__quixos.flakeRef.length > 0 + ) { + return packageNameFromFlakeRef(schema.__quixos.flakeRef); + } + throw new Error( + "Package schema is missing __quixos metadata. Rebuild the schema with quixos helpers.", + ); +}; + +const findDefaultSurfaceId = (schema: BrowserSurfaceSchemaLike) => { + const annotations = Array.isArray(schema.annotations) ? schema.annotations : []; + for (const annotation of annotations) { + const parsed = browserSurfaceAnnotationSchema.safeParse(annotation); + if (parsed.success) { + return parsed.data.surfaceId; + } + } + return "desktop"; +}; + +const resolvePackageName = (target: string | BrowserSurfaceSchemaLike) => + typeof target === "string" + ? normalizePackageName(target) + : resolveSchemaPackageName(target); + +const resolveSurfaceId = ( + target: string | BrowserSurfaceSchemaLike, + surfaceId?: string, +) => surfaceId ?? (typeof target === "string" ? "desktop" : findDefaultSurfaceId(target)); + +export type EmbeddedSurfaceRef< + PackageName extends string = string, + SurfaceId extends string = string, +> = { + packageName: PackageName; + stateContextId: string; + surfaceId: SurfaceId; +}; + +export const embeddedSurfaceRefSchema = z.object({ + packageName: z.string().min(1), + stateContextId: z.string().min(1), + surfaceId: z.string().min(1).default("desktop"), +}); + +export const createEmbeddedSurfaceRefSchema = < + PackageName extends string = string, + SurfaceId extends string = string, +>( + target: string | BrowserSurfaceSchemaLike, + options?: { + surfaceId?: SurfaceId; + }, +) => { + const packageName = resolvePackageName(target) as PackageName; + const surfaceId = resolveSurfaceId( + target, + options?.surfaceId, + ) as SurfaceId; + return z.object({ + packageName: z.literal(packageName), + stateContextId: z.string().min(1), + surfaceId: z.string().min(1).default(surfaceId), + }); +}; + +export const createEmbeddedSurfaceRef = < + PackageName extends string = string, + SurfaceId extends string = string, +>( + target: string | BrowserSurfaceSchemaLike, + options: { + stateContextId: string; + surfaceId?: SurfaceId; + }, +): EmbeddedSurfaceRef => { + const packageName = resolvePackageName(target) as PackageName; + const surfaceId = resolveSurfaceId(target, options.surfaceId) as SurfaceId; + return { + packageName, + stateContextId: options.stateContextId, + surfaceId, + }; +}; + +export const getEmbeddedSurfacePackageName = ( + target: string | BrowserSurfaceSchemaLike, +) => resolvePackageName(target); + +export const getEmbeddedSurfaceId = ( + target: string | BrowserSurfaceSchemaLike, + surfaceId?: string, +) => resolveSurfaceId(target, surfaceId); + +export type { BrowserSurfaceAnnotation }; diff --git a/src/index.ts b/src/index.ts new file mode 100644 index 0000000..a2d5a40 --- /dev/null +++ b/src/index.ts @@ -0,0 +1,1853 @@ +import { AsyncLocalStorage } from "node:async_hooks"; +import crypto from "node:crypto"; +import fs from "node:fs"; +import os from "node:os"; +import path from "node:path"; +import WebSocket from "ws"; +import z, { type ZodTypeAny, type output as ZodOutput } from "zod"; +import { + BootRequestParamsSchema, + CallRequestParamsSchema, + ContextCloseRequestParamsSchema, + ContextOpenRequestParamsSchema, + EventSubscribeRequestParamsSchema, + EventUnsubscribeRequestParamsSchema, + type RuntimeErrorRequestParams, + ServerMessageSchema, + TargetContextOpenRequestParamsSchema, + ValueGetRequestParamsSchema, + ValueUnwatchRequestParamsSchema, + ValueWatchRequestParamsSchema, + parseJson, + sendClientMessage, +} from "./rpc.js"; + +export * from "./rpc.js"; + +export type PayloadMode = "snapshot" | "delta"; + +export type SchemaAnnotation = { + type: string; + [key: string]: unknown; +}; + +export type PackageFunctionSchema = { + description: string; + annotations?: SchemaAnnotation[]; + inputSchema: ZodTypeAny; + outputSchema: ZodTypeAny; +}; + +type StaticResourceSchema = { + description: string; + annotations?: SchemaAnnotation[]; + dataSchema: ZodTypeAny; + history?: boolean; + payloadMode?: PayloadMode; +}; + +type ParameterizedResourceSchema = StaticResourceSchema & { + paramsSchema: ZodTypeAny; +}; + +export type StaticEventSchema = StaticResourceSchema; +export type ParameterizedEventSchema = ParameterizedResourceSchema; +export type EventSchema = StaticEventSchema | ParameterizedEventSchema; +export type EventSchemaMap = Record; + +export type StaticValueSchema = StaticResourceSchema; +export type ParameterizedValueSchema = ParameterizedResourceSchema; +export type ValueSchema = StaticValueSchema | ParameterizedValueSchema; +export type ValueSchemaMap = Record; + +export type PackageSchema< + Functions extends Record = Record< + string, + PackageFunctionSchema + >, + Events extends EventSchemaMap = EventSchemaMap, + Values extends ValueSchemaMap = ValueSchemaMap, +> = { + schemaVersion: 1; + majorVersion: number; + description: string; + annotations?: SchemaAnnotation[]; + functions: Functions; + events: Events; + values: Values; +}; + +export const QUIXOS_MEDIA_JSON_SCHEMA_KEY = "x-quixos-media"; + +export type QuixosDataUrlAnnotation = { + transport: "data-url"; + mimeType: string; + extension?: string; +}; + +type QuixosSchemaMetaRecord = Record; + +const readSchemaMeta = (schema: ZodTypeAny): QuixosSchemaMetaRecord => { + try { + const meta = schema.meta(); + if (meta && typeof meta === "object") { + return { ...(meta as QuixosSchemaMetaRecord) }; + } + } catch { + // ignore + } + return {}; +}; + +export const annotateDataUrlSchema = ( + schema: Schema, + params: { mimeType: string; extension?: string }, +): Schema => + schema.meta({ + ...readSchemaMeta(schema), + [QUIXOS_MEDIA_JSON_SCHEMA_KEY]: { + transport: "data-url", + mimeType: params.mimeType, + ...(params.extension ? { extension: params.extension } : {}), + } satisfies QuixosDataUrlAnnotation, + }) as Schema; + +export const quixosMedia = { + dataUrlString: (params: { mimeType: string; extension?: string }) => + annotateDataUrlSchema(z.string(), params), + annotateDataUrl: annotateDataUrlSchema, +}; + +type FunctionInput = ZodOutput< + Schema["inputSchema"] +>; +type FunctionOutput = ZodOutput< + Schema["outputSchema"] +>; + +type SchemaParams< + Schema extends StaticResourceSchema | ParameterizedResourceSchema, +> = Schema extends { paramsSchema: infer ParamsSchema extends ZodTypeAny } + ? ZodOutput + : undefined; + +type EventParams = SchemaParams; +type EventData = ZodOutput; +type ValueParams = SchemaParams; +type ValueData = ZodOutput; + +export type SubscriptionOptions = { + signal?: AbortSignal; + subscriptionNamespace?: string; +}; + +export type SubscriptionHandle = { + unsubscribe: () => Promise; +}; + +export type UsePackageOptions = { + contextNamespace?: string; + stateContextId?: string; +}; + +type ScopedResource = T & { + withStateContext: (stateContextId: string) => T; + withContextNamespace: (contextNamespace?: string) => T; +}; + +type EventConsumer = ( + data: EventData, +) => void | Promise; + +type ValueConsumer = ( + data: ValueData, +) => void | Promise; + +type StaticEventClientBase = { + consume: ( + handler: EventConsumer, + options?: SubscriptionOptions, + ) => Promise; +}; + +type ParameterizedEventClientBase = { + consume: ( + params: EventParams, + handler: EventConsumer, + options?: SubscriptionOptions, + ) => Promise; +}; + +export type EventClient = + Schema extends ParameterizedEventSchema + ? ScopedResource> + : ScopedResource>; + +export type EventClients = { + [Key in keyof Schema]: EventClient; +}; + +type StaticValueClientBase = { + get: () => Promise>; + watch: ( + handler: ValueConsumer, + options?: SubscriptionOptions, + ) => Promise; +}; + +type ParameterizedValueClientBase = { + get: (params: ValueParams) => Promise>; + watch: ( + params: ValueParams, + handler: ValueConsumer, + options?: SubscriptionOptions, + ) => Promise; +}; + +export type ValueClient = + Schema extends ParameterizedValueSchema + ? ScopedResource> + : ScopedResource>; + +export type ValueClients = { + [Key in keyof Schema]: ValueClient; +}; + +export type PackageContext = { + stateContext: string; + stateDirectory: string; + usePackage: ( + schema: Schema, + options?: UsePackageOptions, + ) => PackageClient; +}; + +export type PackageFunction< + Schema extends PackageFunctionSchema, + Context = any, +> = ( + ctx: Context, + params: FunctionInput, +) => FunctionOutput | Promise>; + +export type PackageFunctions< + Functions extends Record, + Context = any, +> = { + [Key in keyof Functions]: PackageFunction; +}; + +export type EventSink = { + subscriptionId: string; + emit: (data: EventData) => Promise; +}; + +type EventHandler = { + subscribe: ( + ctx: Context, + params: EventParams, + sink: EventSink, + ) => + | void + | (() => void | Promise) + | Promise void | Promise)>; +}; + +export type PackageEvents< + Events extends EventSchemaMap, + Context = any, +> = { + [Key in keyof Events]: EventHandler; +}; + +export type ValueSink = { + subscriptionId: string; + set: (data: ValueData) => Promise; +}; + +type ValueHandler = { + get: ( + ctx: Context, + params: ValueParams, + ) => ValueData | Promise>; + watch?: ( + ctx: Context, + params: ValueParams, + sink: ValueSink, + ) => + | void + | (() => void | Promise) + | Promise void | Promise)>; +}; + +export type PackageValues< + Values extends ValueSchemaMap, + Context = any, +> = { + [Key in keyof Values]: ValueHandler; +}; + +type InternalUsePackageOptions = UsePackageOptions & { + callerStateContextId?: string; +}; + +export type PackageFunctionCaller = + ScopedResource<( + params: FunctionInput, + ) => Promise>>; + +type PackageFunctionCallers< + Functions extends Record, +> = { + [Key in keyof Functions]: PackageFunctionCaller; +}; + +export type PackageClient = { + functions: PackageFunctionCallers; + events: EventClients; + values: ValueClients; + withStateContext: (stateContextId: string) => PackageClient; + withContextNamespace: ( + contextNamespace?: string, + ) => PackageClient; +}; + +export type CreatePackageOptions< + Schema extends PackageSchema, + Context extends PackageContext = PackageContext, +> = { + schema: Schema; + functions: PackageFunctions; + events: PackageEvents; + values: PackageValues; + onCreate?: () => void | Promise; + onContextOpen?: (ctx: Context) => void | Promise; + onContextClose?: (ctx: Context) => void | Promise; + onDestroy?: () => void | Promise; +}; + +type LifecycleManager = {}; + +type PendingRequest = { + resolve: (value: unknown) => void; + reject: (error: Error) => void; + timeout: NodeJS.Timeout; +}; + +type ClientSubscriptionEntry = { + deliver: (data: unknown) => void; + closeLocal: (error?: Error) => void; +}; + +type ServerSubscriptionEntry = { + stateContextId: string; + kind: "event" | "value"; + resourceName: string; + closeRemote: () => Promise; +}; + +type QuixosSchemaMeta = { + name?: string | null; + type?: string | null; + url?: string | null; + rev?: string | null; + flakeRef?: string | null; +}; + +type QuixosSchema = PackageSchema & { __quixos?: QuixosSchemaMeta }; +type RuntimeExecutionContext = PackageContext; +export type RuntimeErrorPhase = RuntimeErrorRequestParams["phase"]; + +export type RuntimeErrorReportParams = { + phase: RuntimeErrorPhase; + error: unknown; + stateContextId?: string; + functionName?: string; + resourceKind?: "event" | "value"; + resourceName?: string; + surfaceId?: string; + hostSessionId?: string; + stack?: string; +}; + +const pendingRequests = new Map(); +const clientSubscriptions = new Map(); +const serverSubscriptions = new Map(); +const runtimeContextStorage = new AsyncLocalStorage(); +const openedRuntimeStateContexts = new Set(); +const DEFAULT_REQUEST_TIMEOUT_MS = 60_000; +const PACKAGE_BOOT_TIMEOUT_MS = 300_000; +const TARGET_CONTEXT_OPEN_TIMEOUT_MS = 300_000; + +let socketPromise: Promise | null = null; +let socketResolve: ((socket: WebSocket) => void) | null = null; +let socketRef: WebSocket | null = null; +let socketAuthenticated = false; +let packageOptions: CreatePackageOptions | null = null; +let runtimeExitTimer: NodeJS.Timeout | null = null; +let runtimeExitRequested = false; + +const clearRuntimeExitTimer = () => { + if (runtimeExitTimer) { + clearTimeout(runtimeExitTimer); + runtimeExitTimer = null; + } +}; + +const scheduleRuntimeExit = (code: number, reason: string) => { + if (runtimeExitRequested) { + return; + } + runtimeExitRequested = true; + clearRuntimeExitTimer(); + runtimeExitTimer = setTimeout(() => { + console.warn(`[quixos-runtime] exiting (${reason})`); + process.exit(code); + }, 100); +}; + +const closeServerSubscription = async (subscriptionId: string) => { + const subscription = serverSubscriptions.get(subscriptionId); + if (!subscription) { + return; + } + serverSubscriptions.delete(subscriptionId); + try { + await subscription.closeRemote(); + } catch (error) { + await reportPackageRuntimeError({ + phase: subscription.kind === "event" ? "event-cleanup" : "value-cleanup", + error, + stateContextId: subscription.stateContextId, + resourceKind: subscription.kind, + resourceName: subscription.resourceName, + }); + throw error; + } +}; + +const normalizeError = (error: unknown) => + error instanceof Error ? error : new Error(String(error)); + +const createRuntimeErrorPayload = (params: RuntimeErrorReportParams) => { + const normalized = normalizeError(params.error); + return { + phase: params.phase, + stateContextId: params.stateContextId, + functionName: params.functionName, + resourceKind: params.resourceKind, + resourceName: params.resourceName, + surfaceId: params.surfaceId, + hostSessionId: params.hostSessionId, + message: normalized.message || "Unknown error", + stack: params.stack ?? normalized.stack, + }; +}; + +export const reportPackageRuntimeError = async ( + params: RuntimeErrorReportParams, +) => { + try { + await sendRequest("runtime-error", createRuntimeErrorPayload(params), 5_000); + } catch { + // Best-effort reporting only. + } +}; + +const resetSocket = () => { + const shouldExitForSocketClose = + socketAuthenticated && + packageOptions !== null && + Boolean(process.env.QUIXOS_ORCH_SECRET); + socketRef = null; + socketPromise = null; + socketResolve = null; + socketAuthenticated = false; + + for (const pending of pendingRequests.values()) { + clearTimeout(pending.timeout); + pending.reject(new Error("Socket closed")); + } + pendingRequests.clear(); + + for (const subscription of clientSubscriptions.values()) { + subscription.closeLocal(new Error("Socket closed")); + } + clientSubscriptions.clear(); + + const activeServerSubscriptionIds = [...serverSubscriptions.keys()]; + for (const subscriptionId of activeServerSubscriptionIds) { + void closeServerSubscription(subscriptionId).catch(() => {}); + } + + if (shouldExitForSocketClose) { + scheduleRuntimeExit(0, "orchestrator-socket-closed"); + } +}; + +const getCurrentStateContextId = () => + runtimeContextStorage.getStore()?.stateContext; + +const getStateDirectoryRoot = () => { + const xdgDataHome = process.env.XDG_DATA_HOME; + if (xdgDataHome && xdgDataHome.length > 0) { + return path.join(xdgDataHome, "quixos", "state-contexts"); + } + return path.join(os.homedir(), ".local", "share", "quixos", "state-contexts"); +}; + +const sanitizePackageNameForPath = (packageName: string) => + encodeURIComponent(packageName); + +const resolveCurrentPackageName = () => { + const meta = (packageOptions?.schema as QuixosSchema | undefined)?.__quixos; + if (meta) { + return resolveTargetPackageName(meta); + } + + const packageRef = process.env.QUIXOS_PACKAGE_REF; + if (packageRef) { + return normalizePackageName(packageNameFromFlakeRef(packageRef)); + } + + throw new Error("Unable to determine current package name for runtime context."); +}; + +const getStateDirectoryForContext = ( + packageName: string, + stateContextId: string, +) => + path.join( + getStateDirectoryRoot(), + sanitizePackageNameForPath(packageName), + stateContextId, + ); + +const withRuntimeContext = async ( + ctx: RuntimeExecutionContext, + fn: () => T | Promise, +) => await runtimeContextStorage.run(ctx, fn); + +export const runWithStateContext = async ( + stateContextId: string, + fn: () => T | Promise, +) => + await withRuntimeContext( + createRuntimeContext(stateContextId), + fn, + ); + +function createRuntimeContext(stateContextId: string): PackageContext { + const packageName = resolveCurrentPackageName(); + const stateDirectory = getStateDirectoryForContext(packageName, stateContextId); + fs.mkdirSync(stateDirectory, { recursive: true }); + + return { + stateContext: stateContextId, + stateDirectory, + usePackage: ( + schema: Schema, + options: UsePackageOptions = {}, + ) => + createPackageClient(schema, { + ...options, + callerStateContextId: stateContextId, + }), + }; +} + +const closeRuntimeContext = async (stateContextId: string) => { + const hasOpenedContext = openedRuntimeStateContexts.has(stateContextId); + const activeServerSubscriptionIds = [...serverSubscriptions.entries()] + .filter(([, subscription]) => subscription.stateContextId === stateContextId) + .map(([subscriptionId]) => subscriptionId); + + if (!hasOpenedContext && activeServerSubscriptionIds.length === 0) { + return; + } + + for (const subscriptionId of activeServerSubscriptionIds) { + await closeServerSubscription(subscriptionId); + } + + if (!hasOpenedContext) { + return; + } + + const ctx = createRuntimeContext(stateContextId); + try { + await withRuntimeContext(ctx, async () => { + await packageOptions?.onContextClose?.(ctx as any); + }); + } finally { + openedRuntimeStateContexts.delete(stateContextId); + } +}; + +const hasParamsSchema = ( + schema: StaticResourceSchema | ParameterizedResourceSchema, +): schema is ParameterizedResourceSchema => "paramsSchema" in schema; + +const parseResourceParams = ( + resourceKind: "event" | "value", + resourceName: string, + schema: Schema, + params: unknown, +) => { + if (!hasParamsSchema(schema)) { + if (params !== undefined) { + throw new Error(`${resourceKind} ${resourceName} does not take params`); + } + return undefined as SchemaParams; + } + + const parsed = schema.paramsSchema.safeParse(params); + if (!parsed.success) { + throw new Error(`Invalid params for ${resourceKind} ${resourceName}`); + } + return parsed.data as SchemaParams; +}; + +const parseResourceData = ( + resourceKind: "event" | "value", + resourceName: string, + schema: Schema, + data: unknown, +) => { + const parsed = schema.dataSchema.safeParse(data); + if (!parsed.success) { + throw new Error(`Invalid data for ${resourceKind} ${resourceName}`); + } + return parsed.data as ZodOutput; +}; + +const normalizePackageName = (value: string) => { + const normalized = value.startsWith("@quixos-package-schemas/") + ? value.slice("@quixos-package-schemas/".length) + : value; + return normalized.endsWith(".git") + ? normalized.slice(0, -".git".length) + : normalized; +}; + +const packageNameFromFlakeRef = (flakeRef: string) => { + const withoutQuery = flakeRef.split("?")[0] ?? flakeRef; + const withoutGitPrefix = withoutQuery.startsWith("git+") + ? withoutQuery.slice(4) + : withoutQuery; + + try { + const parsed = new URL(withoutGitPrefix); + const segments = parsed.pathname.split("/").filter(Boolean); + const packageName = segments.at(-1); + if (packageName) { + return normalizePackageName(packageName); + } + } catch { + // Fall through to a simple path split for non-URL flake refs. + } + + const segments = withoutGitPrefix.split("/").filter(Boolean); + const packageName = segments.at(-1); + if (!packageName) { + throw new Error(`Unable to determine package name for ${flakeRef}`); + } + return normalizePackageName(packageName); +}; + +const resolveTargetPackageName = (meta: QuixosSchemaMeta) => { + if (meta.name) { + return normalizePackageName(meta.name); + } + if (meta.flakeRef) { + return packageNameFromFlakeRef(meta.flakeRef); + } + throw new Error("Package schema is missing package name metadata."); +}; + +const sendErrorResponse = ( + socket: WebSocket, + requestId: string, + error: unknown, +) => { + const normalized = normalizeError(error); + sendClientMessage(socket, { + type: "response", + requestId, + ok: false, + error: normalized.message || "Unknown error", + }); +}; + +const reportAndSendErrorResponse = async ( + socket: WebSocket, + requestId: string, + params: { + phase: RuntimeErrorPhase; + error: unknown; + stateContextId?: string; + functionName?: string; + resourceKind?: "event" | "value"; + resourceName?: string; + }, +) => { + void reportPackageRuntimeError(params); + sendErrorResponse(socket, requestId, params.error); +}; + +const handleRequest = async (socket: WebSocket, message: any) => { + if (!packageOptions) { + sendClientMessage(socket, { + type: "response", + requestId: message.requestId, + ok: false, + error: "Package not initialized", + }); + return; + } + + if (message.method === "stop") { + try { + const activeStateContextIds = [...openedRuntimeStateContexts]; + for (const stateContextId of activeStateContextIds) { + await closeRuntimeContext(stateContextId); + } + await packageOptions.onDestroy?.(); + sendClientMessage(socket, { + type: "response", + requestId: message.requestId, + ok: true, + }); + scheduleRuntimeExit(0, "stop-request"); + } catch (error) { + await reportAndSendErrorResponse(socket, message.requestId, { + phase: "on-destroy", + error, + }); + } + return; + } + + if (message.method === "context-open") { + const parsedParams = ContextOpenRequestParamsSchema.safeParse( + message.params, + ); + if (!parsedParams.success) { + sendClientMessage(socket, { + type: "response", + requestId: message.requestId, + ok: false, + error: "Invalid context open params", + }); + return; + } + + const ctx = createRuntimeContext(parsedParams.data.stateContextId); + + try { + await withRuntimeContext(ctx, async () => { + await packageOptions?.onContextOpen?.(ctx); + }); + openedRuntimeStateContexts.add(ctx.stateContext); + sendClientMessage(socket, { + type: "response", + requestId: message.requestId, + ok: true, + }); + } catch (error) { + await reportAndSendErrorResponse(socket, message.requestId, { + phase: "on-context-open", + error, + stateContextId: ctx.stateContext, + }); + } + return; + } + + if (message.method === "context-close") { + const parsedParams = ContextCloseRequestParamsSchema.safeParse( + message.params, + ); + if (!parsedParams.success) { + sendClientMessage(socket, { + type: "response", + requestId: message.requestId, + ok: false, + error: "Invalid context close params", + }); + return; + } + + try { + await closeRuntimeContext(parsedParams.data.stateContextId); + sendClientMessage(socket, { + type: "response", + requestId: message.requestId, + ok: true, + }); + } catch (error) { + await reportAndSendErrorResponse(socket, message.requestId, { + phase: "on-context-close", + error, + stateContextId: parsedParams.data.stateContextId, + }); + } + return; + } + + if (message.method === "call") { + const parsedParams = CallRequestParamsSchema.safeParse(message.params); + if (!parsedParams.success) { + sendClientMessage(socket, { + type: "response", + requestId: message.requestId, + ok: false, + error: "Invalid call params", + }); + return; + } + + const { functionName, params, stateContextId } = parsedParams.data; + if (!stateContextId) { + sendClientMessage(socket, { + type: "response", + requestId: message.requestId, + ok: false, + error: "Missing state context", + }); + return; + } + + const fn = packageOptions.functions?.[functionName]; + if (!fn) { + sendClientMessage(socket, { + type: "response", + requestId: message.requestId, + ok: false, + error: `Unknown function: ${functionName}`, + }); + return; + } + + const ctx = createRuntimeContext(stateContextId); + + try { + const result = await withRuntimeContext( + ctx, + async () => await fn(ctx, params), + ); + sendClientMessage(socket, { + type: "response", + requestId: message.requestId, + ok: true, + result, + }); + } catch (error) { + await reportAndSendErrorResponse(socket, message.requestId, { + phase: "function", + error, + stateContextId, + functionName, + }); + } + return; + } + + if (message.method === "boot") { + const parsedParams = BootRequestParamsSchema.safeParse(message.params); + if (!parsedParams.success) { + sendClientMessage(socket, { + type: "response", + requestId: message.requestId, + ok: false, + error: "Invalid boot params", + }); + return; + } + sendClientMessage(socket, { + type: "response", + requestId: message.requestId, + ok: true, + }); + return; + } + + if (message.method === "event-subscribe") { + const parsedParams = EventSubscribeRequestParamsSchema.safeParse( + message.params, + ); + if (!parsedParams.success) { + sendClientMessage(socket, { + type: "response", + requestId: message.requestId, + ok: false, + error: "Invalid event subscribe params", + }); + return; + } + + const { eventName, params, stateContextId } = parsedParams.data; + if (!stateContextId) { + sendClientMessage(socket, { + type: "response", + requestId: message.requestId, + ok: false, + error: "Missing state context", + }); + return; + } + + const eventSchema = packageOptions.schema.events?.[eventName]; + const eventHandler = packageOptions.events?.[eventName]; + if (!eventSchema || !eventHandler) { + sendClientMessage(socket, { + type: "response", + requestId: message.requestId, + ok: false, + error: `Unknown event: ${eventName}`, + }); + return; + } + + const ctx = createRuntimeContext(stateContextId); + const subscriptionId = crypto.randomUUID(); + + try { + const parsedResourceParams = parseResourceParams( + "event", + eventName, + eventSchema, + params, + ); + const cleanup = await withRuntimeContext(ctx, async () => + await eventHandler.subscribe(ctx, parsedResourceParams, { + subscriptionId, + emit: async (data) => { + const parsedData = parseResourceData( + "event", + eventName, + eventSchema, + data, + ); + await publishToSubscription(subscriptionId, parsedData); + }, + }), + ); + + serverSubscriptions.set(subscriptionId, { + stateContextId: ctx.stateContext, + kind: "event", + resourceName: eventName, + closeRemote: async () => { + if (typeof cleanup === "function") { + await runWithStateContext(ctx.stateContext, cleanup); + } + }, + }); + + sendClientMessage(socket, { + type: "response", + requestId: message.requestId, + ok: true, + result: { + subscriptionId, + }, + }); + } catch (error) { + await reportAndSendErrorResponse(socket, message.requestId, { + phase: "event-subscribe", + error, + stateContextId, + resourceKind: "event", + resourceName: eventName, + }); + } + return; + } + + if (message.method === "event-unsubscribe") { + const parsedParams = EventUnsubscribeRequestParamsSchema.safeParse( + message.params, + ); + if (!parsedParams.success) { + sendClientMessage(socket, { + type: "response", + requestId: message.requestId, + ok: false, + error: "Invalid event unsubscribe params", + }); + return; + } + + try { + await closeServerSubscription(parsedParams.data.subscriptionId); + sendClientMessage(socket, { + type: "response", + requestId: message.requestId, + ok: true, + }); + } catch (error) { + sendErrorResponse(socket, message.requestId, error); + } + return; + } + + if (message.method === "value-get") { + const parsedParams = ValueGetRequestParamsSchema.safeParse(message.params); + if (!parsedParams.success) { + sendClientMessage(socket, { + type: "response", + requestId: message.requestId, + ok: false, + error: "Invalid value get params", + }); + return; + } + + const { valueName, params, stateContextId } = parsedParams.data; + if (!stateContextId) { + sendClientMessage(socket, { + type: "response", + requestId: message.requestId, + ok: false, + error: "Missing state context", + }); + return; + } + + const valueSchema = packageOptions.schema.values?.[valueName]; + const valueHandler = packageOptions.values?.[valueName]; + if (!valueSchema || !valueHandler) { + sendClientMessage(socket, { + type: "response", + requestId: message.requestId, + ok: false, + error: `Unknown value: ${valueName}`, + }); + return; + } + + const ctx = createRuntimeContext(stateContextId); + + try { + const parsedResourceParams = parseResourceParams( + "value", + valueName, + valueSchema, + params, + ); + const result = await withRuntimeContext(ctx, async () => + await valueHandler.get(ctx, parsedResourceParams), + ); + const parsedResult = parseResourceData( + "value", + valueName, + valueSchema, + result, + ); + sendClientMessage(socket, { + type: "response", + requestId: message.requestId, + ok: true, + result: parsedResult, + }); + } catch (error) { + await reportAndSendErrorResponse(socket, message.requestId, { + phase: "value-get", + error, + stateContextId, + resourceKind: "value", + resourceName: valueName, + }); + } + return; + } + + if (message.method === "value-watch") { + const parsedParams = ValueWatchRequestParamsSchema.safeParse( + message.params, + ); + if (!parsedParams.success) { + sendClientMessage(socket, { + type: "response", + requestId: message.requestId, + ok: false, + error: "Invalid value watch params", + }); + return; + } + + const { valueName, params, stateContextId } = parsedParams.data; + if (!stateContextId) { + sendClientMessage(socket, { + type: "response", + requestId: message.requestId, + ok: false, + error: "Missing state context", + }); + return; + } + + const valueSchema = packageOptions.schema.values?.[valueName]; + const valueHandler = packageOptions.values?.[valueName]; + if (!valueSchema || !valueHandler?.watch) { + sendClientMessage(socket, { + type: "response", + requestId: message.requestId, + ok: false, + error: `Unknown watchable value: ${valueName}`, + }); + return; + } + + const ctx = createRuntimeContext(stateContextId); + const subscriptionId = crypto.randomUUID(); + + try { + const parsedResourceParams = parseResourceParams( + "value", + valueName, + valueSchema, + params, + ); + const cleanup = await withRuntimeContext(ctx, async () => + await valueHandler.watch?.(ctx, parsedResourceParams, { + subscriptionId, + set: async (data) => { + const parsedData = parseResourceData( + "value", + valueName, + valueSchema, + data, + ); + await publishToSubscription(subscriptionId, parsedData); + }, + }), + ); + + serverSubscriptions.set(subscriptionId, { + stateContextId: ctx.stateContext, + kind: "value", + resourceName: valueName, + closeRemote: async () => { + if (typeof cleanup === "function") { + await runWithStateContext(ctx.stateContext, cleanup); + } + }, + }); + + sendClientMessage(socket, { + type: "response", + requestId: message.requestId, + ok: true, + result: { + subscriptionId, + }, + }); + } catch (error) { + await reportAndSendErrorResponse(socket, message.requestId, { + phase: "value-watch", + error, + stateContextId, + resourceKind: "value", + resourceName: valueName, + }); + } + return; + } + + if (message.method === "value-unwatch") { + const parsedParams = ValueUnwatchRequestParamsSchema.safeParse( + message.params, + ); + if (!parsedParams.success) { + sendClientMessage(socket, { + type: "response", + requestId: message.requestId, + ok: false, + error: "Invalid value unwatch params", + }); + return; + } + + try { + await closeServerSubscription(parsedParams.data.subscriptionId); + sendClientMessage(socket, { + type: "response", + requestId: message.requestId, + ok: true, + }); + } catch (error) { + sendErrorResponse(socket, message.requestId, error); + } + return; + } + + sendClientMessage(socket, { + type: "response", + requestId: message.requestId, + ok: false, + error: "Unknown method", + }); +}; + +const handleMessage = (socket: WebSocket, data: WebSocket.RawData) => { + const parsed = parseJson(data.toString()); + if (!parsed.ok) { + return; + } + const messageResult = ServerMessageSchema.safeParse(parsed.value); + if (!messageResult.success) { + return; + } + const message = messageResult.data; + + if (message.type === "auth-ack") { + socketAuthenticated = true; + runtimeExitRequested = false; + clearRuntimeExitTimer(); + if (socketResolve) { + socketResolve(socket); + socketResolve = null; + } + return; + } + + if (message.type === "subscription-data") { + const subscription = clientSubscriptions.get(message.subscriptionId); + if (!subscription) { + return; + } + subscription.deliver(message.data); + return; + } + + if (message.type === "response") { + const pending = pendingRequests.get(message.requestId); + if (!pending) { + return; + } + pendingRequests.delete(message.requestId); + clearTimeout(pending.timeout); + if (message.ok === false) { + pending.reject(new Error(message.error ?? "Unknown error")); + } else { + pending.resolve(message.result); + } + return; + } + + if (message.type === "request") { + void handleRequest(socket, message); + } +}; + +const ensureSocket = async (requireAuth: boolean) => { + if (requireAuth && !process.env.QUIXOS_ORCH_SECRET) { + throw new Error("QUIXOS_ORCH_SECRET is not set; cannot use orchestrator"); + } + + if (!socketPromise) { + socketPromise = new Promise((resolve) => { + socketResolve = resolve; + }); + const socket = new WebSocket("ws://127.0.0.1:6245"); + socketRef = socket; + socket.on("open", () => { + const secret = process.env.QUIXOS_ORCH_SECRET; + if (secret) { + sendClientMessage(socket, { + type: "auth", + secret, + pid: process.pid, + }); + } else if (socketResolve) { + socketResolve(socket); + socketResolve = null; + } + }); + socket.on("message", (eventData) => handleMessage(socket, eventData)); + socket.on("close", resetSocket); + socket.on("error", resetSocket); + } + + const socket = await socketPromise; + if (requireAuth && !socketAuthenticated) { + return await socketPromise; + } + return socket; +}; + +const sendRequest = async ( + method: + | "stop" + | "call" + | "boot" + | "target-context-open" + | "runtime-error" + | "context-open" + | "context-close" + | "event-subscribe" + | "subscription-ready" + | "event-unsubscribe" + | "value-get" + | "value-watch" + | "value-unwatch" + | "subscription-publish", + params: Record, + timeoutMs?: number, +) => { + const socket = await ensureSocket(true); + const requestId = crypto.randomUUID(); + const effectiveTimeoutMs = + timeoutMs ?? + (method === "boot" + ? PACKAGE_BOOT_TIMEOUT_MS + : method === "target-context-open" || method === "context-open" + ? TARGET_CONTEXT_OPEN_TIMEOUT_MS + : DEFAULT_REQUEST_TIMEOUT_MS); + return await new Promise((resolve, reject) => { + const timeout = setTimeout(() => { + pendingRequests.delete(requestId); + reject(new Error("Request timed out")); + }, effectiveTimeoutMs); + pendingRequests.set(requestId, { resolve, reject, timeout }); + sendClientMessage(socket, { + type: "request", + requestId, + method, + params, + }); + }); +}; + +export const publishToSubscription = async ( + subscriptionId: string, + data: unknown, +) => { + await sendRequest("subscription-publish", { + subscriptionId, + data, + }); +}; + +const createPackageClient = ( + schema: Schema, + baseOptions: InternalUsePackageOptions = {}, +): PackageClient => { + const meta = (schema as QuixosSchema).__quixos; + if (!meta?.flakeRef) { + throw new Error( + "Package schema is missing __quixos metadata. Rebuild the schema with quixos helpers.", + ); + } + + const targetPackageName = resolveTargetPackageName(meta); + let bootError: Error | null = null; + let bootPromise: Promise | null = null; + + const ensureBooted = async () => { + if (!bootPromise) { + bootPromise = (async () => { + try { + await sendRequest("boot", { + target: meta.flakeRef, + }); + } catch (error) { + bootError = error instanceof Error ? error : new Error(String(error)); + } + })(); + } + await bootPromise; + if (bootError) { + throw bootError; + } + }; + + const createClient = ( + scope: InternalUsePackageOptions, + ): PackageClient => { + let readyError: Error | null = null; + let readyPromise: Promise | null = null; + + const resolveRouting = (callerStateContextId?: string) => ({ + callerStateContextId: + scope.callerStateContextId ?? callerStateContextId, + targetStateContextId: scope.stateContextId, + contextNamespace: scope.stateContextId ? undefined : scope.contextNamespace, + }); + + const missingStateContextError = () => + new Error( + `No active state context for package ${targetPackageName}. Move this call into \`onContextOpen(ctx)\` or another handler with \`ctx.usePackage(...)\`, or pass \`stateContextId\` explicitly.`, + ); + + const ensurePackageReady = async (callerStateContextId?: string) => { + if (!readyPromise) { + readyPromise = (async () => { + try { + await ensureBooted(); + const routing = resolveRouting(callerStateContextId); + if (!routing.callerStateContextId && !routing.targetStateContextId) { + return; + } + + await sendRequest("target-context-open", { + target: meta.flakeRef, + targetPackageName, + callerStateContextId: routing.callerStateContextId, + stateContextId: routing.targetStateContextId, + contextNamespace: routing.contextNamespace, + }); + } catch (error) { + readyError = + error instanceof Error ? error : new Error(String(error)); + } + })(); + } + + await readyPromise; + if (readyError) { + throw readyError; + } + }; + + const scopedResourceMethods = ( + lookup: (client: PackageClient) => T, + ) => ({ + withStateContext: (stateContextId: string) => + lookup( + createClient({ + ...scope, + stateContextId, + }), + ), + withContextNamespace: (contextNamespace?: string) => + lookup( + createClient({ + ...scope, + contextNamespace, + }), + ), + }); + + const createManagedSubscription = ( + subscriptionId: string, + resourceKind: "event" | "value", + resourceName: string, + schemaType: SchemaType, + handler: ( + data: ZodOutput, + ) => void | Promise, + unsubscribeMethod: "event-unsubscribe" | "value-unwatch", + subscriberStateContextId?: string, + options?: SubscriptionOptions, + ): SubscriptionHandle => { + let closed = false; + let abortHandler: (() => void) | null = null; + let deliveryChain: Promise = Promise.resolve(); + + if (clientSubscriptions.has(subscriptionId)) { + throw new Error( + `Subscription ${subscriptionId} is already attached. Pass \`subscriptionNamespace\` to create a distinct logical subscription.`, + ); + } + + const closeLocal = (_error?: Error) => { + if (closed) { + return; + } + closed = true; + clientSubscriptions.delete(subscriptionId); + if (options?.signal && abortHandler) { + options.signal.removeEventListener("abort", abortHandler); + } + }; + + const sendUnsubscribe = async () => { + try { + await sendRequest(unsubscribeMethod, { + subscriptionId, + }); + } catch { + // Ignore unsubscribe errors; local close already happened. + } + }; + + const unsubscribe = async () => { + if (closed) { + return; + } + closeLocal(); + await sendUnsubscribe(); + }; + + const fail = (error: Error) => { + closeLocal(error); + void sendUnsubscribe(); + }; + + const deliver = (data: unknown) => { + if (closed) { + return; + } + + let parsedData: ZodOutput; + try { + parsedData = parseResourceData( + resourceKind, + resourceName, + schemaType, + data, + ); + } catch (error) { + fail(error instanceof Error ? error : new Error(String(error))); + return; + } + + deliveryChain = deliveryChain + .then(async () => { + if (closed) { + return; + } + const invoke = async () => { + await handler(parsedData); + }; + if (subscriberStateContextId) { + await runWithStateContext(subscriberStateContextId, invoke); + return; + } + await invoke(); + }) + .catch((error) => { + fail(error instanceof Error ? error : new Error(String(error))); + }); + }; + + clientSubscriptions.set(subscriptionId, { + deliver, + closeLocal, + }); + + if (options?.signal) { + abortHandler = () => { + void unsubscribe(); + }; + if (options.signal.aborted) { + void unsubscribe(); + } else { + options.signal.addEventListener("abort", abortHandler); + } + } + + return { + unsubscribe, + }; + }; + + const consumeEvent = async ( + eventName: string, + eventSchema: EventSchema, + params: unknown, + handler: (data: unknown) => void | Promise, + options?: SubscriptionOptions, + ) => { + const callerStateContextId = getCurrentStateContextId(); + await ensurePackageReady(callerStateContextId); + const routing = resolveRouting(callerStateContextId); + if (!routing.callerStateContextId && !routing.targetStateContextId) { + throw missingStateContextError(); + } + + const result = await sendRequest("event-subscribe", { + target: meta.flakeRef, + targetPackageName, + eventName, + params, + callerStateContextId: routing.callerStateContextId, + stateContextId: routing.targetStateContextId, + contextNamespace: routing.contextNamespace, + subscriptionNamespace: options?.subscriptionNamespace, + }); + const subscriptionId = (result as { subscriptionId?: string }) + ?.subscriptionId; + if (!subscriptionId) { + throw new Error(`Subscription failed for event ${eventName}`); + } + const handle = createManagedSubscription( + subscriptionId, + "event", + eventName, + eventSchema, + handler, + "event-unsubscribe", + callerStateContextId, + options, + ); + try { + await sendRequest("subscription-ready", { + subscriptionId, + }); + } catch (error) { + await handle.unsubscribe(); + throw error; + } + return handle; + }; + + const getValue = async ( + valueName: string, + valueSchema: ValueSchema, + params: unknown, + ) => { + const callerStateContextId = getCurrentStateContextId(); + await ensurePackageReady(callerStateContextId); + const routing = resolveRouting(callerStateContextId); + if (!routing.callerStateContextId && !routing.targetStateContextId) { + throw missingStateContextError(); + } + + const result = await sendRequest("value-get", { + target: meta.flakeRef, + targetPackageName, + valueName, + params, + callerStateContextId: routing.callerStateContextId, + stateContextId: routing.targetStateContextId, + contextNamespace: routing.contextNamespace, + }); + return parseResourceData("value", valueName, valueSchema, result); + }; + + const watchValue = async ( + valueName: string, + valueSchema: ValueSchema, + params: unknown, + handler: (data: unknown) => void | Promise, + options?: SubscriptionOptions, + ) => { + const callerStateContextId = getCurrentStateContextId(); + await ensurePackageReady(callerStateContextId); + const routing = resolveRouting(callerStateContextId); + if (!routing.callerStateContextId && !routing.targetStateContextId) { + throw missingStateContextError(); + } + + const result = await sendRequest("value-watch", { + target: meta.flakeRef, + targetPackageName, + valueName, + params, + callerStateContextId: routing.callerStateContextId, + stateContextId: routing.targetStateContextId, + contextNamespace: routing.contextNamespace, + subscriptionNamespace: options?.subscriptionNamespace, + }); + const subscriptionId = (result as { subscriptionId?: string }) + ?.subscriptionId; + if (!subscriptionId) { + throw new Error(`Watch failed for value ${valueName}`); + } + const handle = createManagedSubscription( + subscriptionId, + "value", + valueName, + valueSchema, + handler, + "value-unwatch", + callerStateContextId, + options, + ); + try { + await sendRequest("subscription-ready", { + subscriptionId, + }); + } catch (error) { + await handle.unsubscribe(); + throw error; + } + return handle; + }; + + const functions = new Proxy( + {}, + { + get: (_target, prop) => { + if (typeof prop !== "string") { + return undefined; + } + const call = async (params: unknown) => { + const callerStateContextId = getCurrentStateContextId(); + await ensurePackageReady(callerStateContextId); + const routing = resolveRouting(callerStateContextId); + if (!routing.callerStateContextId && !routing.targetStateContextId) { + throw missingStateContextError(); + } + return await sendRequest("call", { + target: meta.flakeRef, + targetPackageName, + functionName: prop, + params, + callerStateContextId: routing.callerStateContextId, + stateContextId: routing.targetStateContextId, + contextNamespace: routing.contextNamespace, + }); + }; + return Object.assign( + call, + scopedResourceMethods((client) => client.functions[prop]), + ); + }, + }, + ) as PackageFunctionCallers; + + const events = {} as EventClients; + const eventEntries = Object.entries( + (schema.events ?? {}) as EventSchemaMap, + ); + for (const [eventName, eventSchema] of eventEntries) { + if (hasParamsSchema(eventSchema)) { + (events as Record)[eventName] = Object.assign({ + consume: async ( + params: unknown, + handler: (data: unknown) => void | Promise, + options?: SubscriptionOptions, + ) => { + const parsedParams = eventSchema.paramsSchema.safeParse(params); + if (!parsedParams.success) { + throw new Error(`Invalid params for event ${eventName}`); + } + return await consumeEvent( + eventName, + eventSchema, + parsedParams.data, + handler, + options, + ); + }, + }, scopedResourceMethods((client) => client.events[eventName])); + } else { + (events as Record)[eventName] = Object.assign({ + consume: async ( + handler: (data: unknown) => void | Promise, + options?: SubscriptionOptions, + ) => + await consumeEvent( + eventName, + eventSchema, + undefined, + handler, + options, + ), + }, scopedResourceMethods((client) => client.events[eventName])); + } + } + + const values = {} as ValueClients; + const valueEntries = Object.entries( + (schema.values ?? {}) as ValueSchemaMap, + ); + for (const [valueName, valueSchema] of valueEntries) { + if (hasParamsSchema(valueSchema)) { + (values as Record)[valueName] = Object.assign({ + get: async (params: unknown) => { + const parsedParams = valueSchema.paramsSchema.safeParse(params); + if (!parsedParams.success) { + throw new Error(`Invalid params for value ${valueName}`); + } + return await getValue(valueName, valueSchema, parsedParams.data); + }, + watch: async ( + params: unknown, + handler: (data: unknown) => void | Promise, + options?: SubscriptionOptions, + ) => { + const parsedParams = valueSchema.paramsSchema.safeParse(params); + if (!parsedParams.success) { + throw new Error(`Invalid params for value ${valueName}`); + } + return await watchValue( + valueName, + valueSchema, + parsedParams.data, + handler, + options, + ); + }, + }, scopedResourceMethods((client) => client.values[valueName])); + } else { + (values as Record)[valueName] = Object.assign({ + get: async () => await getValue(valueName, valueSchema, undefined), + watch: async ( + handler: (data: unknown) => void | Promise, + options?: SubscriptionOptions, + ) => + await watchValue( + valueName, + valueSchema, + undefined, + handler, + options, + ), + }, scopedResourceMethods((client) => client.values[valueName])); + } + } + + const eagerCallerStateContextId = + scope.callerStateContextId ?? getCurrentStateContextId(); + if (eagerCallerStateContextId || scope.stateContextId) { + void ensurePackageReady(eagerCallerStateContextId).catch((error) => { + console.warn( + `Failed to eagerly open child context for ${targetPackageName}: ${ + error instanceof Error ? error.message : String(error) + }`, + ); + }); + } + + return { + functions, + events, + values, + withStateContext: (stateContextId: string) => + createClient({ + ...scope, + stateContextId, + }), + withContextNamespace: (contextNamespace?: string) => + createClient({ + ...scope, + contextNamespace, + }), + }; + }; + + return createClient(baseOptions); +}; + +export const createPackage = < + Schema extends PackageSchema, + Context extends PackageContext = PackageContext, +>( + options: CreatePackageOptions, +): LifecycleManager => { + packageOptions = options; + void ensureSocket(false); + + void (async () => { + try { + await options.onCreate?.(); + } catch (error) { + await reportPackageRuntimeError({ + phase: "on-create", + error, + }); + console.error( + "[quixos-runtime] onCreate failed:", + normalizeError(error).stack ?? normalizeError(error).message, + ); + } + })(); + + return {}; +}; diff --git a/src/rpc.ts b/src/rpc.ts new file mode 100644 index 0000000..60a750b --- /dev/null +++ b/src/rpc.ts @@ -0,0 +1,249 @@ +import z from "zod"; + +export const AuthMessageSchema = z.object({ + type: z.literal("auth"), + secret: z.string().min(1), + pid: z.number().int().nonnegative().optional(), +}); + +export const AuthAckMessageSchema = z.object({ + type: z.literal("auth-ack"), +}); + +export const RpcMethodSchema = z.enum([ + "stop", + "call", + "boot", + "target-context-open", + "runtime-error", + "context-open", + "context-close", + "event-subscribe", + "subscription-ready", + "event-unsubscribe", + "value-get", + "value-watch", + "value-unwatch", + "subscription-publish", +]); + +export const RequestMessageSchema = z.object({ + type: z.literal("request"), + requestId: z.string().min(1), + method: RpcMethodSchema, + params: z.record(z.string(), z.unknown()), +}); + +const RoutedStateContextFields = { + target: z.string().min(1).optional(), + targetPackageName: z.string().min(1).optional(), + callerStateContextId: z.string().min(1).optional(), + stateContextId: z.string().min(1).optional(), + contextNamespace: z.string().min(1).optional(), +}; + +export const CallRequestParamsSchema = z.object({ + ...RoutedStateContextFields, + functionName: z.string().min(1), + params: z.unknown(), +}); + +export const BootRequestParamsSchema = z.object({ + target: z.string().min(1), +}); + +export const TargetContextOpenRequestParamsSchema = z.object({ + ...RoutedStateContextFields, + target: z.string().min(1), + targetPackageName: z.string().min(1).optional(), +}); + +export const RuntimeErrorRequestParamsSchema = z.object({ + phase: z.enum([ + "on-create", + "on-destroy", + "on-context-open", + "on-context-close", + "browser-surface", + "function", + "event-subscribe", + "event-cleanup", + "value-get", + "value-watch", + "value-cleanup", + "subscription-publish", + "internal", + ]), + stateContextId: z.string().min(1).optional(), + functionName: z.string().min(1).optional(), + resourceKind: z.enum(["event", "value"]).optional(), + resourceName: z.string().min(1).optional(), + surfaceId: z.string().min(1).optional(), + hostSessionId: z.string().min(1).optional(), + message: z.string().min(1), + stack: z.string().min(1).optional(), +}); + +export const ContextOpenRequestParamsSchema = z.object({ + stateContextId: z.string().min(1), +}); + +export const ContextCloseRequestParamsSchema = z.object({ + stateContextId: z.string().min(1), +}); + +export const EventSubscribeRequestParamsSchema = z.object({ + ...RoutedStateContextFields, + eventName: z.string().min(1), + params: z.unknown().optional(), + subscriptionNamespace: z.string().min(1).optional(), +}); + +export const EventUnsubscribeRequestParamsSchema = z.object({ + subscriptionId: z.string().min(1), +}); + +export const SubscriptionReadyRequestParamsSchema = z.object({ + subscriptionId: z.string().min(1), +}); + +export const ValueGetRequestParamsSchema = z.object({ + ...RoutedStateContextFields, + valueName: z.string().min(1), + params: z.unknown().optional(), +}); + +export const ValueWatchRequestParamsSchema = z.object({ + ...RoutedStateContextFields, + valueName: z.string().min(1), + params: z.unknown().optional(), + subscriptionNamespace: z.string().min(1).optional(), +}); + +export const ValueUnwatchRequestParamsSchema = z.object({ + subscriptionId: z.string().min(1), +}); + +export const SubscriptionPublishRequestParamsSchema = z.object({ + subscriptionId: z.string().min(1), + data: z.unknown(), +}); + +export const SubscriptionResponseSchema = z.object({ + subscriptionId: z.string().min(1), +}); + +export const SubscriptionDataMessageSchema = z.object({ + type: z.literal("subscription-data"), + subscriptionId: z.string().min(1), + data: z.unknown(), +}); + +export const ResponseOkMessageSchema = z.object({ + type: z.literal("response"), + requestId: z.string().min(1), + ok: z.literal(true), + result: z.unknown().optional(), +}); + +export const ResponseErrorMessageSchema = z.object({ + type: z.literal("response"), + requestId: z.string().min(1), + ok: z.literal(false), + error: z.string(), +}); + +export const ResponseMessageSchema = z.union([ + ResponseOkMessageSchema, + ResponseErrorMessageSchema, +]); + +export const ServerMessageSchema = z.union([ + AuthAckMessageSchema, + RequestMessageSchema, + ResponseMessageSchema, + SubscriptionDataMessageSchema, +]); + +export const ClientMessageSchema = z.union([ + AuthMessageSchema, + ResponseMessageSchema, + RequestMessageSchema, +]); + +export type AuthMessage = z.infer; +export type AuthAckMessage = z.infer; +export type RequestMessage = z.infer; +export type CallRequestParams = z.infer; +export type BootRequestParams = z.infer; +export type TargetContextOpenRequestParams = z.infer< + typeof TargetContextOpenRequestParamsSchema +>; +export type RuntimeErrorRequestParams = z.infer< + typeof RuntimeErrorRequestParamsSchema +>; +export type ContextOpenRequestParams = z.infer< + typeof ContextOpenRequestParamsSchema +>; +export type ContextCloseRequestParams = z.infer< + typeof ContextCloseRequestParamsSchema +>; +export type EventSubscribeRequestParams = z.infer< + typeof EventSubscribeRequestParamsSchema +>; +export type EventUnsubscribeRequestParams = z.infer< + typeof EventUnsubscribeRequestParamsSchema +>; +export type SubscriptionReadyRequestParams = z.infer< + typeof SubscriptionReadyRequestParamsSchema +>; +export type ValueGetRequestParams = z.infer; +export type ValueWatchRequestParams = z.infer< + typeof ValueWatchRequestParamsSchema +>; +export type ValueUnwatchRequestParams = z.infer< + typeof ValueUnwatchRequestParamsSchema +>; +export type SubscriptionPublishRequestParams = z.infer< + typeof SubscriptionPublishRequestParamsSchema +>; +export type SubscriptionResponse = z.infer; +export type SubscriptionDataMessage = z.infer< + typeof SubscriptionDataMessageSchema +>; +export type ResponseMessage = z.infer; +export type ClientMessage = z.infer; +export type ServerMessage = z.infer; + +type SocketLike = { + send: (data: string) => void; +}; + +export const sendMessage = ( + socket: SocketLike, + message: Message, +) => { + socket.send(JSON.stringify(message)); +}; + +export const sendClientMessage = ( + socket: SocketLike, + message: ClientMessage, +) => { + sendMessage(socket, message); +}; + +export const sendServerMessage = ( + socket: SocketLike, + message: ServerMessage, +) => { + sendMessage(socket, message); +}; + +export const parseJson = (value: string) => { + try { + return { ok: true as const, value: JSON.parse(value) }; + } catch { + return { ok: false as const }; + } +}; diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..32ae8b3 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,23 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "NodeNext", + "moduleResolution": "NodeNext", + "lib": ["ES2022", "DOM"], + "strict": true, + "outDir": "dist", + "rootDir": ".", + "declaration": true, + "declarationMap": true, + "sourceMap": true, + "jsx": "react-jsx", + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "skipLibCheck": true, + "resolveJsonModule": true, + "verbatimModuleSyntax": true, + "types": ["node"] + }, + "include": ["src/**/*.ts", "src/**/*.tsx", "quixos-package-schema.ts"], + "exclude": ["node_modules", ".yarn"] +} diff --git a/yarn.lock b/yarn.lock new file mode 100644 index 0000000..445ff26 --- /dev/null +++ b/yarn.lock @@ -0,0 +1,106 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@types/node@*": + version "25.9.1" + resolved "https://registry.yarnpkg.com/@types/node/-/node-25.9.1.tgz#3bda556db500ae4319c08e7fc9ab94f19013ba0b" + integrity sha512-xfrlY7UD5rMJk3ZVJP8BNzS28J36YJg+xp+LPXV1TdWxr8uMH5A860QNxYDGQe/ylDSgjxE52Q9VnO7p75tJxg== + dependencies: + undici-types ">=7.24.0 <7.24.7" + +"@types/node@^24": + version "24.12.4" + resolved "https://registry.yarnpkg.com/@types/node/-/node-24.12.4.tgz#2709745569811dcbdc57c097fafdd387c6330382" + integrity sha512-GUUEShf+PBCGW2KaXwcIt3Yk+e3pkKwWKb9GSyM9WQVE+ep2jzmHdGsHzu4wgcZy5fN9FBdVzjpBQsYlpfpgLA== + dependencies: + undici-types "~7.16.0" + +"@types/prop-types@*": + version "15.7.15" + resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.15.tgz#e6e5a86d602beaca71ce5163fadf5f95d70931c7" + integrity sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw== + +"@types/react-dom@^18.3.1": + version "18.3.7" + resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-18.3.7.tgz#b89ddf2cd83b4feafcc4e2ea41afdfb95a0d194f" + integrity sha512-MEe3UeoENYVFXzoXEWsvcpg6ZvlrFNlOQ7EOsvhI3CfAXwzPfO8Qwuxd40nepsYKqyyVQnTdEfv68q91yLcKrQ== + +"@types/react@^18.3.12": + version "18.3.29" + resolved "https://registry.yarnpkg.com/@types/react/-/react-18.3.29.tgz#7f3b6e1515499d4fd199cc8fd4710114be36c1a2" + integrity sha512-ch0qJdr2JY0r04NXSprbK6TXOgnaJ1Tz23fm5W+z0/CBah6BSBc3n96h7K9GOtwh0HrilNWHIBzE1Ko4Dcw/Wg== + dependencies: + "@types/prop-types" "*" + csstype "^3.2.2" + +"@types/ws@^8.18.1": + version "8.18.1" + resolved "https://registry.yarnpkg.com/@types/ws/-/ws-8.18.1.tgz#48464e4bf2ddfd17db13d845467f6070ffea4aa9" + integrity sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg== + dependencies: + "@types/node" "*" + +csstype@^3.2.2: + version "3.2.3" + resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.2.3.tgz#ec48c0f3e993e50648c86da559e2610995cf989a" + integrity sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ== + +"js-tokens@^3.0.0 || ^4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" + integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== + +loose-envify@^1.1.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" + integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q== + dependencies: + js-tokens "^3.0.0 || ^4.0.0" + +react-dom@^18.3.1: + version "18.3.1" + resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-18.3.1.tgz#c2265d79511b57d479b3dd3fdfa51536494c5cb4" + integrity sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw== + dependencies: + loose-envify "^1.1.0" + scheduler "^0.23.2" + +react@^18.3.1: + version "18.3.1" + resolved "https://registry.yarnpkg.com/react/-/react-18.3.1.tgz#49ab892009c53933625bd16b2533fc754cab2891" + integrity sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ== + dependencies: + loose-envify "^1.1.0" + +scheduler@^0.23.2: + version "0.23.2" + resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.23.2.tgz#414ba64a3b282892e944cf2108ecc078d115cdc3" + integrity sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ== + dependencies: + loose-envify "^1.1.0" + +typescript@^5.9.3: + version "5.9.3" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.9.3.tgz#5b4f59e15310ab17a216f5d6cf53ee476ede670f" + integrity sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw== + +"undici-types@>=7.24.0 <7.24.7": + version "7.24.6" + resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-7.24.6.tgz#61275b485d7fd4e9d269c7cf04ec2873c9cc0f91" + integrity sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg== + +undici-types@~7.16.0: + version "7.16.0" + resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-7.16.0.tgz#ffccdff36aea4884cbfce9a750a0580224f58a46" + integrity sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw== + +ws@^8.18.3: + version "8.21.0" + resolved "https://registry.yarnpkg.com/ws/-/ws-8.21.0.tgz#012e413fc07429945121b0c153158c4343086951" + integrity sha512-Vsp28b7DRcimFQvrqu2Wek3z1iYxDCWqHYB8Qsnk/S4RfaCQzPGPyBNuVjJV3cd6UiKtUtp6sNM77gWvzcCH+g== + +zod@^4.3.6: + version "4.4.3" + resolved "https://registry.yarnpkg.com/zod/-/zod-4.4.3.tgz#b680f172885d18bbebf21a834ea25e55a1bbf356" + integrity sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==