@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
@@ -1,25 +1,27 @@
1
- /**
2
- * Transport implementation for browser WebSocket (client-side).
3
- */
4
- export class WebSocketTransport {
5
- constructor(ws) {
6
- this.ws = ws;
7
- }
8
- send(data) {
9
- this.ws.send(data);
10
- }
11
- onMessage(handler) {
12
- this.ws.onmessage = (event) => {
13
- handler(event.data);
14
- };
15
- }
16
- onClose(handler) {
17
- this.ws.onclose = handler;
18
- }
19
- close() {
20
- this.ws.close();
21
- }
22
- get bufferedAmount() {
23
- return this.ws.bufferedAmount;
24
- }
25
- }
1
+ // src/web-socket-transport.ts
2
+ var WebSocketTransport = class {
3
+ constructor(ws) {
4
+ this.ws = ws;
5
+ }
6
+ send(data) {
7
+ this.ws.send(data);
8
+ }
9
+ onMessage(handler) {
10
+ this.ws.onmessage = (event) => {
11
+ handler(event.data);
12
+ };
13
+ }
14
+ onClose(handler) {
15
+ this.ws.onclose = handler;
16
+ }
17
+ close() {
18
+ this.ws.close();
19
+ }
20
+ get bufferedAmount() {
21
+ return this.ws.bufferedAmount;
22
+ }
23
+ };
24
+ export {
25
+ WebSocketTransport
26
+ };
27
+ //# sourceMappingURL=web-socket-transport.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/web-socket-transport.ts"],"sourcesContent":["import { Transport } from \"./transport.js\";\n\n/**\n * Transport implementation for browser WebSocket (client-side).\n */\nexport class WebSocketTransport implements Transport {\n constructor(private ws: globalThis.WebSocket) {}\n\n send(data: string): void {\n this.ws.send(data);\n }\n\n onMessage(handler: (data: string) => void): void {\n this.ws.onmessage = (event: MessageEvent) => {\n handler(event.data);\n };\n }\n\n onClose(handler: () => void): void {\n this.ws.onclose = handler;\n }\n\n close(): void {\n this.ws.close();\n }\n\n get bufferedAmount(): number {\n return this.ws.bufferedAmount;\n }\n}\n"],"mappings":";AAKO,IAAM,qBAAN,MAA8C;AAAA,EACnD,YAAoB,IAA0B;AAA1B;AAAA,EAA2B;AAAA,EAE/C,KAAK,MAAoB;AACvB,SAAK,GAAG,KAAK,IAAI;AAAA,EACnB;AAAA,EAEA,UAAU,SAAuC;AAC/C,SAAK,GAAG,YAAY,CAAC,UAAwB;AAC3C,cAAQ,MAAM,IAAI;AAAA,IACpB;AAAA,EACF;AAAA,EAEA,QAAQ,SAA2B;AACjC,SAAK,GAAG,UAAU;AAAA,EACpB;AAAA,EAEA,QAAc;AACZ,SAAK,GAAG,MAAM;AAAA,EAChB;AAAA,EAEA,IAAI,iBAAyB;AAC3B,WAAO,KAAK,GAAG;AAAA,EACjB;AACF;","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@derivation/rpc",
3
- "version": "0.3.5",
3
+ "version": "0.5.0",
4
4
  "homepage": "https://github.com/derivationjs/rpc",
