@replit/river 0.10.10 → 0.10.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -89,6 +89,7 @@ In another file for the client (to create a separate entrypoint),
89
89
  import WebSocket from 'isomorphic-ws';
90
90
  import { WebSocketClientTransport } from '@replit/river/transport/ws/client';
91
91
  import { createClient } from '@replit/river';
92
+ import type ServiceSurface from './server';
92
93
 
93
94
  const websocketUrl = `ws://localhost:3000`;
94
95
  const transport = new WebSocketClientTransport(
@@ -118,6 +119,24 @@ bindLogger(console.log);
118
119
  setLevel('info');
119
120
  ```
120
121
 
122
+ To listen for connection status changes,
123
+
124
+ ```ts
125
+ transport.addEventListener('connectionStatus', (evt) => {
126
+ if (evt.status === 'connect') {
127
+ // do something
128
+ } else if (evt.status === 'disconnect') {
129
+ // do something else
130
+ }
131
+ });
132
+ ```
133
+
134
+ > [!note] WebSocket connection behaviour
135
+ > WebSocket is an idle protocol. This means that when the underlying connection drops, the WebSocket
136
+ > may still think it is still connected (e.g. turning off the network via devtools).
137
+ > You can use `window.addEventListener('online', ...);` and `window.addEventListener('offline', ...);` to
138
+ > know if you need to recreate the transport.
139
+
121
140
  ### Further examples
122
141
 
123
142
  We've also provided an end-to-end testing environment using Next.js, and a simple backend connected
@@ -27,14 +27,15 @@ var WebSocketClientTransport = class extends Transport {
27
27
  reconnectPromises;
28
28
  tryReconnecting = true;
29
29
  /**
30
- * Creates a new WebSocketTransport instance.
30
+ * Creates a new WebSocketClientTransport instance.
31
31
  * @param wsGetter A function that returns a Promise that resolves to a WebSocket instance.
32
- * @param clientId The ID of the client using the transport.
32
+ * @param sessionId The ID of the client using the transport. This should be unique per session.
33
+ * @param serverId The ID of the server this transport is connecting to.
33
34
  * @param providedOptions An optional object containing configuration options for the transport.
34
35
  */
35
- constructor(wsGetter, clientId, serverId, providedOptions) {
36
+ constructor(wsGetter, sessionId, serverId, providedOptions) {
36
37
  const options = { ...defaultOptions, ...providedOptions };
37
- super(options.codec, clientId);
38
+ super(options.codec, sessionId);
38
39
  this.wsGetter = wsGetter;
39
40
  this.serverId = serverId;
40
41
  this.options = options;
@@ -55,23 +56,28 @@ var WebSocketClientTransport = class extends Transport {
55
56
  }
56
57
  reconnectPromise = new Promise(async (resolve) => {
57
58
  log?.info(`${this.clientId} -- establishing a new websocket to ${to}`);
58
- const ws = await this.wsGetter(to);
59
- if (ws.readyState === ws.OPEN) {
60
- return resolve({ ws });
59
+ try {
60
+ const ws = await this.wsGetter(to);
61
+ if (ws.readyState === ws.OPEN) {
62
+ return resolve({ ws });
63
+ }
64
+ if (ws.readyState === ws.CLOSING || ws.readyState === ws.CLOSED) {
65
+ return resolve({ err: "ws is closing or closed" });
66
+ }
67
+ const onOpen = () => {
68
+ ws.removeEventListener("open", onOpen);
69
+ resolve({ ws });
70
+ };
71
+ const onClose = (evt) => {
72
+ ws.removeEventListener("close", onClose);
73
+ resolve({ err: evt.reason });
74
+ };
75
+ ws.addEventListener("open", onOpen);
76
+ ws.addEventListener("close", onClose);
77
+ } catch (e) {
78
+ const reason = e instanceof Error ? e.message : "unknown reason";
79
+ return resolve({ err: `couldn't get a new websocket: ${reason}` });
61
80
  }
62
- if (ws.readyState === ws.CLOSING || ws.readyState === ws.CLOSED) {
63
- return resolve({ err: "ws is closing or closed" });
64
- }
65
- const onOpen = () => {
66
- ws.removeEventListener("open", onOpen);
67
- resolve({ ws });
68
- };
69
- const onClose = (evt) => {
70
- ws.removeEventListener("close", onClose);
71
- resolve({ err: evt.reason });
72
- };
73
- ws.addEventListener("open", onOpen);
74
- ws.addEventListener("close", onClose);
75
81
  });
76
82
  this.reconnectPromises.set(to, reconnectPromise);
77
83
  }
