@derivation/rpc 0.6.0 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +8 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +5 -2
- package/dist/index.d.ts +5 -2
- package/dist/index.js +10 -1
- package/dist/index.js.map +1 -1
- package/dist/iso.cjs +9 -0
- package/dist/iso.cjs.map +1 -1
- package/dist/iso.d.cts +6 -1
- package/dist/iso.d.ts +6 -1
- package/dist/iso.js +8 -0
- package/dist/iso.js.map +1 -1
- package/dist/shared-worker.cjs +20 -0
- package/dist/shared-worker.cjs.map +1 -1
- package/dist/shared-worker.d.cts +2 -1
- package/dist/shared-worker.d.ts +2 -1
- package/dist/shared-worker.js +20 -0
- package/dist/shared-worker.js.map +1 -1
- package/dist/{stream-types-Q_EqNLtO.d.cts → stream-types-Cn-LsIb9.d.cts} +1 -0
- package/dist/{stream-types-Q_EqNLtO.d.ts → stream-types-Cn-LsIb9.d.ts} +1 -0
- package/dist/websocket-client.cjs +258 -44
- package/dist/websocket-client.cjs.map +1 -1
- package/dist/websocket-client.d.cts +26 -4
- package/dist/websocket-client.d.ts +26 -4
- package/dist/websocket-client.js +258 -44
- package/dist/websocket-client.js.map +1 -1
- package/dist/websocket-server.cjs +45 -5
- package/dist/websocket-server.cjs.map +1 -1
- package/dist/websocket-server.d.cts +8 -1
- package/dist/websocket-server.d.ts +8 -1
- package/dist/websocket-server.js +45 -5
- package/dist/websocket-server.js.map +1 -1
- package/package.json +6 -11
package/dist/shared-worker.js
CHANGED
|
@@ -77,11 +77,13 @@ var SharedWorkerClient = class {
|
|
|
77
77
|
this.pendingStreams = /* @__PURE__ */ new Map();
|
|
78
78
|
this.pendingMutations = /* @__PURE__ */ new Map();
|
|
79
79
|
this.activeStreams = /* @__PURE__ */ new Map();
|
|
80
|
+
this.activeStreamSnapshots = /* @__PURE__ */ new Map();
|
|
80
81
|
this.registry = new FinalizationRegistry(
|
|
81
82
|
([id, name]) => {
|
|
82
83
|
console.log(`\u{1F9F9} Stream ${id} (${name}) collected \u2014 unsubscribing`);
|
|
83
84
|
this.sendMessage(ClientMessage.unsubscribe(id));
|
|
84
85
|
this.activeStreams.delete(id);
|
|
86
|
+
this.activeStreamSnapshots.delete(id);
|
|
85
87
|
}
|
|
86
88
|
);
|
|
87
89
|
this.port.onmessage = (event) => {
|
|
@@ -98,6 +100,19 @@ var SharedWorkerClient = class {
|
|
|
98
100
|
if (resolve) {
|
|
99
101
|
resolve(message.snapshot);
|
|
100
102
|
this.pendingStreams.delete(message.id);
|
|
103
|
+
break;
|
|
104
|
+
}
|
|
105
|
+
const active = this.activeStreamSnapshots.get(message.id);
|
|
106
|
+
if (active) {
|
|
107
|
+
const input = active.input.deref();
|
|
108
|
+
if (input) {
|
|
109
|
+
active.sink.reset(message.snapshot, input);
|
|
110
|
+
this.graph.step();
|
|
111
|
+
} else {
|
|
112
|
+
this.sendMessage(ClientMessage.unsubscribe(message.id));
|
|
113
|
+
this.activeStreams.delete(message.id);
|
|
114
|
+
this.activeStreamSnapshots.delete(message.id);
|
|
115
|
+
}
|
|
101
116
|
}
|
|
102
117
|
break;
|
|
103
118
|
}
|
|
@@ -115,6 +130,7 @@ var SharedWorkerClient = class {
|
|
|
115
130
|
console.log(`\u{1F9F9} Sink ${id} GC'd \u2014 auto-unsubscribing`);
|
|
116
131
|
this.sendMessage(ClientMessage.unsubscribe(id));
|
|
117
132
|
this.activeStreams.delete(id);
|
|
133
|
+
this.activeStreamSnapshots.delete(id);
|
|
118
134
|
}
|
|
119
135
|
}
|
|
120
136
|
console.log("[Client] Stepping graph");
|
|
@@ -162,6 +178,10 @@ var SharedWorkerClient = class {
|
|
|
162
178
|
const { stream, input } = sinkAdapter.build();
|
|
163
179
|
const inputRef = new WeakRef(input);
|
|
164
180
|
this.activeStreams.set(id, changer(sinkAdapter, inputRef));
|
|
181
|
+
this.activeStreamSnapshots.set(id, {
|
|
182
|
+
sink: sinkAdapter,
|
|
183
|
+
input: inputRef
|
|
184
|
+
});
|
|
165
185
|
this.registry.register(input, [id, String(key)]);
|
|
166
186
|
return stream;
|
|
167
187
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/client-message.ts","../src/shared-worker/shared-worker-client.ts","../src/server-message.ts","../src/shared-worker/shared-worker-client-handler.ts","../src/weak-list.ts","../src/shared-worker/shared-worker-server.ts"],"sourcesContent":["import { z } from \"zod\";\n\nexport const SubscribeMessageSchema = z.object({\n type: z.literal(\"subscribe\"),\n id: z.number(),\n name: z.string(),\n args: z.looseObject({}),\n});\nexport type SubscribeMessage = z.infer<typeof SubscribeMessageSchema>;\n\nexport const UnsubscribeMessageSchema = z.object({\n type: z.literal(\"unsubscribe\"),\n id: z.number(),\n});\nexport type UnsubscribeMessage = z.infer<typeof UnsubscribeMessageSchema>;\n\nexport const HeartbeatMessageSchema = z.object({\n type: z.literal(\"heartbeat\"),\n});\nexport type HeartbeatMessage = z.infer<typeof HeartbeatMessageSchema>;\n\nexport const CallMessageSchema = z.object({\n type: z.literal(\"call\"),\n id: z.number(),\n name: z.string(),\n args: z.looseObject({}),\n});\nexport type CallMessage = z.infer<typeof CallMessageSchema>;\n\nexport const PresenceMessageSchema = z.object({\n type: z.literal(\"presence\"),\n data: z.looseObject({}),\n});\nexport type PresenceMessage = z.infer<typeof PresenceMessageSchema>;\n\nexport const ClientMessageSchema = z.discriminatedUnion(\"type\", [\n SubscribeMessageSchema,\n UnsubscribeMessageSchema,\n HeartbeatMessageSchema,\n CallMessageSchema,\n PresenceMessageSchema,\n]);\nexport type ClientMessage = z.infer<typeof ClientMessageSchema>;\n\nexport function parseClientMessage(data: unknown): ClientMessage {\n return ClientMessageSchema.parse(data);\n}\n\nexport const ClientMessage = {\n subscribe: (\n id: number,\n name: string,\n args: object,\n ): SubscribeMessage => ({\n type: \"subscribe\",\n id,\n name,\n args: args as Record<string, unknown>,\n }),\n unsubscribe: (id: number): UnsubscribeMessage => ({\n type: \"unsubscribe\",\n id,\n }),\n call: (\n id: number,\n name: string,\n args: object,\n ): CallMessage => ({\n type: \"call\",\n id,\n name,\n args: args as Record<string, unknown>,\n }),\n heartbeat: (): HeartbeatMessage => ({\n type: \"heartbeat\",\n }),\n presence: (data: object): PresenceMessage => ({\n type: \"presence\",\n data: data as Record<string, unknown>,\n }),\n};\n","import { ClientMessage } from \"../client-message.js\";\nimport { ServerMessage } from \"../server-message.js\";\nimport type { Graph } from \"derivation\";\nimport type {\n Sink,\n StreamSinks,\n RPCDefinition,\n MutationResult,\n} from \"../stream-types.js\";\n\nfunction changer<T extends object, I extends object>(\n sink: Sink<T, I>,\n input: WeakRef<I>,\n): (change: object) => void {\n return (change) => {\n const i = input.deref();\n if (i) {\n sink.apply(change, i);\n }\n };\n}\n\nexport class SharedWorkerClient<Defs extends RPCDefinition> {\n private nextId = 1;\n private pendingStreams = new Map<number, (snapshot: object) => void>();\n private pendingMutations = new Map<\n number,\n (result: MutationResult<unknown>) => void\n >();\n private activeStreams = new Map<number, (change: object) => void>();\n\n private registry = new FinalizationRegistry<[number, string]>(\n ([id, name]) => {\n console.log(`🧹 Stream ${id} (${name}) collected — unsubscribing`);\n this.sendMessage(ClientMessage.unsubscribe(id));\n this.activeStreams.delete(id);\n },\n );\n\n constructor(\n private port: MessagePort,\n private sinks: StreamSinks<Defs[\"streams\"]>,\n private graph: Graph,\n ) {\n this.port.onmessage = (event: MessageEvent) => {\n const message = JSON.parse(event.data as string) as ServerMessage;\n this.handleMessage(message);\n };\n this.port.start();\n }\n\n private handleMessage(message: ServerMessage) {\n switch (message.type) {\n case \"snapshot\": {\n console.log(`[Client] Received snapshot for stream ${message.id}`);\n const resolve = this.pendingStreams.get(message.id);\n if (resolve) {\n resolve(message.snapshot as object);\n this.pendingStreams.delete(message.id);\n }\n break;\n }\n case \"delta\": {\n const changeCount = Object.keys(message.changes).length;\n console.log(\n `[Client] Received delta with ${changeCount} changes for streams: ${Object.keys(message.changes).join(\", \")}`,\n );\n for (const [idStr, change] of Object.entries(message.changes)) {\n const id = Number(idStr);\n const sink = this.activeStreams.get(id);\n\n if (sink && change && typeof change === \"object\") {\n sink(change);\n } else if (!sink) {\n console.log(`🧹 Sink ${id} GC'd — auto-unsubscribing`);\n this.sendMessage(ClientMessage.unsubscribe(id));\n this.activeStreams.delete(id);\n }\n }\n console.log(\"[Client] Stepping graph\");\n this.graph.step();\n break;\n }\n case \"result\": {\n console.log(\n `[Client] Received mutation result for call ${message.id}:`,\n message.success ? \"success\" : \"error\",\n );\n const resolve = this.pendingMutations.get(message.id);\n if (resolve) {\n if (message.success) {\n resolve({ success: true, value: message.value });\n } else {\n resolve({\n success: false,\n error: message.error || \"Unknown error\",\n });\n }\n this.pendingMutations.delete(message.id);\n }\n break;\n }\n case \"heartbeat\":\n console.log(\"[Client] Received heartbeat\");\n break;\n }\n }\n\n private sendMessage(message: ClientMessage) {\n this.port.postMessage(JSON.stringify(message));\n }\n\n async run<Key extends keyof Defs[\"streams\"]>(\n key: Key,\n args: Defs[\"streams\"][Key][\"args\"],\n ): Promise<Defs[\"streams\"][Key][\"returnType\"]> {\n console.log(\n `Running stream ${String(key)} with args ${JSON.stringify(args)}`,\n );\n const id = this.nextId++;\n\n this.sendMessage(ClientMessage.subscribe(id, String(key), args));\n\n const snapshot = await new Promise<object>((resolve) => {\n this.pendingStreams.set(id, resolve);\n });\n\n const endpoint = this.sinks[key];\n const sinkAdapter = endpoint(snapshot);\n const { stream, input } = sinkAdapter.build();\n const inputRef = new WeakRef(input);\n this.activeStreams.set(id, changer(sinkAdapter, inputRef));\n this.registry.register(input, [id, String(key)]);\n\n return stream;\n }\n\n async call<Key extends keyof Defs[\"mutations\"]>(\n key: Key,\n args: Defs[\"mutations\"][Key][\"args\"],\n ): Promise<MutationResult<Defs[\"mutations\"][Key][\"result\"]>> {\n console.log(\n `Calling mutation ${String(key)} with args ${JSON.stringify(args)}`,\n );\n const id = this.nextId++;\n\n this.sendMessage(\n ClientMessage.call(id, String(key), args as Record<string, unknown>),\n );\n\n const result = await new Promise<\n MutationResult<Defs[\"mutations\"][Key][\"result\"]>\n >((resolve) => {\n this.pendingMutations.set(\n id,\n resolve as (result: MutationResult<unknown>) => void,\n );\n });\n\n return result;\n }\n\n close() {\n this.port.close();\n }\n\n setPresence(value: Record<string, unknown>): void {\n this.sendMessage(ClientMessage.presence(value));\n }\n}\n\n/**\n * Create a client connected to a SharedWorker.\n *\n * @example\n * ```typescript\n * // main.ts\n * const graph = new Graph();\n * const worker = new SharedWorker('/worker.js');\n *\n * const client = createSharedWorkerClient(worker.port, {\n * streams: {\n * todos: setSink(graph, todoIso),\n * },\n * }, graph);\n *\n * const todos = await client.run('todos', { filter: 'active' });\n * ```\n */\nexport function createSharedWorkerClient<Defs extends RPCDefinition>(\n port: MessagePort,\n sinks: StreamSinks<Defs[\"streams\"]>,\n graph: Graph,\n): SharedWorkerClient<Defs> {\n return new SharedWorkerClient<Defs>(port, sinks, graph);\n}\n","import { z } from \"zod\";\n\nexport const HeartbeatMessageSchema = z.object({\n type: z.literal(\"heartbeat\"),\n});\nexport type HeartbeatMessage = z.infer<typeof HeartbeatMessageSchema>;\n\nexport const SubscribedSchema = z.object({\n type: z.literal(\"snapshot\"),\n id: z.number(),\n snapshot: z.unknown(),\n});\nexport type SubscribedMessage = z.infer<typeof SubscribedSchema>;\n\nexport const DeltaMessageSchema = z.object({\n type: z.literal(\"delta\"),\n changes: z.record(z.number(), z.unknown()),\n});\nexport type DeltaMessage = z.infer<typeof DeltaMessageSchema>;\n\nexport const ResultMessageSchema = z.object({\n type: z.literal(\"result\"),\n id: z.number(),\n success: z.boolean(),\n value: z.unknown().optional(),\n error: z.string().optional(),\n});\nexport type ResultMessage = z.infer<typeof ResultMessageSchema>;\n\nexport const ServerMessageSchema = z.discriminatedUnion(\"type\", [\n HeartbeatMessageSchema,\n SubscribedSchema,\n DeltaMessageSchema,\n ResultMessageSchema,\n]);\nexport type ServerMessage = z.infer<typeof ServerMessageSchema>;\n\nexport const ServerMessage = {\n heartbeat: (): HeartbeatMessage => ({\n type: \"heartbeat\",\n }),\n subscribed: (id: number, snapshot: unknown): SubscribedMessage => ({\n type: \"snapshot\",\n id,\n snapshot,\n }),\n delta: (changes: Record<string, unknown>): DeltaMessage => ({\n type: \"delta\",\n changes: changes,\n }),\n resultSuccess: (id: number, value: unknown): ResultMessage => ({\n type: \"result\",\n id,\n success: true,\n value,\n }),\n resultError: (id: number, error: string): ResultMessage => ({\n type: \"result\",\n id,\n success: false,\n error,\n }),\n};\n","import { parseClientMessage, ClientMessage } from \"../client-message.js\";\nimport { ServerMessage } from \"../server-message.js\";\nimport {\n Source,\n StreamEndpoints,\n MutationEndpoints,\n RPCDefinition,\n} from \"../stream-types.js\";\nimport { PresenceHandler } from \"../presence-manager.js\";\n\n/**\n * Client handler for SharedWorker connections (browser-only).\n * Simplified version without rate limiting, heartbeats, or inactivity timeouts.\n * Designed for same-origin trusted connections.\n */\nexport class SharedWorkerClientHandler<Defs extends RPCDefinition, Ctx = void> {\n private readonly port: MessagePort;\n private readonly context: Ctx;\n private readonly streamEndpoints: StreamEndpoints<Defs[\"streams\"], Ctx>;\n private readonly mutationEndpoints: MutationEndpoints<Defs[\"mutations\"], Ctx>;\n private readonly presenceHandler?: PresenceHandler;\n private currentPresence?: Record<string, unknown>;\n private closed = false;\n private readonly streams = new Map<number, Source<unknown>>();\n\n constructor(\n port: MessagePort,\n context: Ctx,\n streamEndpoints: StreamEndpoints<Defs[\"streams\"], Ctx>,\n mutationEndpoints: MutationEndpoints<Defs[\"mutations\"], Ctx>,\n presenceHandler?: PresenceHandler,\n ) {\n this.port = port;\n this.context = context;\n this.streamEndpoints = streamEndpoints;\n this.mutationEndpoints = mutationEndpoints;\n this.presenceHandler = presenceHandler;\n\n console.log(\"new client connected\");\n\n // Set up transport handlers\n this.port.onmessage = (event: MessageEvent) => {\n this.handleMessage(event.data as string);\n };\n this.port.onmessageerror = () => this.handleDisconnect();\n }\n\n handleMessage(message: string) {\n let data: object;\n try {\n data = JSON.parse(message);\n } catch {\n console.error(\"Invalid JSON received:\", message);\n return this.close();\n }\n\n let parsed: ClientMessage;\n try {\n parsed = parseClientMessage(data);\n } catch (error) {\n console.error(\"Invalid client message:\", error);\n return this.close();\n }\n\n this.handleClientMessage(parsed);\n }\n\n async handleClientMessage(message: ClientMessage) {\n switch (message.type) {\n case \"subscribe\": {\n const { id, name, args } = message;\n\n if (!(name in this.streamEndpoints)) {\n console.error(`Unknown stream: ${name}`);\n this.close();\n return;\n }\n\n const endpoint = this.streamEndpoints[name as keyof Defs[\"streams\"]];\n\n try {\n const source = await endpoint(\n args as Defs[\"streams\"][keyof Defs[\"streams\"]][\"args\"],\n this.context,\n );\n this.streams.set(id, source);\n this.sendMessage(ServerMessage.subscribed(id, source.Snapshot));\n console.log(`Client subscribed to \"${name}\" (${id})`);\n } catch (err) {\n console.error(`Error building stream ${name}:`, err);\n this.close();\n }\n break;\n }\n\n case \"unsubscribe\": {\n const { id } = message;\n this.streams.delete(id);\n console.log(`Client unsubscribed from ${id}`);\n break;\n }\n\n case \"call\": {\n const { id, name, args } = message;\n\n if (!(name in this.mutationEndpoints)) {\n console.error(`Unknown mutation: ${name}`);\n this.close();\n return;\n }\n\n const endpoint =\n this.mutationEndpoints[name as keyof Defs[\"mutations\"]];\n\n endpoint(\n args as Defs[\"mutations\"][keyof Defs[\"mutations\"]][\"args\"],\n this.context,\n )\n .then((result) => {\n if (result.success) {\n this.sendMessage(ServerMessage.resultSuccess(id, result.value));\n console.log(`Mutation \"${name}\" (${id}) completed successfully`);\n } else {\n this.sendMessage(ServerMessage.resultError(id, result.error));\n console.log(\n `Mutation \"${name}\" (${id}) returned error: ${result.error}`,\n );\n }\n })\n .catch((err) => {\n console.error(\n `Unhandled exception in mutation \"${name}\" (${id}):`,\n err,\n );\n this.close();\n });\n break;\n }\n\n case \"heartbeat\":\n break;\n\n case \"presence\": {\n if (!this.presenceHandler) {\n console.error(\"Presence not configured\");\n this.close();\n return;\n }\n\n const { data } = message;\n\n if (this.currentPresence !== undefined) {\n this.presenceHandler.update(this.currentPresence, data);\n } else {\n this.presenceHandler.add(data);\n }\n\n this.currentPresence = data;\n break;\n }\n }\n }\n\n handleStep() {\n if (this.closed) return;\n const changes: Record<number, unknown> = {};\n\n for (const [id, source] of this.streams) {\n const change = source.LastChange;\n if (change === null) continue;\n changes[id] = change;\n }\n\n if (Object.keys(changes).length > 0) {\n console.log(\n `[ClientHandler] Sending delta with ${Object.keys(changes).length} changes for streams: ${Object.keys(changes).join(\", \")}`,\n );\n this.sendMessage(ServerMessage.delta(changes));\n }\n }\n\n sendMessage(message: ServerMessage) {\n if (this.closed) return;\n this.port.postMessage(JSON.stringify(message));\n }\n\n private handleDisconnect() {\n this.close();\n }\n\n close() {\n if (this.closed) return;\n console.log(\"[ClientHandler] Closing client connection\");\n this.closed = true;\n\n if (this.presenceHandler && this.currentPresence) {\n this.presenceHandler.remove(this.currentPresence);\n }\n\n console.log(\n `[ClientHandler] Cleaning up ${this.streams.size} active streams`,\n );\n this.streams.clear();\n\n try {\n this.port.close();\n } catch {}\n }\n}\n","export default class WeakList<T extends object> implements Iterable<T> {\n private items: WeakRef<T>[] = [];\n\n add(value: T): void {\n this.items.push(new WeakRef(value));\n }\n\n *[Symbol.iterator](): Iterator<T> {\n const newItems: WeakRef<T>[] = [];\n\n for (const ref of this.items) {\n const value = ref.deref();\n if (value !== undefined) {\n yield value;\n newItems.push(ref);\n }\n }\n\n this.items = newItems;\n }\n}\n","import { Graph } from \"derivation\";\nimport { SharedWorkerClientHandler } from \"./shared-worker-client-handler.js\";\nimport WeakList from \"../weak-list.js\";\nimport {\n StreamEndpoints,\n MutationEndpoints,\n RPCDefinition,\n} from \"../stream-types.js\";\nimport { PresenceHandler } from \"../presence-manager.js\";\n\nexport type SharedWorkerServerOptions<\n Defs extends RPCDefinition,\n Ctx = void,\n> = {\n streams: StreamEndpoints<Defs[\"streams\"], Ctx>;\n mutations: MutationEndpoints<Defs[\"mutations\"], Ctx>;\n createContext: (port: MessagePort) => Ctx | Promise<Ctx>;\n presenceHandler?: PresenceHandler;\n};\n\n/**\n * Set up a SharedWorker server for RPC communication.\n * This creates a shared Graph that all connected tabs can interact with.\n *\n * @example\n * ```typescript\n * // worker.ts\n * const { graph } = setupSharedWorker({\n * streams: {\n * todos: async (args, ctx) => new ReactiveSourceAdapter(source, iso),\n * },\n * mutations: {\n * addTodo: async (args, ctx) => ({ success: true, value: newTodo }),\n * },\n * createContext: (port) => ({ portId: crypto.randomUUID() }),\n * });\n * ```\n */\nexport function setupSharedWorker<Defs extends RPCDefinition, Ctx = void>(\n options: SharedWorkerServerOptions<Defs, Ctx>,\n graph: Graph,\n) {\n const { streams, mutations, createContext, presenceHandler } = options;\n\n const clients = new WeakList<SharedWorkerClientHandler<Defs, Ctx>>();\n\n // After each graph step, broadcast deltas to all connected clients\n graph.afterStep(() => {\n console.log(\"[SharedWorker] Broadcasting deltas to clients\");\n for (const client of clients) {\n client.handleStep();\n }\n });\n\n // Handle SharedWorker connections\n const globalScope = self as unknown as SharedWorkerGlobalScope;\n globalScope.onconnect = (event: MessageEvent) => {\n console.log(\"[SharedWorker] New client connecting...\");\n const port = event.ports[0];\n const messageBuffer: string[] = [];\n let client: SharedWorkerClientHandler<Defs, Ctx> | null = null;\n\n // Set up temporary message handler to buffer messages\n const tempMessageHandler = (e: MessageEvent) => {\n console.log(\"[SharedWorker] Buffering message during setup:\", e.data);\n messageBuffer.push(e.data);\n };\n port.onmessage = tempMessageHandler;\n\n // Create context (handle both sync and async)\n Promise.resolve(createContext(port))\n .then((context) => {\n console.log(\"[SharedWorker] Context created, setting up client handler\");\n // Create client handler\n client = new SharedWorkerClientHandler<Defs, Ctx>(\n port,\n context,\n streams,\n mutations,\n presenceHandler,\n );\n clients.add(client);\n console.log(\"[SharedWorker] Client handler registered\");\n\n // Process buffered messages\n console.log(`[SharedWorker] Processing ${messageBuffer.length} buffered messages`);\n for (const msg of messageBuffer) {\n client.handleMessage(msg);\n }\n messageBuffer.length = 0;\n\n // Start the port\n port.start();\n console.log(\"[SharedWorker] Client connection ready\");\n })\n .catch((err) => {\n console.error(\"[SharedWorker] Error creating context:\", err);\n port.close();\n });\n };\n}\n"],"mappings":";AAAA,SAAS,SAAS;AAEX,IAAM,yBAAyB,EAAE,OAAO;AAAA,EAC7C,MAAM,EAAE,QAAQ,WAAW;AAAA,EAC3B,IAAI,EAAE,OAAO;AAAA,EACb,MAAM,EAAE,OAAO;AAAA,EACf,MAAM,EAAE,YAAY,CAAC,CAAC;AACxB,CAAC;AAGM,IAAM,2BAA2B,EAAE,OAAO;AAAA,EAC/C,MAAM,EAAE,QAAQ,aAAa;AAAA,EAC7B,IAAI,EAAE,OAAO;AACf,CAAC;AAGM,IAAM,yBAAyB,EAAE,OAAO;AAAA,EAC7C,MAAM,EAAE,QAAQ,WAAW;AAC7B,CAAC;AAGM,IAAM,oBAAoB,EAAE,OAAO;AAAA,EACxC,MAAM,EAAE,QAAQ,MAAM;AAAA,EACtB,IAAI,EAAE,OAAO;AAAA,EACb,MAAM,EAAE,OAAO;AAAA,EACf,MAAM,EAAE,YAAY,CAAC,CAAC;AACxB,CAAC;AAGM,IAAM,wBAAwB,EAAE,OAAO;AAAA,EAC5C,MAAM,EAAE,QAAQ,UAAU;AAAA,EAC1B,MAAM,EAAE,YAAY,CAAC,CAAC;AACxB,CAAC;AAGM,IAAM,sBAAsB,EAAE,mBAAmB,QAAQ;AAAA,EAC9D;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAGM,SAAS,mBAAmB,MAA8B;AAC/D,SAAO,oBAAoB,MAAM,IAAI;AACvC;AAEO,IAAM,gBAAgB;AAAA,EAC3B,WAAW,CACT,IACA,MACA,UACsB;AAAA,IACtB,MAAM;AAAA,IACN;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAAA,EACA,aAAa,CAAC,QAAoC;AAAA,IAChD,MAAM;AAAA,IACN;AAAA,EACF;AAAA,EACA,MAAM,CACJ,IACA,MACA,UACiB;AAAA,IACjB,MAAM;AAAA,IACN;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAAA,EACA,WAAW,OAAyB;AAAA,IAClC,MAAM;AAAA,EACR;AAAA,EACA,UAAU,CAAC,UAAmC;AAAA,IAC5C,MAAM;AAAA,IACN;AAAA,EACF;AACF;;;ACtEA,SAAS,QACP,MACA,OAC0B;AAC1B,SAAO,CAAC,WAAW;AACjB,UAAM,IAAI,MAAM,MAAM;AACtB,QAAI,GAAG;AACL,WAAK,MAAM,QAAQ,CAAC;AAAA,IACtB;AAAA,EACF;AACF;AAEO,IAAM,qBAAN,MAAqD;AAAA,EAiB1D,YACU,MACA,OACA,OACR;AAHQ;AACA;AACA;AAnBV,SAAQ,SAAS;AACjB,SAAQ,iBAAiB,oBAAI,IAAwC;AACrE,SAAQ,mBAAmB,oBAAI,IAG7B;AACF,SAAQ,gBAAgB,oBAAI,IAAsC;AAElE,SAAQ,WAAW,IAAI;AAAA,MACrB,CAAC,CAAC,IAAI,IAAI,MAAM;AACd,gBAAQ,IAAI,oBAAa,EAAE,KAAK,IAAI,kCAA6B;AACjE,aAAK,YAAY,cAAc,YAAY,EAAE,CAAC;AAC9C,aAAK,cAAc,OAAO,EAAE;AAAA,MAC9B;AAAA,IACF;AAOE,SAAK,KAAK,YAAY,CAAC,UAAwB;AAC7C,YAAM,UAAU,KAAK,MAAM,MAAM,IAAc;AAC/C,WAAK,cAAc,OAAO;AAAA,IAC5B;AACA,SAAK,KAAK,MAAM;AAAA,EAClB;AAAA,EAEQ,cAAc,SAAwB;AAC5C,YAAQ,QAAQ,MAAM;AAAA,MACpB,KAAK,YAAY;AACf,gBAAQ,IAAI,yCAAyC,QAAQ,EAAE,EAAE;AACjE,cAAM,UAAU,KAAK,eAAe,IAAI,QAAQ,EAAE;AAClD,YAAI,SAAS;AACX,kBAAQ,QAAQ,QAAkB;AAClC,eAAK,eAAe,OAAO,QAAQ,EAAE;AAAA,QACvC;AACA;AAAA,MACF;AAAA,MACA,KAAK,SAAS;AACZ,cAAM,cAAc,OAAO,KAAK,QAAQ,OAAO,EAAE;AACjD,gBAAQ;AAAA,UACN,gCAAgC,WAAW,yBAAyB,OAAO,KAAK,QAAQ,OAAO,EAAE,KAAK,IAAI,CAAC;AAAA,QAC7G;AACA,mBAAW,CAAC,OAAO,MAAM,KAAK,OAAO,QAAQ,QAAQ,OAAO,GAAG;AAC7D,gBAAM,KAAK,OAAO,KAAK;AACvB,gBAAM,OAAO,KAAK,cAAc,IAAI,EAAE;AAEtC,cAAI,QAAQ,UAAU,OAAO,WAAW,UAAU;AAChD,iBAAK,MAAM;AAAA,UACb,WAAW,CAAC,MAAM;AAChB,oBAAQ,IAAI,kBAAW,EAAE,iCAA4B;AACrD,iBAAK,YAAY,cAAc,YAAY,EAAE,CAAC;AAC9C,iBAAK,cAAc,OAAO,EAAE;AAAA,UAC9B;AAAA,QACF;AACA,gBAAQ,IAAI,yBAAyB;AACrC,aAAK,MAAM,KAAK;AAChB;AAAA,MACF;AAAA,MACA,KAAK,UAAU;AACb,gBAAQ;AAAA,UACN,8CAA8C,QAAQ,EAAE;AAAA,UACxD,QAAQ,UAAU,YAAY;AAAA,QAChC;AACA,cAAM,UAAU,KAAK,iBAAiB,IAAI,QAAQ,EAAE;AACpD,YAAI,SAAS;AACX,cAAI,QAAQ,SAAS;AACnB,oBAAQ,EAAE,SAAS,MAAM,OAAO,QAAQ,MAAM,CAAC;AAAA,UACjD,OAAO;AACL,oBAAQ;AAAA,cACN,SAAS;AAAA,cACT,OAAO,QAAQ,SAAS;AAAA,YAC1B,CAAC;AAAA,UACH;AACA,eAAK,iBAAiB,OAAO,QAAQ,EAAE;AAAA,QACzC;AACA;AAAA,MACF;AAAA,MACA,KAAK;AACH,gBAAQ,IAAI,6BAA6B;AACzC;AAAA,IACJ;AAAA,EACF;AAAA,EAEQ,YAAY,SAAwB;AAC1C,SAAK,KAAK,YAAY,KAAK,UAAU,OAAO,CAAC;AAAA,EAC/C;AAAA,EAEA,MAAM,IACJ,KACA,MAC6C;AAC7C,YAAQ;AAAA,MACN,kBAAkB,OAAO,GAAG,CAAC,cAAc,KAAK,UAAU,IAAI,CAAC;AAAA,IACjE;AACA,UAAM,KAAK,KAAK;AAEhB,SAAK,YAAY,cAAc,UAAU,IAAI,OAAO,GAAG,GAAG,IAAI,CAAC;AAE/D,UAAM,WAAW,MAAM,IAAI,QAAgB,CAAC,YAAY;AACtD,WAAK,eAAe,IAAI,IAAI,OAAO;AAAA,IACrC,CAAC;AAED,UAAM,WAAW,KAAK,MAAM,GAAG;AAC/B,UAAM,cAAc,SAAS,QAAQ;AACrC,UAAM,EAAE,QAAQ,MAAM,IAAI,YAAY,MAAM;AAC5C,UAAM,WAAW,IAAI,QAAQ,KAAK;AAClC,SAAK,cAAc,IAAI,IAAI,QAAQ,aAAa,QAAQ,CAAC;AACzD,SAAK,SAAS,SAAS,OAAO,CAAC,IAAI,OAAO,GAAG,CAAC,CAAC;AAE/C,WAAO;AAAA,EACT;AAAA,EAEA,MAAM,KACJ,KACA,MAC2D;AAC3D,YAAQ;AAAA,MACN,oBAAoB,OAAO,GAAG,CAAC,cAAc,KAAK,UAAU,IAAI,CAAC;AAAA,IACnE;AACA,UAAM,KAAK,KAAK;AAEhB,SAAK;AAAA,MACH,cAAc,KAAK,IAAI,OAAO,GAAG,GAAG,IAA+B;AAAA,IACrE;AAEA,UAAM,SAAS,MAAM,IAAI,QAEvB,CAAC,YAAY;AACb,WAAK,iBAAiB;AAAA,QACpB;AAAA,QACA;AAAA,MACF;AAAA,IACF,CAAC;AAED,WAAO;AAAA,EACT;AAAA,EAEA,QAAQ;AACN,SAAK,KAAK,MAAM;AAAA,EAClB;AAAA,EAEA,YAAY,OAAsC;AAChD,SAAK,YAAY,cAAc,SAAS,KAAK,CAAC;AAAA,EAChD;AACF;AAoBO,SAAS,yBACd,MACA,OACA,OAC0B;AAC1B,SAAO,IAAI,mBAAyB,MAAM,OAAO,KAAK;AACxD;;;ACnMA,SAAS,KAAAA,UAAS;AAEX,IAAMC,0BAAyBD,GAAE,OAAO;AAAA,EAC7C,MAAMA,GAAE,QAAQ,WAAW;AAC7B,CAAC;AAGM,IAAM,mBAAmBA,GAAE,OAAO;AAAA,EACvC,MAAMA,GAAE,QAAQ,UAAU;AAAA,EAC1B,IAAIA,GAAE,OAAO;AAAA,EACb,UAAUA,GAAE,QAAQ;AACtB,CAAC;AAGM,IAAM,qBAAqBA,GAAE,OAAO;AAAA,EACzC,MAAMA,GAAE,QAAQ,OAAO;AAAA,EACvB,SAASA,GAAE,OAAOA,GAAE,OAAO,GAAGA,GAAE,QAAQ,CAAC;AAC3C,CAAC;AAGM,IAAM,sBAAsBA,GAAE,OAAO;AAAA,EAC1C,MAAMA,GAAE,QAAQ,QAAQ;AAAA,EACxB,IAAIA,GAAE,OAAO;AAAA,EACb,SAASA,GAAE,QAAQ;AAAA,EACnB,OAAOA,GAAE,QAAQ,EAAE,SAAS;AAAA,EAC5B,OAAOA,GAAE,OAAO,EAAE,SAAS;AAC7B,CAAC;AAGM,IAAM,sBAAsBA,GAAE,mBAAmB,QAAQ;AAAA,EAC9DC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAGM,IAAM,gBAAgB;AAAA,EAC3B,WAAW,OAAyB;AAAA,IAClC,MAAM;AAAA,EACR;AAAA,EACA,YAAY,CAAC,IAAY,cAA0C;AAAA,IACjE,MAAM;AAAA,IACN;AAAA,IACA;AAAA,EACF;AAAA,EACA,OAAO,CAAC,aAAoD;AAAA,IAC1D,MAAM;AAAA,IACN;AAAA,EACF;AAAA,EACA,eAAe,CAAC,IAAY,WAAmC;AAAA,IAC7D,MAAM;AAAA,IACN;AAAA,IACA,SAAS;AAAA,IACT;AAAA,EACF;AAAA,EACA,aAAa,CAAC,IAAY,WAAkC;AAAA,IAC1D,MAAM;AAAA,IACN;AAAA,IACA,SAAS;AAAA,IACT;AAAA,EACF;AACF;;;AC/CO,IAAM,4BAAN,MAAwE;AAAA,EAU7E,YACE,MACA,SACA,iBACA,mBACA,iBACA;AATF,SAAQ,SAAS;AACjB,SAAiB,UAAU,oBAAI,IAA6B;AAS1D,SAAK,OAAO;AACZ,SAAK,UAAU;AACf,SAAK,kBAAkB;AACvB,SAAK,oBAAoB;AACzB,SAAK,kBAAkB;AAEvB,YAAQ,IAAI,sBAAsB;AAGlC,SAAK,KAAK,YAAY,CAAC,UAAwB;AAC7C,WAAK,cAAc,MAAM,IAAc;AAAA,IACzC;AACA,SAAK,KAAK,iBAAiB,MAAM,KAAK,iBAAiB;AAAA,EACzD;AAAA,EAEA,cAAc,SAAiB;AAC7B,QAAI;AACJ,QAAI;AACF,aAAO,KAAK,MAAM,OAAO;AAAA,IAC3B,SAAQ;AACN,cAAQ,MAAM,0BAA0B,OAAO;AAC/C,aAAO,KAAK,MAAM;AAAA,IACpB;AAEA,QAAI;AACJ,QAAI;AACF,eAAS,mBAAmB,IAAI;AAAA,IAClC,SAAS,OAAO;AACd,cAAQ,MAAM,2BAA2B,KAAK;AAC9C,aAAO,KAAK,MAAM;AAAA,IACpB;AAEA,SAAK,oBAAoB,MAAM;AAAA,EACjC;AAAA,EAEA,MAAM,oBAAoB,SAAwB;AAChD,YAAQ,QAAQ,MAAM;AAAA,MACpB,KAAK,aAAa;AAChB,cAAM,EAAE,IAAI,MAAM,KAAK,IAAI;AAE3B,YAAI,EAAE,QAAQ,KAAK,kBAAkB;AACnC,kBAAQ,MAAM,mBAAmB,IAAI,EAAE;AACvC,eAAK,MAAM;AACX;AAAA,QACF;AAEA,cAAM,WAAW,KAAK,gBAAgB,IAA6B;AAEnE,YAAI;AACF,gBAAM,SAAS,MAAM;AAAA,YACnB;AAAA,YACA,KAAK;AAAA,UACP;AACA,eAAK,QAAQ,IAAI,IAAI,MAAM;AAC3B,eAAK,YAAY,cAAc,WAAW,IAAI,OAAO,QAAQ,CAAC;AAC9D,kBAAQ,IAAI,yBAAyB,IAAI,MAAM,EAAE,GAAG;AAAA,QACtD,SAAS,KAAK;AACZ,kBAAQ,MAAM,yBAAyB,IAAI,KAAK,GAAG;AACnD,eAAK,MAAM;AAAA,QACb;AACA;AAAA,MACF;AAAA,MAEA,KAAK,eAAe;AAClB,cAAM,EAAE,GAAG,IAAI;AACf,aAAK,QAAQ,OAAO,EAAE;AACtB,gBAAQ,IAAI,4BAA4B,EAAE,EAAE;AAC5C;AAAA,MACF;AAAA,MAEA,KAAK,QAAQ;AACX,cAAM,EAAE,IAAI,MAAM,KAAK,IAAI;AAE3B,YAAI,EAAE,QAAQ,KAAK,oBAAoB;AACrC,kBAAQ,MAAM,qBAAqB,IAAI,EAAE;AACzC,eAAK,MAAM;AACX;AAAA,QACF;AAEA,cAAM,WACJ,KAAK,kBAAkB,IAA+B;AAExD;AAAA,UACE;AAAA,UACA,KAAK;AAAA,QACP,EACG,KAAK,CAAC,WAAW;AAChB,cAAI,OAAO,SAAS;AAClB,iBAAK,YAAY,cAAc,cAAc,IAAI,OAAO,KAAK,CAAC;AAC9D,oBAAQ,IAAI,aAAa,IAAI,MAAM,EAAE,0BAA0B;AAAA,UACjE,OAAO;AACL,iBAAK,YAAY,cAAc,YAAY,IAAI,OAAO,KAAK,CAAC;AAC5D,oBAAQ;AAAA,cACN,aAAa,IAAI,MAAM,EAAE,qBAAqB,OAAO,KAAK;AAAA,YAC5D;AAAA,UACF;AAAA,QACF,CAAC,EACA,MAAM,CAAC,QAAQ;AACd,kBAAQ;AAAA,YACN,oCAAoC,IAAI,MAAM,EAAE;AAAA,YAChD;AAAA,UACF;AACA,eAAK,MAAM;AAAA,QACb,CAAC;AACH;AAAA,MACF;AAAA,MAEA,KAAK;AACH;AAAA,MAEF,KAAK,YAAY;AACf,YAAI,CAAC,KAAK,iBAAiB;AACzB,kBAAQ,MAAM,yBAAyB;AACvC,eAAK,MAAM;AACX;AAAA,QACF;AAEA,cAAM,EAAE,KAAK,IAAI;AAEjB,YAAI,KAAK,oBAAoB,QAAW;AACtC,eAAK,gBAAgB,OAAO,KAAK,iBAAiB,IAAI;AAAA,QACxD,OAAO;AACL,eAAK,gBAAgB,IAAI,IAAI;AAAA,QAC/B;AAEA,aAAK,kBAAkB;AACvB;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA,EAEA,aAAa;AACX,QAAI,KAAK,OAAQ;AACjB,UAAM,UAAmC,CAAC;AAE1C,eAAW,CAAC,IAAI,MAAM,KAAK,KAAK,SAAS;AACvC,YAAM,SAAS,OAAO;AACtB,UAAI,WAAW,KAAM;AACrB,cAAQ,EAAE,IAAI;AAAA,IAChB;AAEA,QAAI,OAAO,KAAK,OAAO,EAAE,SAAS,GAAG;AACnC,cAAQ;AAAA,QACN,sCAAsC,OAAO,KAAK,OAAO,EAAE,MAAM,yBAAyB,OAAO,KAAK,OAAO,EAAE,KAAK,IAAI,CAAC;AAAA,MAC3H;AACA,WAAK,YAAY,cAAc,MAAM,OAAO,CAAC;AAAA,IAC/C;AAAA,EACF;AAAA,EAEA,YAAY,SAAwB;AAClC,QAAI,KAAK,OAAQ;AACjB,SAAK,KAAK,YAAY,KAAK,UAAU,OAAO,CAAC;AAAA,EAC/C;AAAA,EAEQ,mBAAmB;AACzB,SAAK,MAAM;AAAA,EACb;AAAA,EAEA,QAAQ;AACN,QAAI,KAAK,OAAQ;AACjB,YAAQ,IAAI,2CAA2C;AACvD,SAAK,SAAS;AAEd,QAAI,KAAK,mBAAmB,KAAK,iBAAiB;AAChD,WAAK,gBAAgB,OAAO,KAAK,eAAe;AAAA,IAClD;AAEA,YAAQ;AAAA,MACN,+BAA+B,KAAK,QAAQ,IAAI;AAAA,IAClD;AACA,SAAK,QAAQ,MAAM;AAEnB,QAAI;AACF,WAAK,KAAK,MAAM;AAAA,IAClB,SAAQ;AAAA,IAAC;AAAA,EACX;AACF;;;AChNA,IAAqB,WAArB,MAAuE;AAAA,EAAvE;AACE,SAAQ,QAAsB,CAAC;AAAA;AAAA,EAE/B,IAAI,OAAgB;AAClB,SAAK,MAAM,KAAK,IAAI,QAAQ,KAAK,CAAC;AAAA,EACpC;AAAA,EAEA,EAAE,OAAO,QAAQ,IAAiB;AAChC,UAAM,WAAyB,CAAC;AAEhC,eAAW,OAAO,KAAK,OAAO;AAC5B,YAAM,QAAQ,IAAI,MAAM;AACxB,UAAI,UAAU,QAAW;AACvB,cAAM;AACN,iBAAS,KAAK,GAAG;AAAA,MACnB;AAAA,IACF;AAEA,SAAK,QAAQ;AAAA,EACf;AACF;;;ACkBO,SAAS,kBACd,SACA,OACA;AACA,QAAM,EAAE,SAAS,WAAW,eAAe,gBAAgB,IAAI;AAE/D,QAAM,UAAU,IAAI,SAA+C;AAGnE,QAAM,UAAU,MAAM;AACpB,YAAQ,IAAI,+CAA+C;AAC3D,eAAW,UAAU,SAAS;AAC5B,aAAO,WAAW;AAAA,IACpB;AAAA,EACF,CAAC;AAGD,QAAM,cAAc;AACpB,cAAY,YAAY,CAAC,UAAwB;AAC/C,YAAQ,IAAI,yCAAyC;AACrD,UAAM,OAAO,MAAM,MAAM,CAAC;AAC1B,UAAM,gBAA0B,CAAC;AACjC,QAAI,SAAsD;AAG1D,UAAM,qBAAqB,CAAC,MAAoB;AAC9C,cAAQ,IAAI,kDAAkD,EAAE,IAAI;AACpE,oBAAc,KAAK,EAAE,IAAI;AAAA,IAC3B;AACA,SAAK,YAAY;AAGjB,YAAQ,QAAQ,cAAc,IAAI,CAAC,EAChC,KAAK,CAAC,YAAY;AACjB,cAAQ,IAAI,2DAA2D;AAEvE,eAAS,IAAI;AAAA,QACX;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF;AACA,cAAQ,IAAI,MAAM;AAClB,cAAQ,IAAI,0CAA0C;AAGtD,cAAQ,IAAI,6BAA6B,cAAc,MAAM,oBAAoB;AACjF,iBAAW,OAAO,eAAe;AAC/B,eAAO,cAAc,GAAG;AAAA,MAC1B;AACA,oBAAc,SAAS;AAGvB,WAAK,MAAM;AACX,cAAQ,IAAI,wCAAwC;AAAA,IACtD,CAAC,EACA,MAAM,CAAC,QAAQ;AACd,cAAQ,MAAM,0CAA0C,GAAG;AAC3D,WAAK,MAAM;AAAA,IACb,CAAC;AAAA,EACL;AACF;","names":["z","HeartbeatMessageSchema"]}
|
|
1
|
+
{"version":3,"sources":["../src/client-message.ts","../src/shared-worker/shared-worker-client.ts","../src/server-message.ts","../src/shared-worker/shared-worker-client-handler.ts","../src/weak-list.ts","../src/shared-worker/shared-worker-server.ts"],"sourcesContent":["import { z } from \"zod\";\n\nexport const SubscribeMessageSchema = z.object({\n type: z.literal(\"subscribe\"),\n id: z.number(),\n name: z.string(),\n args: z.looseObject({}),\n});\nexport type SubscribeMessage = z.infer<typeof SubscribeMessageSchema>;\n\nexport const UnsubscribeMessageSchema = z.object({\n type: z.literal(\"unsubscribe\"),\n id: z.number(),\n});\nexport type UnsubscribeMessage = z.infer<typeof UnsubscribeMessageSchema>;\n\nexport const HeartbeatMessageSchema = z.object({\n type: z.literal(\"heartbeat\"),\n});\nexport type HeartbeatMessage = z.infer<typeof HeartbeatMessageSchema>;\n\nexport const CallMessageSchema = z.object({\n type: z.literal(\"call\"),\n id: z.number(),\n name: z.string(),\n args: z.looseObject({}),\n});\nexport type CallMessage = z.infer<typeof CallMessageSchema>;\n\nexport const PresenceMessageSchema = z.object({\n type: z.literal(\"presence\"),\n data: z.looseObject({}),\n});\nexport type PresenceMessage = z.infer<typeof PresenceMessageSchema>;\n\nexport const ClientMessageSchema = z.discriminatedUnion(\"type\", [\n SubscribeMessageSchema,\n UnsubscribeMessageSchema,\n HeartbeatMessageSchema,\n CallMessageSchema,\n PresenceMessageSchema,\n]);\nexport type ClientMessage = z.infer<typeof ClientMessageSchema>;\n\nexport function parseClientMessage(data: unknown): ClientMessage {\n return ClientMessageSchema.parse(data);\n}\n\nexport const ClientMessage = {\n subscribe: (\n id: number,\n name: string,\n args: object,\n ): SubscribeMessage => ({\n type: \"subscribe\",\n id,\n name,\n args: args as Record<string, unknown>,\n }),\n unsubscribe: (id: number): UnsubscribeMessage => ({\n type: \"unsubscribe\",\n id,\n }),\n call: (\n id: number,\n name: string,\n args: object,\n ): CallMessage => ({\n type: \"call\",\n id,\n name,\n args: args as Record<string, unknown>,\n }),\n heartbeat: (): HeartbeatMessage => ({\n type: \"heartbeat\",\n }),\n presence: (data: object): PresenceMessage => ({\n type: \"presence\",\n data: data as Record<string, unknown>,\n }),\n};\n","import { ClientMessage } from \"../client-message.js\";\nimport { ServerMessage } from \"../server-message.js\";\nimport type { Graph } from \"derivation\";\nimport type {\n Sink,\n StreamSinks,\n RPCDefinition,\n MutationResult,\n} from \"../stream-types.js\";\n\nfunction changer<T extends object, I extends object>(\n sink: Sink<T, I>,\n input: WeakRef<I>,\n): (change: object) => void {\n return (change) => {\n const i = input.deref();\n if (i) {\n sink.apply(change, i);\n }\n };\n}\n\nexport class SharedWorkerClient<Defs extends RPCDefinition> {\n private nextId = 1;\n private pendingStreams = new Map<number, (snapshot: object) => void>();\n private pendingMutations = new Map<\n number,\n (result: MutationResult<unknown>) => void\n >();\n private activeStreams = new Map<number, (change: object) => void>();\n private activeStreamSnapshots = new Map<\n number,\n { sink: Sink<object, object>; input: WeakRef<object> }\n >();\n\n private registry = new FinalizationRegistry<[number, string]>(\n ([id, name]) => {\n console.log(`🧹 Stream ${id} (${name}) collected — unsubscribing`);\n this.sendMessage(ClientMessage.unsubscribe(id));\n this.activeStreams.delete(id);\n this.activeStreamSnapshots.delete(id);\n },\n );\n\n constructor(\n private port: MessagePort,\n private sinks: StreamSinks<Defs[\"streams\"]>,\n private graph: Graph,\n ) {\n this.port.onmessage = (event: MessageEvent) => {\n const message = JSON.parse(event.data as string) as ServerMessage;\n this.handleMessage(message);\n };\n this.port.start();\n }\n\n private handleMessage(message: ServerMessage) {\n switch (message.type) {\n case \"snapshot\": {\n console.log(`[Client] Received snapshot for stream ${message.id}`);\n const resolve = this.pendingStreams.get(message.id);\n if (resolve) {\n resolve(message.snapshot as object);\n this.pendingStreams.delete(message.id);\n break;\n }\n\n const active = this.activeStreamSnapshots.get(message.id);\n if (active) {\n const input = active.input.deref();\n if (input) {\n active.sink.reset(message.snapshot as object, input);\n this.graph.step();\n } else {\n this.sendMessage(ClientMessage.unsubscribe(message.id));\n this.activeStreams.delete(message.id);\n this.activeStreamSnapshots.delete(message.id);\n }\n }\n break;\n }\n case \"delta\": {\n const changeCount = Object.keys(message.changes).length;\n console.log(\n `[Client] Received delta with ${changeCount} changes for streams: ${Object.keys(message.changes).join(\", \")}`,\n );\n for (const [idStr, change] of Object.entries(message.changes)) {\n const id = Number(idStr);\n const sink = this.activeStreams.get(id);\n\n if (sink && change && typeof change === \"object\") {\n sink(change);\n } else if (!sink) {\n console.log(`🧹 Sink ${id} GC'd — auto-unsubscribing`);\n this.sendMessage(ClientMessage.unsubscribe(id));\n this.activeStreams.delete(id);\n this.activeStreamSnapshots.delete(id);\n }\n }\n console.log(\"[Client] Stepping graph\");\n this.graph.step();\n break;\n }\n case \"result\": {\n console.log(\n `[Client] Received mutation result for call ${message.id}:`,\n message.success ? \"success\" : \"error\",\n );\n const resolve = this.pendingMutations.get(message.id);\n if (resolve) {\n if (message.success) {\n resolve({ success: true, value: message.value });\n } else {\n resolve({\n success: false,\n error: message.error || \"Unknown error\",\n });\n }\n this.pendingMutations.delete(message.id);\n }\n break;\n }\n case \"heartbeat\":\n console.log(\"[Client] Received heartbeat\");\n break;\n }\n }\n\n private sendMessage(message: ClientMessage) {\n this.port.postMessage(JSON.stringify(message));\n }\n\n async run<Key extends keyof Defs[\"streams\"]>(\n key: Key,\n args: Defs[\"streams\"][Key][\"args\"],\n ): Promise<Defs[\"streams\"][Key][\"returnType\"]> {\n console.log(\n `Running stream ${String(key)} with args ${JSON.stringify(args)}`,\n );\n const id = this.nextId++;\n\n this.sendMessage(ClientMessage.subscribe(id, String(key), args));\n\n const snapshot = await new Promise<object>((resolve) => {\n this.pendingStreams.set(id, resolve);\n });\n\n const endpoint = this.sinks[key];\n const sinkAdapter = endpoint(snapshot);\n const { stream, input } = sinkAdapter.build();\n const inputRef = new WeakRef(input);\n this.activeStreams.set(id, changer(sinkAdapter, inputRef));\n this.activeStreamSnapshots.set(id, {\n sink: sinkAdapter as Sink<object, object>,\n input: inputRef as WeakRef<object>,\n });\n this.registry.register(input, [id, String(key)]);\n\n return stream;\n }\n\n async call<Key extends keyof Defs[\"mutations\"]>(\n key: Key,\n args: Defs[\"mutations\"][Key][\"args\"],\n ): Promise<MutationResult<Defs[\"mutations\"][Key][\"result\"]>> {\n console.log(\n `Calling mutation ${String(key)} with args ${JSON.stringify(args)}`,\n );\n const id = this.nextId++;\n\n this.sendMessage(\n ClientMessage.call(id, String(key), args as Record<string, unknown>),\n );\n\n const result = await new Promise<\n MutationResult<Defs[\"mutations\"][Key][\"result\"]>\n >((resolve) => {\n this.pendingMutations.set(\n id,\n resolve as (result: MutationResult<unknown>) => void,\n );\n });\n\n return result;\n }\n\n close() {\n this.port.close();\n }\n\n setPresence(value: Record<string, unknown>): void {\n this.sendMessage(ClientMessage.presence(value));\n }\n}\n\n/**\n * Create a client connected to a SharedWorker.\n *\n * @example\n * ```typescript\n * // main.ts\n * const graph = new Graph();\n * const worker = new SharedWorker('/worker.js');\n *\n * const client = createSharedWorkerClient(worker.port, {\n * streams: {\n * todos: setSink(graph, todoIso),\n * },\n * }, graph);\n *\n * const todos = await client.run('todos', { filter: 'active' });\n * ```\n */\nexport function createSharedWorkerClient<Defs extends RPCDefinition>(\n port: MessagePort,\n sinks: StreamSinks<Defs[\"streams\"]>,\n graph: Graph,\n): SharedWorkerClient<Defs> {\n return new SharedWorkerClient<Defs>(port, sinks, graph);\n}\n","import { z } from \"zod\";\n\nexport const HeartbeatMessageSchema = z.object({\n type: z.literal(\"heartbeat\"),\n});\nexport type HeartbeatMessage = z.infer<typeof HeartbeatMessageSchema>;\n\nexport const SubscribedSchema = z.object({\n type: z.literal(\"snapshot\"),\n id: z.number(),\n snapshot: z.unknown(),\n});\nexport type SubscribedMessage = z.infer<typeof SubscribedSchema>;\n\nexport const DeltaMessageSchema = z.object({\n type: z.literal(\"delta\"),\n changes: z.record(z.number(), z.unknown()),\n});\nexport type DeltaMessage = z.infer<typeof DeltaMessageSchema>;\n\nexport const ResultMessageSchema = z.object({\n type: z.literal(\"result\"),\n id: z.number(),\n success: z.boolean(),\n value: z.unknown().optional(),\n error: z.string().optional(),\n});\nexport type ResultMessage = z.infer<typeof ResultMessageSchema>;\n\nexport const ServerMessageSchema = z.discriminatedUnion(\"type\", [\n HeartbeatMessageSchema,\n SubscribedSchema,\n DeltaMessageSchema,\n ResultMessageSchema,\n]);\nexport type ServerMessage = z.infer<typeof ServerMessageSchema>;\n\nexport const ServerMessage = {\n heartbeat: (): HeartbeatMessage => ({\n type: \"heartbeat\",\n }),\n subscribed: (id: number, snapshot: unknown): SubscribedMessage => ({\n type: \"snapshot\",\n id,\n snapshot,\n }),\n delta: (changes: Record<string, unknown>): DeltaMessage => ({\n type: \"delta\",\n changes: changes,\n }),\n resultSuccess: (id: number, value: unknown): ResultMessage => ({\n type: \"result\",\n id,\n success: true,\n value,\n }),\n resultError: (id: number, error: string): ResultMessage => ({\n type: \"result\",\n id,\n success: false,\n error,\n }),\n};\n","import { parseClientMessage, ClientMessage } from \"../client-message.js\";\nimport { ServerMessage } from \"../server-message.js\";\nimport {\n Source,\n StreamEndpoints,\n MutationEndpoints,\n RPCDefinition,\n} from \"../stream-types.js\";\nimport { PresenceHandler } from \"../presence-manager.js\";\n\n/**\n * Client handler for SharedWorker connections (browser-only).\n * Simplified version without rate limiting, heartbeats, or inactivity timeouts.\n * Designed for same-origin trusted connections.\n */\nexport class SharedWorkerClientHandler<Defs extends RPCDefinition, Ctx = void> {\n private readonly port: MessagePort;\n private readonly context: Ctx;\n private readonly streamEndpoints: StreamEndpoints<Defs[\"streams\"], Ctx>;\n private readonly mutationEndpoints: MutationEndpoints<Defs[\"mutations\"], Ctx>;\n private readonly presenceHandler?: PresenceHandler;\n private currentPresence?: Record<string, unknown>;\n private closed = false;\n private readonly streams = new Map<number, Source<unknown>>();\n\n constructor(\n port: MessagePort,\n context: Ctx,\n streamEndpoints: StreamEndpoints<Defs[\"streams\"], Ctx>,\n mutationEndpoints: MutationEndpoints<Defs[\"mutations\"], Ctx>,\n presenceHandler?: PresenceHandler,\n ) {\n this.port = port;\n this.context = context;\n this.streamEndpoints = streamEndpoints;\n this.mutationEndpoints = mutationEndpoints;\n this.presenceHandler = presenceHandler;\n\n console.log(\"new client connected\");\n\n // Set up transport handlers\n this.port.onmessage = (event: MessageEvent) => {\n this.handleMessage(event.data as string);\n };\n this.port.onmessageerror = () => this.handleDisconnect();\n }\n\n handleMessage(message: string) {\n let data: object;\n try {\n data = JSON.parse(message);\n } catch {\n console.error(\"Invalid JSON received:\", message);\n return this.close();\n }\n\n let parsed: ClientMessage;\n try {\n parsed = parseClientMessage(data);\n } catch (error) {\n console.error(\"Invalid client message:\", error);\n return this.close();\n }\n\n this.handleClientMessage(parsed);\n }\n\n async handleClientMessage(message: ClientMessage) {\n switch (message.type) {\n case \"subscribe\": {\n const { id, name, args } = message;\n\n if (!(name in this.streamEndpoints)) {\n console.error(`Unknown stream: ${name}`);\n this.close();\n return;\n }\n\n const endpoint = this.streamEndpoints[name as keyof Defs[\"streams\"]];\n\n try {\n const source = await endpoint(\n args as Defs[\"streams\"][keyof Defs[\"streams\"]][\"args\"],\n this.context,\n );\n this.streams.set(id, source);\n this.sendMessage(ServerMessage.subscribed(id, source.Snapshot));\n console.log(`Client subscribed to \"${name}\" (${id})`);\n } catch (err) {\n console.error(`Error building stream ${name}:`, err);\n this.close();\n }\n break;\n }\n\n case \"unsubscribe\": {\n const { id } = message;\n this.streams.delete(id);\n console.log(`Client unsubscribed from ${id}`);\n break;\n }\n\n case \"call\": {\n const { id, name, args } = message;\n\n if (!(name in this.mutationEndpoints)) {\n console.error(`Unknown mutation: ${name}`);\n this.close();\n return;\n }\n\n const endpoint =\n this.mutationEndpoints[name as keyof Defs[\"mutations\"]];\n\n endpoint(\n args as Defs[\"mutations\"][keyof Defs[\"mutations\"]][\"args\"],\n this.context,\n )\n .then((result) => {\n if (result.success) {\n this.sendMessage(ServerMessage.resultSuccess(id, result.value));\n console.log(`Mutation \"${name}\" (${id}) completed successfully`);\n } else {\n this.sendMessage(ServerMessage.resultError(id, result.error));\n console.log(\n `Mutation \"${name}\" (${id}) returned error: ${result.error}`,\n );\n }\n })\n .catch((err) => {\n console.error(\n `Unhandled exception in mutation \"${name}\" (${id}):`,\n err,\n );\n this.close();\n });\n break;\n }\n\n case \"heartbeat\":\n break;\n\n case \"presence\": {\n if (!this.presenceHandler) {\n console.error(\"Presence not configured\");\n this.close();\n return;\n }\n\n const { data } = message;\n\n if (this.currentPresence !== undefined) {\n this.presenceHandler.update(this.currentPresence, data);\n } else {\n this.presenceHandler.add(data);\n }\n\n this.currentPresence = data;\n break;\n }\n }\n }\n\n handleStep() {\n if (this.closed) return;\n const changes: Record<number, unknown> = {};\n\n for (const [id, source] of this.streams) {\n const change = source.LastChange;\n if (change === null) continue;\n changes[id] = change;\n }\n\n if (Object.keys(changes).length > 0) {\n console.log(\n `[ClientHandler] Sending delta with ${Object.keys(changes).length} changes for streams: ${Object.keys(changes).join(\", \")}`,\n );\n this.sendMessage(ServerMessage.delta(changes));\n }\n }\n\n sendMessage(message: ServerMessage) {\n if (this.closed) return;\n this.port.postMessage(JSON.stringify(message));\n }\n\n private handleDisconnect() {\n this.close();\n }\n\n close() {\n if (this.closed) return;\n console.log(\"[ClientHandler] Closing client connection\");\n this.closed = true;\n\n if (this.presenceHandler && this.currentPresence) {\n this.presenceHandler.remove(this.currentPresence);\n }\n\n console.log(\n `[ClientHandler] Cleaning up ${this.streams.size} active streams`,\n );\n this.streams.clear();\n\n try {\n this.port.close();\n } catch {}\n }\n}\n","export default class WeakList<T extends object> implements Iterable<T> {\n private items: WeakRef<T>[] = [];\n\n add(value: T): void {\n this.items.push(new WeakRef(value));\n }\n\n *[Symbol.iterator](): Iterator<T> {\n const newItems: WeakRef<T>[] = [];\n\n for (const ref of this.items) {\n const value = ref.deref();\n if (value !== undefined) {\n yield value;\n newItems.push(ref);\n }\n }\n\n this.items = newItems;\n }\n}\n","import { Graph } from \"derivation\";\nimport { SharedWorkerClientHandler } from \"./shared-worker-client-handler.js\";\nimport WeakList from \"../weak-list.js\";\nimport {\n StreamEndpoints,\n MutationEndpoints,\n RPCDefinition,\n} from \"../stream-types.js\";\nimport { PresenceHandler } from \"../presence-manager.js\";\n\nexport type SharedWorkerServerOptions<\n Defs extends RPCDefinition,\n Ctx = void,\n> = {\n streams: StreamEndpoints<Defs[\"streams\"], Ctx>;\n mutations: MutationEndpoints<Defs[\"mutations\"], Ctx>;\n createContext: (port: MessagePort) => Ctx | Promise<Ctx>;\n presenceHandler?: PresenceHandler;\n};\n\n/**\n * Set up a SharedWorker server for RPC communication.\n * This creates a shared Graph that all connected tabs can interact with.\n *\n * @example\n * ```typescript\n * // worker.ts\n * const { graph } = setupSharedWorker({\n * streams: {\n * todos: async (args, ctx) => new ReactiveSourceAdapter(source, iso),\n * },\n * mutations: {\n * addTodo: async (args, ctx) => ({ success: true, value: newTodo }),\n * },\n * createContext: (port) => ({ portId: crypto.randomUUID() }),\n * });\n * ```\n */\nexport function setupSharedWorker<Defs extends RPCDefinition, Ctx = void>(\n options: SharedWorkerServerOptions<Defs, Ctx>,\n graph: Graph,\n) {\n const { streams, mutations, createContext, presenceHandler } = options;\n\n const clients = new WeakList<SharedWorkerClientHandler<Defs, Ctx>>();\n\n // After each graph step, broadcast deltas to all connected clients\n graph.afterStep(() => {\n console.log(\"[SharedWorker] Broadcasting deltas to clients\");\n for (const client of clients) {\n client.handleStep();\n }\n });\n\n // Handle SharedWorker connections\n const globalScope = self as unknown as SharedWorkerGlobalScope;\n globalScope.onconnect = (event: MessageEvent) => {\n console.log(\"[SharedWorker] New client connecting...\");\n const port = event.ports[0];\n const messageBuffer: string[] = [];\n let client: SharedWorkerClientHandler<Defs, Ctx> | null = null;\n\n // Set up temporary message handler to buffer messages\n const tempMessageHandler = (e: MessageEvent) => {\n console.log(\"[SharedWorker] Buffering message during setup:\", e.data);\n messageBuffer.push(e.data);\n };\n port.onmessage = tempMessageHandler;\n\n // Create context (handle both sync and async)\n Promise.resolve(createContext(port))\n .then((context) => {\n console.log(\"[SharedWorker] Context created, setting up client handler\");\n // Create client handler\n client = new SharedWorkerClientHandler<Defs, Ctx>(\n port,\n context,\n streams,\n mutations,\n presenceHandler,\n );\n clients.add(client);\n console.log(\"[SharedWorker] Client handler registered\");\n\n // Process buffered messages\n console.log(`[SharedWorker] Processing ${messageBuffer.length} buffered messages`);\n for (const msg of messageBuffer) {\n client.handleMessage(msg);\n }\n messageBuffer.length = 0;\n\n // Start the port\n port.start();\n console.log(\"[SharedWorker] Client connection ready\");\n })\n .catch((err) => {\n console.error(\"[SharedWorker] Error creating context:\", err);\n port.close();\n });\n };\n}\n"],"mappings":"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","names":["z","HeartbeatMessageSchema"]}
|
|
@@ -83,23 +83,24 @@ var ClientMessage = {
|
|
|
83
83
|
};
|
|
84
84
|
|
|
85
85
|
// src/websocket/web-socket-client.ts
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
}
|
|
92
|
-
};
|
|
86
|
+
var DISCONNECTED_ERROR = "WebSocket is disconnected";
|
|
87
|
+
var CONNECTION_CLOSED_ERROR = "WebSocket connection closed";
|
|
88
|
+
function randomJitter(range) {
|
|
89
|
+
if (range <= 0) return 0;
|
|
90
|
+
return (Math.random() * 2 - 1) * range;
|
|
93
91
|
}
|
|
94
92
|
var WebSocketClient = class {
|
|
95
|
-
constructor(
|
|
96
|
-
this.
|
|
93
|
+
constructor(url, sinks, graph, reconnectOptions = {}) {
|
|
94
|
+
this.url = url;
|
|
97
95
|
this.sinks = sinks;
|
|
98
96
|
this.graph = graph;
|
|
99
97
|
this.nextId = 1;
|
|
100
98
|
this.pendingStreams = /* @__PURE__ */ new Map();
|
|
101
99
|
this.pendingMutations = /* @__PURE__ */ new Map();
|
|
102
100
|
this.activeStreams = /* @__PURE__ */ new Map();
|
|
101
|
+
this.reconnectAttempts = 0;
|
|
102
|
+
this.connected = false;
|
|
103
|
+
this.closed = false;
|
|
103
104
|
this.registry = new FinalizationRegistry(
|
|
104
105
|
([id, name]) => {
|
|
105
106
|
console.log(`\u{1F9F9} Stream ${id} (${name}) collected \u2014 unsubscribing`);
|
|
@@ -107,14 +108,142 @@ var WebSocketClient = class {
|
|
|
107
108
|
this.activeStreams.delete(id);
|
|
108
109
|
}
|
|
109
110
|
);
|
|
110
|
-
|
|
111
|
+
var _a, _b, _c, _d;
|
|
112
|
+
this.reconnectOptions = {
|
|
113
|
+
baseDelayMs: (_a = reconnectOptions.baseDelayMs) != null ? _a : 500,
|
|
114
|
+
maxDelayMs: (_b = reconnectOptions.maxDelayMs) != null ? _b : 15e3,
|
|
115
|
+
jitterRatio: (_c = reconnectOptions.jitterRatio) != null ? _c : 0.2,
|
|
116
|
+
maxReconnectAttempts: (_d = reconnectOptions.maxReconnectAttempts) != null ? _d : Number.POSITIVE_INFINITY
|
|
117
|
+
};
|
|
118
|
+
this.ws = this.createSocket();
|
|
119
|
+
this.bindSocket(this.ws);
|
|
120
|
+
this.connected = this.isSocketOpen();
|
|
121
|
+
if (this.connected) {
|
|
122
|
+
this.resetHeartbeat();
|
|
123
|
+
this.resetInactivity();
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
createSocket() {
|
|
127
|
+
return new globalThis.WebSocket(this.url);
|
|
128
|
+
}
|
|
129
|
+
bindSocket(socket) {
|
|
130
|
+
this.ws = socket;
|
|
131
|
+
socket.onopen = () => {
|
|
132
|
+
if (this.closed || this.ws !== socket) {
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
this.handleSocketOpen();
|
|
136
|
+
};
|
|
137
|
+
socket.onmessage = (event) => {
|
|
138
|
+
if (this.closed || this.ws !== socket) {
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
111
141
|
const message = JSON.parse(event.data);
|
|
112
142
|
this.handleMessage(message);
|
|
113
143
|
};
|
|
144
|
+
socket.onerror = () => {
|
|
145
|
+
if (this.closed || this.ws !== socket) {
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
this.handleTransportClose();
|
|
149
|
+
};
|
|
150
|
+
socket.onclose = () => {
|
|
151
|
+
if (this.closed || this.ws !== socket) {
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
this.handleTransportClose();
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
handleSocketOpen() {
|
|
158
|
+
this.connected = true;
|
|
159
|
+
this.reconnectAttempts = 0;
|
|
160
|
+
if (this.reconnectTimeout) {
|
|
161
|
+
clearTimeout(this.reconnectTimeout);
|
|
162
|
+
this.reconnectTimeout = void 0;
|
|
163
|
+
}
|
|
114
164
|
this.resetHeartbeat();
|
|
115
165
|
this.resetInactivity();
|
|
166
|
+
this.resubscribeAll();
|
|
167
|
+
if (this.currentPresence) {
|
|
168
|
+
this.sendMessage(ClientMessage.presence(this.currentPresence));
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
handleTransportClose() {
|
|
172
|
+
if (!this.connected && this.reconnectTimeout) {
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
this.connected = false;
|
|
176
|
+
if (this.heartbeatTimeout) {
|
|
177
|
+
clearTimeout(this.heartbeatTimeout);
|
|
178
|
+
this.heartbeatTimeout = void 0;
|
|
179
|
+
}
|
|
180
|
+
if (this.inactivityTimeout) {
|
|
181
|
+
clearTimeout(this.inactivityTimeout);
|
|
182
|
+
this.inactivityTimeout = void 0;
|
|
183
|
+
}
|
|
184
|
+
this.failPendingMutations(DISCONNECTED_ERROR);
|
|
185
|
+
this.scheduleReconnect();
|
|
186
|
+
}
|
|
187
|
+
scheduleReconnect() {
|
|
188
|
+
if (this.closed || this.reconnectTimeout) {
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
191
|
+
if (this.reconnectAttempts >= this.reconnectOptions.maxReconnectAttempts) {
|
|
192
|
+
this.failPendingStreams(CONNECTION_CLOSED_ERROR);
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
195
|
+
const exponent = Math.min(this.reconnectAttempts, 30);
|
|
196
|
+
const baseDelay = this.reconnectOptions.baseDelayMs * 2 ** exponent;
|
|
197
|
+
const cappedDelay = Math.min(baseDelay, this.reconnectOptions.maxDelayMs);
|
|
198
|
+
const jitter = cappedDelay * this.reconnectOptions.jitterRatio;
|
|
199
|
+
const delay = Math.max(0, Math.round(cappedDelay + randomJitter(jitter)));
|
|
200
|
+
this.reconnectAttempts += 1;
|
|
201
|
+
this.reconnectTimeout = setTimeout(() => {
|
|
202
|
+
this.reconnectTimeout = void 0;
|
|
203
|
+
if (this.closed) {
|
|
204
|
+
return;
|
|
205
|
+
}
|
|
206
|
+
try {
|
|
207
|
+
const socket = this.createSocket();
|
|
208
|
+
this.bindSocket(socket);
|
|
209
|
+
this.connected = this.isSocketOpen();
|
|
210
|
+
if (this.connected) {
|
|
211
|
+
this.handleSocketOpen();
|
|
212
|
+
}
|
|
213
|
+
} catch (err) {
|
|
214
|
+
console.error("Failed to create reconnect socket:", err);
|
|
215
|
+
this.scheduleReconnect();
|
|
216
|
+
}
|
|
217
|
+
}, delay);
|
|
218
|
+
}
|
|
219
|
+
resubscribeAll() {
|
|
220
|
+
for (const [id, pending] of this.pendingStreams) {
|
|
221
|
+
this.sendMessage(ClientMessage.subscribe(id, pending.name, pending.args));
|
|
222
|
+
}
|
|
223
|
+
for (const [id, active] of this.activeStreams) {
|
|
224
|
+
if (!active.input.deref()) {
|
|
225
|
+
this.activeStreams.delete(id);
|
|
226
|
+
continue;
|
|
227
|
+
}
|
|
228
|
+
this.sendMessage(ClientMessage.subscribe(id, active.name, active.args));
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
failPendingStreams(message) {
|
|
232
|
+
for (const [id, pending] of this.pendingStreams) {
|
|
233
|
+
pending.reject(new Error(message));
|
|
234
|
+
this.pendingStreams.delete(id);
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
failPendingMutations(message) {
|
|
238
|
+
for (const [id, resolve] of this.pendingMutations) {
|
|
239
|
+
resolve({ success: false, error: message });
|
|
240
|
+
this.pendingMutations.delete(id);
|
|
241
|
+
}
|
|
116
242
|
}
|
|
117
243
|
resetHeartbeat() {
|
|
244
|
+
if (!this.connected || this.closed) {
|
|
245
|
+
return;
|
|
246
|
+
}
|
|
118
247
|
if (this.heartbeatTimeout) {
|
|
119
248
|
clearTimeout(this.heartbeatTimeout);
|
|
120
249
|
}
|
|
@@ -123,11 +252,18 @@ var WebSocketClient = class {
|
|
|
123
252
|
}, 1e4);
|
|
124
253
|
}
|
|
125
254
|
resetInactivity() {
|
|
255
|
+
if (!this.connected || this.closed) {
|
|
256
|
+
return;
|
|
257
|
+
}
|
|
126
258
|
if (this.inactivityTimeout) {
|
|
127
259
|
clearTimeout(this.inactivityTimeout);
|
|
128
260
|
}
|
|
129
261
|
this.inactivityTimeout = setTimeout(() => {
|
|
130
|
-
|
|
262
|
+
try {
|
|
263
|
+
this.ws.close();
|
|
264
|
+
} catch (e) {
|
|
265
|
+
this.handleTransportClose();
|
|
266
|
+
}
|
|
131
267
|
}, 3e4);
|
|
132
268
|
}
|
|
133
269
|
handleMessage(message) {
|
|
@@ -135,25 +271,46 @@ var WebSocketClient = class {
|
|
|
135
271
|
switch (message.type) {
|
|
136
272
|
case "snapshot": {
|
|
137
273
|
console.log(`[WebSocketClient] Received snapshot for stream ${message.id}`);
|
|
138
|
-
const
|
|
139
|
-
if (
|
|
140
|
-
resolve(message.snapshot);
|
|
274
|
+
const pending = this.pendingStreams.get(message.id);
|
|
275
|
+
if (pending) {
|
|
276
|
+
pending.resolve(message.snapshot);
|
|
141
277
|
this.pendingStreams.delete(message.id);
|
|
278
|
+
break;
|
|
279
|
+
}
|
|
280
|
+
const active = this.activeStreams.get(message.id);
|
|
281
|
+
if (!active) {
|
|
282
|
+
break;
|
|
142
283
|
}
|
|
284
|
+
const input = active.input.deref();
|
|
285
|
+
if (!input) {
|
|
286
|
+
this.sendMessage(ClientMessage.unsubscribe(message.id));
|
|
287
|
+
this.activeStreams.delete(message.id);
|
|
288
|
+
break;
|
|
289
|
+
}
|
|
290
|
+
active.sink.reset(message.snapshot, input);
|
|
291
|
+
this.graph.step();
|
|
143
292
|
break;
|
|
144
293
|
}
|
|
145
294
|
case "delta": {
|
|
146
295
|
const changeCount = Object.keys(message.changes).length;
|
|
147
|
-
console.log(
|
|
296
|
+
console.log(
|
|
297
|
+
`[WebSocketClient] Received delta with ${changeCount} changes for streams: ${Object.keys(message.changes).join(", ")}`
|
|
298
|
+
);
|
|
148
299
|
for (const [idStr, change] of Object.entries(message.changes)) {
|
|
149
300
|
const id = Number(idStr);
|
|
150
|
-
const
|
|
151
|
-
if (
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
301
|
+
const active = this.activeStreams.get(id);
|
|
302
|
+
if (!active) {
|
|
303
|
+
this.sendMessage(ClientMessage.unsubscribe(id));
|
|
304
|
+
continue;
|
|
305
|
+
}
|
|
306
|
+
const input = active.input.deref();
|
|
307
|
+
if (!input) {
|
|
155
308
|
this.sendMessage(ClientMessage.unsubscribe(id));
|
|
156
309
|
this.activeStreams.delete(id);
|
|
310
|
+
continue;
|
|
311
|
+
}
|
|
312
|
+
if (change && typeof change === "object") {
|
|
313
|
+
active.sink.apply(change, input);
|
|
157
314
|
}
|
|
158
315
|
}
|
|
159
316
|
console.log("[WebSocketClient] Stepping graph");
|
|
@@ -161,19 +318,23 @@ var WebSocketClient = class {
|
|
|
161
318
|
break;
|
|
162
319
|
}
|
|
163
320
|
case "result": {
|
|
164
|
-
console.log(
|
|
321
|
+
console.log(
|
|
322
|
+
`[WebSocketClient] Received mutation result for call ${message.id}:`,
|
|
323
|
+
message.success ? "success" : "error"
|
|
324
|
+
);
|
|
165
325
|
const resolve = this.pendingMutations.get(message.id);
|
|
166
|
-
if (resolve) {
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
326
|
+
if (!resolve) {
|
|
327
|
+
break;
|
|
328
|
+
}
|
|
329
|
+
if (message.success) {
|
|
330
|
+
resolve({ success: true, value: message.value });
|
|
331
|
+
} else {
|
|
332
|
+
resolve({
|
|
333
|
+
success: false,
|
|
334
|
+
error: message.error || "Unknown error"
|
|
335
|
+
});
|
|
176
336
|
}
|
|
337
|
+
this.pendingMutations.delete(message.id);
|
|
177
338
|
break;
|
|
178
339
|
}
|
|
179
340
|
case "heartbeat":
|
|
@@ -181,52 +342,105 @@ var WebSocketClient = class {
|
|
|
181
342
|
break;
|
|
182
343
|
}
|
|
183
344
|
}
|
|
345
|
+
isSocketOpen() {
|
|
346
|
+
return this.ws.readyState === this.ws.OPEN;
|
|
347
|
+
}
|
|
184
348
|
sendMessage(message) {
|
|
349
|
+
if (this.closed) {
|
|
350
|
+
return false;
|
|
351
|
+
}
|
|
352
|
+
if (!this.isSocketOpen()) {
|
|
353
|
+
return false;
|
|
354
|
+
}
|
|
185
355
|
this.resetHeartbeat();
|
|
186
|
-
|
|
356
|
+
try {
|
|
357
|
+
this.ws.send(JSON.stringify(message));
|
|
358
|
+
return true;
|
|
359
|
+
} catch (e) {
|
|
360
|
+
this.handleTransportClose();
|
|
361
|
+
return false;
|
|
362
|
+
}
|
|
187
363
|
}
|
|
188
364
|
async run(key, args) {
|
|
189
365
|
console.log(
|
|
190
366
|
`Running stream ${String(key)} with args ${JSON.stringify(args)}`
|
|
191
367
|
);
|
|
192
368
|
const id = this.nextId++;
|
|
193
|
-
|
|
194
|
-
const
|
|
195
|
-
|
|
369
|
+
const name = String(key);
|
|
370
|
+
const typedArgs = args;
|
|
371
|
+
const snapshot = await new Promise((resolve, reject) => {
|
|
372
|
+
this.pendingStreams.set(id, { name, args: typedArgs, resolve, reject });
|
|
373
|
+
this.sendMessage(ClientMessage.subscribe(id, name, typedArgs));
|
|
196
374
|
});
|
|
197
375
|
const endpoint = this.sinks[key];
|
|
198
376
|
const sinkAdapter = endpoint(snapshot);
|
|
199
377
|
const { stream, input } = sinkAdapter.build();
|
|
200
378
|
const inputRef = new WeakRef(input);
|
|
201
|
-
this.activeStreams.set(id,
|
|
202
|
-
|
|
379
|
+
this.activeStreams.set(id, {
|
|
380
|
+
name,
|
|
381
|
+
args: typedArgs,
|
|
382
|
+
sink: sinkAdapter,
|
|
383
|
+
input: inputRef
|
|
384
|
+
});
|
|
385
|
+
this.registry.register(input, [id, name]);
|
|
203
386
|
return stream;
|
|
204
387
|
}
|
|
205
388
|
async call(key, args) {
|
|
206
389
|
console.log(
|
|
207
390
|
`Calling mutation ${String(key)} with args ${JSON.stringify(args)}`
|
|
208
391
|
);
|
|
392
|
+
if (!this.connected || !this.isSocketOpen()) {
|
|
393
|
+
return {
|
|
394
|
+
success: false,
|
|
395
|
+
error: DISCONNECTED_ERROR
|
|
396
|
+
};
|
|
397
|
+
}
|
|
209
398
|
const id = this.nextId++;
|
|
210
|
-
|
|
211
|
-
ClientMessage.call(id, String(key), args)
|
|
212
|
-
);
|
|
213
|
-
const result = await new Promise((resolve) => {
|
|
399
|
+
const resultPromise = new Promise((resolve) => {
|
|
214
400
|
this.pendingMutations.set(
|
|
215
401
|
id,
|
|
216
402
|
resolve
|
|
217
403
|
);
|
|
218
404
|
});
|
|
219
|
-
|
|
405
|
+
const sent = this.sendMessage(
|
|
406
|
+
ClientMessage.call(id, String(key), args)
|
|
407
|
+
);
|
|
408
|
+
if (!sent) {
|
|
409
|
+
this.pendingMutations.delete(id);
|
|
410
|
+
return {
|
|
411
|
+
success: false,
|
|
412
|
+
error: DISCONNECTED_ERROR
|
|
413
|
+
};
|
|
414
|
+
}
|
|
415
|
+
return resultPromise;
|
|
220
416
|
}
|
|
221
417
|
close() {
|
|
222
|
-
|
|
223
|
-
|
|
418
|
+
if (this.closed) {
|
|
419
|
+
return;
|
|
420
|
+
}
|
|
421
|
+
this.closed = true;
|
|
422
|
+
this.connected = false;
|
|
423
|
+
if (this.heartbeatTimeout) {
|
|
424
|
+
clearTimeout(this.heartbeatTimeout);
|
|
425
|
+
this.heartbeatTimeout = void 0;
|
|
426
|
+
}
|
|
427
|
+
if (this.inactivityTimeout) {
|
|
428
|
+
clearTimeout(this.inactivityTimeout);
|
|
429
|
+
this.inactivityTimeout = void 0;
|
|
430
|
+
}
|
|
431
|
+
if (this.reconnectTimeout) {
|
|
432
|
+
clearTimeout(this.reconnectTimeout);
|
|
433
|
+
this.reconnectTimeout = void 0;
|
|
434
|
+
}
|
|
435
|
+
this.failPendingStreams(CONNECTION_CLOSED_ERROR);
|
|
436
|
+
this.failPendingMutations(CONNECTION_CLOSED_ERROR);
|
|
224
437
|
try {
|
|
225
438
|
this.ws.close();
|
|
226
439
|
} catch (e) {
|
|
227
440
|
}
|
|
228
441
|
}
|
|
229
442
|
setPresence(value) {
|
|
443
|
+
this.currentPresence = value;
|
|
230
444
|
this.sendMessage(ClientMessage.presence(value));
|
|
231
445
|
}
|
|
232
446
|
};
|