snapreq 0.0.3 → 0.0.5

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 (69) hide show
  1. package/README.md +12 -0
  2. package/{types → build}/capabilities.d.ts +1 -0
  3. package/build/capabilities.d.ts.map +1 -0
  4. package/{src → build}/capabilities.js +11 -12
  5. package/{types → build}/errors.d.ts +18 -0
  6. package/build/errors.d.ts.map +1 -0
  7. package/build/errors.js +82 -0
  8. package/{types → build}/headers.d.ts +1 -0
  9. package/build/headers.d.ts.map +1 -0
  10. package/build/headers.js +89 -0
  11. package/{types → build}/request.d.ts +1 -0
  12. package/build/request.d.ts.map +1 -0
  13. package/build/request.js +89 -0
  14. package/{types → build}/response.d.ts +21 -1
  15. package/build/response.d.ts.map +1 -0
  16. package/build/response.js +171 -0
  17. package/{types → build}/retry.d.ts +1 -0
  18. package/build/retry.d.ts.map +1 -0
  19. package/build/retry.js +95 -0
  20. package/{types → build}/snap-req.d.ts +72 -1
  21. package/build/snap-req.d.ts.map +1 -0
  22. package/build/snap-req.js +349 -0
  23. package/{types → build}/transports/fetch-transport.d.ts +1 -0
  24. package/build/transports/fetch-transport.d.ts.map +1 -0
  25. package/build/transports/fetch-transport.js +116 -0
  26. package/build/transports/fetch.d.ts +2 -0
  27. package/build/transports/fetch.d.ts.map +1 -0
  28. package/build/transports/fetch.js +3 -0
  29. package/{types → build}/transports/node-transport.d.ts +1 -0
  30. package/build/transports/node-transport.d.ts.map +1 -0
  31. package/build/transports/node-transport.js +287 -0
  32. package/build/transports/proxy-bounce-transport.d.ts +27 -0
  33. package/build/transports/proxy-bounce-transport.d.ts.map +1 -0
  34. package/build/transports/proxy-bounce-transport.js +116 -0
  35. package/{types → build}/transports/select.d.ts +1 -0
  36. package/build/transports/select.d.ts.map +1 -0
  37. package/build/transports/select.js +69 -0
  38. package/{types → build}/transports/xhr-transport.d.ts +1 -0
  39. package/build/transports/xhr-transport.d.ts.map +1 -0
  40. package/build/transports/xhr-transport.js +93 -0
  41. package/build/transports/xhr.d.ts +2 -0
  42. package/build/transports/xhr.d.ts.map +1 -0
  43. package/build/transports/xhr.js +3 -0
  44. package/{types → build}/websocket/websocket-channel.d.ts +1 -0
  45. package/build/websocket/websocket-channel.d.ts.map +1 -0
  46. package/build/websocket/websocket-channel.js +162 -0
  47. package/{types → build}/websocket/websocket-client.d.ts +1 -0
  48. package/build/websocket/websocket-client.d.ts.map +1 -0
  49. package/build/websocket/websocket-client.js +920 -0
  50. package/{types → build}/websocket/websocket-connection.d.ts +1 -0
  51. package/build/websocket/websocket-connection.d.ts.map +1 -0
  52. package/build/websocket/websocket-connection.js +148 -0
  53. package/build/websocket.d.ts +3 -0
  54. package/build/websocket.d.ts.map +1 -0
  55. package/build/websocket.js +4 -0
  56. package/package.json +20 -23
  57. package/src/errors.js +0 -69
  58. package/src/headers.js +0 -92
  59. package/src/request.js +0 -104
  60. package/src/response.js +0 -156
  61. package/src/retry.js +0 -105
  62. package/src/snap-req.js +0 -248
  63. package/src/transports/fetch-transport.js +0 -128
  64. package/src/transports/node-transport.js +0 -323
  65. package/src/transports/select.js +0 -73
  66. package/src/transports/xhr-transport.js +0 -112
  67. package/src/websocket/websocket-channel.js +0 -176
  68. package/src/websocket/websocket-client.js +0 -1034
  69. package/src/websocket/websocket-connection.js +0 -154
