@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
@@ -170,7 +170,12 @@ declare class Session<ConnType extends Connection> {
170
170
  /**
171
171
  * The unique ID of this session.
172
172
  */
173
- debugId: string;
173
+ id: string;
174
+ /**
175
+ * What the other side advertised as their session ID
176
+ * for this session.
177
+ */
178
+ advertisedSessionId?: string;
174
179
  /**
175
180
  * Number of messages we've sent along this session (excluding handshake and acks)
176
181
  */
@@ -191,8 +196,8 @@ declare class Session<ConnType extends Connection> {
191
196
  /**
192
197
  * The interval for sending heartbeats.
193
198
  */
194
- private heartbeat?;
195
- constructor(from: TransportClientId, connectedTo: TransportClientId, conn: ConnType | undefined, options: SessionOptions);
199
+ private heartbeat;
200
+ constructor(conn: ConnType | undefined, from: TransportClientId, to: TransportClientId, options: SessionOptions);
196
201
  /**
197
202
  * Sends a message over the session's connection.
198
203
  * If the connection is not ready or the message fails to send, the message can be buffered for retry unless skipped.
@@ -222,6 +227,7 @@ declare const ProtocolError: {
222
227
  readonly RetriesExceeded: "conn_retry_exceeded";
223
228
  readonly HandshakeFailed: "handshake_failed";
224
229
  readonly UseAfterDestroy: "use_after_destroy";
230
+ readonly MessageOrderingViolated: "message_ordering_violated";
225
231
  };
226
232
  type ProtocolErrorType = (typeof ProtocolError)[keyof typeof ProtocolError];
227
233
  interface EventMap {
@@ -308,14 +314,10 @@ declare class LeakyBucketRateLimit {
308
314
  * @property {'destroyed'} destroyed - The transport is permanently destroyed and cannot be reopened.
309
315
  */
310
316
  type TransportStatus = 'open' | 'closed' | 'destroyed';
311
- type ProvidedTransportOptions = Partial<SessionOptions>;
312
- type TransportOptions = Required<ProvidedTransportOptions>;
313
- type ProvidedClientTransportOptions = {
314
- connectionRetryOptions?: Partial<ConnectionRetryOptions>;
315
- } & ProvidedTransportOptions;
316
- type ClientTransportOptions = SessionOptions & {
317
- connectionRetryOptions: ConnectionRetryOptions;
318
- };
317
+ type TransportOptions = SessionOptions;
318
+ type ProvidedTransportOptions = Partial<TransportOptions>;
319
+ type ProvidedClientTransportOptions = Partial<ClientTransportOptions>;
320
+ type ClientTransportOptions = SessionOptions & ConnectionRetryOptions;
319
321
  /**
320
322
  * Transports manage the lifecycle (creation/deletion) of sessions and connections. Its responsibilities include:
321
323
  *
@@ -347,13 +349,6 @@ type ClientTransportOptions = SessionOptions & {
347
349
  * @abstract
348
350
  */
349
351
  declare abstract class Transport<ConnType extends Connection> {
350
- /**
351
- * Unique per instance of the transport.
352
- * This allows us to distinguish reconnects to different
353
- * transports.
354
- */
355
- instanceId: string;
356
- connectedInstanceIds: Map<string, string>;
357
352
  /**
358
353
  * A flag indicating whether the transport has been destroyed.
359
354
  * A destroyed transport will not attempt to reconnect and cannot be used again.
@@ -404,8 +399,9 @@ declare abstract class Transport<ConnType extends Connection> {
404
399
  * and we know the identity of the connected client.
405
400
  * @param conn The connection object.
406
401
  */
407
- protected onConnect(conn: ConnType, connectedTo: TransportClientId, instanceId: string): Session<ConnType>;
408
- private createSession;
402
+ protected onConnect(conn: ConnType, connectedTo: TransportClientId, advertisedSessionId: string): Session<ConnType>;
403
+ protected createSession(to: TransportClientId, conn?: ConnType): Session<ConnType>;
404
+ protected getOrCreateSession(to: TransportClientId, conn?: ConnType): Session<ConnType>;
409
405
  protected deleteSession(session: Session<ConnType>): void;
410
406
  /**
411
407
  * The downstream implementation needs to call this when a connection is closed.
@@ -469,13 +465,16 @@ declare abstract class ClientTransport<ConnType extends Connection> extends Tran
469
465
  */
470
466
  inflightConnectionPromises: Map<TransportClientId, Promise<ConnType>>;
471
467
  retryBudget: LeakyBucketRateLimit;
472
- tryReconnecting: boolean;
473
- constructor(clientId: TransportClientId, providedOptions?: ProvidedTransportOptions);
468
+ /**
469
+ * A flag indicating whether the transport should automatically reconnect
470
+ * when a connection is dropped.
471
+ * Realistically, this should always be true for clients unless you are writing
472
+ * tests or a special case where you don't want to reconnect.
473
+ */
474
+ reconnectOnConnectionDrop: boolean;
475
+ constructor(clientId: TransportClientId, providedOptions?: ProvidedClientTransportOptions);
474
476
  protected handleConnection(conn: ConnType, to: TransportClientId): void;
475
- receiveHandshakeResponseMessage(data: Uint8Array): false | {
476
- instanceId: string;
477
- from: string;
478
- };
477
+ receiveHandshakeResponseMessage(data: Uint8Array, conn: ConnType): Session<ConnType> | false;
479
478
  /**
480
479
  * Abstract method that creates a new {@link Connection} object.
481
480
  * This should call {@link handleConnection} when the connection is created.
@@ -496,10 +495,7 @@ declare abstract class ClientTransport<ConnType extends Connection> extends Tran
496
495
  declare abstract class ServerTransport<ConnType extends Connection> extends Transport<ConnType> {
497
496
  constructor(clientId: TransportClientId, providedOptions?: ProvidedTransportOptions);
498
497
  protected handleConnection(conn: ConnType): void;
499
- receiveHandshakeRequestMessage(data: Uint8Array, conn: ConnType): false | {
500
- instanceId: string;
501
- from: string;
502
- };
498
+ receiveHandshakeRequestMessage(data: Uint8Array, conn: ConnType): Session<ConnType> | false;
503
499
  }
504
500
 
505
- export { Connection as C, EventMap as E, OpaqueTransportMessage as O, PartialTransportMessage as P, ServerTransport as S, Transport as T, ClientTransport as a, TransportClientId as b, ProvidedClientTransportOptions as c, ProvidedTransportOptions as d, Session as e, TransportStatus as f, TransportMessageSchema as g, OpaqueTransportMessageSchema as h, TransportMessage as i, isStreamOpen as j, isStreamClose as k, EventTypes as l, EventHandler as m, ProtocolError as n, ProtocolErrorType as o };
501
+ export { Connection as C, EventMap as E, OpaqueTransportMessage as O, PartialTransportMessage as P, SessionOptions as S, Transport as T, ClientTransport as a, TransportClientId as b, ProvidedClientTransportOptions as c, ServerTransport as d, ProvidedTransportOptions as e, Session as f, TransportStatus as g, TransportMessageSchema as h, OpaqueTransportMessageSchema as i, TransportMessage as j, isStreamOpen as k, isStreamClose as l, EventTypes as m, EventHandler as n, ProtocolError as o, ProtocolErrorType as p };
@@ -1,6 +1,6 @@
1
1
  import { TObject, TUnion, TString, TSchema, TNever, TLiteral, Static } from '@sinclair/typebox';
2
2
  import { Pushable } from 'it-pushable';
3
- import { b as TransportClientId, e as Session, C as Connection } from './index-2e402bb8.js';
3
+ import { b as TransportClientId, f as Session, C as Connection } from './index-8df0bdfb.js';
4
4
 
5
5
  type TLiteralString = TLiteral<string>;
6
6
  type RiverErrorSchema = TObject<{
@@ -567,14 +567,14 @@ var ControlMessageCloseSchema = import_typebox3.Type.Object({
567
567
  var ControlMessageHandshakeRequestSchema = import_typebox3.Type.Object({
568
568
  type: import_typebox3.Type.Literal("HANDSHAKE_REQ"),
569
569
  protocolVersion: import_typebox3.Type.String(),
570
- instanceId: import_typebox3.Type.String()
570
+ sessionId: import_typebox3.Type.String()
571
571
  });
572
572
  var ControlMessageHandshakeResponseSchema = import_typebox3.Type.Object({
573
573
  type: import_typebox3.Type.Literal("HANDSHAKE_RESP"),
574
574
  status: import_typebox3.Type.Union([
575
575
  import_typebox3.Type.Object({
576
576
  ok: import_typebox3.Type.Literal(true),
577
- instanceId: import_typebox3.Type.String()
577
+ sessionId: import_typebox3.Type.String()
578
578
  }),
579
579
  import_typebox3.Type.Object({
580
580
  ok: import_typebox3.Type.Literal(false),
@@ -655,8 +655,13 @@ function _createRecursiveProxy(callback, path) {
655
655
  });
656
656
  return proxy;
657
657
  }
658
- var createClient = (transport, serverId, eagerlyConnect = true) => {
659
- if (eagerlyConnect) {
658
+ var defaultClientOptions = {
659
+ connectOnInvoke: true,
660
+ eagerlyConnect: true
661
+ };
662
+ var createClient = (transport, serverId, providedClientOptions = {}) => {
663
+ const options = { ...defaultClientOptions, ...providedClientOptions };
664
+ if (options.eagerlyConnect) {
660
665
  void transport.connect(serverId);
661
666
  }
662
667
  return _createRecursiveProxy(async (opts) => {
@@ -672,6 +677,9 @@ var createClient = (transport, serverId, eagerlyConnect = true) => {
672
677
  input
673
678
  )}`
674
679
  );
680
+ if (options.connectOnInvoke && !transport.connections.has(serverId)) {
681
+ void transport.connect(serverId);
682
+ }
675
683
  if (procType === "rpc") {
676
684
  return handleRpc(
677
685
  transport,
@@ -1,7 +1,7 @@
1
1
  import { TObject, TUnion, Static } from '@sinclair/typebox';
2
- import { e as ProcedureMap, c as RiverUncaughtSchema, U as Unbranded, B as Branded, A as AnyProcedure, P as PayloadType, b as Result, R as RiverError, S as ServiceContext } from '../procedures-f0226890.js';
3
- export { E as Err, O as Ok, a as Procedure, d as ProcedureResult, f as RPCProcedure, m as RiverErrorSchema, j as ServiceContextWithState, k as ServiceContextWithTransportInfo, i as StreamProcedure, h as SubscriptionProcedure, l as UNCAUGHT_ERROR, g as UploadProcedure, V as ValidProcType } from '../procedures-f0226890.js';
4
- import { S as ServerTransport, C as Connection, a as ClientTransport, b as TransportClientId } from '../index-2e402bb8.js';
2
+ import { e as ProcedureMap, c as RiverUncaughtSchema, U as Unbranded, B as Branded, A as AnyProcedure, P as PayloadType, b as Result, R as RiverError, S as ServiceContext } from '../procedures-b5ddb54d.js';
3
+ export { E as Err, O as Ok, a as Procedure, d as ProcedureResult, f as RPCProcedure, m as RiverErrorSchema, j as ServiceContextWithState, k as ServiceContextWithTransportInfo, i as StreamProcedure, h as SubscriptionProcedure, l as UNCAUGHT_ERROR, g as UploadProcedure, V as ValidProcType } from '../procedures-b5ddb54d.js';
4
+ import { d as ServerTransport, C as Connection, a as ClientTransport, b as TransportClientId } from '../index-8df0bdfb.js';
5
5
  import { Pushable } from 'it-pushable';
6
6
  import '../types-3e5768ec.js';
7
7
 
@@ -381,6 +381,10 @@ type ServiceClient<Router extends AnyService> = {
381
381
  type ServerClient<Srv extends Server<ServiceSchemaMap>> = {
382
382
  [SvcName in keyof Srv['services']]: ServiceClient<Srv['services'][SvcName]>;
383
383
  };
384
+ interface ClientOptions {
385
+ connectOnInvoke: boolean;
386
+ eagerlyConnect: boolean;
387
+ }
384
388
  /**
385
389
  * Creates a client for a given server using the provided transport.
386
390
  * Note that the client only needs the type of the server, not the actual
@@ -394,6 +398,6 @@ type ServerClient<Srv extends Server<ServiceSchemaMap>> = {
394
398
  * @param {Transport} transport - The transport to use for communication.
395
399
  * @returns The client for the server.
396
400
  */
397
- declare const createClient: <Srv extends Server<ServiceSchemaMap>>(transport: ClientTransport<Connection>, serverId: TransportClientId, eagerlyConnect?: boolean) => ServerClient<Srv>;
401
+ declare const createClient: <Srv extends Server<ServiceSchemaMap>>(transport: ClientTransport<Connection>, serverId: TransportClientId, providedClientOptions?: Partial<ClientOptions>) => ServerClient<Srv>;
398
402
 
399
403
  export { PayloadType, ProcErrors, ProcHandler, ProcInit, ProcInput, ProcOutput, ProcType, ProcedureMap, Result, RiverError, RiverUncaughtSchema, Server, ServerClient, Service, ServiceConfiguration, ServiceContext, ServiceSchema, createClient, createServer };
@@ -1,7 +1,7 @@
1
1
  import { TObject, TUnion, Static } from '@sinclair/typebox';
2
- import { e as ProcedureMap, c as RiverUncaughtSchema, U as Unbranded, B as Branded, A as AnyProcedure, P as PayloadType, b as Result, R as RiverError, S as ServiceContext } from '../procedures-f0226890.js';
3
- export { E as Err, O as Ok, a as Procedure, d as ProcedureResult, f as RPCProcedure, m as RiverErrorSchema, j as ServiceContextWithState, k as ServiceContextWithTransportInfo, i as StreamProcedure, h as SubscriptionProcedure, l as UNCAUGHT_ERROR, g as UploadProcedure, V as ValidProcType } from '../procedures-f0226890.js';
4
- import { S as ServerTransport, C as Connection, a as ClientTransport, b as TransportClientId } from '../index-2e402bb8.js';
2
+ import { e as ProcedureMap, c as RiverUncaughtSchema, U as Unbranded, B as Branded, A as AnyProcedure, P as PayloadType, b as Result, R as RiverError, S as ServiceContext } from '../procedures-b5ddb54d.js';
3
+ export { E as Err, O as Ok, a as Procedure, d as ProcedureResult, f as RPCProcedure, m as RiverErrorSchema, j as ServiceContextWithState, k as ServiceContextWithTransportInfo, i as StreamProcedure, h as SubscriptionProcedure, l as UNCAUGHT_ERROR, g as UploadProcedure, V as ValidProcType } from '../procedures-b5ddb54d.js';
4
+ import { d as ServerTransport, C as Connection, a as ClientTransport, b as TransportClientId } from '../index-8df0bdfb.js';
5
5
  import { Pushable } from 'it-pushable';
6
6
  import '../types-3e5768ec.js';
7
7
 
@@ -381,6 +381,10 @@ type ServiceClient<Router extends AnyService> = {
381
381
  type ServerClient<Srv extends Server<ServiceSchemaMap>> = {
382
382
  [SvcName in keyof Srv['services']]: ServiceClient<Srv['services'][SvcName]>;
383
383
  };
384
+ interface ClientOptions {
385
+ connectOnInvoke: boolean;
386
+ eagerlyConnect: boolean;
387
+ }
384
388
  /**
385
389
  * Creates a client for a given server using the provided transport.
386
390
  * Note that the client only needs the type of the server, not the actual
@@ -394,6 +398,6 @@ type ServerClient<Srv extends Server<ServiceSchemaMap>> = {
394
398
  * @param {Transport} transport - The transport to use for communication.
395
399
  * @returns The client for the server.
396
400
  */
397
- declare const createClient: <Srv extends Server<ServiceSchemaMap>>(transport: ClientTransport<Connection>, serverId: TransportClientId, eagerlyConnect?: boolean) => ServerClient<Srv>;
401
+ declare const createClient: <Srv extends Server<ServiceSchemaMap>>(transport: ClientTransport<Connection>, serverId: TransportClientId, providedClientOptions?: Partial<ClientOptions>) => ServerClient<Srv>;
398
402
 
399
403
  export { PayloadType, ProcErrors, ProcHandler, ProcInit, ProcInput, ProcOutput, ProcType, ProcedureMap, Result, RiverError, RiverUncaughtSchema, Server, ServerClient, Service, ServiceConfiguration, ServiceContext, ServiceSchema, createClient, createServer };
@@ -7,8 +7,8 @@ import {
7
7
  UNCAUGHT_ERROR,
8
8
  createClient,
9
9
  createServer
10
- } from "../chunk-Q6WPGM3K.js";
11
- import "../chunk-GFRAOY75.js";
10
+ } from "../chunk-7IQO434V.js";
11
+ import "../chunk-VH3NGOXQ.js";
12
12
  import "../chunk-H4BYJELI.js";
13
13
  export {
14
14
  Err,
@@ -52,18 +52,18 @@ var ControlMessageAckSchema = import_typebox.Type.Object({
52
52
  var ControlMessageCloseSchema = import_typebox.Type.Object({
53
53
  type: import_typebox.Type.Literal("CLOSE")
54
54
  });
55
- var PROTOCOL_VERSION = "v1";
55
+ var PROTOCOL_VERSION = "v1.1";
56
56
  var ControlMessageHandshakeRequestSchema = import_typebox.Type.Object({
57
57
  type: import_typebox.Type.Literal("HANDSHAKE_REQ"),
58
58
  protocolVersion: import_typebox.Type.String(),
59
- instanceId: import_typebox.Type.String()
59
+ sessionId: import_typebox.Type.String()
60
60
  });
61
61
  var ControlMessageHandshakeResponseSchema = import_typebox.Type.Object({
62
62
  type: import_typebox.Type.Literal("HANDSHAKE_RESP"),
63
63
  status: import_typebox.Type.Union([
64
64
  import_typebox.Type.Object({
65
65
  ok: import_typebox.Type.Literal(true),
66
- instanceId: import_typebox.Type.String()
66
+ sessionId: import_typebox.Type.String()
67
67
  }),
68
68
  import_typebox.Type.Object({
69
69
  ok: import_typebox.Type.Literal(false),
@@ -80,7 +80,7 @@ var ControlMessagePayloadSchema = import_typebox.Type.Union([
80
80
  var OpaqueTransportMessageSchema = TransportMessageSchema(
81
81
  import_typebox.Type.Unknown()
82
82
  );
83
- function handshakeRequestMessage(from, to, instanceId) {
83
+ function handshakeRequestMessage(from, to, sessionId) {
84
84
  return {
85
85
  id: (0, import_nanoid.nanoid)(),
86
86
  from,
@@ -92,7 +92,7 @@ function handshakeRequestMessage(from, to, instanceId) {
92
92
  payload: {
93
93
  type: "HANDSHAKE_REQ",
94
94
  protocolVersion: PROTOCOL_VERSION,
95
- instanceId
95
+ sessionId
96
96
  }
97
97
  };
98
98
  }
@@ -125,7 +125,12 @@ var Session = class {
125
125
  /**
126
126
  * The unique ID of this session.
127
127
  */
128
- debugId;
128
+ id;
129
+ /**
130
+ * What the other side advertised as their session ID
131
+ * for this session.
132
+ */
133
+ advertisedSessionId;
129
134
  /**
130
135
  * Number of messages we've sent along this session (excluding handshake and acks)
131
136
  */
@@ -147,11 +152,11 @@ var Session = class {
147
152
  * The interval for sending heartbeats.
148
153
  */
149
154
  heartbeat;
150
- constructor(from, connectedTo, conn, options) {
155
+ constructor(conn, from, to, options) {
156
+ this.id = `session-${nanoid2(12)}`;
151
157
  this.options = options;
152
- this.debugId = `sess-${unsafeId()}`;
153
158
  this.from = from;
154
- this.to = connectedTo;
159
+ this.to = to;
155
160
  this.connection = conn;
156
161
  this.codec = options.codec;
157
162
  this.heartbeatMisses = 0;
@@ -191,7 +196,7 @@ var Session = class {
191
196
  log?.info(
192
197
  `${this.from} -- closing connection (id: ${this.connection.debugId}) to ${this.to} due to inactivity`
193
198
  );
194
- this.closeStaleConnection(this.connection);
199
+ this.closeStaleConnection();
195
200
  }
196
201
  return;
197
202
  }
@@ -222,33 +227,37 @@ var Session = class {
222
227
  log?.debug(`${this.from} -- resending ${msg.id} (seq: ${msg.seq})`);
223
228
  const ok = this.connection.send(this.codec.toBuffer(msg));
224
229
  if (!ok) {
225
- 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)`;
230
+ 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)`;
226
231
  log?.error(msg2);
227
232
  throw new Error(msg2);
228
233
  }
229
234
  }
230
235
  }
231
236
  updateBookkeeping(ack, seq) {
237
+ if (seq + 1 < this.ack) {
238
+ log?.error(`${this.from} -- received stale seq ${seq} + 1 < ${this.ack}`);
239
+ return;
240
+ }
232
241
  this.sendBuffer = this.sendBuffer.filter((unacked) => unacked.seq > ack);
233
242
  this.ack = seq + 1;
234
243
  }
235
244
  closeStaleConnection(conn) {
236
- if (!this.connection || this.connection !== conn)
245
+ if (this.connection === void 0 || this.connection === conn)
237
246
  return;
238
247
  log?.info(
239
- `${this.from} -- closing old inner connection (id: ${this.connection.debugId}) from session (id: ${this.debugId}) to ${this.to}`
248
+ `${this.from} -- closing old inner connection (id: ${this.connection.debugId}) from session (id: ${this.id}) to ${this.to}`
240
249
  );
241
250
  this.connection.close();
242
251
  this.connection = void 0;
243
252
  }
244
253
  replaceWithNewConnection(newConn) {
245
- this.closeStaleConnection(this.connection);
254
+ this.closeStaleConnection(newConn);
246
255
  this.cancelGrace();
247
256
  this.connection = newConn;
248
257
  }
249
258
  beginGrace(cb) {
250
259
  log?.info(
251
- `${this.from} -- starting ${this.options.sessionDisconnectGraceMs}ms grace period until session (id: ${this.debugId}) to ${this.to} is closed`
260
+ `${this.from} -- starting ${this.options.sessionDisconnectGraceMs}ms grace period until session (id: ${this.id}) to ${this.to} is closed`
252
261
  );
253
262
  this.disconnectionGrace = setTimeout(() => {
254
263
  this.close();
@@ -259,11 +268,12 @@ var Session = class {
259
268
  cancelGrace() {
260
269
  this.heartbeatMisses = 0;
261
270
  clearTimeout(this.disconnectionGrace);
271
+ this.disconnectionGrace = void 0;
262
272
  }
263
273
  // closed when we want to discard the whole session
264
274
  // (i.e. shutdown or session disconnect)
265
275
  close() {
266
- this.closeStaleConnection(this.connection);
276
+ this.closeStaleConnection();
267
277
  this.cancelGrace();
268
278
  this.resetBufferedMessages();
269
279
  clearInterval(this.heartbeat);
@@ -392,7 +402,8 @@ var import_value = require("@sinclair/typebox/value");
392
402
  var ProtocolError = {
393
403
  RetriesExceeded: "conn_retry_exceeded",
394
404
  HandshakeFailed: "handshake_failed",
395
- UseAfterDestroy: "use_after_destroy"
405
+ UseAfterDestroy: "use_after_destroy",
406
+ MessageOrderingViolated: "message_ordering_violated"
396
407
  };
397
408
  var EventDispatcher = class {
398
409
  eventListeners = {};
@@ -421,9 +432,6 @@ var EventDispatcher = class {
421
432
  }
422
433
  };
423
434
 
424
- // transport/transport.ts
425
- var import_nanoid3 = require("nanoid");
426
-
427
435
  // util/stringify.ts
428
436
  function coerceErrorString(err) {
429
437
  if (err instanceof Error) {
@@ -570,17 +578,10 @@ var defaultConnectionRetryOptions = {
570
578
  budgetRestoreIntervalMs: 200
571
579
  };
572
580
  var defaultClientTransportOptions = {
573
- connectionRetryOptions: defaultConnectionRetryOptions,
574
- ...defaultTransportOptions
581
+ ...defaultTransportOptions,
582
+ ...defaultConnectionRetryOptions
575
583
  };
576
584
  var Transport = class {
577
- /**
578
- * Unique per instance of the transport.
579
- * This allows us to distinguish reconnects to different
580
- * transports.
581
- */
582
- instanceId = (0, import_nanoid3.nanoid)();
583
- connectedInstanceIds = /* @__PURE__ */ new Map();
584
585
  /**
585
586
  * A flag indicating whether the transport has been destroyed.
586
587
  * A destroyed transport will not attempt to reconnect and cannot be used again.
@@ -633,41 +634,41 @@ var Transport = class {
633
634
  * and we know the identity of the connected client.
634
635
  * @param conn The connection object.
635
636
  */
636
- onConnect(conn, connectedTo, instanceId) {
637
+ onConnect(conn, connectedTo, advertisedSessionId) {
637
638
  this.eventDispatcher.dispatchEvent("connectionStatus", {
638
639
  status: "connect",
639
640
  conn
640
641
  });
641
642
  let oldSession = this.sessions.get(connectedTo);
642
- const lastInstanceId = this.connectedInstanceIds.get(connectedTo);
643
- if (oldSession && lastInstanceId !== void 0 && lastInstanceId !== instanceId) {
643
+ if (oldSession?.advertisedSessionId && oldSession.advertisedSessionId !== advertisedSessionId) {
644
644
  log?.warn(
645
- `${this.clientId} -- connection from ${connectedTo} is a different instance (got: ${instanceId}, last connected to: ${lastInstanceId}), starting a new session`
645
+ `${this.clientId} -- connection from ${connectedTo} is a different session (id: ${advertisedSessionId}, last connected to: ${oldSession.advertisedSessionId}), starting a new session`
646
646
  );
647
647
  oldSession.close();
648
648
  this.deleteSession(oldSession);
649
649
  oldSession = void 0;
650
650
  }
651
- this.connectedInstanceIds.set(connectedTo, instanceId);
652
651
  if (oldSession === void 0) {
653
652
  const newSession = this.createSession(connectedTo, conn);
653
+ newSession.advertisedSessionId = advertisedSessionId;
654
654
  log?.info(
655
- `${this.clientId} -- new connection (id: ${conn.debugId}) for new session (id: ${newSession.debugId}) to ${connectedTo}`
655
+ `${this.clientId} -- new connection (id: ${conn.debugId}) for new session (id: ${newSession.id}) to ${connectedTo}`
656
656
  );
657
657
  return newSession;
658
658
  }
659
659
  log?.info(
660
- `${this.clientId} -- new connection (id: ${conn.debugId}) for existing session (id: ${oldSession.debugId}) to ${connectedTo}`
660
+ `${this.clientId} -- new connection (id: ${conn.debugId}) for existing session (id: ${oldSession.id}) to ${connectedTo}`
661
661
  );
662
662
  oldSession.replaceWithNewConnection(conn);
663
663
  oldSession.sendBufferedMessages();
664
+ oldSession.advertisedSessionId = advertisedSessionId;
664
665
  return oldSession;
665
666
  }
666
- createSession(connectedTo, conn) {
667
+ createSession(to, conn) {
667
668
  const session = new Session(
668
- this.clientId,
669
- connectedTo,
670
669
  conn,
670
+ this.clientId,
671
+ to,
671
672
  this.options
672
673
  );
673
674
  this.sessions.set(session.to, session);
@@ -677,10 +678,20 @@ var Transport = class {
677
678
  });
678
679
  return session;
679
680
  }
681
+ getOrCreateSession(to, conn) {
682
+ let session = this.sessions.get(to);
683
+ if (!session) {
684
+ session = this.createSession(to, conn);
685
+ log?.info(
686
+ `${this.clientId} -- no session for ${to}, created a new one (id: ${session.id})`
687
+ );
688
+ }
689
+ return session;
690
+ }
680
691
  deleteSession(session) {
681
692
  this.sessions.delete(session.to);
682
693
  log?.info(
683
- `${this.clientId} -- session ${session.debugId} disconnect from ${session.to}`
694
+ `${this.clientId} -- session ${session.id} disconnect from ${session.to}`
684
695
  );
685
696
  this.eventDispatcher.dispatchEvent("sessionStatus", {
686
697
  status: "disconnect",
@@ -748,11 +759,14 @@ var Transport = class {
748
759
  )}`
749
760
  );
750
761
  } else {
762
+ const errMsg = `received out-of-order msg (got seq: ${msg.seq}, wanted seq: ${session.nextExpectedSeq})`;
751
763
  log?.error(
752
- `${this.clientId} -- received out-of-order msg (got: ${msg.seq}, wanted: ${session.nextExpectedSeq}), marking connection as dead: ${JSON.stringify(msg)}`
764
+ `${this.clientId} -- fatal: ${errMsg}, marking connection as dead: ${JSON.stringify(
765
+ msg
766
+ )}`
753
767
  );
768
+ this.protocolError(ProtocolError.MessageOrderingViolated, errMsg);
754
769
  session.close();
755
- this.deleteSession(session);
756
770
  }
757
771
  return;
758
772
  }
@@ -799,14 +813,7 @@ var Transport = class {
799
813
  );
800
814
  return void 0;
801
815
  }
802
- let session = this.sessions.get(to);
803
- if (!session) {
804
- session = this.createSession(to, void 0);
805
- log?.info(
806
- `${this.clientId} -- no session for ${to}, created a new one (id: ${session.debugId})`
807
- );
808
- }
809
- return session.send(msg);
816
+ return this.getOrCreateSession(to).send(msg);
810
817
  }
811
818
  // control helpers
812
819
  sendCloseStream(to, streamId) {
@@ -858,7 +865,13 @@ var ClientTransport = class extends Transport {
858
865
  */
859
866
  inflightConnectionPromises;
860
867
  retryBudget;
861
- tryReconnecting = true;
868
+ /**
869
+ * A flag indicating whether the transport should automatically reconnect
870
+ * when a connection is dropped.
871
+ * Realistically, this should always be true for clients unless you are writing
872
+ * tests or a special case where you don't want to reconnect.
873
+ */
874
+ reconnectOnConnectionDrop = true;
862
875
  constructor(clientId, providedOptions) {
863
876
  super(clientId, providedOptions);
864
877
  this.options = {
@@ -866,21 +879,20 @@ var ClientTransport = class extends Transport {
866
879
  ...providedOptions
867
880
  };
868
881
  this.inflightConnectionPromises = /* @__PURE__ */ new Map();
869
- this.retryBudget = new LeakyBucketRateLimit(
870
- this.options.connectionRetryOptions
871
- );
882
+ this.retryBudget = new LeakyBucketRateLimit(this.options);
872
883
  }
873
884
  handleConnection(conn, to) {
874
885
  if (this.state !== "open")
875
886
  return;
876
887
  let session = void 0;
877
888
  const handshakeHandler = (data) => {
878
- const handshake = this.receiveHandshakeResponseMessage(data);
879
- if (!handshake) {
889
+ const maybeSession = this.receiveHandshakeResponseMessage(data, conn);
890
+ if (!maybeSession) {
880
891
  conn.close();
881
892
  return;
893
+ } else {
894
+ session = maybeSession;
882
895
  }
883
- session = this.onConnect(conn, handshake.from, handshake.instanceId);
884
896
  conn.removeDataListener(handshakeHandler);
885
897
  conn.addDataListener((data2) => {
886
898
  const parsed = this.parseMsg(data2);
@@ -900,7 +912,7 @@ var ClientTransport = class extends Transport {
900
912
  `${this.clientId} -- connection (id: ${conn.debugId}) to ${to} disconnected`
901
913
  );
902
914
  this.inflightConnectionPromises.delete(to);
903
- if (this.tryReconnecting) {
915
+ if (this.reconnectOnConnectionDrop) {
904
916
  void this.connect(to);
905
917
  }
906
918
  });
@@ -910,7 +922,7 @@ var ClientTransport = class extends Transport {
910
922
  );
911
923
  });
912
924
  }
913
- receiveHandshakeResponseMessage(data) {
925
+ receiveHandshakeResponseMessage(data, conn) {
914
926
  const parsed = this.parseMsg(data);
915
927
  if (!parsed) {
916
928
  this.protocolError(
@@ -943,12 +955,14 @@ var ClientTransport = class extends Transport {
943
955
  );
944
956
  return false;
945
957
  }
946
- const instanceId = parsed.payload.status.instanceId;
947
- log?.debug(
948
- `${this.clientId} -- handshake from ${parsed.from} ok (instance: ${instanceId})`
958
+ log?.debug(`${this.clientId} -- handshake from ${parsed.from} ok`);
959
+ const session = this.onConnect(
960
+ conn,
961
+ parsed.from,
962
+ parsed.payload.status.sessionId
949
963
  );
950
964
  this.retryBudget.startRestoringBudget(parsed.from);
951
- return { instanceId, from: parsed.from };
965
+ return session;
952
966
  }
953
967
  /**
954
968
  * Manually attempts to connect to a client.
@@ -1006,7 +1020,7 @@ var ClientTransport = class extends Transport {
1006
1020
  } catch (error) {
1007
1021
  this.inflightConnectionPromises.delete(to);
1008
1022
  const errStr = coerceErrorString(error);
1009
- if (!this.tryReconnecting || !canProceedWithConnection()) {
1023
+ if (!this.reconnectOnConnectionDrop || !canProceedWithConnection()) {
1010
1024
  log?.warn(`${this.clientId} -- connection to ${to} failed (${errStr})`);
1011
1025
  } else {
1012
1026
  log?.warn(
@@ -1017,11 +1031,8 @@ var ClientTransport = class extends Transport {
1017
1031
  }
1018
1032
  }
1019
1033
  sendHandshake(to, conn) {
1020
- const requestMsg = handshakeRequestMessage(
1021
- this.clientId,
1022
- to,
1023
- this.instanceId
1024
- );
1034
+ const session = this.getOrCreateSession(to, conn);
1035
+ const requestMsg = handshakeRequestMessage(this.clientId, to, session.id);
1025
1036
  log?.debug(`${this.clientId} -- sending handshake request to ${to}`);
1026
1037
  conn.send(this.codec.toBuffer(requestMsg));
1027
1038
  }
@@ -1,5 +1,5 @@
1
- import { a as ClientTransport, c as ProvidedClientTransportOptions, b as TransportClientId } from '../../../index-2e402bb8.js';
2
- import { U as UdsConnection } from '../../../connection-94896f3b.js';
1
+ import { a as ClientTransport, c as ProvidedClientTransportOptions, b as TransportClientId } from '../../../index-8df0bdfb.js';
2
+ import { U as UdsConnection } from '../../../connection-0767dc6b.js';
3
3
  import '../../../types-3e5768ec.js';
4
4
  import '@sinclair/typebox';
5
5
  import 'node:net';
@@ -1,5 +1,5 @@
1
- import { a as ClientTransport, c as ProvidedClientTransportOptions, b as TransportClientId } from '../../../index-2e402bb8.js';
2
- import { U as UdsConnection } from '../../../connection-94896f3b.js';
1
+ import { a as ClientTransport, c as ProvidedClientTransportOptions, b as TransportClientId } from '../../../index-8df0bdfb.js';
2
+ import { U as UdsConnection } from '../../../connection-0767dc6b.js';
3
3
  import '../../../types-3e5768ec.js';
4
4
  import '@sinclair/typebox';
5
5
  import 'node:net';
@@ -1,10 +1,10 @@
1
1
  import {
2
2
  UdsConnection
3
- } from "../../../chunk-L65XWBX2.js";
3
+ } from "../../../chunk-VJRLJ3JU.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";