@replit/river 0.16.1 → 0.17.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 (39) hide show
  1. package/README.md +25 -11
  2. package/dist/{chunk-Q6WPGM3K.js → chunk-7IQO434V.js} +11 -3
  3. package/dist/{chunk-7SPCAA6Q.js → chunk-LQMPJI3S.js} +101 -97
  4. package/dist/{chunk-GFRAOY75.js → chunk-VH3NGOXQ.js} +8 -17
  5. package/dist/{chunk-L65XWBX2.js → chunk-VJRLJ3JU.js} +1 -1
  6. package/dist/{chunk-XLJGKNV2.js → chunk-Y6DLSCKU.js} +1 -1
  7. package/dist/{connection-94896f3b.d.ts → connection-0767dc6b.d.ts} +1 -1
  8. package/dist/{connection-99346822.d.ts → connection-f31edbcd.d.ts} +1 -1
  9. package/dist/{index-2e402bb8.d.ts → index-8df0bdfb.d.ts} +27 -31
  10. package/dist/{procedures-f0226890.d.ts → procedures-b5ddb54d.d.ts} +1 -1
  11. package/dist/router/index.cjs +12 -4
  12. package/dist/router/index.d.cts +8 -4
  13. package/dist/router/index.d.ts +8 -4
  14. package/dist/router/index.js +2 -2
  15. package/dist/transport/impls/uds/client.cjs +80 -69
  16. package/dist/transport/impls/uds/client.d.cts +2 -2
  17. package/dist/transport/impls/uds/client.d.ts +2 -2
  18. package/dist/transport/impls/uds/client.js +3 -3
  19. package/dist/transport/impls/uds/server.cjs +82 -94
  20. package/dist/transport/impls/uds/server.d.cts +2 -2
  21. package/dist/transport/impls/uds/server.d.ts +2 -2
  22. package/dist/transport/impls/uds/server.js +3 -3
  23. package/dist/transport/impls/ws/client.cjs +80 -69
  24. package/dist/transport/impls/ws/client.d.cts +2 -2
  25. package/dist/transport/impls/ws/client.d.ts +2 -2
  26. package/dist/transport/impls/ws/client.js +3 -3
  27. package/dist/transport/impls/ws/server.cjs +82 -94
  28. package/dist/transport/impls/ws/server.d.cts +2 -2
  29. package/dist/transport/impls/ws/server.d.ts +2 -2
  30. package/dist/transport/impls/ws/server.js +3 -3
  31. package/dist/transport/index.cjs +107 -115
  32. package/dist/transport/index.d.cts +1 -1
  33. package/dist/transport/index.d.ts +1 -1
  34. package/dist/transport/index.js +2 -2
  35. package/dist/util/testHelpers.cjs +45 -18
  36. package/dist/util/testHelpers.d.cts +4 -9
  37. package/dist/util/testHelpers.d.ts +4 -9
  38. package/dist/util/testHelpers.js +8 -14
  39. package/package.json +1 -1
