@igoforth/ws-rpc 1.0.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 (100) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +446 -0
  3. package/dist/adapters/client.d.ts +117 -0
  4. package/dist/adapters/client.js +241 -0
  5. package/dist/adapters/cloudflare-do.d.ts +72 -0
  6. package/dist/adapters/cloudflare-do.js +192 -0
  7. package/dist/adapters/index.d.ts +13 -0
  8. package/dist/adapters/index.js +16 -0
  9. package/dist/adapters/server.d.ts +10 -0
  10. package/dist/adapters/server.js +122 -0
  11. package/dist/adapters/types.d.ts +125 -0
  12. package/dist/adapters/types.js +3 -0
  13. package/dist/codecs/cbor.d.ts +16 -0
  14. package/dist/codecs/cbor.js +36 -0
  15. package/dist/codecs/factory.d.ts +3 -0
  16. package/dist/codecs/factory.js +3 -0
  17. package/dist/codecs/index.d.ts +5 -0
  18. package/dist/codecs/index.js +5 -0
  19. package/dist/codecs/json.d.ts +4 -0
  20. package/dist/codecs/json.js +4 -0
  21. package/dist/codecs/msgpack.d.ts +16 -0
  22. package/dist/codecs/msgpack.js +34 -0
  23. package/dist/codecs-BmYG2d_U.js +0 -0
  24. package/dist/default-BkrMd28n.js +253 -0
  25. package/dist/default-xDNNMrg0.d.ts +129 -0
  26. package/dist/durable-MZjkvyS6.js +165 -0
  27. package/dist/errors-5BfreE63.js +96 -0
  28. package/dist/errors.d.ts +69 -0
  29. package/dist/errors.js +7 -0
  30. package/dist/factory-3ziwTuZe.js +132 -0
  31. package/dist/factory-C1v0AEHY.d.ts +101 -0
  32. package/dist/index-Be7jjS77.d.ts +1 -0
  33. package/dist/index.d.ts +14 -0
  34. package/dist/index.js +14 -0
  35. package/dist/interface-C4S-WCqW.d.ts +120 -0
  36. package/dist/json-54Z2bIIs.d.ts +22 -0
  37. package/dist/json-Bshec-bZ.js +41 -0
  38. package/dist/memory-Bqb3KEVr.js +48 -0
  39. package/dist/memory-D1nGjzzH.d.ts +41 -0
  40. package/dist/multi-peer-BAi9yVzp.js +242 -0
  41. package/dist/peers/default.d.ts +8 -0
  42. package/dist/peers/default.js +8 -0
  43. package/dist/peers/durable.d.ts +136 -0
  44. package/dist/peers/durable.js +9 -0
  45. package/dist/peers/index.d.ts +10 -0
  46. package/dist/peers/index.js +9 -0
  47. package/dist/protocol-DA84zrc2.d.ts +211 -0
  48. package/dist/protocol-_mpoOPp6.js +192 -0
  49. package/dist/protocol.d.ts +6 -0
  50. package/dist/protocol.js +6 -0
  51. package/dist/reconnect-CGAA_1Gf.js +26 -0
  52. package/dist/reconnect-DbcN0R_1.d.ts +35 -0
  53. package/dist/schema-CN5HHHku.d.ts +108 -0
  54. package/dist/schema.d.ts +2 -0
  55. package/dist/schema.js +43 -0
  56. package/dist/server-zTjpJpoX.d.ts +209 -0
  57. package/dist/sql-CCjc6Bid.js +142 -0
  58. package/dist/sql-DPmHOeZy.d.ts +131 -0
  59. package/dist/storage/index.d.ts +8 -0
  60. package/dist/storage/index.js +7 -0
  61. package/dist/storage/interface.d.ts +3 -0
  62. package/dist/storage/interface.js +0 -0
  63. package/dist/storage/memory.d.ts +7 -0
  64. package/dist/storage/memory.js +6 -0
  65. package/dist/storage/sql.d.ts +7 -0
  66. package/dist/storage/sql.js +6 -0
  67. package/dist/types-Be-qmQu0.d.ts +111 -0
  68. package/dist/types-D_psiH09.js +13 -0
  69. package/dist/types.d.ts +7 -0
  70. package/dist/types.js +3 -0
  71. package/dist/utils/index.d.ts +2 -0
  72. package/dist/utils/index.js +3 -0
  73. package/dist/utils/reconnect.d.ts +2 -0
  74. package/dist/utils/reconnect.js +3 -0
  75. package/package.json +156 -0
  76. package/src/adapters/client.ts +396 -0
  77. package/src/adapters/cloudflare-do.ts +346 -0
  78. package/src/adapters/index.ts +16 -0
  79. package/src/adapters/multi-peer.ts +404 -0
  80. package/src/adapters/server.ts +192 -0
  81. package/src/adapters/types.ts +202 -0
  82. package/src/codecs/cbor.ts +42 -0
  83. package/src/codecs/factory.ts +210 -0
  84. package/src/codecs/index.ts +30 -0
  85. package/src/codecs/json.ts +42 -0
  86. package/src/codecs/msgpack.ts +36 -0
  87. package/src/errors.ts +105 -0
  88. package/src/index.ts +102 -0
  89. package/src/peers/default.ts +433 -0
  90. package/src/peers/durable.ts +280 -0
  91. package/src/peers/index.ts +13 -0
  92. package/src/protocol.ts +306 -0
  93. package/src/schema.ts +167 -0
  94. package/src/storage/index.ts +20 -0
  95. package/src/storage/interface.ts +146 -0
  96. package/src/storage/memory.ts +84 -0
  97. package/src/storage/sql.ts +266 -0
  98. package/src/types.ts +158 -0
  99. package/src/utils/index.ts +9 -0
  100. package/src/utils/reconnect.ts +51 -0
