@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.
Files changed (96) hide show
  1. package/dist/client-DJZfuakf.d.cts +27 -0
  2. package/dist/{client.d.ts → client-TPsVZH_B.d.ts} +7 -4
  3. package/dist/index.cjs +335 -0
  4. package/dist/index.cjs.map +1 -0
  5. package/dist/index.d.cts +55 -0
  6. package/dist/index.d.ts +55 -7
  7. package/dist/index.js +305 -6
  8. package/dist/index.js.map +1 -0
  9. package/dist/iso.cjs +151 -0
  10. package/dist/iso.cjs.map +1 -0
  11. package/dist/iso.d.cts +52 -0
  12. package/dist/iso.d.ts +35 -19
  13. package/dist/iso.js +99 -101
  14. package/dist/iso.js.map +1 -0
  15. package/dist/{presence-manager.d.ts → presence-manager-4LlEyuGp.d.cts} +3 -1
  16. package/dist/presence-manager-4LlEyuGp.d.ts +7 -0
  17. package/dist/shared-worker-client.cjs +271 -0
  18. package/dist/shared-worker-client.cjs.map +1 -0
  19. package/dist/shared-worker-client.d.cts +26 -0
  20. package/dist/shared-worker-client.d.ts +8 -4
  21. package/dist/shared-worker-client.js +243 -24
  22. package/dist/shared-worker-client.js.map +1 -0
  23. package/dist/shared-worker-server.cjs +363 -0
  24. package/dist/shared-worker-server.cjs.map +1 -0
  25. package/dist/shared-worker-server.d.cts +31 -0
  26. package/dist/shared-worker-server.d.ts +9 -6
  27. package/dist/shared-worker-server.js +332 -58
  28. package/dist/shared-worker-server.js.map +1 -0
  29. package/dist/stream-types-Q_EqNLtO.d.cts +46 -0
  30. package/dist/stream-types-Q_EqNLtO.d.ts +46 -0
  31. package/dist/transport.cjs +19 -0
  32. package/dist/transport.cjs.map +1 -0
  33. package/dist/transport.d.cts +29 -0
  34. package/dist/transport.d.ts +3 -1
  35. package/dist/transport.js +1 -1
  36. package/dist/transport.js.map +1 -0
  37. package/dist/web-socket-server.cjs +469 -0
  38. package/dist/web-socket-server.cjs.map +1 -0
  39. package/dist/web-socket-server.d.cts +14 -0
  40. package/dist/web-socket-server.d.ts +10 -7
  41. package/dist/web-socket-server.js +437 -52
  42. package/dist/web-socket-server.js.map +1 -0
  43. package/dist/web-socket-transport.cjs +52 -0
  44. package/dist/web-socket-transport.cjs.map +1 -0
  45. package/dist/web-socket-transport.d.cts +16 -0
  46. package/dist/web-socket-transport.d.ts +5 -2
  47. package/dist/web-socket-transport.js +27 -25
  48. package/dist/web-socket-transport.js.map +1 -0
  49. package/package.json +26 -17
  50. package/dist/client-handler.d.ts +0 -27
  51. package/dist/client-handler.js +0 -187
  52. package/dist/client-message.d.ts +0 -57
  53. package/dist/client-message.js +0 -59
  54. package/dist/client.js +0 -133
  55. package/dist/messageport-transport.d.ts +0 -13
  56. package/dist/messageport-transport.js +0 -28
  57. package/dist/node-web-socket-transport.d.ts +0 -16
  58. package/dist/node-web-socket-transport.js +0 -33
  59. package/dist/presence-manager.js +0 -1
  60. package/dist/queue.d.ts +0 -9
  61. package/dist/queue.js +0 -32
  62. package/dist/rate-limiter.d.ts +0 -7
  63. package/dist/rate-limiter.js +0 -24
  64. package/dist/reactive-map-adapter.d.ts +0 -24
  65. package/dist/reactive-map-adapter.js +0 -43
  66. package/dist/reactive-set-adapter.d.ts +0 -24
  67. package/dist/reactive-set-adapter.js +0 -41
  68. package/dist/server-message.d.ts +0 -48
  69. package/dist/server-message.js +0 -52
  70. package/dist/shared-worker-client-handler.d.ts +0 -27
  71. package/dist/shared-worker-client-handler.js +0 -149
  72. package/dist/stream-adapter.d.ts +0 -23
  73. package/dist/stream-adapter.js +0 -35
  74. package/dist/stream-types.d.ts +0 -44
  75. package/dist/stream-types.js +0 -1
  76. package/dist/tests/context.test.d.ts +0 -1
  77. package/dist/tests/context.test.js +0 -252
  78. package/dist/tests/iso.test.d.ts +0 -1
  79. package/dist/tests/iso.test.js +0 -186
  80. package/dist/tests/messages.test.d.ts +0 -1
  81. package/dist/tests/messages.test.js +0 -152
  82. package/dist/tests/mutations.test.d.ts +0 -1
  83. package/dist/tests/mutations.test.js +0 -122
  84. package/dist/tests/queue.test.d.ts +0 -1
  85. package/dist/tests/queue.test.js +0 -84
  86. package/dist/tests/reactive-map-adapter.test.d.ts +0 -1
  87. package/dist/tests/reactive-map-adapter.test.js +0 -190
  88. package/dist/tests/reactive-set-adapter.test.d.ts +0 -1
  89. package/dist/tests/reactive-set-adapter.test.js +0 -157
  90. package/dist/tests/stream-adapter.test.d.ts +0 -1
  91. package/dist/tests/stream-adapter.test.js +0 -119
  92. package/dist/tests/weak-list.test.d.ts +0 -1
  93. package/dist/tests/weak-list.test.js +0 -100
  94. package/dist/tsconfig.tsbuildinfo +0 -1
  95. package/dist/weak-list.d.ts +0 -5
  96. 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 "derivation";