@@ -48,18 +48,18 @@ var ControlMessageAckSchema = import_typebox.Type.Object({
48
48
  var ControlMessageCloseSchema = import_typebox.Type.Object({
49
49
  type: import_typebox.Type.Literal("CLOSE")
50
50
  });
51
- var PROTOCOL_VERSION = "v1";
51
+ var PROTOCOL_VERSION = "v1.1";
52
52
  var ControlMessageHandshakeRequestSchema = import_typebox.Type.Object({
53
53
  type: import_typebox.Type.Literal("HANDSHAKE_REQ"),
54
54
  protocolVersion: import_typebox.Type.String(),
55
- instanceId: import_typebox.Type.String()
55
+ sessionId: import_typebox.Type.String()
56
56
  });
57
57
  var ControlMessageHandshakeResponseSchema = import_typebox.Type.Object({
58
58
  type: import_typebox.Type.Literal("HANDSHAKE_RESP"),
59
59
  status: import_typebox.Type.Union([
60
60
  import_typebox.Type.Object({
61
61
  ok: import_typebox.Type.Literal(true),
62
- instanceId: import_typebox.Type.String()
62
+ sessionId: import_typebox.Type.String()
63
63
  }),
64
64
  import_typebox.Type.Object({
65
65
  ok: import_typebox.Type.Literal(false),
@@ -76,7 +76,7 @@ var ControlMessagePayloadSchema = import_typebox.Type.Union([
76
76
  var OpaqueTransportMessageSchema = TransportMessageSchema(
77
77
  import_typebox.Type.Unknown()
78
78
  );
79
- function handshakeRequestMessage(from, to, instanceId) {
79
+ function handshakeRequestMessage(from, to, sessionId) {
80
80
  return {
81
81
  id: (0, import_nanoid.nanoid)(),
82
82
  from,
@@ -88,7 +88,7 @@ function handshakeRequestMessage(from, to, instanceId) {
88
88
  payload: {
89
89
  type: "HANDSHAKE_REQ",
90
90
  protocolVersion: PROTOCOL_VERSION,
91
- instanceId
91
+ sessionId
92
92
  }
93
93
  };
94
94
  }
@@ -103,7 +103,8 @@ var log;
103
103
  var ProtocolError = {
104
104
  RetriesExceeded: "conn_retry_exceeded",
105
105
  HandshakeFailed: "handshake_failed",
106
- UseAfterDestroy: "use_after_destroy"
106
+ UseAfterDestroy: "use_after_destroy",
107
+ MessageOrderingViolated: "message_ordering_violated"
107
108
  };
108
109
  var EventDispatcher = class {
109
110
  eventListeners = {};
@@ -158,7 +159,12 @@ var Session = class {
158
159
  /**
159
160
  * The unique ID of this session.
160
161
  */
161
- debugId;
162
+ id;
163
+ /**
164
+ * What the other side advertised as their session ID
165
+ * for this session.
166
+ */
167
+ advertisedSessionId;
162
168
  /**
163
169
  * Number of messages we've sent along this session (excluding handshake and acks)
164
170
  */
@@ -180,11 +186,11 @@ var Session = class {
180
186
  * The interval for sending heartbeats.
181
187
  */
182
188
  heartbeat;
183
- constructor(from, connectedTo, conn, options) {
189
+ constructor(conn, from, to, options) {
190
+ this.id = `session-${nanoid2(12)}`;
184
191
  this.options = options;
185
- this.debugId = `sess-${unsafeId()}`;
186
192
  this.from = from;
187
- this.to = connectedTo;
193
+ this.to = to;
188
194
  this.connection = conn;
189
195
  this.codec = options.codec;
190
196
  this.heartbeatMisses = 0;
@@ -224,7 +230,7 @@ var Session = class {
224
230
  log?.info(
225
231
  `${this.from} -- closing connection (id: ${this.connection.debugId}) to ${this.to} due to inactivity`
226
232
  );
227
- this.closeStaleConnection(this.connection);
233
+ this.closeStaleConnection();
228
234
  }
229
235
  return;
230
236
  }
@@ -255,33 +261,37 @@ var Session = class {
255
261
  log?.debug(`${this.from} -- resending ${msg.id} (seq: ${msg.seq})`);
256
262
  const ok = this.connection.send(this.codec.toBuffer(msg));
257
263
  if (!ok) {
258
- const msg2 = `${this.from} -- failed to send buffered message to ${this.to} in session (id: ${this.debugId}) (if you hit this code path something is seriously wrong)`;
264
+ const msg2 = `${this.from} -- failed to send buffered message to ${this.to} in session (id: ${this.id}) (if you hit this code path something is seriously wrong)`;
259
265
  log?.error(msg2);
260
266
  throw new Error(msg2);
261
267
  }
262
268
  }
263
269
  }
264
270
  updateBookkeeping(ack, seq) {
271
+ if (seq + 1 < this.ack) {
272
+ log?.error(`${this.from} -- received stale seq ${seq} + 1 < ${this.ack}`);
273
+ return;
274
+ }
265
275
  this.sendBuffer = this.sendBuffer.filter((unacked) => unacked.seq > ack);
266
276
  this.ack = seq + 1;
267
277
  }
268
278
  closeStaleConnection(conn) {
269
- if (!this.connection || this.connection !== conn)
279
+ if (this.connection === void 0 || this.connection === conn)
270
280
  return;
271
281
  log?.info(
272
- `${this.from} -- closing old inner connection (id: ${this.connection.debugId}) from session (id: ${this.debugId}) to ${this.to}`
282
+ `${this.from} -- closing old inner connection (id: ${this.connection.debugId}) from session (id: ${this.id}) to ${this.to}`
273
283
  );
274
284
  this.connection.close();
275
285
  this.connection = void 0;
276
286
  }
277
287
  replaceWithNewConnection(newConn) {
278
- this.closeStaleConnection(this.connection);
288
+ this.closeStaleConnection(newConn);
279
289
  this.cancelGrace();
280
290
  this.connection = newConn;
281
291
  }
282
292
  beginGrace(cb) {
283
293
  log?.info(
284
- `${this.from} -- starting ${this.options.sessionDisconnectGraceMs}ms grace period until session (id: ${this.debugId}) to ${this.to} is closed`
294
+ `${this.from} -- starting ${this.options.sessionDisconnectGraceMs}ms grace period until session (id: ${this.id}) to ${this.to} is closed`
285
295
  );
286
296
  this.disconnectionGrace = setTimeout(() => {
287
297
  this.close();
@@ -292,11 +302,12 @@ var Session = class {
292
302
  cancelGrace() {
293
303
  this.heartbeatMisses = 0;
294
304
  clearTimeout(this.disconnectionGrace);
305
+ this.disconnectionGrace = void 0;
295
306
  }
296
307
  // closed when we want to discard the whole session
297
308
  // (i.e. shutdown or session disconnect)
298
309
  close() {
299
- this.closeStaleConnection(this.connection);
310
+ this.closeStaleConnection();
300
311
  this.cancelGrace();
301
312
  this.resetBufferedMessages();
302
313
  clearInterval(this.heartbeat);
@@ -325,9 +336,6 @@ var Session = class {
325
336
  }
326
337
  };
327
338
 
328
- // transport/transport.ts
329
- var import_nanoid3 = require("nanoid");
330
-
331
339
  // util/stringify.ts
332
340
  function coerceErrorString(err) {
333
341
  if (err instanceof Error) {
@@ -474,17 +482,10 @@ var defaultConnectionRetryOptions = {
474
482
  budgetRestoreIntervalMs: 200
475
483
  };
476
484
  var defaultClientTransportOptions = {
477
- connectionRetryOptions: defaultConnectionRetryOptions,
478
- ...defaultTransportOptions
485
+ ...defaultTransportOptions,
486
+ ...defaultConnectionRetryOptions
479
487
  };
480
488
  var Transport = class {
481
- /**
482
- * Unique per instance of the transport.
483
- * This allows us to distinguish reconnects to different
484
- * transports.
485
- */
486
- instanceId = (0, import_nanoid3.nanoid)();
487
- connectedInstanceIds = /* @__PURE__ */ new Map();
488
489
  /**
489
490
  * A flag indicating whether the transport has been destroyed.
490
491
  * A destroyed transport will not attempt to reconnect and cannot be used again.
@@ -537,41 +538,41 @@ var Transport = class {
537
538
  * and we know the identity of the connected client.
538
539
  * @param conn The connection object.
539
540
  */
540
- onConnect(conn, connectedTo, instanceId) {
541
+ onConnect(conn, connectedTo, advertisedSessionId) {
541
542
  this.eventDispatcher.dispatchEvent("connectionStatus", {
542
543
  status: "connect",
543
544
  conn
544
545
  });
545
546
  let oldSession = this.sessions.get(connectedTo);
546
- const lastInstanceId = this.connectedInstanceIds.get(connectedTo);
547
- if (oldSession && lastInstanceId !== void 0 && lastInstanceId !== instanceId) {
547
+ if (oldSession?.advertisedSessionId && oldSession.advertisedSessionId !== advertisedSessionId) {
548
548
  log?.warn(
549
- `${this.clientId} -- connection from ${connectedTo} is a different instance (got: ${instanceId}, last connected to: ${lastInstanceId}), starting a new session`
549
+ `${this.clientId} -- connection from ${connectedTo} is a different session (id: ${advertisedSessionId}, last connected to: ${oldSession.advertisedSessionId}), starting a new session`
550
550
  );
551
551
  oldSession.close();
552
552
  this.deleteSession(oldSession);
553
553
  oldSession = void 0;
554
554
  }
555
- this.connectedInstanceIds.set(connectedTo, instanceId);
556
555
  if (oldSession === void 0) {
557
556
  const newSession = this.createSession(connectedTo, conn);
557
+ newSession.advertisedSessionId = advertisedSessionId;
558
558
  log?.info(
559
- `${this.clientId} -- new connection (id: ${conn.debugId}) for new session (id: ${newSession.debugId}) to ${connectedTo}`
559
+ `${this.clientId} -- new connection (id: ${conn.debugId}) for new session (id: ${newSession.id}) to ${connectedTo}`
560
560
  );
561
561
  return newSession;
562
562
  }
563
563
  log?.info(
564
- `${this.clientId} -- new connection (id: ${conn.debugId}) for existing session (id: ${oldSession.debugId}) to ${connectedTo}`
564
+ `${this.clientId} -- new connection (id: ${conn.debugId}) for existing session (id: ${oldSession.id}) to ${connectedTo}`
565
565
  );
566
566
  oldSession.replaceWithNewConnection(conn);
567
567
  oldSession.sendBufferedMessages();
568
+ oldSession.advertisedSessionId = advertisedSessionId;
568
569
  return oldSession;
569
570
  }
570
- createSession(connectedTo, conn) {
571
+ createSession(to, conn) {
571
572
  const session = new Session(
572
- this.clientId,
573
- connectedTo,
574
573
  conn,
574
+ this.clientId,
575
+ to,
575
576
  this.options
576
577
  );
577
578
  this.sessions.set(session.to, session);
@@ -581,10 +582,20 @@ var Transport = class {
581
582
  });
582
583
  return session;
583
584
  }
585
+ getOrCreateSession(to, conn) {
586
+ let session = this.sessions.get(to);
587
+ if (!session) {
588
+ session = this.createSession(to, conn);
589
+ log?.info(
590
+ `${this.clientId} -- no session for ${to}, created a new one (id: ${session.id})`
591
+ );
592
+ }
593
+ return session;
594
+ }
584
595
  deleteSession(session) {
585
596
  this.sessions.delete(session.to);
586
597
  log?.info(
587
- `${this.clientId} -- session ${session.debugId} disconnect from ${session.to}`
598
+ `${this.clientId} -- session ${session.id} disconnect from ${session.to}`
588
599
  );
589
600
  this.eventDispatcher.dispatchEvent("sessionStatus", {
590
601
  status: "disconnect",
@@ -652,11 +663,14 @@ var Transport = class {
652
663
  )}`
653
664
  );
654
665
  } else {
666
+ const errMsg = `received out-of-order msg (got seq: ${msg.seq}, wanted seq: ${session.nextExpectedSeq})`;
655
667
  log?.error(
656
- `${this.clientId} -- received out-of-order msg (got: ${msg.seq}, wanted: ${session.nextExpectedSeq}), marking connection as dead: ${JSON.stringify(msg)}`
668
+ `${this.clientId} -- fatal: ${errMsg}, marking connection as dead: ${JSON.stringify(
669
+ msg
670
+ )}`
657
671
  );
672
+ this.protocolError(ProtocolError.MessageOrderingViolated, errMsg);
658
673
  session.close();
659
- this.deleteSession(session);
660
674
  }
661
675
  return;
662
676
  }
@@ -703,14 +717,7 @@ var Transport = class {
703
717
  );
704
718
  return void 0;
705
719
  }
706
- let session = this.sessions.get(to);
707
- if (!session) {
708
- session = this.createSession(to, void 0);
709
- log?.info(
710
- `${this.clientId} -- no session for ${to}, created a new one (id: ${session.debugId})`
711
- );
712
- }
713
- return session.send(msg);
720
+ return this.getOrCreateSession(to).send(msg);
714
721
  }
715
722
  // control helpers
716
723
  sendCloseStream(to, streamId) {
@@ -762,7 +769,13 @@ var ClientTransport = class extends Transport {
762
769
  */
763
770
  inflightConnectionPromises;
764
771
  retryBudget;
765
- tryReconnecting = true;
772
+ /**
773
+ * A flag indicating whether the transport should automatically reconnect
774
+ * when a connection is dropped.
775
+ * Realistically, this should always be true for clients unless you are writing
776
+ * tests or a special case where you don't want to reconnect.
777
+ */
778
+ reconnectOnConnectionDrop = true;
766
779
  constructor(clientId, providedOptions) {
767
780
  super(clientId, providedOptions);
768
781
  this.options = {
@@ -770,21 +783,20 @@ var ClientTransport = class extends Transport {
770
783
  ...providedOptions
771
784
  };
772
785
  this.inflightConnectionPromises = /* @__PURE__ */ new Map();
773
- this.retryBudget = new LeakyBucketRateLimit(
774
- this.options.connectionRetryOptions
775
- );
786
+ this.retryBudget = new LeakyBucketRateLimit(this.options);
776
787
  }
777
788
  handleConnection(conn, to) {
778
789
  if (this.state !== "open")
779
790
  return;
780
791
  let session = void 0;
781
792
  const handshakeHandler = (data) => {
782
- const handshake = this.receiveHandshakeResponseMessage(data);
783
- if (!handshake) {
793
+ const maybeSession = this.receiveHandshakeResponseMessage(data, conn);
794
+ if (!maybeSession) {
784
795
  conn.close();
785
796
  return;
797
+ } else {
798
+ session = maybeSession;
786
799
  }
787
- session = this.onConnect(conn, handshake.from, handshake.instanceId);
788
800
  conn.removeDataListener(handshakeHandler);
789
801
  conn.addDataListener((data2) => {
790
802
  const parsed = this.parseMsg(data2);
@@ -804,7 +816,7 @@ var ClientTransport = class extends Transport {
804
816
  `${this.clientId} -- connection (id: ${conn.debugId}) to ${to} disconnected`
805
817
  );
806
818
  this.inflightConnectionPromises.delete(to);
807
- if (this.tryReconnecting) {
819
+ if (this.reconnectOnConnectionDrop) {
808
820
  void this.connect(to);
809
821
  }
810
822
  });
@@ -814,7 +826,7 @@ var ClientTransport = class extends Transport {
814
826
  );
815
827
  });
816
828
  }
817
- receiveHandshakeResponseMessage(data) {
829
+ receiveHandshakeResponseMessage(data, conn) {
818
830
  const parsed = this.parseMsg(data);
819
831
  if (!parsed) {
820
832
  this.protocolError(
@@ -847,12 +859,14 @@ var ClientTransport = class extends Transport {
847
859
  );
848
860
  return false;
849
861
  }
850
- const instanceId = parsed.payload.status.instanceId;
851
- log?.debug(
852
- `${this.clientId} -- handshake from ${parsed.from} ok (instance: ${instanceId})`
862
+ log?.debug(`${this.clientId} -- handshake from ${parsed.from} ok`);
863
+ const session = this.onConnect(
864
+ conn,
865
+ parsed.from,
866
+ parsed.payload.status.sessionId
853
867
  );
854
868
  this.retryBudget.startRestoringBudget(parsed.from);
855
- return { instanceId, from: parsed.from };
869
+ return session;
856
870
  }
857
871
  /**
858
872
  * Manually attempts to connect to a client.
@@ -910,7 +924,7 @@ var ClientTransport = class extends Transport {
910
924
  } catch (error) {
911
925
  this.inflightConnectionPromises.delete(to);
912
926
  const errStr = coerceErrorString(error);
913
- if (!this.tryReconnecting || !canProceedWithConnection()) {
927
+ if (!this.reconnectOnConnectionDrop || !canProceedWithConnection()) {
914
928
  log?.warn(`${this.clientId} -- connection to ${to} failed (${errStr})`);
915
929
  } else {
916
930
  log?.warn(
@@ -921,11 +935,8 @@ var ClientTransport = class extends Transport {
921
935
  }
922
936
  }
923
937
  sendHandshake(to, conn) {
924
- const requestMsg = handshakeRequestMessage(
925
- this.clientId,
926
- to,
927
- this.instanceId
928
- );
938
+ const session = this.getOrCreateSession(to, conn);
939
+ const requestMsg = handshakeRequestMessage(this.clientId, to, session.id);
929
940
  log?.debug(`${this.clientId} -- sending handshake request to ${to}`);
930
941
  conn.send(this.codec.toBuffer(requestMsg));
931
942
  }
@@ -1,6 +1,6 @@
1
1
  import WebSocket from 'isomorphic-ws';
2
- import { a as ClientTransport, b as TransportClientId, c as ProvidedClientTransportOptions } from '../../../index-2e402bb8.js';
3
- import { W as WebSocketConnection } from '../../../connection-99346822.js';
2
+ import { a as ClientTransport, b as TransportClientId, c as ProvidedClientTransportOptions } from '../../../index-8df0bdfb.js';
3
+ import { W as WebSocketConnection } from '../../../connection-f31edbcd.js';
4
4
  import '../../../types-3e5768ec.js';
5
5
  import '@sinclair/typebox';
6
6
 
@@ -1,6 +1,6 @@
1
1
  import WebSocket from 'isomorphic-ws';
2
- import { a as ClientTransport, b as TransportClientId, c as ProvidedClientTransportOptions } from '../../../index-2e402bb8.js';
3
- import { W as WebSocketConnection } from '../../../connection-99346822.js';
2
+ import { a as ClientTransport, b as TransportClientId, c as ProvidedClientTransportOptions } from '../../../index-8df0bdfb.js';
3
+ import { W as WebSocketConnection } from '../../../connection-f31edbcd.js';
4
4
  import '../../../types-3e5768ec.js';
5
5
  import '@sinclair/typebox';
6
6
 
@@ -1,10 +1,10 @@
1
1
  import {
2
2
  WebSocketConnection
3
- } from "../../../chunk-XLJGKNV2.js";
3
+ } from "../../../chunk-Y6DLSCKU.js";
4
4
  import {
5
5
  ClientTransport
6
- } from "../../../chunk-7SPCAA6Q.js";
7
- import "../../../chunk-GFRAOY75.js";
6
+ } from "../../../chunk-LQMPJI3S.js";
7
+ import "../../../chunk-VH3NGOXQ.js";
8
8
  import {
9
9
  log
10
10
  } from "../../../chunk-H4BYJELI.js";