@derivation/rpc 0.3.5 → 0.5.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/client-DJZfuakf.d.cts +27 -0
- package/dist/{client.d.ts → client-TPsVZH_B.d.ts} +7 -4
- package/dist/index.cjs +335 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +55 -0
- package/dist/index.d.ts +55 -7
- package/dist/index.js +305 -6
- package/dist/index.js.map +1 -0
- package/dist/iso.cjs +151 -0
- package/dist/iso.cjs.map +1 -0
- package/dist/iso.d.cts +52 -0
- package/dist/iso.d.ts +35 -19
- package/dist/iso.js +99 -101
- package/dist/iso.js.map +1 -0
- package/dist/{presence-manager.d.ts → presence-manager-4LlEyuGp.d.cts} +3 -1
- package/dist/presence-manager-4LlEyuGp.d.ts +7 -0
- package/dist/shared-worker-client.cjs +271 -0
- package/dist/shared-worker-client.cjs.map +1 -0
- package/dist/shared-worker-client.d.cts +26 -0
- package/dist/shared-worker-client.d.ts +8 -4
- package/dist/shared-worker-client.js +243 -24
- package/dist/shared-worker-client.js.map +1 -0
- package/dist/shared-worker-server.cjs +363 -0
- package/dist/shared-worker-server.cjs.map +1 -0
- package/dist/shared-worker-server.d.cts +31 -0
- package/dist/shared-worker-server.d.ts +9 -6
- package/dist/shared-worker-server.js +332 -58
- package/dist/shared-worker-server.js.map +1 -0
- package/dist/stream-types-Q_EqNLtO.d.cts +46 -0
- package/dist/stream-types-Q_EqNLtO.d.ts +46 -0
- package/dist/transport.cjs +19 -0
- package/dist/transport.cjs.map +1 -0
- package/dist/transport.d.cts +29 -0
- package/dist/transport.d.ts +3 -1
- package/dist/transport.js +1 -1
- package/dist/transport.js.map +1 -0
- package/dist/web-socket-server.cjs +469 -0
- package/dist/web-socket-server.cjs.map +1 -0
- package/dist/web-socket-server.d.cts +14 -0
- package/dist/web-socket-server.d.ts +10 -7
- package/dist/web-socket-server.js +437 -52
- package/dist/web-socket-server.js.map +1 -0
- package/dist/web-socket-transport.cjs +52 -0
- package/dist/web-socket-transport.cjs.map +1 -0
- package/dist/web-socket-transport.d.cts +16 -0
- package/dist/web-socket-transport.d.ts +5 -2
- package/dist/web-socket-transport.js +27 -25
- package/dist/web-socket-transport.js.map +1 -0
- package/package.json +26 -17
- package/dist/client-handler.d.ts +0 -27
- package/dist/client-handler.js +0 -187
- package/dist/client-message.d.ts +0 -57
- package/dist/client-message.js +0 -59
- package/dist/client.js +0 -133
- package/dist/messageport-transport.d.ts +0 -13
- package/dist/messageport-transport.js +0 -28
- package/dist/node-web-socket-transport.d.ts +0 -16
- package/dist/node-web-socket-transport.js +0 -33
- package/dist/presence-manager.js +0 -1
- package/dist/queue.d.ts +0 -9
- package/dist/queue.js +0 -32
- package/dist/rate-limiter.d.ts +0 -7
- package/dist/rate-limiter.js +0 -24
- package/dist/reactive-map-adapter.d.ts +0 -24
- package/dist/reactive-map-adapter.js +0 -43
- package/dist/reactive-set-adapter.d.ts +0 -24
- package/dist/reactive-set-adapter.js +0 -41
- package/dist/server-message.d.ts +0 -48
- package/dist/server-message.js +0 -52
- package/dist/shared-worker-client-handler.d.ts +0 -27
- package/dist/shared-worker-client-handler.js +0 -149
- package/dist/stream-adapter.d.ts +0 -23
- package/dist/stream-adapter.js +0 -35
- package/dist/stream-types.d.ts +0 -44
- package/dist/stream-types.js +0 -1
- package/dist/tests/context.test.d.ts +0 -1
- package/dist/tests/context.test.js +0 -252
- package/dist/tests/iso.test.d.ts +0 -1
- package/dist/tests/iso.test.js +0 -186
- package/dist/tests/messages.test.d.ts +0 -1
- package/dist/tests/messages.test.js +0 -152
- package/dist/tests/mutations.test.d.ts +0 -1
- package/dist/tests/mutations.test.js +0 -122
- package/dist/tests/queue.test.d.ts +0 -1
- package/dist/tests/queue.test.js +0 -84
- package/dist/tests/reactive-map-adapter.test.d.ts +0 -1
- package/dist/tests/reactive-map-adapter.test.js +0 -190
- package/dist/tests/reactive-set-adapter.test.d.ts +0 -1
- package/dist/tests/reactive-set-adapter.test.js +0 -157
- package/dist/tests/stream-adapter.test.d.ts +0 -1
- package/dist/tests/stream-adapter.test.js +0 -119
- package/dist/tests/weak-list.test.d.ts +0 -1
- package/dist/tests/weak-list.test.js +0 -100
- package/dist/tsconfig.tsbuildinfo +0 -1
- package/dist/weak-list.d.ts +0 -5
- package/dist/weak-list.js +0 -19
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/shared-worker-server.ts","../src/client-message.ts","../src/server-message.ts","../src/shared-worker-client-handler.ts","../src/weak-list.ts","../src/messageport-transport.ts"],"sourcesContent":["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\";\nimport { MessagePortTransport } from \"./messageport-transport.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 transport and client handler\n const transport = new MessagePortTransport(port);\n client = new SharedWorkerClientHandler<Defs, Ctx>(\n transport,\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","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 { 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\";\nimport { Transport } from \"./transport.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 transport: Transport;\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 transport: Transport,\n context: Ctx,\n streamEndpoints: StreamEndpoints<Defs[\"streams\"], Ctx>,\n mutationEndpoints: MutationEndpoints<Defs[\"mutations\"], Ctx>,\n presenceHandler?: PresenceHandler,\n ) {\n this.transport = transport;\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.transport.onMessage((data: string) => this.handleMessage(data));\n this.transport.onClose(() => 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(\n `Mutation \"${name}\" (${id}) completed successfully`,\n );\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(`[ClientHandler] Sending delta with ${Object.keys(changes).length} changes for streams: ${Object.keys(changes).join(\", \")}`);\n this.sendMessage(ServerMessage.delta(changes));\n }\n }\n\n sendMessage(message: ServerMessage) {\n if (this.closed) return;\n this.transport.send(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(`[ClientHandler] Cleaning up ${this.streams.size} active streams`);\n this.streams.clear();\n\n try {\n this.transport.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 { Transport } from \"./transport.js\";\n\n/**\n * Transport implementation for MessagePort (SharedWorker communication).\n */\nexport class MessagePortTransport implements Transport {\n constructor(private port: MessagePort) {}\n\n send(data: string): void {\n console.log(\"[MessagePortTransport] Sending:\", data.substring(0, 100) + (data.length > 100 ? \"...\" : \"\"));\n this.port.postMessage(data);\n }\n\n onMessage(handler: (data: string) => void): void {\n this.port.onmessage = (event: MessageEvent) => {\n console.log(\"[MessagePortTransport] Received:\", event.data.substring(0, 100) + (event.data.length > 100 ? \"...\" : \"\"));\n handler(event.data);\n };\n }\n\n onClose(handler: () => void): void {\n // MessagePort doesn't have a reliable close event\n // We'll use messageerror as a signal, though it's not perfect\n this.port.onmessageerror = handler;\n }\n\n close(): void {\n this.port.close();\n }\n\n // MessagePort doesn't provide bufferedAmount\n get bufferedAmount(): undefined {\n return undefined;\n }\n}\n"],"mappings":"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","names":["import_zod","HeartbeatMessageSchema"]}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { Graph } from 'derivation';
|
|
2
|
+
import { R as RPCDefinition, g as StreamEndpoints, c as MutationEndpoints } from './stream-types-Q_EqNLtO.cjs';
|
|
3
|
+
import { P as PresenceHandler } from './presence-manager-4LlEyuGp.cjs';
|
|
4
|
+
|
|
5
|
+
type SharedWorkerServerOptions<Defs extends RPCDefinition, Ctx = void> = {
|
|
6
|
+
streams: StreamEndpoints<Defs["streams"], Ctx>;
|
|
7
|
+
mutations: MutationEndpoints<Defs["mutations"], Ctx>;
|
|
8
|
+
createContext: (port: MessagePort) => Ctx | Promise<Ctx>;
|
|
9
|
+
presenceHandler?: PresenceHandler;
|
|
10
|
+
};
|
|
11
|
+
/**
|
|
12
|
+
* Set up a SharedWorker server for RPC communication.
|
|
13
|
+
* This creates a shared Graph that all connected tabs can interact with.
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* ```typescript
|
|
17
|
+
* // worker.ts
|
|
18
|
+
* const { graph } = setupSharedWorker({
|
|
19
|
+
* streams: {
|
|
20
|
+
* todos: async (args, ctx) => new ReactiveSourceAdapter(source, iso),
|
|
21
|
+
* },
|
|
22
|
+
* mutations: {
|
|
23
|
+
* addTodo: async (args, ctx) => ({ success: true, value: newTodo }),
|
|
24
|
+
* },
|
|
25
|
+
* createContext: (port) => ({ portId: crypto.randomUUID() }),
|
|
26
|
+
* });
|
|
27
|
+
* ```
|
|
28
|
+
*/
|
|
29
|
+
declare function setupSharedWorker<Defs extends RPCDefinition, Ctx = void>(options: SharedWorkerServerOptions<Defs, Ctx>, graph: Graph): void;
|
|
30
|
+
|
|
31
|
+
export { type SharedWorkerServerOptions, setupSharedWorker };
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { Graph } from
|
|
2
|
-
import { StreamEndpoints, MutationEndpoints
|
|
3
|
-
import { PresenceHandler } from
|
|
4
|
-
|
|
1
|
+
import { Graph } from 'derivation';
|
|
2
|
+
import { R as RPCDefinition, g as StreamEndpoints, c as MutationEndpoints } from './stream-types-Q_EqNLtO.js';
|
|
3
|
+
import { P as PresenceHandler } from './presence-manager-4LlEyuGp.js';
|
|
4
|
+
|
|
5
|
+
type SharedWorkerServerOptions<Defs extends RPCDefinition, Ctx = void> = {
|
|
5
6
|
streams: StreamEndpoints<Defs["streams"], Ctx>;
|
|
6
7
|
mutations: MutationEndpoints<Defs["mutations"], Ctx>;
|
|
7
8
|
createContext: (port: MessagePort) => Ctx | Promise<Ctx>;
|
|
@@ -16,7 +17,7 @@ export type SharedWorkerServerOptions<Defs extends RPCDefinition, Ctx = void> =
|
|
|
16
17
|
* // worker.ts
|
|
17
18
|
* const { graph } = setupSharedWorker({
|
|
18
19
|
* streams: {
|
|
19
|
-
* todos: async (args, ctx) => new
|
|
20
|
+
* todos: async (args, ctx) => new ReactiveSourceAdapter(source, iso),
|
|
20
21
|
* },
|
|
21
22
|
* mutations: {
|
|
22
23
|
* addTodo: async (args, ctx) => ({ success: true, value: newTodo }),
|
|
@@ -25,4 +26,6 @@ export type SharedWorkerServerOptions<Defs extends RPCDefinition, Ctx = void> =
|
|
|
25
26
|
* });
|
|
26
27
|
* ```
|
|
27
28
|
*/
|
|
28
|
-
|
|
29
|
+
declare function setupSharedWorker<Defs extends RPCDefinition, Ctx = void>(options: SharedWorkerServerOptions<Defs, Ctx>, graph: Graph): void;
|
|
30
|
+
|
|
31
|
+
export { type SharedWorkerServerOptions, setupSharedWorker };
|
|
@@ -1,62 +1,336 @@
|
|
|
1
|
-
|
|
2
|
-
import
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
1
|
+
// src/client-message.ts
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
var SubscribeMessageSchema = z.object({
|
|
4
|
+
type: z.literal("subscribe"),
|
|
5
|
+
id: z.number(),
|
|
6
|
+
name: z.string(),
|
|
7
|
+
args: z.looseObject({})
|
|
8
|
+
});
|
|
9
|
+
var UnsubscribeMessageSchema = z.object({
|
|
10
|
+
type: z.literal("unsubscribe"),
|
|
11
|
+
id: z.number()
|
|
12
|
+
});
|
|
13
|
+
var HeartbeatMessageSchema = z.object({
|
|
14
|
+
type: z.literal("heartbeat")
|
|
15
|
+
});
|
|
16
|
+
var CallMessageSchema = z.object({
|
|
17
|
+
type: z.literal("call"),
|
|
18
|
+
id: z.number(),
|
|
19
|
+
name: z.string(),
|
|
20
|
+
args: z.looseObject({})
|
|
21
|
+
});
|
|
22
|
+
var PresenceMessageSchema = z.object({
|
|
23
|
+
type: z.literal("presence"),
|
|
24
|
+
data: z.looseObject({})
|
|
25
|
+
});
|
|
26
|
+
var ClientMessageSchema = z.discriminatedUnion("type", [
|
|
27
|
+
SubscribeMessageSchema,
|
|
28
|
+
UnsubscribeMessageSchema,
|
|
29
|
+
HeartbeatMessageSchema,
|
|
30
|
+
CallMessageSchema,
|
|
31
|
+
PresenceMessageSchema
|
|
32
|
+
]);
|
|
33
|
+
function parseClientMessage(data) {
|
|
34
|
+
return ClientMessageSchema.parse(data);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// src/server-message.ts
|
|
38
|
+
import { z as z2 } from "zod";
|
|
39
|
+
var HeartbeatMessageSchema2 = z2.object({
|
|
40
|
+
type: z2.literal("heartbeat")
|
|
41
|
+
});
|
|
42
|
+
var SubscribedSchema = z2.object({
|
|
43
|
+
type: z2.literal("snapshot"),
|
|
44
|
+
id: z2.number(),
|
|
45
|
+
snapshot: z2.unknown()
|
|
46
|
+
});
|
|
47
|
+
var DeltaMessageSchema = z2.object({
|
|
48
|
+
type: z2.literal("delta"),
|
|
49
|
+
changes: z2.record(z2.number(), z2.unknown())
|
|
50
|
+
});
|
|
51
|
+
var ResultMessageSchema = z2.object({
|
|
52
|
+
type: z2.literal("result"),
|
|
53
|
+
id: z2.number(),
|
|
54
|
+
success: z2.boolean(),
|
|
55
|
+
value: z2.unknown().optional(),
|
|
56
|
+
error: z2.string().optional()
|
|
57
|
+
});
|
|
58
|
+
var ServerMessageSchema = z2.discriminatedUnion("type", [
|
|
59
|
+
HeartbeatMessageSchema2,
|
|
60
|
+
SubscribedSchema,
|
|
61
|
+
DeltaMessageSchema,
|
|
62
|
+
ResultMessageSchema
|
|
63
|
+
]);
|
|
64
|
+
var ServerMessage = {
|
|
65
|
+
heartbeat: () => ({
|
|
66
|
+
type: "heartbeat"
|
|
67
|
+
}),
|
|
68
|
+
subscribed: (id, snapshot) => ({
|
|
69
|
+
type: "snapshot",
|
|
70
|
+
id,
|
|
71
|
+
snapshot
|
|
72
|
+
}),
|
|
73
|
+
delta: (changes) => ({
|
|
74
|
+
type: "delta",
|
|
75
|
+
changes
|
|
76
|
+
}),
|
|
77
|
+
resultSuccess: (id, value) => ({
|
|
78
|
+
type: "result",
|
|
79
|
+
id,
|
|
80
|
+
success: true,
|
|
81
|
+
value
|
|
82
|
+
}),
|
|
83
|
+
resultError: (id, error) => ({
|
|
84
|
+
type: "result",
|
|
85
|
+
id,
|
|
86
|
+
success: false,
|
|
87
|
+
error
|
|
88
|
+
})
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
// src/shared-worker-client-handler.ts
|
|
92
|
+
var SharedWorkerClientHandler = class {
|
|
93
|
+
constructor(transport, context, streamEndpoints, mutationEndpoints, presenceHandler) {
|
|
94
|
+
this.closed = false;
|
|
95
|
+
this.streams = /* @__PURE__ */ new Map();
|
|
96
|
+
this.transport = transport;
|
|
97
|
+
this.context = context;
|
|
98
|
+
this.streamEndpoints = streamEndpoints;
|
|
99
|
+
this.mutationEndpoints = mutationEndpoints;
|
|
100
|
+
this.presenceHandler = presenceHandler;
|
|
101
|
+
console.log("new client connected");
|
|
102
|
+
this.transport.onMessage((data) => this.handleMessage(data));
|
|
103
|
+
this.transport.onClose(() => this.handleDisconnect());
|
|
104
|
+
}
|
|
105
|
+
handleMessage(message) {
|
|
106
|
+
let data;
|
|
107
|
+
try {
|
|
108
|
+
data = JSON.parse(message);
|
|
109
|
+
} catch (e) {
|
|
110
|
+
console.error("Invalid JSON received:", message);
|
|
111
|
+
return this.close();
|
|
112
|
+
}
|
|
113
|
+
let parsed;
|
|
114
|
+
try {
|
|
115
|
+
parsed = parseClientMessage(data);
|
|
116
|
+
} catch (error) {
|
|
117
|
+
console.error("Invalid client message:", error);
|
|
118
|
+
return this.close();
|
|
119
|
+
}
|
|
120
|
+
this.handleClientMessage(parsed);
|
|
121
|
+
}
|
|
122
|
+
async handleClientMessage(message) {
|
|
123
|
+
switch (message.type) {
|
|
124
|
+
case "subscribe": {
|
|
125
|
+
const { id, name, args } = message;
|
|
126
|
+
if (!(name in this.streamEndpoints)) {
|
|
127
|
+
console.error(`Unknown stream: ${name}`);
|
|
128
|
+
this.close();
|
|
129
|
+
return;
|
|
29
130
|
}
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
131
|
+
const endpoint = this.streamEndpoints[name];
|
|
132
|
+
try {
|
|
133
|
+
const source = await endpoint(
|
|
134
|
+
args,
|
|
135
|
+
this.context
|
|
136
|
+
);
|
|
137
|
+
this.streams.set(id, source);
|
|
138
|
+
this.sendMessage(ServerMessage.subscribed(id, source.Snapshot));
|
|
139
|
+
console.log(`Client subscribed to "${name}" (${id})`);
|
|
140
|
+
} catch (err) {
|
|
141
|
+
console.error(`Error building stream ${name}:`, err);
|
|
142
|
+
this.close();
|
|
143
|
+
}
|
|
144
|
+
break;
|
|
145
|
+
}
|
|
146
|
+
case "unsubscribe": {
|
|
147
|
+
const { id } = message;
|
|
148
|
+
this.streams.delete(id);
|
|
149
|
+
console.log(`Client unsubscribed from ${id}`);
|
|
150
|
+
break;
|
|
151
|
+
}
|
|
152
|
+
case "call": {
|
|
153
|
+
const { id, name, args } = message;
|
|
154
|
+
if (!(name in this.mutationEndpoints)) {
|
|
155
|
+
console.error(`Unknown mutation: ${name}`);
|
|
156
|
+
this.close();
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
159
|
+
const endpoint = this.mutationEndpoints[name];
|
|
160
|
+
endpoint(
|
|
161
|
+
args,
|
|
162
|
+
this.context
|
|
163
|
+
).then((result) => {
|
|
164
|
+
if (result.success) {
|
|
165
|
+
this.sendMessage(ServerMessage.resultSuccess(id, result.value));
|
|
166
|
+
console.log(
|
|
167
|
+
`Mutation "${name}" (${id}) completed successfully`
|
|
168
|
+
);
|
|
169
|
+
} else {
|
|
170
|
+
this.sendMessage(ServerMessage.resultError(id, result.error));
|
|
171
|
+
console.log(
|
|
172
|
+
`Mutation "${name}" (${id}) returned error: ${result.error}`
|
|
173
|
+
);
|
|
174
|
+
}
|
|
175
|
+
}).catch((err) => {
|
|
176
|
+
console.error(
|
|
177
|
+
`Unhandled exception in mutation "${name}" (${id}):`,
|
|
178
|
+
err
|
|
179
|
+
);
|
|
180
|
+
this.close();
|
|
60
181
|
});
|
|
182
|
+
break;
|
|
183
|
+
}
|
|
184
|
+
case "heartbeat":
|
|
185
|
+
break;
|
|
186
|
+
case "presence": {
|
|
187
|
+
if (!this.presenceHandler) {
|
|
188
|
+
console.error("Presence not configured");
|
|
189
|
+
this.close();
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
192
|
+
const { data } = message;
|
|
193
|
+
if (this.currentPresence !== void 0) {
|
|
194
|
+
this.presenceHandler.update(this.currentPresence, data);
|
|
195
|
+
} else {
|
|
196
|
+
this.presenceHandler.add(data);
|
|
197
|
+
}
|
|
198
|
+
this.currentPresence = data;
|
|
199
|
+
break;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
handleStep() {
|
|
204
|
+
if (this.closed) return;
|
|
205
|
+
const changes = {};
|
|
206
|
+
for (const [id, source] of this.streams) {
|
|
207
|
+
const change = source.LastChange;
|
|
208
|
+
if (change === null) continue;
|
|
209
|
+
changes[id] = change;
|
|
210
|
+
}
|
|
211
|
+
if (Object.keys(changes).length > 0) {
|
|
212
|
+
console.log(`[ClientHandler] Sending delta with ${Object.keys(changes).length} changes for streams: ${Object.keys(changes).join(", ")}`);
|
|
213
|
+
this.sendMessage(ServerMessage.delta(changes));
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
sendMessage(message) {
|
|
217
|
+
if (this.closed) return;
|
|
218
|
+
this.transport.send(JSON.stringify(message));
|
|
219
|
+
}
|
|
220
|
+
handleDisconnect() {
|
|
221
|
+
this.close();
|
|
222
|
+
}
|
|
223
|
+
close() {
|
|
224
|
+
if (this.closed) return;
|
|
225
|
+
console.log("[ClientHandler] Closing client connection");
|
|
226
|
+
this.closed = true;
|
|
227
|
+
if (this.presenceHandler && this.currentPresence) {
|
|
228
|
+
this.presenceHandler.remove(this.currentPresence);
|
|
229
|
+
}
|
|
230
|
+
console.log(`[ClientHandler] Cleaning up ${this.streams.size} active streams`);
|
|
231
|
+
this.streams.clear();
|
|
232
|
+
try {
|
|
233
|
+
this.transport.close();
|
|
234
|
+
} catch (e) {
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
};
|
|
238
|
+
|
|
239
|
+
// src/weak-list.ts
|
|
240
|
+
var WeakList = class {
|
|
241
|
+
constructor() {
|
|
242
|
+
this.items = [];
|
|
243
|
+
}
|
|
244
|
+
add(value) {
|
|
245
|
+
this.items.push(new WeakRef(value));
|
|
246
|
+
}
|
|
247
|
+
*[Symbol.iterator]() {
|
|
248
|
+
const newItems = [];
|
|
249
|
+
for (const ref of this.items) {
|
|
250
|
+
const value = ref.deref();
|
|
251
|
+
if (value !== void 0) {
|
|
252
|
+
yield value;
|
|
253
|
+
newItems.push(ref);
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
this.items = newItems;
|
|
257
|
+
}
|
|
258
|
+
};
|
|
259
|
+
|
|
260
|
+
// src/messageport-transport.ts
|
|
261
|
+
var MessagePortTransport = class {
|
|
262
|
+
constructor(port) {
|
|
263
|
+
this.port = port;
|
|
264
|
+
}
|
|
265
|
+
send(data) {
|
|
266
|
+
console.log("[MessagePortTransport] Sending:", data.substring(0, 100) + (data.length > 100 ? "..." : ""));
|
|
267
|
+
this.port.postMessage(data);
|
|
268
|
+
}
|
|
269
|
+
onMessage(handler) {
|
|
270
|
+
this.port.onmessage = (event) => {
|
|
271
|
+
console.log("[MessagePortTransport] Received:", event.data.substring(0, 100) + (event.data.length > 100 ? "..." : ""));
|
|
272
|
+
handler(event.data);
|
|
61
273
|
};
|
|
274
|
+
}
|
|
275
|
+
onClose(handler) {
|
|
276
|
+
this.port.onmessageerror = handler;
|
|
277
|
+
}
|
|
278
|
+
close() {
|
|
279
|
+
this.port.close();
|
|
280
|
+
}
|
|
281
|
+
// MessagePort doesn't provide bufferedAmount
|
|
282
|
+
get bufferedAmount() {
|
|
283
|
+
return void 0;
|
|
284
|
+
}
|
|
285
|
+
};
|
|
286
|
+
|
|
287
|
+
// src/shared-worker-server.ts
|
|
288
|
+
function setupSharedWorker(options, graph) {
|
|
289
|
+
const { streams, mutations, createContext, presenceHandler } = options;
|
|
290
|
+
const clients = new WeakList();
|
|
291
|
+
graph.afterStep(() => {
|
|
292
|
+
console.log("[SharedWorker] Broadcasting deltas to clients");
|
|
293
|
+
for (const client of clients) {
|
|
294
|
+
client.handleStep();
|
|
295
|
+
}
|
|
296
|
+
});
|
|
297
|
+
const globalScope = self;
|
|
298
|
+
globalScope.onconnect = (event) => {
|
|
299
|
+
console.log("[SharedWorker] New client connecting...");
|
|
300
|
+
const port = event.ports[0];
|
|
301
|
+
const messageBuffer = [];
|
|
302
|
+
let client = null;
|
|
303
|
+
const tempMessageHandler = (e) => {
|
|
304
|
+
console.log("[SharedWorker] Buffering message during setup:", e.data);
|
|
305
|
+
messageBuffer.push(e.data);
|
|
306
|
+
};
|
|
307
|
+
port.onmessage = tempMessageHandler;
|
|
308
|
+
Promise.resolve(createContext(port)).then((context) => {
|
|
309
|
+
console.log("[SharedWorker] Context created, setting up client handler");
|
|
310
|
+
const transport = new MessagePortTransport(port);
|
|
311
|
+
client = new SharedWorkerClientHandler(
|
|
312
|
+
transport,
|
|
313
|
+
context,
|
|
314
|
+
streams,
|
|
315
|
+
mutations,
|
|
316
|
+
presenceHandler
|
|
317
|
+
);
|
|
318
|
+
clients.add(client);
|
|
319
|
+
console.log("[SharedWorker] Client handler registered");
|
|
320
|
+
console.log(`[SharedWorker] Processing ${messageBuffer.length} buffered messages`);
|
|
321
|
+
for (const msg of messageBuffer) {
|
|
322
|
+
client.handleMessage(msg);
|
|
323
|
+
}
|
|
324
|
+
messageBuffer.length = 0;
|
|
325
|
+
port.start();
|
|
326
|
+
console.log("[SharedWorker] Client connection ready");
|
|
327
|
+
}).catch((err) => {
|
|
328
|
+
console.error("[SharedWorker] Error creating context:", err);
|
|
329
|
+
port.close();
|
|
330
|
+
});
|
|
331
|
+
};
|
|
62
332
|
}
|
|
333
|
+
export {
|
|
334
|
+
setupSharedWorker
|
|
335
|
+
};
|
|
336
|
+
//# sourceMappingURL=shared-worker-server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/client-message.ts","../src/server-message.ts","../src/shared-worker-client-handler.ts","../src/weak-list.ts","../src/messageport-transport.ts","../src/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 { 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\";\nimport { Transport } from \"./transport.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 transport: Transport;\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 transport: Transport,\n context: Ctx,\n streamEndpoints: StreamEndpoints<Defs[\"streams\"], Ctx>,\n mutationEndpoints: MutationEndpoints<Defs[\"mutations\"], Ctx>,\n presenceHandler?: PresenceHandler,\n ) {\n this.transport = transport;\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.transport.onMessage((data: string) => this.handleMessage(data));\n this.transport.onClose(() => 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(\n `Mutation \"${name}\" (${id}) completed successfully`,\n );\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(`[ClientHandler] Sending delta with ${Object.keys(changes).length} changes for streams: ${Object.keys(changes).join(\", \")}`);\n this.sendMessage(ServerMessage.delta(changes));\n }\n }\n\n sendMessage(message: ServerMessage) {\n if (this.closed) return;\n this.transport.send(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(`[ClientHandler] Cleaning up ${this.streams.size} active streams`);\n this.streams.clear();\n\n try {\n this.transport.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 { Transport } from \"./transport.js\";\n\n/**\n * Transport implementation for MessagePort (SharedWorker communication).\n */\nexport class MessagePortTransport implements Transport {\n constructor(private port: MessagePort) {}\n\n send(data: string): void {\n console.log(\"[MessagePortTransport] Sending:\", data.substring(0, 100) + (data.length > 100 ? \"...\" : \"\"));\n this.port.postMessage(data);\n }\n\n onMessage(handler: (data: string) => void): void {\n this.port.onmessage = (event: MessageEvent) => {\n console.log(\"[MessagePortTransport] Received:\", event.data.substring(0, 100) + (event.data.length > 100 ? \"...\" : \"\"));\n handler(event.data);\n };\n }\n\n onClose(handler: () => void): void {\n // MessagePort doesn't have a reliable close event\n // We'll use messageerror as a signal, though it's not perfect\n this.port.onmessageerror = handler;\n }\n\n close(): void {\n this.port.close();\n }\n\n // MessagePort doesn't provide bufferedAmount\n get bufferedAmount(): undefined {\n return undefined;\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\";\nimport { MessagePortTransport } from \"./messageport-transport.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 transport and client handler\n const transport = new MessagePortTransport(port);\n client = new SharedWorkerClientHandler<Defs, Ctx>(\n transport,\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"]}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
interface Source<ReturnType> {
|
|
2
|
+
get Snapshot(): object;
|
|
3
|
+
get LastChange(): object | null;
|
|
4
|
+
get Stream(): ReturnType;
|
|
5
|
+
}
|
|
6
|
+
interface Sink<SinkType, InputType> {
|
|
7
|
+
apply(change: object, input: InputType): void;
|
|
8
|
+
build(): {
|
|
9
|
+
stream: SinkType;
|
|
10
|
+
input: InputType;
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
type StreamDefinition<Args extends object, ReturnType extends object, SinkType extends ReturnType, InputType extends object> = {
|
|
14
|
+
args: Args;
|
|
15
|
+
returnType: ReturnType;
|
|
16
|
+
sinkType: SinkType;
|
|
17
|
+
inputType: InputType;
|
|
18
|
+
};
|
|
19
|
+
type StreamDefinitions = Record<string, StreamDefinition<object, object, object, object>>;
|
|
20
|
+
type StreamEndpoints<Definitions extends StreamDefinitions, Ctx = void> = {
|
|
21
|
+
[K in keyof Definitions]: (args: Definitions[K]["args"], ctx: Ctx) => Promise<Source<Definitions[K]["returnType"]>>;
|
|
22
|
+
};
|
|
23
|
+
type StreamSinks<Definitions extends StreamDefinitions> = {
|
|
24
|
+
[K in keyof Definitions]: (snapshot: object) => Sink<Definitions[K]["sinkType"], Definitions[K]["inputType"]>;
|
|
25
|
+
};
|
|
26
|
+
type MutationResult<T> = {
|
|
27
|
+
success: true;
|
|
28
|
+
value: T;
|
|
29
|
+
} | {
|
|
30
|
+
success: false;
|
|
31
|
+
error: string;
|
|
32
|
+
};
|
|
33
|
+
type MutationDefinition<Args, Result> = {
|
|
34
|
+
args: Args;
|
|
35
|
+
result: Result;
|
|
36
|
+
};
|
|
37
|
+
type MutationDefinitions = Record<string, MutationDefinition<unknown, unknown>>;
|
|
38
|
+
type MutationEndpoints<Definitions extends MutationDefinitions, Ctx = void> = {
|
|
39
|
+
[K in keyof Definitions]: (args: Definitions[K]["args"], ctx: Ctx) => Promise<MutationResult<Definitions[K]["result"]>>;
|
|
40
|
+
};
|
|
41
|
+
type RPCDefinition = {
|
|
42
|
+
streams: StreamDefinitions;
|
|
43
|
+
mutations: MutationDefinitions;
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
export type { MutationDefinition as M, RPCDefinition as R, Sink as S, Source as a, MutationDefinitions as b, MutationEndpoints as c, MutationResult as d, StreamDefinition as e, StreamDefinitions as f, StreamEndpoints as g, StreamSinks as h };
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
interface Source<ReturnType> {
|
|
2
|
+
get Snapshot(): object;
|
|
3
|
+
get LastChange(): object | null;
|
|
4
|
+
get Stream(): ReturnType;
|
|
5
|
+
}
|
|
6
|
+
interface Sink<SinkType, InputType> {
|
|
7
|
+
apply(change: object, input: InputType): void;
|
|
8
|
+
build(): {
|
|
9
|
+
stream: SinkType;
|
|
10
|
+
input: InputType;
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
type StreamDefinition<Args extends object, ReturnType extends object, SinkType extends ReturnType, InputType extends object> = {
|
|
14
|
+
args: Args;
|
|
15
|
+
returnType: ReturnType;
|
|
16
|
+
sinkType: SinkType;
|
|
17
|
+
inputType: InputType;
|
|
18
|
+
};
|
|
19
|
+
type StreamDefinitions = Record<string, StreamDefinition<object, object, object, object>>;
|
|
20
|
+
type StreamEndpoints<Definitions extends StreamDefinitions, Ctx = void> = {
|
|
21
|
+
[K in keyof Definitions]: (args: Definitions[K]["args"], ctx: Ctx) => Promise<Source<Definitions[K]["returnType"]>>;
|
|
22
|
+
};
|
|
23
|
+
type StreamSinks<Definitions extends StreamDefinitions> = {
|
|
24
|
+
[K in keyof Definitions]: (snapshot: object) => Sink<Definitions[K]["sinkType"], Definitions[K]["inputType"]>;
|
|
25
|
+
};
|
|
26
|
+
type MutationResult<T> = {
|
|
27
|
+
success: true;
|
|
28
|
+
value: T;
|
|
29
|
+
} | {
|
|
30
|
+
success: false;
|
|
31
|
+
error: string;
|
|
32
|
+
};
|
|
33
|
+
type MutationDefinition<Args, Result> = {
|
|
34
|
+
args: Args;
|
|
35
|
+
result: Result;
|
|
36
|
+
};
|
|
37
|
+
type MutationDefinitions = Record<string, MutationDefinition<unknown, unknown>>;
|
|
38
|
+
type MutationEndpoints<Definitions extends MutationDefinitions, Ctx = void> = {
|
|
39
|
+
[K in keyof Definitions]: (args: Definitions[K]["args"], ctx: Ctx) => Promise<MutationResult<Definitions[K]["result"]>>;
|
|
40
|
+
};
|
|
41
|
+
type RPCDefinition = {
|
|
42
|
+
streams: StreamDefinitions;
|
|
43
|
+
mutations: MutationDefinitions;
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
export type { MutationDefinition as M, RPCDefinition as R, Sink as S, Source as a, MutationDefinitions as b, MutationEndpoints as c, MutationResult as d, StreamDefinition as e, StreamDefinitions as f, StreamEndpoints as g, StreamSinks as h };
|