@@ -0,0 +1,241 @@
1
+ import "../factory-3ziwTuZe.js";
2
+ import "../json-Bshec-bZ.js";
3
+ import "../codecs-BmYG2d_U.js";
4
+ import "../protocol-_mpoOPp6.js";
5
+ import "../errors-5BfreE63.js";
6
+ import { t as WebSocketReadyState } from "../types-D_psiH09.js";
7
+ import { n as defaultReconnectOptions, t as calculateReconnectDelay } from "../reconnect-CGAA_1Gf.js";
8
+ import { t as RpcPeer } from "../default-BkrMd28n.js";
9
+ import "./types.js";
10
+
11
+ //#region src/adapters/client.ts
12
+ /**
13
+ * RPC Client with auto-reconnect
14
+ *
15
+ * Manages WebSocket connection lifecycle and provides RPC capabilities.
16
+ */
17
+ var RpcClient = class {
18
+ localSchema;
19
+ remoteSchema;
20
+ provider;
21
+ hooks = {};
22
+ url;
23
+ reconnectOptions;
24
+ defaultTimeout;
25
+ protocols;
26
+ headers;
27
+ WebSocketImpl;
28
+ ws = null;
29
+ peer = null;
30
+ _state = "disconnected";
31
+ reconnectAttempt = 0;
32
+ reconnectTimeout = null;
33
+ intentionalClose = false;
34
+ constructor(options) {
35
+ this.url = options.url;
36
+ this.localSchema = options.localSchema;
37
+ this.remoteSchema = options.remoteSchema;
38
+ this.provider = options.provider;
39
+ this.reconnectOptions = options.reconnect === false ? false : {
40
+ ...defaultReconnectOptions,
41
+ ...options.reconnect
42
+ };
43
+ this.defaultTimeout = options.timeout ?? 3e4;
44
+ this.protocols = options.protocols;
45
+ this.headers = options.headers;
46
+ this.WebSocketImpl = options.WebSocket ?? globalThis.WebSocket;
47
+ if (options.onConnect) this.hooks.onConnect = options.onConnect;
48
+ if (options.onDisconnect) this.hooks.onDisconnect = options.onDisconnect;
49
+ if (options.onReconnect) this.hooks.onReconnect = options.onReconnect;
50
+ if (options.onReconnectFailed) this.hooks.onReconnectFailed = options.onReconnectFailed;
51
+ if (options.onEvent) this.hooks.onEvent = options.onEvent;
52
+ if (options.autoConnect) this.connect();
53
+ }
54
+ /**
55
+ * Current connection state
56
+ */
57
+ get state() {
58
+ return this._state;
59
+ }
60
+ /**
61
+ * Whether the client is currently connected
62
+ */
63
+ get isConnected() {
64
+ return this._state === "connected" && this.peer?.isOpen === true;
65
+ }
66
+ /**
67
+ * Get the driver for calling remote methods
68
+ *
69
+ * @returns Driver proxy for calling remote methods
70
+ * @throws Error if not connected
71
+ */
72
+ get driver() {
73
+ if (!this.peer) throw new Error("Not connected - call connect() first");
74
+ return this.peer.driver;
75
+ }
76
+ /**
77
+ * Emit an event to the server (fire-and-forget)
78
+ *
79
+ * @param event - Event name from local schema
80
+ * @param data - Event data matching the schema
81
+ */
82
+ emit(event, data) {
83
+ if (!this.peer) {
84
+ console.warn(`Cannot emit event '${String(event)}': not connected`);
85
+ return;
86
+ }
87
+ this.peer.emit(event, data);
88
+ }
89
+ /**
90
+ * Connect to the WebSocket server
91
+ *
92
+ * @returns Promise that resolves when connected
93
+ * @throws Error if connection fails
94
+ */
95
+ async connect() {
96
+ if (this._state === "connected" || this._state === "connecting") return;
97
+ this.intentionalClose = false;
98
+ this._state = "connecting";
99
+ return new Promise((resolve, reject) => {
100
+ try {
101
+ let wsOptions;
102
+ if (this.headers) {
103
+ wsOptions = { headers: this.headers };
104
+ if (this.protocols) wsOptions.protocols = this.protocols;
105
+ } else wsOptions = this.protocols;
106
+ this.ws = new this.WebSocketImpl(this.url, wsOptions);
107
+ } catch (error) {
108
+ this._state = "disconnected";
109
+ reject(error);
110
+ return;
111
+ }
112
+ const onOpen = () => {
113
+ cleanup();
114
+ this.handleOpen();
115
+ resolve();
116
+ };
117
+ const onError = (event) => {
118
+ cleanup();
119
+ this._state = "disconnected";
120
+ reject(/* @__PURE__ */ new Error(`WebSocket connection failed: ${event}`));
121
+ };
122
+ const onClose = (event) => {
123
+ cleanup();
124
+ this._state = "disconnected";
125
+ const code = typeof event === "object" && event != null && "code" in event ? event.code : "Unknown code";
126
+ const reason = typeof event === "object" && event != null && "reason" in event ? event.reason : "Unknown reason";
127
+ reject(/* @__PURE__ */ new Error(`WebSocket closed: ${code} ${reason}`));
128
+ };
129
+ const cleanup = () => {
130
+ this.ws?.removeEventListener?.("open", onOpen);
131
+ this.ws?.removeEventListener?.("error", onError);
132
+ this.ws?.removeEventListener?.("close", onClose);
133
+ };
134
+ this.ws.addEventListener?.("open", onOpen);
135
+ this.ws.addEventListener?.("error", onError);
136
+ this.ws.addEventListener?.("close", onClose);
137
+ });
138
+ }
139
+ /**
140
+ * Disconnect from the server
141
+ *
142
+ * @param code - WebSocket close code (default: 1000)
143
+ * @param reason - Close reason message (default: "Client disconnect")
144
+ */
145
+ disconnect(code = 1e3, reason = "Client disconnect") {
146
+ this.intentionalClose = true;
147
+ this.cancelReconnect();
148
+ if (this.peer) {
149
+ this.peer.close();
150
+ this.peer = null;
151
+ }
152
+ if (this.ws && this.ws.readyState !== WebSocketReadyState.CLOSED) this.ws.close(code, reason);
153
+ this.ws = null;
154
+ this._state = "disconnected";
155
+ }
156
+ /**
157
+ * Handle WebSocket open event
158
+ */
159
+ handleOpen() {
160
+ if (!this.ws) return;
161
+ this._state = "connected";
162
+ this.reconnectAttempt = 0;
163
+ this.peer = new RpcPeer({
164
+ ws: this.ws,
165
+ localSchema: this.localSchema,
166
+ remoteSchema: this.remoteSchema,
167
+ provider: this.provider,
168
+ onEvent: this.hooks.onEvent,
169
+ timeout: this.defaultTimeout
170
+ });
171
+ this.ws.onmessage = (event) => {
172
+ if (typeof event === "object" && event != null && "data" in event) this.peer?.handleMessage(event.data);
173
+ else throw new Error(`Received invalid event type in RpcClient.ws.onmessage ${JSON.stringify(event)}`);
174
+ };
175
+ this.ws.onclose = (event) => {
176
+ if (typeof event === "object" && event != null && "code" in event && "reason" in event && typeof event.code === "number" && typeof event.reason === "string") this.handleClose(event.code, event.reason);
177
+ else throw new Error(`Received invalid event type in RpcClient.ws.onclose ${JSON.stringify(event)}`);
178
+ };
179
+ this.ws.onerror = (event) => {
180
+ console.error("WebSocket error:", event);
181
+ };
182
+ this.hooks.onConnect?.();
183
+ }
184
+ /**
185
+ * Handle WebSocket close event
186
+ */
187
+ handleClose(code, reason) {
188
+ this.peer?.close();
189
+ this.peer = null;
190
+ this.ws = null;
191
+ this.hooks.onDisconnect?.(code, reason);
192
+ if (this.intentionalClose) {
193
+ this._state = "disconnected";
194
+ return;
195
+ }
196
+ if (this.reconnectOptions !== false) this.scheduleReconnect();
197
+ else this._state = "disconnected";
198
+ }
199
+ /**
200
+ * Schedule a reconnection attempt
201
+ */
202
+ scheduleReconnect() {
203
+ if (this.reconnectOptions === false) return;
204
+ const { maxAttempts } = this.reconnectOptions;
205
+ if (maxAttempts > 0 && this.reconnectAttempt >= maxAttempts) {
206
+ this._state = "disconnected";
207
+ this.hooks.onReconnectFailed?.();
208
+ return;
209
+ }
210
+ this._state = "reconnecting";
211
+ const delay = calculateReconnectDelay(this.reconnectAttempt, this.reconnectOptions);
212
+ this.reconnectAttempt++;
213
+ this.hooks.onReconnect?.(this.reconnectAttempt, delay);
214
+ this.reconnectTimeout = setTimeout(() => {
215
+ this.reconnectTimeout = null;
216
+ this.attemptReconnect();
217
+ }, delay);
218
+ }
219
+ /**
220
+ * Attempt to reconnect
221
+ */
222
+ async attemptReconnect() {
223
+ try {
224
+ await this.connect();
225
+ } catch {
226
+ if (!this.intentionalClose && this.reconnectOptions !== false) this.scheduleReconnect();
227
+ }
228
+ }
229
+ /**
230
+ * Cancel any pending reconnection
231
+ */
232
+ cancelReconnect() {
233
+ if (this.reconnectTimeout) {
234
+ clearTimeout(this.reconnectTimeout);
235
+ this.reconnectTimeout = null;
236
+ }
237
+ }
238
+ };
239
+
240
+ //#endregion
241
+ export { RpcClient };
@@ -0,0 +1,72 @@
1
+ import { m as RpcSchema, p as Provider } from "../schema-CN5HHHku.js";
2
+ import "../factory-C1v0AEHY.js";
3
+ import "../json-54Z2bIIs.js";
4
+ import "../index-Be7jjS77.js";
5
+ import "../protocol-DA84zrc2.js";
6
+ import { a as IRpcOptions } from "../types-Be-qmQu0.js";
7
+ import { t as RpcPeer } from "../default-xDNNMrg0.js";
8
+ import { IMultiAdapterHooks, IMultiConnectionAdapter } from "./types.js";
9
+ import { Constructor } from "type-fest";
10
+ import { Actor } from "@cloudflare/actors";
11
+
12
+ //#region src/adapters/cloudflare-do.d.ts
13
+
14
+ /**
15
+ * Extended hooks for Durable Object adapter
16
+ */
17
+ interface IDOHooks<TLocalSchema extends RpcSchema, TRemoteSchema extends RpcSchema> extends IMultiAdapterHooks<TLocalSchema, TRemoteSchema> {
18
+ /** Called when a peer is recreated after hibernation */
19
+ onPeerRecreated?(peer: RpcPeer<TLocalSchema, TRemoteSchema>, ws: WebSocket): void;
20
+ }
21
+ /**
22
+ * Constructor type for the RPC mixin result.
23
+ *
24
+ * Subclasses must implement methods from TLocalSchema on `this`.
25
+ * Runtime enforces this when methods are called via RPC.
26
+ */
27
+ type RpcActorConstructor<TBase extends Constructor<Actor<unknown>>, TLocalSchema extends RpcSchema, TRemoteSchema extends RpcSchema> = {
28
+ new (...args: ConstructorParameters<TBase>): InstanceType<TBase> & IMultiConnectionAdapter<TLocalSchema, TRemoteSchema>;
29
+ } & Omit<TBase, "new">;
30
+ /**
31
+ * Create a mixin that adds RPC capabilities to a Durable Object Actor.
32
+ *
33
+ * The resulting class requires implementation of all methods defined in
34
+ * `localSchema`. TypeScript enforces this at compile time.
35
+ *
36
+ * @param Base - The Actor class to extend
37
+ * @param options - RPC configuration including local/remote schemas and timeout
38
+ * @returns A new class with RPC capabilities mixed in
39
+ *
40
+ * @example
41
+ * ```ts
42
+ * const ServerSchema = {
43
+ * methods: {
44
+ * getData: method({
45
+ * input: z.object({}),
46
+ * output: z.object({ data: z.array(z.string()) }),
47
+ * }),
48
+ * },
49
+ * events: {},
50
+ * } as const;
51
+ *
52
+ * class MyDO extends withRpc(Actor, {
53
+ * localSchema: ServerSchema,
54
+ * remoteSchema: ClientSchema,
55
+ * }) {
56
+ * // Required: implement methods from ServerSchema
57
+ * async getData() {
58
+ * return { data: this.dataList };
59
+ * }
60
+ *
61
+ * // Call methods on connected clients
62
+ * async notifyClients() {
63
+ * const results = await this.driver.clientMethod({ info: "update" });
64
+ * }
65
+ * }
66
+ * ```
67
+ */
68
+ declare function withRpc<TLocalSchema extends RpcSchema, TRemoteSchema extends RpcSchema, TEnv, TBase extends Constructor<Actor<TEnv>> & {
69
+ prototype: Provider<TLocalSchema>;
70
+ }>(Base: TBase, options: IRpcOptions<TLocalSchema, TRemoteSchema>): RpcActorConstructor<TBase, TLocalSchema, TRemoteSchema>;
71
+ //#endregion
72
+ export { IDOHooks, RpcActorConstructor, withRpc };
@@ -0,0 +1,192 @@
1
+ import "../factory-3ziwTuZe.js";
2
+ import "../json-Bshec-bZ.js";
3
+ import "../codecs-BmYG2d_U.js";
4
+ import "../protocol-_mpoOPp6.js";
5
+ import "../errors-5BfreE63.js";
6
+ import { t as SqlPendingCallStorage } from "../sql-CCjc6Bid.js";
7
+ import "../default-BkrMd28n.js";
8
+ import { n as createDurableRpcPeerFactory } from "../durable-MZjkvyS6.js";
9
+ import { t as MultiPeerBase } from "../multi-peer-BAi9yVzp.js";
10
+
11
+ //#region src/adapters/cloudflare-do.ts
12
+ /**
13
+ * Concrete MultiPeerBase for Durable Objects using native WebSocket
14
+ */
15
+ var DOMultiPeer = class extends MultiPeerBase {
16
+ hooks;
17
+ _createPeer;
18
+ constructor(options) {
19
+ super(options);
20
+ this.hooks = options.hooks ?? {};
21
+ this._createPeer = createDurableRpcPeerFactory({
22
+ actor: options.actor,
23
+ storage: options.storage,
24
+ ...options.durableTimeout != null && { durableTimeout: options.durableTimeout }
25
+ });
26
+ }
27
+ /**
28
+ * Create an RPC peer for a WebSocket connection.
29
+ * Override to use the actor instance as provider via closure.
30
+ */
31
+ createPeerWithProvider(ws, provider) {
32
+ const peer = this._createPeer({
33
+ ws,
34
+ localSchema: this.localSchema,
35
+ remoteSchema: this.remoteSchema,
36
+ provider,
37
+ onEvent: (event, data) => {
38
+ this.hooks.onEvent?.(peer, event, data);
39
+ },
40
+ timeout: this.timeout
41
+ });
42
+ return peer;
43
+ }
44
+ /**
45
+ * Get or create RPC peer for a WebSocket
46
+ * Handles lazy recreation after hibernation.
47
+ */
48
+ getOrCreatePeer(ws, provider, isHibernationRecovery = false) {
49
+ let peer = this.getPeerFor(ws);
50
+ if (!peer) {
51
+ peer = this.createPeerWithProvider(ws, provider);
52
+ this.addPeer(ws, peer);
53
+ if (isHibernationRecovery) this.hooks.onPeerRecreated?.(peer, ws);
54
+ }
55
+ return peer;
56
+ }
57
+ /**
58
+ * Connect a new WebSocket and create its peer
59
+ */
60
+ connectPeer(ws, provider) {
61
+ const peer = this.createPeerWithProvider(ws, provider);
62
+ this.addPeer(ws, peer);
63
+ return peer;
64
+ }
65
+ /**
66
+ * Disconnect a WebSocket and remove its peer
67
+ */
68
+ disconnectPeer(ws) {
69
+ this.removePeer(ws);
70
+ }
71
+ /**
72
+ * Handle an error on a WebSocket
73
+ */
74
+ handleError(ws, error) {
75
+ const peer = this.getPeerFor(ws);
76
+ if (peer) {
77
+ peer.close();
78
+ this.removePeer(ws);
79
+ }
80
+ this.hooks.onError?.(peer ?? null, error);
81
+ }
82
+ };
83
+ /**
84
+ * Create a mixin that adds RPC capabilities to a Durable Object Actor.
85
+ *
86
+ * The resulting class requires implementation of all methods defined in
87
+ * `localSchema`. TypeScript enforces this at compile time.
88
+ *
89
+ * @param Base - The Actor class to extend
90
+ * @param options - RPC configuration including local/remote schemas and timeout
91
+ * @returns A new class with RPC capabilities mixed in
92
+ *
93
+ * @example
94
+ * ```ts
95
+ * const ServerSchema = {
96
+ * methods: {
97
+ * getData: method({
98
+ * input: z.object({}),
99
+ * output: z.object({ data: z.array(z.string()) }),
100
+ * }),
101
+ * },
102
+ * events: {},
103
+ * } as const;
104
+ *
105
+ * class MyDO extends withRpc(Actor, {
106
+ * localSchema: ServerSchema,
107
+ * remoteSchema: ClientSchema,
108
+ * }) {
109
+ * // Required: implement methods from ServerSchema
110
+ * async getData() {
111
+ * return { data: this.dataList };
112
+ * }
113
+ *
114
+ * // Call methods on connected clients
115
+ * async notifyClients() {
116
+ * const results = await this.driver.clientMethod({ info: "update" });
117
+ * }
118
+ * }
119
+ * ```
120
+ */
121
+ function withRpc(Base, options) {
122
+ return class RpcActor extends Base {
123
+ __rpc = null;
124
+ /**
125
+ * Internal RPC manager (lazily initialized)
126
+ *
127
+ * Handles peer management, message routing, and durable storage.
128
+ * Uses SQL storage from the Durable Object for hibernation-safe calls.
129
+ *
130
+ * @throws Error if DurableObjectStorage is not available
131
+ */
132
+ get _rpc() {
133
+ if (!this.storage.raw) throw new Error("DurableObjectStorage not present in actor `raw`");
134
+ return this.__rpc ??= new DOMultiPeer({
135
+ actor: this,
136
+ storage: new SqlPendingCallStorage(this.storage.raw.sql),
137
+ localSchema: options.localSchema,
138
+ remoteSchema: options.remoteSchema,
139
+ provider: this,
140
+ ...options.timeout !== void 0 && { timeout: options.timeout }
141
+ });
142
+ }
143
+ /**
144
+ * Driver for calling methods on connected clients
145
+ */
146
+ get driver() {
147
+ return this._rpc.driver;
148
+ }
149
+ /**
150
+ * Emit an event to connected clients
151
+ *
152
+ * @param event - Event name from local schema
153
+ * @param data - Event data matching the schema
154
+ * @param ids - Optional array of peer IDs to emit to (broadcasts to all if omitted)
155
+ */
156
+ emit(event, data, ids) {
157
+ this._rpc.emit(event, data, ids);
158
+ }
159
+ /**
160
+ * Get the number of connected peers
161
+ */
162
+ getConnectionCount() {
163
+ return this._rpc.getConnectionCount();
164
+ }
165
+ /**
166
+ * Get the IDs of all connected peers
167
+ */
168
+ getConnectionIds() {
169
+ return this._rpc.getConnectionIds();
170
+ }
171
+ /** Called by Actor when WebSocket connects */
172
+ onWebSocketConnect(ws, _request) {
173
+ this._rpc.connectPeer(ws, this);
174
+ }
175
+ /** Called by Actor when WebSocket message received (handles hibernation recovery) */
176
+ onWebSocketMessage(ws, message) {
177
+ const existingPeer = this._rpc.getPeerFor(ws);
178
+ this._rpc.getOrCreatePeer(ws, this, !existingPeer).handleMessage(message);
179
+ }
180
+ /** Called by Actor when WebSocket disconnects */
181
+ onWebSocketDisconnect(ws) {
182
+ this._rpc.disconnectPeer(ws);
183
+ }
184
+ /** Called by Actor when WebSocket error occurs */
185
+ onWebSocketError(ws, error) {
186
+ this._rpc.handleError(ws, error);
187
+ }
188
+ };
189
+ }
190
+
191
+ //#endregion
192
+ export { withRpc };
@@ -0,0 +1,13 @@
1
+ import "../schema-CN5HHHku.js";
2
+ import "../factory-C1v0AEHY.js";
3
+ import "../json-54Z2bIIs.js";
4
+ import "../index-Be7jjS77.js";
5
+ import "../protocol-DA84zrc2.js";
6
+ import "../types-Be-qmQu0.js";
7
+ import "../default-xDNNMrg0.js";
8
+ import { n as calculateReconnectDelay, r as defaultReconnectOptions, t as ReconnectOptions } from "../reconnect-DbcN0R_1.js";
9
+ import { IAdapterHooks, IConnectionAdapter, IMultiAdapterHooks, IMultiConnectionAdapter, MultiCallOptions, MultiCallResult, MultiDriver } from "./types.js";
10
+ import { ConnectionState, RpcClient, RpcClientOptions } from "./client.js";
11
+ import { RpcActorConstructor, withRpc } from "./cloudflare-do.js";
12
+ import { n as RpcServerOptions, r as MultiPeerBase, t as RpcServer } from "../server-zTjpJpoX.js";
13
+ export { type ConnectionState, IAdapterHooks, IConnectionAdapter, IMultiAdapterHooks, IMultiConnectionAdapter, MultiCallOptions, MultiCallResult, MultiDriver, MultiPeerBase, ReconnectOptions, type RpcActorConstructor, RpcClient, type RpcClientOptions, RpcServer, type RpcServerOptions, calculateReconnectDelay, defaultReconnectOptions, withRpc };
@@ -0,0 +1,16 @@
1
+ import "../factory-3ziwTuZe.js";
2
+ import "../json-Bshec-bZ.js";
3
+ import "../codecs-BmYG2d_U.js";
4
+ import "../protocol-_mpoOPp6.js";
5
+ import "../errors-5BfreE63.js";
6
+ import "../sql-CCjc6Bid.js";
7
+ import { n as defaultReconnectOptions, t as calculateReconnectDelay } from "../reconnect-CGAA_1Gf.js";
8
+ import "../default-BkrMd28n.js";
9
+ import "../durable-MZjkvyS6.js";
10
+ import "./types.js";
11
+ import { RpcClient } from "./client.js";
12
+ import { t as MultiPeerBase } from "../multi-peer-BAi9yVzp.js";
13
+ import { withRpc } from "./cloudflare-do.js";
14
+ import { RpcServer } from "./server.js";
15
+
16
+ export { MultiPeerBase, RpcClient, RpcServer, calculateReconnectDelay, defaultReconnectOptions, withRpc };
@@ -0,0 +1,10 @@
1
+ import "../schema-CN5HHHku.js";
2
+ import "../factory-C1v0AEHY.js";
3
+ import "../json-54Z2bIIs.js";
4
+ import "../index-Be7jjS77.js";
5
+ import "../protocol-DA84zrc2.js";
6
+ import "../types-Be-qmQu0.js";
7
+ import "../default-xDNNMrg0.js";
8
+ import "./types.js";
9
+ import { n as RpcServerOptions, t as RpcServer } from "../server-zTjpJpoX.js";
10
+ export { RpcServer, RpcServerOptions };
@@ -0,0 +1,122 @@
1
+ import "../factory-3ziwTuZe.js";
2
+ import "../json-Bshec-bZ.js";
3
+ import "../codecs-BmYG2d_U.js";
4
+ import "../protocol-_mpoOPp6.js";
5
+ import "../errors-5BfreE63.js";
6
+ import { t as WebSocketReadyState } from "../types-D_psiH09.js";
7
+ import { t as RpcPeer } from "../default-BkrMd28n.js";
8
+ import { t as MultiPeerBase } from "../multi-peer-BAi9yVzp.js";
9
+
10
+ //#region src/adapters/server.ts
11
+ /**
12
+ * RPC Server Adapter
13
+ *
14
+ * Manages RpcPeer instances for incoming WebSocket connections.
15
+ * Works with Node.js `ws`, Bun's native WebSocket, or any compatible server.
16
+ */
17
+ /**
18
+ * RPC Server
19
+ *
20
+ * Manages WebSocket server and client connections with RPC capabilities.
21
+ *
22
+ * @example
23
+ * ```typescript
24
+ * import { WebSocketServer } from "ws";
25
+ * import { RpcServer } from "@igoforth/ws-rpc/adapters/server";
26
+ *
27
+ * const server = new RpcServer({
28
+ * wss: { port: 8080 },
29
+ * WebSocketServer,
30
+ * localSchema: ServerSchema,
31
+ * remoteSchema: ClientSchema,
32
+ * provider: {
33
+ * getUser: async ({ id }) => ({ name: "John", email: "john@example.com" }),
34
+ * },
35
+ * hooks: {
36
+ * onConnect: (peer) => {
37
+ * console.log(`Client ${peer.id} connected`);
38
+ * peer.driver.ping({}).then(console.log);
39
+ * },
40
+ * onDisconnect: (peer) => console.log(`Client ${peer.id} disconnected`),
41
+ * },
42
+ * });
43
+ *
44
+ * // Emit to all clients
45
+ * server.emit("orderUpdated", { orderId: "123", status: "shipped" });
46
+ *
47
+ * // Graceful shutdown
48
+ * process.on("SIGTERM", () => server.close());
49
+ * ```
50
+ */
51
+ var RpcServer = class extends MultiPeerBase {
52
+ wss;
53
+ constructor(options) {
54
+ super({
55
+ localSchema: options.localSchema,
56
+ remoteSchema: options.remoteSchema,
57
+ provider: options.provider,
58
+ ...options.timeout !== void 0 && { timeout: options.timeout },
59
+ ...options.hooks !== void 0 && { hooks: options.hooks }
60
+ });
61
+ if ("on" in options.wss && typeof options.wss.on === "function") this.wss = options.wss;
62
+ else {
63
+ if (!options.WebSocketServer) throw new Error("WebSocketServer constructor required when passing options");
64
+ this.wss = new options.WebSocketServer(options.wss);
65
+ }
66
+ this.wss.on("connection", (ws) => this.handleConnection(ws));
67
+ this.wss.on("error", (error) => this.hooks.onError?.(null, error));
68
+ this.wss.on("close", () => this.hooks.onClose?.());
69
+ }
70
+ handleConnection(ws) {
71
+ const peer = new RpcPeer({
72
+ ws,
73
+ localSchema: this.localSchema,
74
+ remoteSchema: this.remoteSchema,
75
+ provider: this.provider,
76
+ timeout: this.timeout,
77
+ onEvent: (event, data) => {
78
+ this.hooks.onEvent?.(peer, event, data);
79
+ }
80
+ });
81
+ this.addPeer(ws, peer);
82
+ ws.onmessage = (event) => {
83
+ if (typeof event === "object" && event != null && "data" in event && (typeof event.data === "string" || event.data instanceof ArrayBuffer)) peer.handleMessage(event.data);
84
+ };
85
+ ws.onclose = () => {
86
+ this.removePeer(ws);
87
+ };
88
+ ws.onerror = (event) => {
89
+ const error = event instanceof Error ? event : /* @__PURE__ */ new Error(`WebSocket error for peer ${peer.id}`);
90
+ this.hooks.onError?.(peer, error);
91
+ };
92
+ }
93
+ /**
94
+ * Close a peer connection with WebSocket close code/reason
95
+ *
96
+ * @param id - Peer ID to close
97
+ * @param code - WebSocket close code (default: 1000)
98
+ * @param reason - Close reason message (default: "Server disconnect")
99
+ * @returns true if peer was found and closed, false otherwise
100
+ */
101
+ closePeer(id, code = 1e3, reason = "Server disconnect") {
102
+ const entry = this.findPeerEntry(id);
103
+ if (entry) {
104
+ if (entry.connection.readyState !== WebSocketReadyState.CLOSED) entry.connection.close(code, reason);
105
+ return super.closePeer(id);
106
+ }
107
+ return false;
108
+ }
109
+ /**
110
+ * Close the server and all client connections
111
+ *
112
+ * @param callback - Optional callback invoked when server is closed
113
+ */
114
+ close(callback) {
115
+ for (const entry of this.getOpenEntries()) if (entry.connection.readyState !== WebSocketReadyState.CLOSED) entry.connection.close(1001, "Server shutdown");
116
+ this.closeAll();
117
+ this.wss.close(callback);
118
+ }
119
+ };
120
+
121
+ //#endregion
122
+ export { RpcServer };