2
- import { StreamEndpoints, MutationEndpoints, RPCDefinition } from "./stream-types.js";
3
- import { PresenceHandler } from "./presence-manager.js";
4
- export type SharedWorkerServerOptions<Defs extends RPCDefinition, Ctx = void> = {
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 ReactiveSetSourceAdapter(source),
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
- export declare function setupSharedWorker<Defs extends RPCDefinition, Ctx = void>(options: SharedWorkerServerOptions<Defs, Ctx>, graph: Graph): void;
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
- import { SharedWorkerClientHandler } from "./shared-worker-client-handler.js";
2
- import WeakList from "./weak-list.js";
3
- import { MessagePortTransport } from "./messageport-transport.js";
4
- /**
5
- * Set up a SharedWorker server for RPC communication.
6
- * This creates a shared Graph that all connected tabs can interact with.
7
- *
8
- * @example
9
- * ```typescript
10
- * // worker.ts
11
- * const { graph } = setupSharedWorker({
12
- * streams: {
13
- * todos: async (args, ctx) => new ReactiveSetSourceAdapter(source),
14
- * },
15
- * mutations: {
16
- * addTodo: async (args, ctx) => ({ success: true, value: newTodo }),
17
- * },
18
- * createContext: (port) => ({ portId: crypto.randomUUID() }),
19
- * });
20
- * ```
21
- */
22
- export function setupSharedWorker(options, graph) {
23
- const { streams, mutations, createContext, presenceHandler } = options;
24
- const clients = new WeakList();
25
- // After each graph step, broadcast deltas to all connected clients
26
- graph.afterStep(() => {
27
- for (const client of clients) {
28
- client.handleStep();
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
- // Handle SharedWorker connections
32
- const globalScope = self;
33
- globalScope.onconnect = (event) => {
34
- const port = event.ports[0];
35
- const messageBuffer = [];
36
- let client = null;
37
- // Set up temporary message handler to buffer messages
38
- const tempMessageHandler = (e) => {
39
- messageBuffer.push(e.data);
40
- };
41
- port.onmessage = tempMessageHandler;
42
- // Create context (handle both sync and async)
43
- Promise.resolve(createContext(port))
44
- .then((context) => {
45
- // Create transport and client handler
46
- const transport = new MessagePortTransport(port);
47
- client = new SharedWorkerClientHandler(transport, context, streams, mutations, presenceHandler);
48
- clients.add(client);
49
- // Process buffered messages
50
- for (const msg of messageBuffer) {
51
- client.handleMessage(msg);
52
- }
53
- messageBuffer.length = 0;
54
- // Start the port
55
- port.start();
56
- })
57
- .catch((err) => {
58
- console.error("Error creating context:", err);
59
- port.close();
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":";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;;;AC9CA,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;;;AC9CO,IAAM,4BAAN,MAAwE;AAAA,EAU7E,YACE,WACA,SACA,iBACA,mBACA,iBACA;AATF,SAAQ,SAAS;AACjB,SAAiB,UAAU,oBAAI,IAA6B;AAS1D,SAAK,YAAY;AACjB,SAAK,UAAU;AACf,SAAK,kBAAkB;AACvB,SAAK,oBAAoB;AACzB,SAAK,kBAAkB;AAEvB,YAAQ,IAAI,sBAAsB;AAGlC,SAAK,UAAU,UAAU,CAAC,SAAiB,KAAK,cAAc,IAAI,CAAC;AACnE,SAAK,UAAU,QAAQ,MAAM,KAAK,iBAAiB,CAAC;AAAA,EACtD;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;AAAA,cACN,aAAa,IAAI,MAAM,EAAE;AAAA,YAC3B;AAAA,UACF,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,IAAI,sCAAsC,OAAO,KAAK,OAAO,EAAE,MAAM,yBAAyB,OAAO,KAAK,OAAO,EAAE,KAAK,IAAI,CAAC,EAAE;AACvI,WAAK,YAAY,cAAc,MAAM,OAAO,CAAC;AAAA,IAC/C;AAAA,EACF;AAAA,EAEA,YAAY,SAAwB;AAClC,QAAI,KAAK,OAAQ;AACjB,SAAK,UAAU,KAAK,KAAK,UAAU,OAAO,CAAC;AAAA,EAC7C;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,IAAI,+BAA+B,KAAK,QAAQ,IAAI,iBAAiB;AAC7E,SAAK,QAAQ,MAAM;AAEnB,QAAI;AACF,WAAK,UAAU,MAAM;AAAA,IACvB,SAAQ;AAAA,IAAC;AAAA,EACX;AACF;;;AC7MA,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;;;ACfO,IAAM,uBAAN,MAAgD;AAAA,EACrD,YAAoB,MAAmB;AAAnB;AAAA,EAAoB;AAAA,EAExC,KAAK,MAAoB;AACvB,YAAQ,IAAI,mCAAmC,KAAK,UAAU,GAAG,GAAG,KAAK,KAAK,SAAS,MAAM,QAAQ,GAAG;AACxG,SAAK,KAAK,YAAY,IAAI;AAAA,EAC5B;AAAA,EAEA,UAAU,SAAuC;AAC/C,SAAK,KAAK,YAAY,CAAC,UAAwB;AAC7C,cAAQ,IAAI,oCAAoC,MAAM,KAAK,UAAU,GAAG,GAAG,KAAK,MAAM,KAAK,SAAS,MAAM,QAAQ,GAAG;AACrH,cAAQ,MAAM,IAAI;AAAA,IACpB;AAAA,EACF;AAAA,EAEA,QAAQ,SAA2B;AAGjC,SAAK,KAAK,iBAAiB;AAAA,EAC7B;AAAA,EAEA,QAAc;AACZ,SAAK,KAAK,MAAM;AAAA,EAClB;AAAA;AAAA,EAGA,IAAI,iBAA4B;AAC9B,WAAO;AAAA,EACT;AACF;;;ACKO,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,YAAM,YAAY,IAAI,qBAAqB,IAAI;AAC/C,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"]}
@@ -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 };