5
5
  "repository": {
6
6
  "type": "git",
@@ -9,63 +9,72 @@
9
9
  "description": "An RPC library for derivation.",
10
10
  "license": "MIT",
11
11
  "author": "Daniel Waterworth",
12
- "main": "dist/index.js",
12
+ "main": "dist/index.cjs",
13
+ "module": "dist/index.js",
13
14
  "types": "dist/index.d.ts",
14
15
  "type": "module",
15
16
  "exports": {
16
17
  ".": {
17
18
  "types": "./dist/index.d.ts",
18
- "import": "./dist/index.js"
19
+ "import": "./dist/index.js",
20
+ "require": "./dist/index.cjs"
19
21
  },
20
22
  "./iso": {
21
23
  "types": "./dist/iso.d.ts",
22
- "import": "./dist/iso.js"
24
+ "import": "./dist/iso.js",
25
+ "require": "./dist/iso.cjs"
23
26
  },
24
27
  "./worker": {
25
28
  "types": "./dist/shared-worker-server.d.ts",
26
- "import": "./dist/shared-worker-server.js"
29
+ "import": "./dist/shared-worker-server.js",
30
+ "require": "./dist/shared-worker-server.cjs"
27
31
  },
28
32
  "./client": {
29
33
  "types": "./dist/shared-worker-client.d.ts",
30
- "import": "./dist/shared-worker-client.js"
34
+ "import": "./dist/shared-worker-client.js",
35
+ "require": "./dist/shared-worker-client.cjs"
31
36
  },
32
37
  "./transport": {
33
38
  "types": "./dist/transport.d.ts",
34
- "import": "./dist/transport.js"
39
+ "import": "./dist/transport.js",
40
+ "require": "./dist/transport.cjs"
35
41
  },
36
42
  "./browser": {
37
43
  "types": "./dist/web-socket-transport.d.ts",
38
- "import": "./dist/web-socket-transport.js"
44
+ "import": "./dist/web-socket-transport.js",
45
+ "require": "./dist/web-socket-transport.cjs"
39
46
  },
40
47
  "./server": {
41
48
  "types": "./dist/web-socket-server.d.ts",
42
- "import": "./dist/web-socket-server.js"
49
+ "import": "./dist/web-socket-server.js",
50
+ "require": "./dist/web-socket-server.cjs"
43
51
  }
44
52
  },
45
53
  "files": [
46
54
  "dist"
47
55
  ],
48
56
  "scripts": {
49
- "prepare": "tsc -p tsconfig.json",
50
- "build": "tsc -p tsconfig.json",
57
+ "prepare": "tsup",
58
+ "build": "tsup",
51
59
  "test": "vitest run",
52
60
  "test:watch": "vitest",
53
61
  "lint": "eslint src"
54
62
  },
55
63
  "dependencies": {
56
- "@derivation/composable": "^0.3.5",
57
- "@types/node": "^25.1.0",
64
+ "derivation": "^0.5.0",
65
+ "@derivation/composable": "^0.5.0",
66
+ "@types/node": "^25.2.3",
58
67
  "@types/ws": "^8.18.1",
59
- "derivation": "^0.3.5",
60
68
  "immutable": "^5.1.4",
61
69
  "ws": "^8.19.0",
62
70
  "zod": "^4.3.6"
63
71
  },
64
72
  "devDependencies": {
65
- "@typescript-eslint/eslint-plugin": "^8.54.0",
66
- "@typescript-eslint/parser": "^8.54.0",
67
- "eslint": "^9.39.2",
73
+ "@typescript-eslint/eslint-plugin": "^8.55.0",
74
+ "@typescript-eslint/parser": "^8.55.0",
75
+ "eslint": "^9.0.0",
68
76
  "eslint-plugin-import": "^2.32.0",
77
+ "tsup": "^8.5.1",
69
78
  "typescript": "^5.9.3",
70
79
  "vitest": "^4.0.18"
71
80
  }
@@ -1,27 +0,0 @@
1
- import { ClientMessage } from "./client-message.js";
2
- import { ServerMessage } from "./server-message.js";
3
- import { StreamEndpoints, MutationEndpoints, RPCDefinition } from "./stream-types.js";
4
- import { PresenceHandler } from "./presence-manager.js";
5
- import { Transport } from "./transport.js";
6
- export declare class ClientHandler<Defs extends RPCDefinition, Ctx = void> {
7
- private readonly transport;
8
- private readonly context;
9
- private readonly streamEndpoints;
10
- private readonly mutationEndpoints;
11
- private readonly presenceHandler?;
12
- private currentPresence?;
13
- private closed;
14
- private readonly streams;
15
- private heartbeatTimeout;
16
- private inactivityTimeout;
17
- private readonly rateLimiter;
18
- constructor(transport: Transport, context: Ctx, streamEndpoints: StreamEndpoints<Defs["streams"], Ctx>, mutationEndpoints: MutationEndpoints<Defs["mutations"], Ctx>, presenceHandler?: PresenceHandler);
19
- private resetHeartbeat;
20
- private resetInactivity;
21
- handleMessage(message: string): void;
22
- handleClientMessage(message: ClientMessage): Promise<void>;
23
- handleStep(): void;
24
- sendMessage(message: ServerMessage): void;
25
- private handleDisconnect;
26
- close(): void;
27
- }
@@ -1,187 +0,0 @@
1
- import { parseClientMessage } from "./client-message.js";
2
- import { ServerMessage } from "./server-message.js";
3
- import { RateLimiter } from "./rate-limiter.js";
4
- export class ClientHandler {
5
- constructor(transport, context, streamEndpoints, mutationEndpoints, presenceHandler) {
6
- this.closed = false;
7
- this.streams = new Map();
8
- this.transport = transport;
9
- this.context = context;
10
- this.streamEndpoints = streamEndpoints;
11
- this.mutationEndpoints = mutationEndpoints;
12
- this.presenceHandler = presenceHandler;
13
- this.rateLimiter = new RateLimiter(100, 300); // 100 messages over 5 minutes
14
- console.log("new client connected");
15
- // Set up transport handlers
16
- this.transport.onMessage((data) => this.handleMessage(data));
17
- this.transport.onClose(() => this.handleDisconnect());
18
- this.resetHeartbeat();
19
- this.resetInactivity();
20
- }
21
- resetHeartbeat() {
22
- if (this.heartbeatTimeout) {
23
- clearTimeout(this.heartbeatTimeout);
24
- }
25
- this.heartbeatTimeout = setTimeout(() => {
26
- this.sendMessage(ServerMessage.heartbeat());
27
- }, 10000);
28
- }
29
- resetInactivity() {
30
- if (this.inactivityTimeout) {
31
- clearTimeout(this.inactivityTimeout);
32
- }
33
- this.inactivityTimeout = setTimeout(() => {
34
- this.close();
35
- }, 30000);
36
- }
37
- handleMessage(message) {
38
- this.resetInactivity();
39
- // Check rate limit
40
- if (this.rateLimiter.trigger()) {
41
- console.log("Rate limit exceeded, closing connection");
42
- this.close();
43
- return;
44
- }
45
- let data;
46
- try {
47
- data = JSON.parse(message);
48
- }
49
- catch (_a) {
50
- console.error("Invalid JSON received:", message);
51
- return this.close();
52
- }
53
- let parsed;
54
- try {
55
- parsed = parseClientMessage(data);
56
- }
57
- catch (err) {
58
- console.error("Invalid client message:", err);
59
- return this.close();
60
- }
61
- this.handleClientMessage(parsed);
62
- }
63
- async handleClientMessage(message) {
64
- switch (message.type) {
65
- case "subscribe": {
66
- const { id, name, args } = message;
67
- if (!(name in this.streamEndpoints)) {
68
- console.error(`Unknown stream: ${name}`);
69
- this.close();
70
- return;
71
- }
72
- const endpoint = this.streamEndpoints[name];
73
- try {
74
- const source = await endpoint(args, this.context);
75
- this.streams.set(id, source);
76
- this.sendMessage(ServerMessage.subscribed(id, source.Snapshot));
77
- console.log(`Client subscribed to \"${name}\" (${id})`);
78
- }
79
- catch (err) {
80
- console.error(`Error building stream ${name}:`, err);
81
- this.close();
82
- }
83
- break;
84
- }
85
- case "unsubscribe": {
86
- const { id } = message;
87
- this.streams.delete(id);
88
- console.log(`Client unsubscribed from ${id}`);
89
- break;
90
- }
91
- case "call": {
92
- const { id, name, args } = message;
93
- if (!(name in this.mutationEndpoints)) {
94
- console.error(`Unknown mutation: ${name}`);
95
- this.close();
96
- return;
97
- }
98
- const endpoint = this.mutationEndpoints[name];
99
- endpoint(args, this.context)
100
- .then((result) => {
101
- if (result.success) {
102
- this.sendMessage(ServerMessage.resultSuccess(id, result.value));
103
- console.log(`Mutation \"${name}\" (${id}) completed successfully`);
104
- }
105
- else {
106
- this.sendMessage(ServerMessage.resultError(id, result.error));
107
- console.log(`Mutation \"${name}\" (${id}) returned error: ${result.error}`);
108
- }
109
- })
110
- .catch((err) => {
111
- console.error(`Unhandled exception in mutation \"${name}\" (${id}):`, err);
112
- this.close();
113
- });
114
- break;
115
- }
116
- case "heartbeat":
117
- break;
118
- case "presence": {
119
- if (!this.presenceHandler) {
120
- console.error("Presence not configured");
121
- this.close();
122
- return;
123
- }
124
- const { data } = message;
125
- if (this.currentPresence !== undefined) {
126
- this.presenceHandler.update(this.currentPresence, data);
127
- }
128
- else {
129
- this.presenceHandler.add(data);
130
- }
131
- this.currentPresence = data;
132
- break;
133
- }
134
- }
135
- }
136
- handleStep() {
137
- if (this.closed)
138
- return;
139
- const changes = {};
140
- for (const [id, source] of this.streams) {
141
- const change = source.LastChange;
142
- if (change === null)
143
- continue;
144
- changes[id] = change;
145
- }
146
- if (Object.keys(changes).length > 0) {
147
- this.sendMessage(ServerMessage.delta(changes));
148
- }
149
- }
150
- sendMessage(message) {
151
- this.resetHeartbeat();
152
- if (!this.closed) {
153
- // Check buffer if available (WebSocket provides this, MessagePort doesn't)
154
- if (this.transport.bufferedAmount !== undefined &&
155
- this.transport.bufferedAmount > 100 * 1024) {
156
- console.log("Send buffer exceeded 100KB, closing connection");
157
- this.close();
158
- return;
159
- }
160
- try {
161
- this.transport.send(JSON.stringify(message));
162
- }
163
- catch (err) {
164
- console.error("Failed to send message:", err);
165
- this.close();
166
- }
167
- }
168
- }
169
- handleDisconnect() {
170
- console.log("client disconnected");
171
- this.close();
172
- }
173
- close() {
174
- if (this.closed)
175
- return;
176
- this.closed = true;
177
- clearTimeout(this.heartbeatTimeout);
178
- clearTimeout(this.inactivityTimeout);
179
- if (this.presenceHandler && this.currentPresence !== undefined) {
180
- this.presenceHandler.remove(this.currentPresence);
181
- }
182
- try {
183
- this.transport.close();
184
- }
185
- catch (_a) { }
186
- }
187
- }
@@ -1,57 +0,0 @@
1
- import { z } from "zod";
2
- export declare const SubscribeMessageSchema: z.ZodObject<{
3
- type: z.ZodLiteral<"subscribe">;
4
- id: z.ZodNumber;
5
- name: z.ZodString;
6
- args: z.ZodObject<{}, z.core.$loose>;
7
- }, z.core.$strip>;
8
- export type SubscribeMessage = z.infer<typeof SubscribeMessageSchema>;
9
- export declare const UnsubscribeMessageSchema: z.ZodObject<{
10
- type: z.ZodLiteral<"unsubscribe">;
11
- id: z.ZodNumber;
12
- }, z.core.$strip>;
13
- export type UnsubscribeMessage = z.infer<typeof UnsubscribeMessageSchema>;
14
- export declare const HeartbeatMessageSchema: z.ZodObject<{
15
- type: z.ZodLiteral<"heartbeat">;
16
- }, z.core.$strip>;
17
- export type HeartbeatMessage = z.infer<typeof HeartbeatMessageSchema>;
18
- export declare const CallMessageSchema: z.ZodObject<{
19
- type: z.ZodLiteral<"call">;
20
- id: z.ZodNumber;
21
- name: z.ZodString;
22
- args: z.ZodObject<{}, z.core.$loose>;
23
- }, z.core.$strip>;
24
- export type CallMessage = z.infer<typeof CallMessageSchema>;
25
- export declare const PresenceMessageSchema: z.ZodObject<{
26
- type: z.ZodLiteral<"presence">;
27
- data: z.ZodObject<{}, z.core.$loose>;
28
- }, z.core.$strip>;
29
- export type PresenceMessage = z.infer<typeof PresenceMessageSchema>;
30
- export declare const ClientMessageSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
31
- type: z.ZodLiteral<"subscribe">;
32
- id: z.ZodNumber;
33
- name: z.ZodString;
34
- args: z.ZodObject<{}, z.core.$loose>;
35
- }, z.core.$strip>, z.ZodObject<{
36
- type: z.ZodLiteral<"unsubscribe">;
37
- id: z.ZodNumber;
38
- }, z.core.$strip>, z.ZodObject<{
39
- type: z.ZodLiteral<"heartbeat">;
40
- }, z.core.$strip>, z.ZodObject<{
41
- type: z.ZodLiteral<"call">;
42
- id: z.ZodNumber;
43
- name: z.ZodString;
44
- args: z.ZodObject<{}, z.core.$loose>;
45
- }, z.core.$strip>, z.ZodObject<{
46
- type: z.ZodLiteral<"presence">;
47
- data: z.ZodObject<{}, z.core.$loose>;
48
- }, z.core.$strip>], "type">;
49
- export type ClientMessage = z.infer<typeof ClientMessageSchema>;
50
- export declare function parseClientMessage(data: unknown): ClientMessage;
51
- export declare const ClientMessage: {
52
- subscribe: (id: number, name: string, args: object) => SubscribeMessage;
53
- unsubscribe: (id: number) => UnsubscribeMessage;
54
- call: (id: number, name: string, args: object) => CallMessage;
55
- heartbeat: () => HeartbeatMessage;
56
- presence: (data: object) => PresenceMessage;
57
- };
@@ -1,59 +0,0 @@
1
- import { z } from "zod";
2
- export const SubscribeMessageSchema = z.object({
3
- type: z.literal("subscribe"),
4
- id: z.number(),
5
- name: z.string(),
6
- args: z.looseObject({}),
7
- });
8
- export const UnsubscribeMessageSchema = z.object({
9
- type: z.literal("unsubscribe"),
10
- id: z.number(),
11
- });
12
- export const HeartbeatMessageSchema = z.object({
13
- type: z.literal("heartbeat"),
14
- });
15
- export const CallMessageSchema = z.object({
16
- type: z.literal("call"),
17
- id: z.number(),
18
- name: z.string(),
19
- args: z.looseObject({}),
20
- });
21
- export const PresenceMessageSchema = z.object({
22
- type: z.literal("presence"),
23
- data: z.looseObject({}),
24
- });
25
- export const ClientMessageSchema = z.discriminatedUnion("type", [
26
- SubscribeMessageSchema,
27
- UnsubscribeMessageSchema,
28
- HeartbeatMessageSchema,
29
- CallMessageSchema,
30
- PresenceMessageSchema,
31
- ]);
32
- export function parseClientMessage(data) {
33
- return ClientMessageSchema.parse(data);
34
- }
35
- export const ClientMessage = {
36
- subscribe: (id, name, args) => ({
37
- type: "subscribe",
38
- id,
39
- name,
40
- args: args,
41
- }),
42
- unsubscribe: (id) => ({
43
- type: "unsubscribe",
44
- id,
45
- }),
46
- call: (id, name, args) => ({
47
- type: "call",
48
- id,
49
- name,
50
- args: args,
51
- }),
52
- heartbeat: () => ({
53
- type: "heartbeat",
54
- }),
55
- presence: (data) => ({
56
- type: "presence",
57
- data: data,
58
- }),
59
- };
package/dist/client.js DELETED
@@ -1,133 +0,0 @@
1
- import { ClientMessage } from "./client-message.js";
2
- function changer(sink, input) {
3
- return (change) => {
4
- const i = input.deref();
5
- if (i) {
6
- sink.apply(change, i);
7
- }
8
- };
9
- }
10
- export class Client {
11
- resetHeartbeat() {
12
- if (this.heartbeatTimeout) {
13
- clearTimeout(this.heartbeatTimeout);
14
- }
15
- this.heartbeatTimeout = setTimeout(() => {
16
- this.sendMessage(ClientMessage.heartbeat());
17
- }, 10000);
18
- }
19
- resetInactivity() {
20
- if (this.inactivityTimeout) {
21
- clearTimeout(this.inactivityTimeout);
22
- }
23
- this.inactivityTimeout = setTimeout(() => {
24
- this.close();
25
- }, 30000);
26
- }
27
- constructor(transport, sinks, graph) {
28
- this.transport = transport;
29
- this.sinks = sinks;
30
- this.graph = graph;
31
- this.nextId = 1;
32
- this.pendingStreams = new Map();
33
- this.pendingMutations = new Map();
34
- this.activeStreams = new Map();
35
- this.registry = new FinalizationRegistry(([id, name]) => {
36
- console.log(`🧹 Stream ${id} (${name}) collected — unsubscribing`);
37
- this.sendMessage(ClientMessage.unsubscribe(id));
38
- this.activeStreams.delete(id);
39
- });
40
- this.transport.onMessage((data) => {
41
- const message = JSON.parse(data);
42
- this.handleMessage(message);
43
- });
44
- this.resetHeartbeat();
45
- this.resetInactivity();
46
- }
47
- handleMessage(message) {
48
- this.resetInactivity();
49
- switch (message.type) {
50
- case "snapshot": {
51
- const resolve = this.pendingStreams.get(message.id);
52
- if (resolve) {
53
- resolve(message.snapshot);
54
- this.pendingStreams.delete(message.id);
55
- }
56
- break;
57
- }
58
- case "delta": {
59
- for (const [idStr, change] of Object.entries(message.changes)) {
60
- const id = Number(idStr);
61
- const sink = this.activeStreams.get(id);
62
- if (sink && change && typeof change === "object") {
63
- sink(change);
64
- }
65
- else if (!sink) {
66
- console.log(`🧹 Sink ${id} GC'd — auto-unsubscribing`);
67
- this.sendMessage(ClientMessage.unsubscribe(id));
68
- this.activeStreams.delete(id);
69
- }
70
- }
71
- this.graph.step();
72
- break;
73
- }
74
- case "result": {
75
- const resolve = this.pendingMutations.get(message.id);
76
- if (resolve) {
77
- if (message.success) {
78
- resolve({ success: true, value: message.value });
79
- }
80
- else {
81
- resolve({
82
- success: false,
83
- error: message.error || "Unknown error",
84
- });
85
- }
86
- this.pendingMutations.delete(message.id);
87
- }
88
- break;
89
- }
90
- case "heartbeat":
91
- break;
92
- }
93
- }
94
- sendMessage(message) {
95
- this.resetHeartbeat();
96
- this.transport.send(JSON.stringify(message));
97
- }
98
- async run(key, args) {
99
- console.log(`Running stream ${String(key)} with args ${JSON.stringify(args)}`);
100
- const id = this.nextId++;
101
- this.sendMessage(ClientMessage.subscribe(id, String(key), args));
102
- const snapshot = await new Promise((resolve) => {
103
- this.pendingStreams.set(id, resolve);
104
- });
105
- const endpoint = this.sinks[key];
106
- const sinkAdapter = endpoint(snapshot);
107
- const { stream, input } = sinkAdapter.build();
108
- const inputRef = new WeakRef(input);
109
- this.activeStreams.set(id, changer(sinkAdapter, inputRef));
110
- this.registry.register(input, [id, String(key)]);
111
- return stream;
112
- }
113
- async call(key, args) {
114
- console.log(`Calling mutation ${String(key)} with args ${JSON.stringify(args)}`);
115
- const id = this.nextId++;
116
- this.sendMessage(ClientMessage.call(id, String(key), args));
117
- const result = await new Promise((resolve) => {
118
- this.pendingMutations.set(id, resolve);
119
- });
120
- return result;
121
- }
122
- close() {
123
- clearTimeout(this.heartbeatTimeout);
124
- clearTimeout(this.inactivityTimeout);
125
- try {
126
- this.transport.close();
127
- }
128
- catch (_a) { }
129
- }
130
- setPresence(value) {
131
- this.sendMessage(ClientMessage.presence(value));
132
- }
133
- }
@@ -1,13 +0,0 @@
1
- import { Transport } from "./transport.js";
2
- /**
3
- * Transport implementation for MessagePort (SharedWorker communication).
4
- */
5
- export declare class MessagePortTransport implements Transport {
6
- private port;
7
- constructor(port: MessagePort);
8
- send(data: string): void;
9
- onMessage(handler: (data: string) => void): void;
10
- onClose(handler: () => void): void;
11
- close(): void;
12
- get bufferedAmount(): undefined;
13
- }
@@ -1,28 +0,0 @@
1
- /**
2
- * Transport implementation for MessagePort (SharedWorker communication).
3
- */
4
- export class MessagePortTransport {
5
- constructor(port) {
6
- this.port = port;
7
- }
8
- send(data) {
9
- this.port.postMessage(data);
10
- }
11
- onMessage(handler) {
12
- this.port.onmessage = (event) => {
13
- handler(event.data);
14
- };
15
- }
16
- onClose(handler) {
17
- // MessagePort doesn't have a reliable close event
18
- // We'll use messageerror as a signal, though it's not perfect
19
- this.port.onmessageerror = handler;
20
- }
21
- close() {
22
- this.port.close();
23
- }
24
- // MessagePort doesn't provide bufferedAmount
25
- get bufferedAmount() {
26
- return undefined;
27
- }
28
- }
@@ -1,16 +0,0 @@
1
- import { WebSocket } from "ws";
2
- import { Transport } from "./transport.js";
3
- /**
4
- * Transport implementation for Node.js WebSocket (server-side using 'ws' library).
5
- */
6
- export declare class NodeWebSocketTransport implements Transport {
7
- private ws;
8
- private messageHandlerSet;
9
- private closeHandlerSet;
10
- constructor(ws: WebSocket);
11
- send(data: string): void;
12
- onMessage(handler: (data: string) => void): void;
13
- onClose(handler: () => void): void;
14
- close(): void;
15
- get bufferedAmount(): number;
16
- }