@@ -417,14 +417,15 @@ var WebSocketClientTransport = class extends Transport {
417
417
  reconnectPromises;
418
418
  tryReconnecting = true;
419
419
  /**
420
- * Creates a new WebSocketTransport instance.
420
+ * Creates a new WebSocketClientTransport instance.
421
421
  * @param wsGetter A function that returns a Promise that resolves to a WebSocket instance.
422
- * @param clientId The ID of the client using the transport.
422
+ * @param sessionId The ID of the client using the transport. This should be unique per session.
423
+ * @param serverId The ID of the server this transport is connecting to.
423
424
  * @param providedOptions An optional object containing configuration options for the transport.
424
425
  */
425
- constructor(wsGetter, clientId, serverId, providedOptions) {
426
+ constructor(wsGetter, sessionId, serverId, providedOptions) {
426
427
  const options = { ...defaultOptions, ...providedOptions };
427
- super(options.codec, clientId);
428
+ super(options.codec, sessionId);
428
429
  this.wsGetter = wsGetter;
429
430
  this.serverId = serverId;
430
431
  this.options = options;
@@ -445,23 +446,28 @@ var WebSocketClientTransport = class extends Transport {
445
446
  }
446
447
  reconnectPromise = new Promise(async (resolve) => {
447
448
  log?.info(`${this.clientId} -- establishing a new websocket to ${to}`);
448
- const ws = await this.wsGetter(to);
449
- if (ws.readyState === ws.OPEN) {
450
- return resolve({ ws });
449
+ try {
450
+ const ws = await this.wsGetter(to);
451
+ if (ws.readyState === ws.OPEN) {
452
+ return resolve({ ws });
453
+ }
454
+ if (ws.readyState === ws.CLOSING || ws.readyState === ws.CLOSED) {
455
+ return resolve({ err: "ws is closing or closed" });
456
+ }
457
+ const onOpen = () => {
458
+ ws.removeEventListener("open", onOpen);
459
+ resolve({ ws });
460
+ };
461
+ const onClose = (evt) => {
462
+ ws.removeEventListener("close", onClose);
463
+ resolve({ err: evt.reason });
464
+ };
465
+ ws.addEventListener("open", onOpen);
466
+ ws.addEventListener("close", onClose);
467
+ } catch (e) {
468
+ const reason = e instanceof Error ? e.message : "unknown reason";
469
+ return resolve({ err: `couldn't get a new websocket: ${reason}` });
451
470
  }
452
- if (ws.readyState === ws.CLOSING || ws.readyState === ws.CLOSED) {
453
- return resolve({ err: "ws is closing or closed" });
454
- }
455
- const onOpen = () => {
456
- ws.removeEventListener("open", onOpen);
457
- resolve({ ws });
458
- };
459
- const onClose = (evt) => {
460
- ws.removeEventListener("close", onClose);
461
- resolve({ err: evt.reason });
462
- };
463
- ws.addEventListener("open", onOpen);
464
- ws.addEventListener("close", onClose);
465
471
  });
466
472
  this.reconnectPromises.set(to, reconnectPromise);
467
473
  }
@@ -29,12 +29,13 @@ declare class WebSocketClientTransport extends Transport<WebSocketConnection> {
29
29
  reconnectPromises: Map<TransportClientId, Promise<WebSocketResult>>;
30
30
  tryReconnecting: boolean;
31
31
  /**
32
- * Creates a new WebSocketTransport instance.
32
+ * Creates a new WebSocketClientTransport instance.
33
33
  * @param wsGetter A function that returns a Promise that resolves to a WebSocket instance.
34
- * @param clientId The ID of the client using the transport.
34
+ * @param sessionId The ID of the client using the transport. This should be unique per session.
35
+ * @param serverId The ID of the server this transport is connecting to.
35
36
  * @param providedOptions An optional object containing configuration options for the transport.
36
37
  */
37
- constructor(wsGetter: () => Promise<WebSocket>, clientId: TransportClientId, serverId: TransportClientId, providedOptions?: Partial<Options>);
38
+ constructor(wsGetter: () => Promise<WebSocket>, sessionId: TransportClientId, serverId: TransportClientId, providedOptions?: Partial<Options>);
38
39
  createNewConnection(to: string, attempt?: number): Promise<void>;
39
40
  close(): Promise<void>;
40
41
  destroy(): Promise<void>;
@@ -29,12 +29,13 @@ declare class WebSocketClientTransport extends Transport<WebSocketConnection> {
29
29
  reconnectPromises: Map<TransportClientId, Promise<WebSocketResult>>;
30
30
  tryReconnecting: boolean;
31
31
  /**
32
- * Creates a new WebSocketTransport instance.
32
+ * Creates a new WebSocketClientTransport instance.
33
33
  * @param wsGetter A function that returns a Promise that resolves to a WebSocket instance.
34
- * @param clientId The ID of the client using the transport.
34
+ * @param sessionId The ID of the client using the transport. This should be unique per session.
35
+ * @param serverId The ID of the server this transport is connecting to.
35
36
  * @param providedOptions An optional object containing configuration options for the transport.
36
37
  */
37
- constructor(wsGetter: () => Promise<WebSocket>, clientId: TransportClientId, serverId: TransportClientId, providedOptions?: Partial<Options>);
38
+ constructor(wsGetter: () => Promise<WebSocket>, sessionId: TransportClientId, serverId: TransportClientId, providedOptions?: Partial<Options>);
38
39
  createNewConnection(to: string, attempt?: number): Promise<void>;
39
40
  close(): Promise<void>;
40
41
  destroy(): Promise<void>;
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  WebSocketClientTransport
3
- } from "../../../chunk-FTVAZZ6B.js";
3
+ } from "../../../chunk-KW5VFJRN.js";
4
4
  import "../../../chunk-L7D75G4K.js";
5
5
  import "../../../chunk-R6H2BIMC.js";
6
6
  import "../../../chunk-LQXPKF3A.js";
@@ -454,14 +454,15 @@ var WebSocketClientTransport = class extends Transport {
454
454
  reconnectPromises;
455
455
  tryReconnecting = true;
456
456
  /**
457
- * Creates a new WebSocketTransport instance.
457
+ * Creates a new WebSocketClientTransport instance.
458
458
  * @param wsGetter A function that returns a Promise that resolves to a WebSocket instance.
459
- * @param clientId The ID of the client using the transport.
459
+ * @param sessionId The ID of the client using the transport. This should be unique per session.
460
+ * @param serverId The ID of the server this transport is connecting to.
460
461
  * @param providedOptions An optional object containing configuration options for the transport.
461
462
  */
462
- constructor(wsGetter, clientId, serverId, providedOptions) {
463
+ constructor(wsGetter, sessionId, serverId, providedOptions) {
463
464
  const options = { ...defaultOptions, ...providedOptions };
464
- super(options.codec, clientId);
465
+ super(options.codec, sessionId);
465
466
  this.wsGetter = wsGetter;
466
467
  this.serverId = serverId;
467
468
  this.options = options;
@@ -482,23 +483,28 @@ var WebSocketClientTransport = class extends Transport {
482
483
  }
483
484
  reconnectPromise = new Promise(async (resolve) => {
484
485
  log?.info(`${this.clientId} -- establishing a new websocket to ${to}`);
485
- const ws = await this.wsGetter(to);
486
- if (ws.readyState === ws.OPEN) {
487
- return resolve({ ws });
488
- }
489
- if (ws.readyState === ws.CLOSING || ws.readyState === ws.CLOSED) {
490
- return resolve({ err: "ws is closing or closed" });
486
+ try {
487
+ const ws = await this.wsGetter(to);
488
+ if (ws.readyState === ws.OPEN) {
489
+ return resolve({ ws });
490
+ }
491
+ if (ws.readyState === ws.CLOSING || ws.readyState === ws.CLOSED) {
492
+ return resolve({ err: "ws is closing or closed" });
493
+ }
494
+ const onOpen = () => {
495
+ ws.removeEventListener("open", onOpen);
496
+ resolve({ ws });
497
+ };
498
+ const onClose = (evt) => {
499
+ ws.removeEventListener("close", onClose);
500
+ resolve({ err: evt.reason });
501
+ };
502
+ ws.addEventListener("open", onOpen);
503
+ ws.addEventListener("close", onClose);
504
+ } catch (e) {
505
+ const reason = e instanceof Error ? e.message : "unknown reason";
506
+ return resolve({ err: `couldn't get a new websocket: ${reason}` });
491
507
  }
492
- const onOpen = () => {
493
- ws.removeEventListener("open", onOpen);
494
- resolve({ ws });
495
- };
496
- const onClose = (evt) => {
497
- ws.removeEventListener("close", onClose);
498
- resolve({ err: evt.reason });
499
- };
500
- ws.addEventListener("open", onOpen);
501
- ws.addEventListener("close", onClose);
502
508
  });
503
509
  this.reconnectPromises.set(to, reconnectPromise);
504
510
  }
@@ -4,7 +4,7 @@ import {
4
4
  } from "../chunk-3JGVFWKQ.js";
5
5
  import {
6
6
  WebSocketClientTransport
7
- } from "../chunk-FTVAZZ6B.js";
7
+ } from "../chunk-KW5VFJRN.js";
8
8
  import {
9
9
  WebSocketServerTransport
10
10
  } from "../chunk-PJ2EUO7O.js";
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@replit/river",
3
3
  "description": "It's like tRPC but... with JSON Schema Support, duplex streaming and support for service multiplexing. Transport agnostic!",
4
- "version": "0.10.10",
4
+ "version": "0.10.11",
5
5
  "type": "module",
6
6
  "exports": {
7
7
  ".": {