@@ -0,0 +1,920 @@
1
+ // @ts-check
2
+ import SnapReqWebSocketConnection from "./websocket-connection.js";
3
+ import SnapReqWebSocketChannel from "./websocket-channel.js";
4
+ const DEFAULT_RECONNECT_DELAYS = [1000, 2000, 4000, 8000, 15000];
5
+ /**
6
+ * A small WebSocket client that mirrors simple HTTP-style calls and channel
7
+ * subscriptions over `globalThis.WebSocket`, so the same code runs on web, Expo
8
+ * / React Native and Node. Supports optional auto-reconnect with exponential
9
+ * backoff, session resumption and listener re-subscription.
10
+ *
11
+ * Response bodies are returned as raw parsed JSON; apps that need their own
12
+ * (de)serialization should apply it around `post`/`get` and the response
13
+ * `json()`.
14
+ */
15
+ export default class SnapReqWebSocketClient {
16
+ /**
17
+ * @param {object} args - Options object.
18
+ * @param {string} args.url - Full WebSocket URL, e.g. `ws://localhost:3006/websocket`.
19
+ * @param {boolean} [args.autoReconnect] - Enable auto-reconnect with exponential backoff.
20
+ * @param {boolean} [args.debug] - Whether to log debug output.
21
+ * @param {{getIsOnline?: () => boolean | Promise<boolean>, subscribe?: (callback: (isOnline: boolean) => void) => (() => void) | {remove: () => void}}} [args.networkMonitor] - Optional online-state adapter. When provided, auto-reconnect can wait for the network to report online before reconnecting, and open sockets are closed when the monitor reports offline.
22
+ * @param {number[]} [args.reconnectDelays] - Backoff delays in ms (default: [1000, 2000, 4000, 8000, 15000]).
23
+ * @param {{get: () => string | null | undefined | Promise<string | null | undefined>, set: (sessionId: string) => void | Promise<void>, clear: () => void | Promise<void>}} [args.sessionStore] - Optional sessionId persistence hook surviving reloads (localStorage, a cookie, SQLite, etc.).
24
+ * @param {(value: any) => any} [args.deserialize] - Optional transform applied to a response body inside `response.json()`. Lets an app re-hydrate its own wire format. Defaults to identity.
25
+ */
26
+ constructor({ autoReconnect = true, debug = false, deserialize, networkMonitor, reconnectDelays, sessionStore, url } = /** @type {any} */ ({})) {
27
+ /**
28
+ * @param {MessageEvent<any>} event - Event payload.
29
+ * @returns {void}
30
+ */
31
+ this.onMessage = (event) => {
32
+ const raw = typeof event.data === "string" ? event.data : event.data?.toString?.();
33
+ if (!raw)
34
+ return;
35
+ /** @type {Record<string, any>} */
36
+ let message;
37
+ try {
38
+ message = JSON.parse(raw);
39
+ }
40
+ catch (error) {
41
+ this._debug("Failed to parse websocket message", error);
42
+ return;
43
+ }
44
+ const { type } = message;
45
+ if (type === "response") {
46
+ const { id } = message;
47
+ const pending = id ? this.pendingRequests.get(id) : undefined;
48
+ if (pending) {
49
+ this.pendingRequests.delete(id);
50
+ pending.resolve(new SnapReqWebSocketResponse(message, this._deserialize));
51
+ }
52
+ else {
53
+ this._debug(`No pending request for response id ${id}`);
54
+ }
55
+ }
56
+ else if (type === "subscribed") {
57
+ const subscriptionKey = this._subscriptionKey(message.channel, message.params);
58
+ const pendingSubscription = this.pendingSubscriptions.get(subscriptionKey);
59
+ if (pendingSubscription) {
60
+ this.pendingSubscriptions.delete(subscriptionKey);
61
+ pendingSubscription.resolve();
62
+ }
63
+ }
64
+ else if (type === "event") {
65
+ const { channel, payload } = message;
66
+ for (const listenerEntry of this.listeners.values()) {
67
+ if (listenerEntry.channel !== channel)
68
+ continue;
69
+ listenerEntry.callbacks.forEach((/** @type {(payload: any, message?: Record<string, any>) => void} */ callback) => {
70
+ try {
71
+ callback(payload, message);
72
+ }
73
+ catch (error) {
74
+ this._debug("Listener error", error);
75
+ }
76
+ });
77
+ }
78
+ }
79
+ else if (type === "replay-gap") {
80
+ const subscriptionKey = this._subscriptionKey(message.channel, message.params);
81
+ const pendingSubscription = this.pendingSubscriptions.get(subscriptionKey);
82
+ if (pendingSubscription) {
83
+ this.pendingSubscriptions.delete(subscriptionKey);
84
+ pendingSubscription.reject(new Error(`Replay gap for ${message.channel}`));
85
+ }
86
+ }
87
+ else if (type === "connection-opened") {
88
+ const connection = this._connections.get(message.connectionId);
89
+ connection?._handleOpened();
90
+ }
91
+ else if (type === "connection-message") {
92
+ const connection = this._connections.get(message.connectionId);
93
+ connection?._handleMessage(message.body);
94
+ }
95
+ else if (type === "connection-closed") {
96
+ const connection = this._connections.get(message.connectionId);
97
+ if (connection) {
98
+ this._connections.delete(message.connectionId);
99
+ connection._handleClosed(message.reason || "server_close");
100
+ }
101
+ }
102
+ else if (type === "connection-error") {
103
+ const connection = this._connections.get(message.connectionId);
104
+ if (connection) {
105
+ this._connections.delete(message.connectionId);
106
+ connection._handleClosed(`error: ${message.message || "connection-error"}`);
107
+ }
108
+ }
109
+ else if (type === "channel-subscribed") {
110
+ const sub = this._channelSubscriptions.get(message.subscriptionId);
111
+ sub?._handleSubscribed();
112
+ }
113
+ else if (type === "channel-message") {
114
+ const sub = this._channelSubscriptions.get(message.subscriptionId);
115
+ sub?._handleMessage(message.body);
116
+ }
117
+ else if (type === "channel-unsubscribed") {
118
+ const sub = this._channelSubscriptions.get(message.subscriptionId);
119
+ if (sub) {
120
+ this._channelSubscriptions.delete(message.subscriptionId);
121
+ sub._handleClosed("server_unsubscribe");
122
+ }
123
+ }
124
+ else if (type === "channel-error") {
125
+ const sub = this._channelSubscriptions.get(message.subscriptionId);
126
+ if (sub) {
127
+ this._channelSubscriptions.delete(message.subscriptionId);
128
+ sub._handleClosed(`error: ${message.message || "channel-error"}`);
129
+ }
130
+ }
131
+ else if (type === "session-established") {
132
+ this._pendingSessionId = typeof message.sessionId === "string" ? message.sessionId : null;
133
+ // First connect: cache sessionId for future resume attempts.
134
+ if (!this._awaitingResume) {
135
+ this._sessionId = this._pendingSessionId;
136
+ if (this._sessionId) {
137
+ this._persistSessionId(this._sessionId);
138
+ }
139
+ this._markSessionReady();
140
+ this._sendPendingChannelSubscriptions();
141
+ }
142
+ }
143
+ else if (type === "session-resumed") {
144
+ this._awaitingResume = false;
145
+ this._pendingSessionId = null;
146
+ this._sessionId = message.sessionId;
147
+ this._persistSessionId(message.sessionId);
148
+ this._markSessionReady();
149
+ this._sendPendingChannelSubscriptions();
150
+ // Fire onResume on every live handle so user code knows the session came
151
+ // back with state intact.
152
+ for (const connection of this._connections.values())
153
+ connection._handleResumed();
154
+ for (const subscription of this._channelSubscriptions.values())
155
+ subscription._handleResumed();
156
+ }
157
+ else if (type === "session-gone") {
158
+ this._awaitingResume = false;
159
+ this._sessionId = null;
160
+ this._pendingSessionId = null;
161
+ this._clearPersistedSessionId();
162
+ // Tear down every live handle — their server-side counterparts are gone.
163
+ const connections = [...this._connections.values()];
164
+ this._connections.clear();
165
+ for (const connection of connections)
166
+ connection._handleClosed("session_gone");
167
+ const subs = [...this._channelSubscriptions.values()];
168
+ this._channelSubscriptions.clear();
169
+ for (const subscription of subs)
170
+ subscription._handleClosed("session_gone");
171
+ this._markSessionReady();
172
+ }
173
+ else if (type === "error" && message.id) {
174
+ const pending = this.pendingRequests.get(message.id);
175
+ if (pending) {
176
+ this.pendingRequests.delete(message.id);
177
+ pending.reject(new Error(message.error || "Unknown websocket error"));
178
+ }
179
+ }
180
+ };
181
+ /**
182
+ * Rejects all pending requests when the socket closes. Schedules reconnect if
183
+ * enabled.
184
+ * @returns {void}
185
+ */
186
+ this.onClose = () => {
187
+ this.disconnectedSince || (this.disconnectedSince = Date.now());
188
+ this._resetSessionReadyState();
189
+ for (const [id, { reject }] of this.pendingRequests.entries()) {
190
+ reject(new Error(`Websocket closed before response for ${id}`));
191
+ }
192
+ for (const { reject } of this.pendingSubscriptions.values()) {
193
+ reject(new Error("Websocket closed before subscription acknowledgement"));
194
+ }
195
+ if (this._sessionId && this.autoReconnect) {
196
+ // Session may resume when we reconnect — keep the handles alive and fire
197
+ // onDisconnect so user code can pause UI work.
198
+ for (const connection of this._connections.values())
199
+ connection._handleDisconnected();
200
+ for (const subscription of this._channelSubscriptions.values())
201
+ subscription._handleDisconnected();
202
+ }
203
+ else {
204
+ // No resume path: tear down every live connection / channel sub.
205
+ const connections = [...this._connections.values()];
206
+ this._connections.clear();
207
+ for (const connection of connections) {
208
+ connection._handleClosed("session_destroyed");
209
+ }
210
+ const channelSubs = [...this._channelSubscriptions.values()];
211
+ this._channelSubscriptions.clear();
212
+ for (const subscription of channelSubs) {
213
+ subscription._handleClosed("session_destroyed");
214
+ }
215
+ this._sessionId = null;
216
+ }
217
+ this.pendingRequests.clear();
218
+ this.pendingSubscriptions.clear();
219
+ this.connectPromise = undefined;
220
+ if (!this.autoReconnect)
221
+ return;
222
+ void this._shouldWaitForOnline().then((shouldWaitForOnline) => {
223
+ if (!shouldWaitForOnline) {
224
+ this._scheduleReconnect();
225
+ }
226
+ });
227
+ };
228
+ if (!globalThis.WebSocket)
229
+ throw new Error("WebSocket global is not available");
230
+ if (!url)
231
+ throw new Error("SnapReqWebSocketClient requires a url");
232
+ /** @type {(value: any) => any} */
233
+ this._deserialize = deserialize || ((value) => value);
234
+ /** @type {boolean} */
235
+ this.autoReconnect = autoReconnect;
236
+ this.debug = debug;
237
+ /** @type {number | null} */
238
+ this.disconnectedSince = null;
239
+ this.pendingRequests = new Map();
240
+ this.pendingSubscriptions = new Map();
241
+ /** @type {number} */
242
+ this.reconnectAttempt = 0;
243
+ /** @type {number} */
244
+ this.connectionAttempts = 0;
245
+ /** @type {number[]} */
246
+ this.reconnectDelays = reconnectDelays || DEFAULT_RECONNECT_DELAYS;
247
+ /** @type {ReturnType<typeof setTimeout> | null} */
248
+ this.reconnectTimer = null;
249
+ this.url = url;
250
+ this.listeners = new Map();
251
+ this.nextID = 1;
252
+ /** @type {(() => void | Promise<void>) | null} */
253
+ this.onReconnect = null;
254
+ /** @type {Record<string, any>} */
255
+ this._metadata = {};
256
+ /** @type {Map<string, SnapReqWebSocketConnection>} */
257
+ this._connections = new Map();
258
+ /** @type {Map<string, SnapReqWebSocketChannel>} */
259
+ this._channelSubscriptions = new Map();
260
+ this._nextConnectionIdSeq = 1;
261
+ this._nextSubscriptionIdSeq = 1;
262
+ /** @type {string | null} - sessionId received from `session-established`; sent on reconnect for resumption. */
263
+ this._sessionId = null;
264
+ /** @type {boolean} - true between a reconnect and the session-resumed / session-gone reply. */
265
+ this._awaitingResume = false;
266
+ /** @type {boolean} - true once the current socket has an active session ready for app messages. */
267
+ this._sessionReady = false;
268
+ /** @type {string | null} - provisional session id announced before a resume attempt finishes. */
269
+ this._pendingSessionId = null;
270
+ /** @type {Promise<void> | null} */
271
+ this._sessionReadyPromise = null;
272
+ /** @type {(() => void) | null} */
273
+ this._resolveSessionReady = null;
274
+ /** @type {unknown | null} */
275
+ this._sessionReadyError = null;
276
+ /** @type {{get: () => string | null | undefined | Promise<string | null | undefined>, set: (sessionId: string) => void | Promise<void>, clear: () => void | Promise<void>} | undefined} */
277
+ this._sessionStore = sessionStore;
278
+ /** @type {boolean} - true once the sessionStore has been consulted for a restored id. */
279
+ this._sessionStoreRestored = false;
280
+ /** @type {{getIsOnline?: () => boolean | Promise<boolean>, subscribe?: (callback: (isOnline: boolean) => void) => (() => void) | {remove: () => void}} | undefined} */
281
+ this._networkMonitor = networkMonitor;
282
+ /** @type {null | (() => void) | {remove: () => void}} */
283
+ this._networkMonitorSubscription = null;
284
+ /** @type {boolean} */
285
+ this._waitingForOnline = false;
286
+ }
287
+ /** @returns {boolean} - Whether the socket is open. */
288
+ isOpen() {
289
+ return Boolean(this.socket && this.socket.readyState === this.socket.OPEN);
290
+ }
291
+ /** @returns {boolean} - Whether the session is ready for app messages. */
292
+ isSessionReady() {
293
+ return this._sessionReady;
294
+ }
295
+ /**
296
+ * Opens a 1:1 connection of the given type against the server. Requires the
297
+ * socket to already be connected (call `connect()` first).
298
+ * @param {string} connectionType - Name the server registered the class under.
299
+ * @param {{params?: Record<string, any>, onConnect?: () => void, onMessage?: (body: any) => void, onDisconnect?: () => void, onResume?: () => void, onClose?: (reason: string) => void}} [options] - Connection options.
300
+ * @returns {SnapReqWebSocketConnection} - The connection handle.
301
+ */
302
+ openConnection(connectionType, options = {}) {
303
+ if (!this.isOpen())
304
+ throw new Error("Websocket is not open; call connect() first");
305
+ const connectionId = `c${this._nextConnectionIdSeq++}`;
306
+ const connection = new SnapReqWebSocketConnection({
307
+ client: this,
308
+ connectionId,
309
+ connectionType,
310
+ params: options.params,
311
+ onConnect: options.onConnect,
312
+ onMessage: options.onMessage,
313
+ onDisconnect: options.onDisconnect,
314
+ onResume: options.onResume,
315
+ onClose: options.onClose
316
+ });
317
+ this._connections.set(connectionId, connection);
318
+ this._sendMessage({
319
+ type: "connection-open",
320
+ connectionId,
321
+ connectionType,
322
+ params: options.params || {}
323
+ });
324
+ return connection;
325
+ }
326
+ /**
327
+ * Drops a connection handle from the registry.
328
+ * @param {string} connectionId - The connection id.
329
+ * @returns {void}
330
+ */
331
+ _removeConnection(connectionId) {
332
+ this._connections.delete(connectionId);
333
+ }
334
+ /**
335
+ * Subscribes to a named channel. If the socket is not yet open, the
336
+ * subscription is queued and sent once a connection is established.
337
+ * @param {string} channelType - Name the server registered the channel under.
338
+ * @param {{params?: Record<string, any>, lastEventId?: string, onMessage?: (body: any) => void, onDisconnect?: () => void, onResume?: () => void, onClose?: (reason: string) => void}} [options] - Subscription options.
339
+ * @returns {SnapReqWebSocketChannel} - The subscription handle.
340
+ */
341
+ subscribeChannel(channelType, options = {}) {
342
+ const subscriptionId = `s${this._nextSubscriptionIdSeq++}`;
343
+ const subscription = new SnapReqWebSocketChannel({
344
+ client: this,
345
+ subscriptionId,
346
+ channelType,
347
+ lastEventId: options.lastEventId,
348
+ params: options.params,
349
+ onMessage: options.onMessage,
350
+ onDisconnect: options.onDisconnect,
351
+ onResume: options.onResume,
352
+ onClose: options.onClose
353
+ });
354
+ this._channelSubscriptions.set(subscriptionId, subscription);
355
+ this._sendChannelSubscribe(subscription);
356
+ return subscription;
357
+ }
358
+ /**
359
+ * @param {string} subscriptionId - The subscription id.
360
+ * @returns {void}
361
+ */
362
+ _removeChannelSubscription(subscriptionId) {
363
+ this._channelSubscriptions.delete(subscriptionId);
364
+ }
365
+ /**
366
+ * @param {SnapReqWebSocketChannel} subscription - The subscription to send.
367
+ * @returns {void}
368
+ */
369
+ _sendChannelSubscribe(subscription) {
370
+ if (!this.isOpen() || !this.isSessionReady() || !subscription._needsSubscribe())
371
+ return;
372
+ // Send first and only mark as sent on success. If the socket closes between
373
+ // `isOpen()` and `send()` and `_sendMessage` throws, `_subscribeSent` must
374
+ // stay false so the reconnect path's `_sendPendingChannelSubscriptions()`
375
+ // can retry.
376
+ try {
377
+ this._sendMessage({
378
+ type: "channel-subscribe",
379
+ subscriptionId: subscription.subscriptionId,
380
+ channelType: subscription.channelType,
381
+ params: subscription.params,
382
+ ...(subscription.lastEventId ? { lastEventId: subscription.lastEventId } : {})
383
+ });
384
+ subscription._markSubscribeSent();
385
+ }
386
+ catch (error) {
387
+ // Transient closed-socket race: leave the subscription retryable so
388
+ // `_sendPendingChannelSubscriptions()` can resend after reconnect.
389
+ if (!this.isOpen())
390
+ throw error;
391
+ // Non-recoverable send failure on an open socket (e.g. JSON.stringify
392
+ // failing on BigInt/cyclic params). Close the subscription and remove it
393
+ // so it cannot poison future `_sendPendingChannelSubscriptions()` loops.
394
+ this._channelSubscriptions.delete(subscription.subscriptionId);
395
+ subscription._handleClosed(`send_failed: ${error instanceof Error ? error.message : String(error)}`);
396
+ }
397
+ }
398
+ /** @returns {void} */
399
+ _sendPendingChannelSubscriptions() {
400
+ for (const subscription of this._channelSubscriptions.values()) {
401
+ this._sendChannelSubscribe(subscription);
402
+ }
403
+ }
404
+ /** @returns {Promise<boolean>} - Whether the network reports online. */
405
+ async _isOnline() {
406
+ if (!this._networkMonitor?.getIsOnline)
407
+ return true;
408
+ try {
409
+ return await this._networkMonitor.getIsOnline() !== false;
410
+ }
411
+ catch (error) {
412
+ this._debug("networkMonitor.getIsOnline failed", error);
413
+ return true;
414
+ }
415
+ }
416
+ /** @returns {Promise<boolean>} - Whether reconnect should wait for online. */
417
+ async _shouldWaitForOnline() {
418
+ if (!this._networkMonitor)
419
+ return false;
420
+ const isOnline = await this._isOnline();
421
+ if (isOnline)
422
+ return false;
423
+ this._waitingForOnline = true;
424
+ this._cancelPendingReconnect();
425
+ return true;
426
+ }
427
+ /** @returns {void} */
428
+ _ensureNetworkMonitorSubscription() {
429
+ if (!this._networkMonitor?.subscribe || this._networkMonitorSubscription)
430
+ return;
431
+ this._networkMonitorSubscription = this._networkMonitor.subscribe((isOnline) => {
432
+ if (!this.autoReconnect)
433
+ return;
434
+ if (isOnline) {
435
+ if (!this._waitingForOnline)
436
+ return;
437
+ this._waitingForOnline = false;
438
+ void this._attemptReconnect();
439
+ return;
440
+ }
441
+ this._waitingForOnline = true;
442
+ this._cancelPendingReconnect();
443
+ if (this.isOpen()) {
444
+ void this.dropConnection();
445
+ }
446
+ });
447
+ }
448
+ /** @returns {void} */
449
+ _teardownNetworkMonitorSubscription() {
450
+ if (!this._networkMonitorSubscription)
451
+ return;
452
+ if (typeof this._networkMonitorSubscription === "function") {
453
+ this._networkMonitorSubscription();
454
+ }
455
+ else {
456
+ this._networkMonitorSubscription.remove();
457
+ }
458
+ this._networkMonitorSubscription = null;
459
+ }
460
+ /**
461
+ * Sets a global metadata value that is sent to the server. When the socket is
462
+ * open, a metadata update message is sent immediately.
463
+ * @param {string} key - Metadata key.
464
+ * @param {any} value - Metadata value (null to clear).
465
+ * @returns {void}
466
+ */
467
+ setMetadata(key, value) {
468
+ if (value === null || value === undefined) {
469
+ delete this._metadata[key];
470
+ }
471
+ else {
472
+ this._metadata[key] = value;
473
+ }
474
+ if (this.socket && this.socket.readyState === this.socket.OPEN) {
475
+ this._sendMessage({ type: "metadata", data: { ...this._metadata } });
476
+ }
477
+ }
478
+ /** @returns {Record<string, any>} - Current metadata. */
479
+ getMetadata() {
480
+ return { ...this._metadata };
481
+ }
482
+ /**
483
+ * Ensures a WebSocket connection is open. Auto-reconnect and online gating are
484
+ * enabled by default.
485
+ * @param {{autoReconnect?: boolean, waitForOnline?: boolean, resetReconnectState?: boolean}} [options] - Connect options.
486
+ * @returns {Promise<void>} - Resolves once connected and the session is ready.
487
+ */
488
+ async connect({ autoReconnect = this.autoReconnect, waitForOnline = true, resetReconnectState = true } = {}) {
489
+ this.autoReconnect = autoReconnect;
490
+ if (this.autoReconnect) {
491
+ this._ensureNetworkMonitorSubscription();
492
+ }
493
+ else {
494
+ this._waitingForOnline = false;
495
+ this._cancelPendingReconnect();
496
+ this._teardownNetworkMonitorSubscription();
497
+ }
498
+ if (waitForOnline && this.autoReconnect && !await this._isOnline()) {
499
+ this._waitingForOnline = true;
500
+ return;
501
+ }
502
+ if (resetReconnectState) {
503
+ this.reconnectAttempt = 0;
504
+ }
505
+ if (this.socket && this.socket.readyState === this.socket.OPEN)
506
+ return;
507
+ if (this.connectPromise)
508
+ return this.connectPromise;
509
+ this._resetSessionReadyState();
510
+ this._waitingForOnline = false;
511
+ this.connectionAttempts += 1;
512
+ this.connectPromise = new Promise((resolve, reject) => {
513
+ this.socket = new WebSocket(this.url);
514
+ const cleanup = () => {
515
+ this.socket?.removeEventListener("open", onOpen);
516
+ this.socket?.removeEventListener("error", onError);
517
+ };
518
+ const onOpen = () => {
519
+ cleanup();
520
+ resolve(undefined);
521
+ };
522
+ const onError = (/** @type {Event & {error?: unknown}} */ event) => {
523
+ cleanup();
524
+ const error = event?.error || new Error("Websocket connection error");
525
+ reject(error);
526
+ };
527
+ this.socket.addEventListener("open", onOpen);
528
+ this.socket.addEventListener("error", onError);
529
+ this.socket.addEventListener("message", this.onMessage);
530
+ this.socket.addEventListener("close", this.onClose);
531
+ });
532
+ await this.connectPromise;
533
+ // Cold restore from external persistence (sessionStore) on the very first
534
+ // connect: apps wire this up to survive a full page reload.
535
+ if (!this._sessionId && !this._sessionStoreRestored && this._sessionStore) {
536
+ this._sessionStoreRestored = true;
537
+ try {
538
+ const storedId = await this._sessionStore.get();
539
+ if (typeof storedId === "string" && storedId.length > 0) {
540
+ this._sessionId = storedId;
541
+ }
542
+ }
543
+ catch (error) {
544
+ this._debug("sessionStore.get failed", error);
545
+ }
546
+ }
547
+ // If we have a cached sessionId from a prior connect, ask the server to
548
+ // resume it. The server replies with either `session-resumed` (state
549
+ // preserved) or `session-gone` (start fresh).
550
+ if (this._sessionId) {
551
+ this._awaitingResume = true;
552
+ this._sendMessage({ type: "session-resume", sessionId: this._sessionId });
553
+ // Fire onDisconnect on live handles so apps can pause UI work until
554
+ // session-resumed / session-gone arrives.
555
+ for (const connection of this._connections.values())
556
+ connection._handleDisconnected();
557
+ for (const subscription of this._channelSubscriptions.values())
558
+ subscription._handleDisconnected();
559
+ }
560
+ if (Object.keys(this._metadata).length > 0) {
561
+ this._sendMessage({ type: "metadata", data: { ...this._metadata } });
562
+ }
563
+ await this._waitForSessionReady();
564
+ this.disconnectedSince = null;
565
+ }
566
+ /**
567
+ * Closes the WebSocket and clears pending state.
568
+ * @returns {Promise<void>} - Resolves once closed.
569
+ */
570
+ async close() {
571
+ this.autoReconnect = false;
572
+ this._waitingForOnline = false;
573
+ this._cancelPendingReconnect();
574
+ this._teardownNetworkMonitorSubscription();
575
+ if (!this.socket)
576
+ return;
577
+ if (this.socket.readyState === this.socket.CLOSED) {
578
+ this.socket = undefined;
579
+ this.connectPromise = undefined;
580
+ this._resetSessionReadyState();
581
+ return;
582
+ }
583
+ await new Promise((resolve) => {
584
+ this.socket?.addEventListener("close", () => resolve(undefined));
585
+ this.socket?.close();
586
+ });
587
+ this.socket = undefined;
588
+ this.connectPromise = undefined;
589
+ this._resetSessionReadyState();
590
+ }
591
+ /**
592
+ * Disables auto-reconnect and closes the WebSocket.
593
+ * @returns {Promise<void>} - Resolves once closed.
594
+ */
595
+ async disconnectAndStopReconnect() {
596
+ await this.close();
597
+ }
598
+ /**
599
+ * Closes the raw socket without disabling auto-reconnect. Used by tests to
600
+ * simulate an unexpected network drop.
601
+ * @returns {Promise<void>} - Resolves once the socket has closed.
602
+ */
603
+ async dropConnection() {
604
+ if (!this.socket)
605
+ return;
606
+ await new Promise((resolve) => {
607
+ this.socket?.addEventListener("close", () => resolve(undefined));
608
+ this.socket?.close();
609
+ });
610
+ this.connectPromise = undefined;
611
+ this._resetSessionReadyState();
612
+ }
613
+ /**
614
+ * Performs a POST request over the WebSocket.
615
+ * @param {string} path - Path.
616
+ * @param {any} [body] - Request body.
617
+ * @param {{headers?: Record<string, string>}} [options] - Request options such as headers.
618
+ * @returns {Promise<SnapReqWebSocketResponse>} - The response.
619
+ */
620
+ async post(path, body, options = {}) {
621
+ return await this.request("POST", path, { ...options, body });
622
+ }
623
+ /**
624
+ * Performs a GET request over the WebSocket.
625
+ * @param {string} path - Path.
626
+ * @param {{headers?: Record<string, string>}} [options] - Request options such as headers.
627
+ * @returns {Promise<SnapReqWebSocketResponse>} - The response.
628
+ */
629
+ async get(path, options = {}) {
630
+ return await this.request("GET", path, options);
631
+ }
632
+ /**
633
+ * Subscribes to a channel for server-sent events.
634
+ * @param {string} channel - Channel name.
635
+ * @param {(payload: any) => void} callback - Callback function.
636
+ * @returns {() => void} - Unsubscribe function.
637
+ */
638
+ on(channel, callback) {
639
+ return this.subscribe(channel, {}, callback);
640
+ }
641
+ /**
642
+ * Returns a snapshot of the client's connection state.
643
+ * @returns {{disconnectedSince: number | null, isOpen: boolean, listenerCount: number}} - State snapshot.
644
+ */
645
+ state() {
646
+ return {
647
+ disconnectedSince: this.disconnectedSince,
648
+ isOpen: !!this.socket && this.socket.readyState === this.socket.OPEN,
649
+ listenerCount: this.listeners.size + this._channelSubscriptions.size
650
+ };
651
+ }
652
+ /**
653
+ * Subscribes to a channel for server-sent events with optional params.
654
+ * @param {string} channel - Channel name.
655
+ * @param {{lastEventId?: string, params?: Record<string, any>}} options - Subscription options.
656
+ * @param {(payload: any, message?: Record<string, any>) => void} callback - Callback function.
657
+ * @returns {(() => void) & {ready: Promise<void>}} - Unsubscribe function with readiness promise.
658
+ */
659
+ subscribe(channel, options, callback) {
660
+ const params = options?.params;
661
+ const lastEventId = options?.lastEventId;
662
+ const subscriptionKey = this._subscriptionKey(channel, params);
663
+ if (!this.listeners.has(subscriptionKey)) {
664
+ /** @type {((value?: void) => void) | undefined} */
665
+ let resolveReady;
666
+ /** @type {((error: unknown) => void) | undefined} */
667
+ let rejectReady;
668
+ const ready = new Promise((resolve, reject) => {
669
+ resolveReady = resolve;
670
+ rejectReady = reject;
671
+ });
672
+ this.listeners.set(subscriptionKey, {
673
+ callbacks: new Set(),
674
+ channel,
675
+ params,
676
+ ready
677
+ });
678
+ this.pendingSubscriptions.set(subscriptionKey, {
679
+ reject: rejectReady || (() => { }),
680
+ resolve: resolveReady || (() => { })
681
+ });
682
+ void this.connect().then(() => {
683
+ this._sendMessage({ channel, lastEventId, params, type: "subscribe" });
684
+ }).catch((error) => this._debug("Subscribe failed", error));
685
+ }
686
+ const listenerEntry = this.listeners.get(subscriptionKey);
687
+ if (!listenerEntry)
688
+ throw new Error("Listeners map not initialized");
689
+ listenerEntry.callbacks.add(callback);
690
+ const unsubscribe = () => {
691
+ listenerEntry.callbacks.delete(callback);
692
+ if (listenerEntry.callbacks.size === 0) {
693
+ this.listeners.delete(subscriptionKey);
694
+ }
695
+ };
696
+ unsubscribe.ready = listenerEntry.ready;
697
+ return unsubscribe;
698
+ }
699
+ /**
700
+ * Subscribes to a channel and waits until the server acknowledges it.
701
+ * @param {string} channel - Channel name.
702
+ * @param {{lastEventId?: string, params?: Record<string, any>}} options - Subscription options.
703
+ * @param {(payload: any, message?: Record<string, any>) => void} callback - Callback function.
704
+ * @returns {Promise<(() => void) & {ready: Promise<void>}>} - Ready unsubscribe handle.
705
+ */
706
+ async subscribeAndWait(channel, options, callback) {
707
+ const unsubscribe = this.subscribe(channel, options, callback);
708
+ await unsubscribe.ready;
709
+ return unsubscribe;
710
+ }
711
+ /**
712
+ * @param {string} method - HTTP method.
713
+ * @param {string} path - Path.
714
+ * @param {object} [options] - Options object.
715
+ * @param {any} [options.body] - Request body.
716
+ * @param {Record<string, string>} [options.headers] - Header list.
717
+ * @returns {Promise<SnapReqWebSocketResponse>} - The response.
718
+ */
719
+ async request(method, path, { body, headers } = {}) {
720
+ await this.connect();
721
+ const id = `ws-${this.nextID++}`;
722
+ const payload = {
723
+ body,
724
+ headers,
725
+ id,
726
+ method,
727
+ path,
728
+ type: "request"
729
+ };
730
+ return await new Promise((resolve, reject) => {
731
+ this.pendingRequests.set(id, { resolve, reject });
732
+ this._sendMessage(payload);
733
+ });
734
+ }
735
+ /**
736
+ * @param {string} channel - Channel name.
737
+ * @param {Record<string, any> | undefined} params - Subscription params.
738
+ * @returns {string} - Stable subscription key.
739
+ */
740
+ _subscriptionKey(channel, params) {
741
+ return JSON.stringify([channel, params || null]);
742
+ }
743
+ /**
744
+ * @param {Record<string, any>} payload - Payload data.
745
+ * @returns {void}
746
+ */
747
+ _sendMessage(payload) {
748
+ if (!this.socket || this.socket.readyState !== this.socket.OPEN) {
749
+ throw new Error("Websocket is not open");
750
+ }
751
+ const json = JSON.stringify(payload);
752
+ this._debug("Sending", json);
753
+ this.socket.send(json);
754
+ }
755
+ /** @returns {void} */
756
+ _cancelPendingReconnect() {
757
+ if (this.reconnectTimer) {
758
+ globalThis.clearTimeout(this.reconnectTimer);
759
+ this.reconnectTimer = null;
760
+ }
761
+ }
762
+ /** @returns {void} */
763
+ _scheduleReconnect() {
764
+ this._cancelPendingReconnect();
765
+ const delay = this.reconnectDelays[Math.min(this.reconnectAttempt, this.reconnectDelays.length - 1)];
766
+ this.reconnectTimer = globalThis.setTimeout(() => {
767
+ this.reconnectTimer = null;
768
+ void this._attemptReconnect();
769
+ }, delay);
770
+ this.reconnectAttempt += 1;
771
+ }
772
+ /** @returns {Promise<void>} */
773
+ async _attemptReconnect() {
774
+ if (!this.autoReconnect)
775
+ return;
776
+ if (!await this._isOnline()) {
777
+ this._waitingForOnline = true;
778
+ return;
779
+ }
780
+ try {
781
+ this._waitingForOnline = false;
782
+ this.connectionAttempts += 1;
783
+ await this.connect({ autoReconnect: this.autoReconnect, resetReconnectState: false, waitForOnline: false });
784
+ this.reconnectAttempt = 0;
785
+ this.disconnectedSince = null;
786
+ this._resubscribeActiveListeners();
787
+ if (typeof this.onReconnect === "function") {
788
+ await this.onReconnect();
789
+ }
790
+ }
791
+ catch (error) {
792
+ this._debug("Reconnect attempt failed:", error);
793
+ if (this.autoReconnect) {
794
+ this._scheduleReconnect();
795
+ }
796
+ }
797
+ }
798
+ /**
799
+ * Re-sends subscribe messages for all active listeners after reconnection.
800
+ * @returns {void}
801
+ */
802
+ _resubscribeActiveListeners() {
803
+ for (const [, listenerEntry] of this.listeners) {
804
+ try {
805
+ this._sendMessage({
806
+ channel: listenerEntry.channel,
807
+ params: listenerEntry.params,
808
+ type: "subscribe"
809
+ });
810
+ }
811
+ catch (error) {
812
+ this._debug("Re-subscribe failed:", error);
813
+ }
814
+ }
815
+ }
816
+ /**
817
+ * @param {...any} args - Log arguments.
818
+ * @returns {void}
819
+ */
820
+ _debug(...args) {
821
+ if (!this.debug)
822
+ return;
823
+ console.debug("[SnapReqWebSocketClient]", ...args);
824
+ }
825
+ /**
826
+ * @param {string} sessionId - Id to persist through the configured sessionStore.
827
+ * @returns {void}
828
+ */
829
+ _persistSessionId(sessionId) {
830
+ if (!this._sessionStore)
831
+ return;
832
+ try {
833
+ const result = this._sessionStore.set(sessionId);
834
+ if (result && typeof result.then === "function") {
835
+ result.catch((/** @type {unknown} */ error) => this._debug("sessionStore.set failed", error));
836
+ }
837
+ }
838
+ catch (error) {
839
+ this._debug("sessionStore.set failed", error);
840
+ }
841
+ }
842
+ /** @returns {void} */
843
+ _clearPersistedSessionId() {
844
+ if (!this._sessionStore)
845
+ return;
846
+ try {
847
+ const result = this._sessionStore.clear();
848
+ if (result && typeof result.then === "function") {
849
+ result.catch((/** @type {unknown} */ error) => this._debug("sessionStore.clear failed", error));
850
+ }
851
+ }
852
+ catch (error) {
853
+ this._debug("sessionStore.clear failed", error);
854
+ }
855
+ }
856
+ /** @returns {Promise<void>} - Resolves once the session is ready. */
857
+ _waitForSessionReady() {
858
+ if (this._sessionReady)
859
+ return Promise.resolve();
860
+ if (!this._sessionReadyPromise || !this._resolveSessionReady) {
861
+ this._sessionReadyPromise = new Promise((resolve) => {
862
+ this._resolveSessionReady = resolve;
863
+ });
864
+ }
865
+ return this._sessionReadyPromise.then(() => {
866
+ if (this._sessionReadyError) {
867
+ const error = this._sessionReadyError;
868
+ this._sessionReadyError = null;
869
+ throw error;
870
+ }
871
+ });
872
+ }
873
+ /** @returns {void} */
874
+ _markSessionReady() {
875
+ if (this._sessionReady)
876
+ return;
877
+ this._sessionReady = true;
878
+ this._sessionReadyError = null;
879
+ this._resolveSessionReady?.();
880
+ this._resolveSessionReady = null;
881
+ this._sessionReadyPromise = null;
882
+ }
883
+ /**
884
+ * @param {unknown} [error] - Reason for the reset.
885
+ * @returns {void}
886
+ */
887
+ _resetSessionReadyState(error = new Error("Websocket session readiness was reset")) {
888
+ this._sessionReady = false;
889
+ this._pendingSessionId = null;
890
+ this._sessionReadyError = error;
891
+ this._resolveSessionReady?.();
892
+ this._sessionReadyPromise = null;
893
+ this._resolveSessionReady = null;
894
+ }
895
+ }
896
+ /** A response to a request made over the WebSocket transport. */
897
+ export class SnapReqWebSocketResponse {
898
+ /**
899
+ * @param {object} message - The response message.
900
+ * @param {(value: any) => any} [deserialize] - Transform applied to the parsed body in `json()`. Defaults to identity.
901
+ */
902
+ constructor(message, deserialize) {
903
+ const responseMessage = /** @type {{body?: any, headers?: Record<string, any>, id?: string | number | null, statusCode?: number, statusMessage?: string, type?: string}} */ (message);
904
+ this.body = responseMessage.body;
905
+ this.headers = responseMessage.headers || {};
906
+ this.id = responseMessage.id;
907
+ this.statusCode = responseMessage.statusCode || 200;
908
+ this.statusMessage = responseMessage.statusMessage || "OK";
909
+ this.type = responseMessage.type;
910
+ this._deserialize = deserialize || ((value) => value);
911
+ }
912
+ /** @returns {any} - The parsed (and optionally deserialized) JSON body. */
913
+ json() {
914
+ if (typeof this.body !== "string") {
915
+ throw new Error("Response body is not a string");
916
+ }
917
+ return this._deserialize(JSON.parse(this.body));
918
+ }
919
+ }
920
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoid2Vic29ja2V0LWNsaWVudC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy93ZWJzb2NrZXQvd2Vic29ja2V0LWNsaWVudC5qcyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxZQUFZO0FBRVosT0FBTywwQkFBMEIsTUFBTSwyQkFBMkIsQ0FBQTtBQUNsRSxPQUFPLHVCQUF1QixNQUFNLHdCQUF3QixDQUFBO0FBRTVELE1BQU0sd0JBQXdCLEdBQUcsQ0FBQyxJQUFJLEVBQUUsSUFBSSxFQUFFLElBQUksRUFBRSxJQUFJLEVBQUUsS0FBSyxDQUFDLENBQUE7QUFFaEU7Ozs7Ozs7OztHQVNHO0FBQ0gsTUFBTSxDQUFDLE9BQU8sT0FBTyxzQkFBc0I7SUFRekM7Ozs7Ozs7OztPQVNHO0lBQ0gsWUFBWSxFQUFDLGFBQWEsR0FBRyxJQUFJLEVBQUUsS0FBSyxHQUFHLEtBQUssRUFBRSxXQUFXLEVBQUUsY0FBYyxFQUFFLGVBQWUsRUFBRSxZQUFZLEVBQUUsR0FBRyxFQUFDLEdBQUcsa0JBQWtCLENBQUMsQ0FBQyxFQUFFLENBQUM7UUFva0I1STs7O1dBR0c7UUFDSCxjQUFTLEdBQUcsQ0FBQyxLQUFLLEVBQUUsRUFBRTtZQUNwQixNQUFNLEdBQUcsR0FBRyxPQUFPLEtBQUssQ0FBQyxJQUFJLEtBQUssUUFBUSxDQUFDLENBQUMsQ0FBQyxLQUFLLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyxLQUFLLENBQUMsSUFBSSxFQUFFLFFBQVEsRUFBRSxFQUFFLENBQUE7WUFFbEYsSUFBSSxDQUFDLEdBQUc7Z0JBQUUsT0FBTTtZQUVoQixrQ0FBa0M7WUFDbEMsSUFBSSxPQUFPLENBQUE7WUFFWCxJQUFJLENBQUM7Z0JBQ0gsT0FBTyxHQUFHLElBQUksQ0FBQyxLQUFLLENBQUMsR0FBRyxDQUFDLENBQUE7WUFDM0IsQ0FBQztZQUFDLE9BQU8sS0FBSyxFQUFFLENBQUM7Z0JBQ2YsSUFBSSxDQUFDLE1BQU0sQ0FBQyxtQ0FBbUMsRUFBRSxLQUFL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