@replit/river 0.207.1 → 0.207.3

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 (56) hide show
  1. package/dist/adapter-f2b6e211.d.ts +46 -0
  2. package/dist/{chunk-7LMZNSVC.js → chunk-B7REV3ZV.js} +6 -5
  3. package/dist/{chunk-7LMZNSVC.js.map → chunk-B7REV3ZV.js.map} +1 -1
  4. package/dist/{chunk-QMAVXV4Z.js → chunk-BO7MFCO6.js} +1136 -132
  5. package/dist/chunk-BO7MFCO6.js.map +1 -0
  6. package/dist/{chunk-BCCZA7SX.js → chunk-QGPYCXV4.js} +2 -2
  7. package/dist/{chunk-BCCZA7SX.js.map → chunk-QGPYCXV4.js.map} +1 -1
  8. package/dist/codec/index.cjs +157 -23
  9. package/dist/codec/index.cjs.map +1 -1
  10. package/dist/codec/index.d.cts +5 -1
  11. package/dist/codec/index.d.ts +5 -1
  12. package/dist/codec/index.js +6 -20
  13. package/dist/codec/index.js.map +1 -1
  14. package/dist/{connection-933c87b2.d.ts → connection-06d72f2e.d.ts} +3 -2
  15. package/dist/index-02554794.d.ts +37 -0
  16. package/dist/logging/index.d.cts +2 -1
  17. package/dist/logging/index.d.ts +2 -1
  18. package/dist/{message-ffacb98a.d.ts → message-01c3e85a.d.ts} +1 -35
  19. package/dist/router/index.cjs +1 -1
  20. package/dist/router/index.cjs.map +1 -1
  21. package/dist/router/index.d.cts +6 -5
  22. package/dist/router/index.d.ts +6 -5
  23. package/dist/router/index.js +1 -1
  24. package/dist/{services-4cd29829.d.ts → services-87887bc5.d.ts} +16 -11
  25. package/dist/testUtil/index.cjs +992 -829
  26. package/dist/testUtil/index.cjs.map +1 -1
  27. package/dist/testUtil/index.d.cts +4 -3
  28. package/dist/testUtil/index.d.ts +4 -3
  29. package/dist/testUtil/index.js +18 -13
  30. package/dist/testUtil/index.js.map +1 -1
  31. package/dist/transport/impls/ws/client.cjs +293 -193
  32. package/dist/transport/impls/ws/client.cjs.map +1 -1
  33. package/dist/transport/impls/ws/client.d.cts +5 -4
  34. package/dist/transport/impls/ws/client.d.ts +5 -4
  35. package/dist/transport/impls/ws/client.js +5 -7
  36. package/dist/transport/impls/ws/client.js.map +1 -1
  37. package/dist/transport/impls/ws/server.cjs +230 -117
  38. package/dist/transport/impls/ws/server.cjs.map +1 -1
  39. package/dist/transport/impls/ws/server.d.cts +5 -4
  40. package/dist/transport/impls/ws/server.d.ts +5 -4
  41. package/dist/transport/impls/ws/server.js +5 -7
  42. package/dist/transport/impls/ws/server.js.map +1 -1
  43. package/dist/transport/index.cjs +408 -259
  44. package/dist/transport/index.cjs.map +1 -1
  45. package/dist/transport/index.d.cts +7 -6
  46. package/dist/transport/index.d.ts +7 -6
  47. package/dist/transport/index.js +4 -9
  48. package/package.json +1 -1
  49. package/dist/chunk-AJGIY2UB.js +0 -56
  50. package/dist/chunk-AJGIY2UB.js.map +0 -1
  51. package/dist/chunk-CRD3HDVN.js +0 -438
  52. package/dist/chunk-CRD3HDVN.js.map +0 -1
  53. package/dist/chunk-I27WBSMZ.js +0 -377
  54. package/dist/chunk-I27WBSMZ.js.map +0 -1
  55. package/dist/chunk-QMAVXV4Z.js.map +0 -1
  56. package/dist/types-3e5768ec.d.ts +0 -20
@@ -1,18 +1,114 @@
1
1
  import {
2
+ ControlMessageHandshakeRequestSchema,
3
+ ControlMessageHandshakeResponseSchema,
4
+ HandshakeErrorCustomHandlerFatalResponseCodes,
5
+ HandshakeErrorRetriableResponseCodes,
2
6
  OpaqueTransportMessageSchema,
7
+ acceptedProtocolVersions,
8
+ coerceErrorString,
3
9
  createConnectionTelemetryInfo,
4
10
  createSessionTelemetryInfo,
11
+ currentProtocolVersion,
5
12
  generateId,
13
+ getPropagationContext,
6
14
  getTracer,
15
+ handshakeRequestMessage,
16
+ handshakeResponseMessage,
17
+ isAcceptedProtocolVersion,
7
18
  isAck
8
- } from "./chunk-BCCZA7SX.js";
19
+ } from "./chunk-QGPYCXV4.js";
9
20
  import {
10
21
  BaseLogger,
11
22
  createLogProxy
12
23
  } from "./chunk-CC7RN7GI.js";
13
- import {
14
- NaiveJsonCodec
15
- } from "./chunk-AJGIY2UB.js";
24
+
25
+ // transport/events.ts
26
+ var ProtocolError = {
27
+ RetriesExceeded: "conn_retry_exceeded",
28
+ HandshakeFailed: "handshake_failed",
29
+ MessageOrderingViolated: "message_ordering_violated",
30
+ InvalidMessage: "invalid_message",
31
+ MessageSendFailure: "message_send_failure"
32
+ };
33
+ var EventDispatcher = class {
34
+ eventListeners = {};
35
+ removeAllListeners() {
36
+ this.eventListeners = {};
37
+ }
38
+ numberOfListeners(eventType) {
39
+ return this.eventListeners[eventType]?.size ?? 0;
40
+ }
41
+ addEventListener(eventType, handler) {
42
+ if (!this.eventListeners[eventType]) {
43
+ this.eventListeners[eventType] = /* @__PURE__ */ new Set();
44
+ }
45
+ this.eventListeners[eventType]?.add(handler);
46
+ }
47
+ removeEventListener(eventType, handler) {
48
+ const handlers = this.eventListeners[eventType];
49
+ if (handlers) {
50
+ this.eventListeners[eventType]?.delete(handler);
51
+ }
52
+ }
53
+ dispatchEvent(eventType, event) {
54
+ const handlers = this.eventListeners[eventType];
55
+ if (handlers) {
56
+ const copy = [...handlers];
57
+ for (const handler of copy) {
58
+ handler(event);
59
+ }
60
+ }
61
+ }
62
+ };
63
+
64
+ // codec/json.ts
65
+ var encoder = new TextEncoder();
66
+ var decoder = new TextDecoder();
67
+ function uint8ArrayToBase64(uint8Array) {
68
+ let binary = "";
69
+ uint8Array.forEach((byte) => {
70
+ binary += String.fromCharCode(byte);
71
+ });
72
+ return btoa(binary);
73
+ }
74
+ function base64ToUint8Array(base64) {
75
+ const binaryString = atob(base64);
76
+ const uint8Array = new Uint8Array(binaryString.length);
77
+ for (let i = 0; i < binaryString.length; i++) {
78
+ uint8Array[i] = binaryString.charCodeAt(i);
79
+ }
80
+ return uint8Array;
81
+ }
82
+ var NaiveJsonCodec = {
83
+ toBuffer: (obj) => {
84
+ return encoder.encode(
85
+ JSON.stringify(obj, function replacer(key) {
86
+ const val = this[key];
87
+ if (val instanceof Uint8Array) {
88
+ return { $t: uint8ArrayToBase64(val) };
89
+ } else {
90
+ return val;
91
+ }
92
+ })
93
+ );
94
+ },
95
+ fromBuffer: (buff) => {
96
+ const parsed = JSON.parse(
97
+ decoder.decode(buff),
98
+ function reviver(_key, val) {
99
+ if (val?.$t) {
100
+ return base64ToUint8Array(val.$t);
101
+ } else {
102
+ return val;
103
+ }
104
+ }
105
+ );
106
+ if (typeof parsed !== "object" || parsed === null) {
107
+ throw new Error("unpacked msg is not an object");
108
+ }
109
+ return parsed;
110
+ }
111
+ };
16
112
 
17
113
  // transport/options.ts
18
114
  var defaultTransportOptions = {
@@ -40,7 +136,6 @@ var defaultServerTransportOptions = {
40
136
  };
41
137
 
42
138
  // transport/sessionStateMachine/common.ts
43
- import { Value } from "@sinclair/typebox/value";
44
139
  var SessionState = /* @__PURE__ */ ((SessionState2) => {
45
140
  SessionState2["NoConnection"] = "NoConnection";
46
141
  SessionState2["BackingOff"] = "BackingOff";
@@ -109,34 +204,16 @@ var StateMachineState = class {
109
204
  var CommonSession = class extends StateMachineState {
110
205
  from;
111
206
  options;
207
+ codec;
112
208
  tracer;
113
209
  log;
114
- constructor({ from, options, log, tracer }) {
210
+ constructor({ from, options, log, tracer, codec }) {
115
211
  super();
116
212
  this.from = from;
117
213
  this.options = options;
118
214
  this.log = log;
119
215
  this.tracer = tracer;
120
- }
121
- parseMsg(msg) {
122
- const parsedMsg = this.options.codec.fromBuffer(msg);
123
- if (parsedMsg === null) {
124
- this.log?.error(
125
- `received malformed msg: ${Buffer.from(msg).toString("base64")}`,
126
- this.loggingMetadata
127
- );
128
- return null;
129
- }
130
- if (!Value.Check(OpaqueTransportMessageSchema, parsedMsg)) {
131
- this.log?.error(`received invalid msg: ${JSON.stringify(parsedMsg)}`, {
132
- ...this.loggingMetadata,
133
- validationErrors: [
134
- ...Value.Errors(OpaqueTransportMessageSchema, parsedMsg)
135
- ]
136
- });
137
- return null;
138
- }
139
- return parsedMsg;
216
+ this.codec = codec;
140
217
  }
141
218
  };
142
219
  var IdentifiedSession = class extends CommonSession {
@@ -196,9 +273,6 @@ var IdentifiedSession = class extends CommonSession {
196
273
  return metadata;
197
274
  }
198
275
  constructMsg(partialMsg) {
199
- if (this._isConsumed) {
200
- throw new Error(ERR_CONSUMED);
201
- }
202
276
  const msg = {
203
277
  ...partialMsg,
204
278
  id: generateId(),
@@ -216,7 +290,10 @@ var IdentifiedSession = class extends CommonSession {
216
290
  send(msg) {
217
291
  const constructedMsg = this.constructMsg(msg);
218
292
  this.sendBuffer.push(constructedMsg);
219
- return constructedMsg.id;
293
+ return {
294
+ ok: true,
295
+ value: constructedMsg.id
296
+ };
220
297
  }
221
298
  _handleStateExit() {
222
299
  }
@@ -248,6 +325,23 @@ var IdentifiedSessionWithGracePeriod = class extends IdentifiedSession {
248
325
  super._handleClose();
249
326
  }
250
327
  };
328
+ function sendMessage(conn, codec, msg) {
329
+ const buff = codec.toBuffer(msg);
330
+ if (!buff.ok) {
331
+ return buff;
332
+ }
333
+ const sent = conn.send(buff.value);
334
+ if (!sent) {
335
+ return {
336
+ ok: false,
337
+ reason: "failed to send message"
338
+ };
339
+ }
340
+ return {
341
+ ok: true,
342
+ value: msg.id
343
+ };
344
+ }
251
345
 
252
346
  // transport/sessionStateMachine/SessionConnecting.ts
253
347
  var SessionConnecting = class extends IdentifiedSessionWithGracePeriod {
@@ -300,8 +394,8 @@ var SessionConnecting = class extends IdentifiedSessionWithGracePeriod {
300
394
  }
301
395
  }
302
396
  _handleClose() {
303
- this.bestEffortClose();
304
397
  super._handleClose();
398
+ this.bestEffortClose();
305
399
  }
306
400
  };
307
401
 
@@ -341,18 +435,18 @@ var SessionWaitingForHandshake = class extends CommonSession {
341
435
  };
342
436
  }
343
437
  onHandshakeData = (msg) => {
344
- const parsedMsg = this.parseMsg(msg);
345
- if (parsedMsg === null) {
438
+ const parsedMsgRes = this.codec.fromBuffer(msg);
439
+ if (!parsedMsgRes.ok) {
346
440
  this.listeners.onInvalidHandshake(
347
- "could not parse message",
441
+ `could not parse handshake message: ${parsedMsgRes.reason}`,
348
442
  "MALFORMED_HANDSHAKE"
349
443
  );
350
444
  return;
351
445
  }
352
- this.listeners.onHandshake(parsedMsg);
446
+ this.listeners.onHandshake(parsedMsgRes.value);
353
447
  };
354
448
  sendHandshake(msg) {
355
- return this.conn.send(this.options.codec.toBuffer(msg));
449
+ return sendMessage(this.conn, this.codec, msg);
356
450
  }
357
451
  _handleStateExit() {
358
452
  this.conn.removeDataListener(this.onHandshakeData);
@@ -390,18 +484,18 @@ var SessionHandshaking = class extends IdentifiedSessionWithGracePeriod {
390
484
  };
391
485
  }
392
486
  onHandshakeData = (msg) => {
393
- const parsedMsg = this.parseMsg(msg);
394
- if (parsedMsg === null) {
487
+ const parsedMsgRes = this.codec.fromBuffer(msg);
488
+ if (!parsedMsgRes.ok) {
395
489
  this.listeners.onInvalidHandshake(
396
- "could not parse message",
490
+ `could not parse handshake message: ${parsedMsgRes.reason}`,
397
491
  "MALFORMED_HANDSHAKE"
398
492
  );
399
493
  return;
400
494
  }
401
- this.listeners.onHandshake(parsedMsg);
495
+ this.listeners.onHandshake(parsedMsgRes.value);
402
496
  };
403
497
  sendHandshake(msg) {
404
- return this.conn.send(this.options.codec.toBuffer(msg));
498
+ return sendMessage(this.conn, this.codec, msg);
405
499
  }
406
500
  _handleStateExit() {
407
501
  super._handleStateExit();
@@ -426,12 +520,15 @@ var SessionConnected = class extends IdentifiedSession {
426
520
  conn;
427
521
  listeners;
428
522
  heartbeatHandle;
429
- heartbeatMisses = 0;
430
- isActivelyHeartbeating;
523
+ heartbeatMissTimeout;
524
+ isActivelyHeartbeating = false;
431
525
  updateBookkeeping(ack, seq) {
432
526
  this.sendBuffer = this.sendBuffer.filter((unacked) => unacked.seq >= ack);
433
527
  this.ack = seq + 1;
434
- this.heartbeatMisses = 0;
528
+ if (this.heartbeatMissTimeout) {
529
+ clearTimeout(this.heartbeatMissTimeout);
530
+ }
531
+ this.startMissingHeartbeatTimeout();
435
532
  }
436
533
  assertSendOrdering(constructedMsg) {
437
534
  if (constructedMsg.seq > this.seqSent + 1) {
@@ -448,9 +545,13 @@ var SessionConnected = class extends IdentifiedSession {
448
545
  const constructedMsg = this.constructMsg(msg);
449
546
  this.assertSendOrdering(constructedMsg);
450
547
  this.sendBuffer.push(constructedMsg);
451
- this.conn.send(this.options.codec.toBuffer(constructedMsg));
548
+ const res = sendMessage(this.conn, this.codec, constructedMsg);
549
+ if (!res.ok) {
550
+ this.listeners.onMessageSendFailure(constructedMsg, res.reason);
551
+ return res;
552
+ }
452
553
  this.seqSent = constructedMsg.seq;
453
- return constructedMsg.id;
554
+ return res;
454
555
  }
455
556
  constructor(props) {
456
557
  super(props);
@@ -459,6 +560,8 @@ var SessionConnected = class extends IdentifiedSession {
459
560
  this.conn.addDataListener(this.onMessageData);
460
561
  this.conn.addCloseListener(this.listeners.onConnectionClosed);
461
562
  this.conn.addErrorListener(this.listeners.onConnectionErrored);
563
+ }
564
+ sendBufferedMessages() {
462
565
  if (this.sendBuffer.length > 0) {
463
566
  this.log?.info(
464
567
  `sending ${this.sendBuffer.length} buffered messages, starting at seq ${this.nextSeq()}`,
@@ -466,30 +569,15 @@ var SessionConnected = class extends IdentifiedSession {
466
569
  );
467
570
  for (const msg of this.sendBuffer) {
468
571
  this.assertSendOrdering(msg);
469
- this.conn.send(this.options.codec.toBuffer(msg));
572
+ const res = sendMessage(this.conn, this.codec, msg);
573
+ if (!res.ok) {
574
+ this.listeners.onMessageSendFailure(msg, res.reason);
575
+ return res;
576
+ }
470
577
  this.seqSent = msg.seq;
471
578
  }
472
579
  }
473
- this.isActivelyHeartbeating = false;
474
- this.heartbeatHandle = setInterval(() => {
475
- const misses = this.heartbeatMisses;
476
- const missDuration = misses * this.options.heartbeatIntervalMs;
477
- if (misses >= this.options.heartbeatsUntilDead) {
478
- this.log?.info(
479
- `closing connection to ${this.to} due to inactivity (missed ${misses} heartbeats which is ${missDuration}ms)`,
480
- this.loggingMetadata
481
- );
482
- this.telemetry.span.addEvent("closing connection due to inactivity");
483
- this.conn.close();
484
- clearInterval(this.heartbeatHandle);
485
- this.heartbeatHandle = void 0;
486
- return;
487
- }
488
- if (this.isActivelyHeartbeating) {
489
- this.sendHeartbeat();
490
- }
491
- this.heartbeatMisses++;
492
- }, this.options.heartbeatIntervalMs);
580
+ return { ok: true, value: void 0 };
493
581
  }
494
582
  get loggingMetadata() {
495
583
  return {
@@ -497,31 +585,46 @@ var SessionConnected = class extends IdentifiedSession {
497
585
  ...this.conn.loggingMetadata
498
586
  };
499
587
  }
588
+ startMissingHeartbeatTimeout() {
589
+ const maxMisses = this.options.heartbeatsUntilDead;
590
+ const missDuration = maxMisses * this.options.heartbeatIntervalMs;
591
+ this.heartbeatMissTimeout = setTimeout(() => {
592
+ this.log?.info(
593
+ `closing connection to ${this.to} due to inactivity (missed ${maxMisses} heartbeats which is ${missDuration}ms)`,
594
+ this.loggingMetadata
595
+ );
596
+ this.telemetry.span.addEvent(
597
+ "closing connection due to missing heartbeat"
598
+ );
599
+ this.conn.close();
600
+ }, missDuration);
601
+ }
500
602
  startActiveHeartbeat() {
501
603
  this.isActivelyHeartbeating = true;
604
+ this.heartbeatHandle = setInterval(() => {
605
+ this.sendHeartbeat();
606
+ }, this.options.heartbeatIntervalMs);
502
607
  }
503
608
  sendHeartbeat() {
504
609
  this.log?.debug("sending heartbeat", this.loggingMetadata);
505
- this.send({
610
+ const heartbeat = {
506
611
  streamId: "heartbeat",
507
612
  controlFlags: 1 /* AckBit */,
508
613
  payload: {
509
614
  type: "ACK"
510
615
  }
511
- });
512
- }
513
- closeConnection() {
514
- this.conn.removeDataListener(this.onMessageData);
515
- this.conn.removeCloseListener(this.listeners.onConnectionClosed);
516
- this.conn.removeErrorListener(this.listeners.onConnectionErrored);
517
- this.conn.close();
616
+ };
617
+ this.send(heartbeat);
518
618
  }
519
619
  onMessageData = (msg) => {
520
- const parsedMsg = this.parseMsg(msg);
521
- if (parsedMsg === null) {
522
- this.listeners.onInvalidMessage("could not parse message");
620
+ const parsedMsgRes = this.codec.fromBuffer(msg);
621
+ if (!parsedMsgRes.ok) {
622
+ this.listeners.onInvalidMessage(
623
+ `could not parse message: ${parsedMsgRes.reason}`
624
+ );
523
625
  return;
524
626
  }
627
+ const parsedMsg = parsedMsgRes.value;
525
628
  if (parsedMsg.seq !== this.ack) {
526
629
  if (parsedMsg.seq < this.ack) {
527
630
  this.log?.debug(
@@ -542,7 +645,7 @@ var SessionConnected = class extends IdentifiedSession {
542
645
  code: SpanStatusCode.ERROR,
543
646
  message: reason
544
647
  });
545
- this.closeConnection();
648
+ this.conn.close();
546
649
  }
547
650
  return;
548
651
  }
@@ -560,9 +663,7 @@ var SessionConnected = class extends IdentifiedSession {
560
663
  transportMessage: parsedMsg
561
664
  });
562
665
  if (!this.isActivelyHeartbeating) {
563
- void Promise.resolve().then(() => {
564
- this.sendHeartbeat();
565
- });
666
+ this.sendHeartbeat();
566
667
  }
567
668
  };
568
669
  _handleStateExit() {
@@ -574,6 +675,10 @@ var SessionConnected = class extends IdentifiedSession {
574
675
  clearInterval(this.heartbeatHandle);
575
676
  this.heartbeatHandle = void 0;
576
677
  }
678
+ if (this.heartbeatMissTimeout) {
679
+ clearTimeout(this.heartbeatMissTimeout);
680
+ this.heartbeatMissTimeout = void 0;
681
+ }
577
682
  }
578
683
  _handleClose() {
579
684
  super._handleClose();
@@ -605,6 +710,62 @@ var SessionBackingOff = class extends IdentifiedSessionWithGracePeriod {
605
710
  }
606
711
  };
607
712
 
713
+ // codec/binary.ts
714
+ import { decode, encode } from "@msgpack/msgpack";
715
+ var BinaryCodec = {
716
+ toBuffer(obj) {
717
+ return encode(obj, { ignoreUndefined: true });
718
+ },
719
+ fromBuffer: (buff) => {
720
+ const res = decode(buff);
721
+ if (typeof res !== "object" || res === null) {
722
+ throw new Error("unpacked msg is not an object");
723
+ }
724
+ return res;
725
+ }
726
+ };
727
+
728
+ // codec/adapter.ts
729
+ import { Value } from "@sinclair/typebox/value";
730
+ var CodecMessageAdapter = class {
731
+ constructor(codec) {
732
+ this.codec = codec;
733
+ }
734
+ toBuffer(msg) {
735
+ try {
736
+ return {
737
+ ok: true,
738
+ value: this.codec.toBuffer(msg)
739
+ };
740
+ } catch (e) {
741
+ return {
742
+ ok: false,
743
+ reason: coerceErrorString(e)
744
+ };
745
+ }
746
+ }
747
+ fromBuffer(buf) {
748
+ try {
749
+ const parsedMsg = this.codec.fromBuffer(buf);
750
+ if (!Value.Check(OpaqueTransportMessageSchema, parsedMsg)) {
751
+ return {
752
+ ok: false,
753
+ reason: "transport message schema mismatch"
754
+ };
755
+ }
756
+ return {
757
+ ok: true,
758
+ value: parsedMsg
759
+ };
760
+ } catch (e) {
761
+ return {
762
+ ok: false,
763
+ reason: coerceErrorString(e)
764
+ };
765
+ }
766
+ }
767
+ };
768
+
608
769
  // transport/sessionStateMachine/transitions.ts
609
770
  function inheritSharedSession(session) {
610
771
  return {
@@ -619,7 +780,8 @@ function inheritSharedSession(session) {
619
780
  options: session.options,
620
781
  log: session.log,
621
782
  tracer: session.tracer,
622
- protocolVersion: session.protocolVersion
783
+ protocolVersion: session.protocolVersion,
784
+ codec: session.codec
623
785
  };
624
786
  }
625
787
  function inheritSharedSessionWithGrace(session) {
@@ -648,7 +810,8 @@ var SessionStateGraph = {
648
810
  options,
649
811
  protocolVersion,
650
812
  tracer,
651
- log
813
+ log,
814
+ codec: new CodecMessageAdapter(options.codec)
652
815
  });
653
816
  session.log?.info(`session ${session.id} created in NoConnection state`, {
654
817
  ...session.loggingMetadata,
@@ -663,7 +826,8 @@ var SessionStateGraph = {
663
826
  from,
664
827
  options,
665
828
  tracer,
666
- log
829
+ log,
830
+ codec: new CodecMessageAdapter(options.codec)
667
831
  });
668
832
  session.log?.info(`session created in WaitingForHandshake state`, {
669
833
  ...session.loggingMetadata,
@@ -741,6 +905,7 @@ var SessionStateGraph = {
741
905
  listeners,
742
906
  ...carriedState
743
907
  });
908
+ session.startMissingHeartbeatTimeout();
744
909
  session.log?.info(
745
910
  `session ${session.id} transition from Handshaking to Connected`,
746
911
  {
@@ -776,7 +941,8 @@ var SessionStateGraph = {
776
941
  options,
777
942
  tracer: pendingSession.tracer,
778
943
  log: pendingSession.log,
779
- protocolVersion
944
+ protocolVersion,
945
+ codec: new CodecMessageAdapter(options.codec)
780
946
  }
781
947
  );
782
948
  pendingSession._handleStateExit();
@@ -786,6 +952,7 @@ var SessionStateGraph = {
786
952
  listeners,
787
953
  ...carriedState
788
954
  });
955
+ session.startMissingHeartbeatTimeout();
789
956
  conn.telemetry = createConnectionTelemetryInfo(
790
957
  session.tracer,
791
958
  conn,
@@ -916,44 +1083,6 @@ var ServerSessionStateGraph = {
916
1083
  }
917
1084
  };
918
1085
 
919
- // transport/events.ts
920
- var ProtocolError = {
921
- RetriesExceeded: "conn_retry_exceeded",
922
- HandshakeFailed: "handshake_failed",
923
- MessageOrderingViolated: "message_ordering_violated",
924
- InvalidMessage: "invalid_message"
925
- };
926
- var EventDispatcher = class {
927
- eventListeners = {};
928
- removeAllListeners() {
929
- this.eventListeners = {};
930
- }
931
- numberOfListeners(eventType) {
932
- return this.eventListeners[eventType]?.size ?? 0;
933
- }
934
- addEventListener(eventType, handler) {
935
- if (!this.eventListeners[eventType]) {
936
- this.eventListeners[eventType] = /* @__PURE__ */ new Set();
937
- }
938
- this.eventListeners[eventType]?.add(handler);
939
- }
940
- removeEventListener(eventType, handler) {
941
- const handlers = this.eventListeners[eventType];
942
- if (handlers) {
943
- this.eventListeners[eventType]?.delete(handler);
944
- }
945
- }
946
- dispatchEvent(eventType, event) {
947
- const handlers = this.eventListeners[eventType];
948
- if (handlers) {
949
- const copy = [...handlers];
950
- for (const handler of copy) {
951
- handler(event);
952
- }
953
- }
954
- }
955
- };
956
-
957
1086
  // transport/transport.ts
958
1087
  var Transport = class {
959
1088
  /**
@@ -1164,13 +1293,886 @@ var Transport = class {
1164
1293
  );
1165
1294
  }
1166
1295
  const sameSession = session.id === sessionId;
1167
- if (!sameSession) {
1296
+ if (!sameSession || session._isConsumed) {
1168
1297
  throw new Error(
1169
1298
  `session scope for ${sessionId} has ended (transition), can't send`
1170
1299
  );
1171
1300
  }
1172
- return session.send(msg);
1301
+ const res = session.send(msg);
1302
+ if (!res.ok) {
1303
+ throw new Error(res.reason);
1304
+ }
1305
+ return res.value;
1306
+ };
1307
+ }
1308
+ };
1309
+
1310
+ // transport/client.ts
1311
+ import { SpanStatusCode as SpanStatusCode2 } from "@opentelemetry/api";
1312
+
1313
+ // transport/rateLimit.ts
1314
+ var LeakyBucketRateLimit = class {
1315
+ budgetConsumed;
1316
+ intervalHandle;
1317
+ options;
1318
+ constructor(options) {
1319
+ this.options = options;
1320
+ this.budgetConsumed = 0;
1321
+ }
1322
+ getBackoffMs() {
1323
+ if (this.getBudgetConsumed() === 0) {
1324
+ return 0;
1325
+ }
1326
+ const exponent = Math.max(0, this.getBudgetConsumed() - 1);
1327
+ const jitter = Math.floor(Math.random() * this.options.maxJitterMs);
1328
+ const backoffMs = Math.min(
1329
+ this.options.baseIntervalMs * 2 ** exponent,
1330
+ this.options.maxBackoffMs
1331
+ );
1332
+ return backoffMs + jitter;
1333
+ }
1334
+ get totalBudgetRestoreTime() {
1335
+ return this.options.budgetRestoreIntervalMs * this.options.attemptBudgetCapacity;
1336
+ }
1337
+ consumeBudget() {
1338
+ this.stopLeak();
1339
+ this.budgetConsumed = this.getBudgetConsumed() + 1;
1340
+ }
1341
+ getBudgetConsumed() {
1342
+ return this.budgetConsumed;
1343
+ }
1344
+ hasBudget() {
1345
+ return this.getBudgetConsumed() < this.options.attemptBudgetCapacity;
1346
+ }
1347
+ startRestoringBudget() {
1348
+ if (this.intervalHandle) {
1349
+ return;
1350
+ }
1351
+ const restoreBudgetForUser = () => {
1352
+ const currentBudget = this.budgetConsumed;
1353
+ if (!currentBudget) {
1354
+ this.stopLeak();
1355
+ return;
1356
+ }
1357
+ const newBudget = currentBudget - 1;
1358
+ if (newBudget === 0) {
1359
+ return;
1360
+ }
1361
+ this.budgetConsumed = newBudget;
1173
1362
  };
1363
+ this.intervalHandle = setInterval(
1364
+ restoreBudgetForUser,
1365
+ this.options.budgetRestoreIntervalMs
1366
+ );
1367
+ }
1368
+ stopLeak() {
1369
+ if (!this.intervalHandle) {
1370
+ return;
1371
+ }
1372
+ clearInterval(this.intervalHandle);
1373
+ this.intervalHandle = void 0;
1374
+ }
1375
+ close() {
1376
+ this.stopLeak();
1377
+ }
1378
+ };
1379
+
1380
+ // transport/client.ts
1381
+ import { Value as Value2 } from "@sinclair/typebox/value";
1382
+ var ClientTransport = class extends Transport {
1383
+ /**
1384
+ * The options for this transport.
1385
+ */
1386
+ options;
1387
+ retryBudget;
1388
+ /**
1389
+ * A flag indicating whether the transport should automatically reconnect
1390
+ * when a connection is dropped.
1391
+ * Realistically, this should always be true for clients unless you are writing
1392
+ * tests or a special case where you don't want to reconnect.
1393
+ */
1394
+ reconnectOnConnectionDrop = true;
1395
+ /**
1396
+ * Optional handshake options for this client.
1397
+ */
1398
+ handshakeExtensions;
1399
+ sessions;
1400
+ constructor(clientId, providedOptions) {
1401
+ super(clientId, providedOptions);
1402
+ this.sessions = /* @__PURE__ */ new Map();
1403
+ this.options = {
1404
+ ...defaultClientTransportOptions,
1405
+ ...providedOptions
1406
+ };
1407
+ this.retryBudget = new LeakyBucketRateLimit(this.options);
1408
+ }
1409
+ extendHandshake(options) {
1410
+ this.handshakeExtensions = options;
1411
+ }
1412
+ tryReconnecting(to) {
1413
+ const oldSession = this.sessions.get(to);
1414
+ if (!this.options.enableTransparentSessionReconnects && oldSession) {
1415
+ this.deleteSession(oldSession);
1416
+ }
1417
+ if (this.reconnectOnConnectionDrop && this.getStatus() === "open") {
1418
+ this.connect(to);
1419
+ }
1420
+ }
1421
+ /*
1422
+ * Creates a raw unconnected session object.
1423
+ * This is mostly a River internal, you shouldn't need to use this directly.
1424
+ */
1425
+ createUnconnectedSession(to) {
1426
+ const session = ClientSessionStateGraph.entrypoint(
1427
+ to,
1428
+ this.clientId,
1429
+ {
1430
+ onSessionGracePeriodElapsed: () => {
1431
+ this.onSessionGracePeriodElapsed(session);
1432
+ }
1433
+ },
1434
+ this.options,
1435
+ currentProtocolVersion,
1436
+ this.tracer,
1437
+ this.log
1438
+ );
1439
+ this.createSession(session);
1440
+ return session;
1441
+ }
1442
+ // listeners
1443
+ onConnectingFailed(session) {
1444
+ const noConnectionSession = super.onConnectingFailed(session);
1445
+ this.tryReconnecting(noConnectionSession.to);
1446
+ return noConnectionSession;
1447
+ }
1448
+ onConnClosed(session) {
1449
+ const noConnectionSession = super.onConnClosed(session);
1450
+ this.tryReconnecting(noConnectionSession.to);
1451
+ return noConnectionSession;
1452
+ }
1453
+ onConnectionEstablished(session, conn) {
1454
+ const handshakingSession = ClientSessionStateGraph.transition.ConnectingToHandshaking(
1455
+ session,
1456
+ conn,
1457
+ {
1458
+ onConnectionErrored: (err) => {
1459
+ const errStr = coerceErrorString(err);
1460
+ this.log?.error(
1461
+ `connection to ${handshakingSession.to} errored during handshake: ${errStr}`,
1462
+ handshakingSession.loggingMetadata
1463
+ );
1464
+ },
1465
+ onConnectionClosed: () => {
1466
+ this.log?.warn(
1467
+ `connection to ${handshakingSession.to} closed during handshake`,
1468
+ handshakingSession.loggingMetadata
1469
+ );
1470
+ this.onConnClosed(handshakingSession);
1471
+ },
1472
+ onHandshake: (msg) => {
1473
+ this.onHandshakeResponse(handshakingSession, msg);
1474
+ },
1475
+ onInvalidHandshake: (reason, code) => {
1476
+ this.log?.error(
1477
+ `invalid handshake: ${reason}`,
1478
+ handshakingSession.loggingMetadata
1479
+ );
1480
+ this.deleteSession(session, { unhealthy: true });
1481
+ this.protocolError({
1482
+ type: ProtocolError.HandshakeFailed,
1483
+ code,
1484
+ message: reason
1485
+ });
1486
+ },
1487
+ onHandshakeTimeout: () => {
1488
+ this.log?.error(
1489
+ `connection to ${handshakingSession.to} timed out during handshake`,
1490
+ handshakingSession.loggingMetadata
1491
+ );
1492
+ this.onConnClosed(handshakingSession);
1493
+ },
1494
+ onSessionGracePeriodElapsed: () => {
1495
+ this.onSessionGracePeriodElapsed(handshakingSession);
1496
+ }
1497
+ }
1498
+ );
1499
+ this.updateSession(handshakingSession);
1500
+ void this.sendHandshake(handshakingSession);
1501
+ return handshakingSession;
1502
+ }
1503
+ rejectHandshakeResponse(session, reason, metadata) {
1504
+ session.conn.telemetry?.span.setStatus({
1505
+ code: SpanStatusCode2.ERROR,
1506
+ message: reason
1507
+ });
1508
+ this.log?.warn(reason, metadata);
1509
+ this.deleteSession(session, { unhealthy: true });
1510
+ }
1511
+ onHandshakeResponse(session, msg) {
1512
+ if (!Value2.Check(ControlMessageHandshakeResponseSchema, msg.payload)) {
1513
+ const reason = `received invalid handshake response`;
1514
+ this.rejectHandshakeResponse(session, reason, {
1515
+ ...session.loggingMetadata,
1516
+ transportMessage: msg,
1517
+ validationErrors: [
1518
+ ...Value2.Errors(ControlMessageHandshakeResponseSchema, msg.payload)
1519
+ ]
1520
+ });
1521
+ return;
1522
+ }
1523
+ if (!msg.payload.status.ok) {
1524
+ const retriable = Value2.Check(
1525
+ HandshakeErrorRetriableResponseCodes,
1526
+ msg.payload.status.code
1527
+ );
1528
+ const reason = `handshake failed: ${msg.payload.status.reason}`;
1529
+ const to = session.to;
1530
+ this.rejectHandshakeResponse(session, reason, {
1531
+ ...session.loggingMetadata,
1532
+ transportMessage: msg
1533
+ });
1534
+ if (retriable) {
1535
+ this.tryReconnecting(to);
1536
+ } else {
1537
+ this.protocolError({
1538
+ type: ProtocolError.HandshakeFailed,
1539
+ code: msg.payload.status.code,
1540
+ message: reason
1541
+ });
1542
+ }
1543
+ return;
1544
+ }
1545
+ if (msg.payload.status.sessionId !== session.id) {
1546
+ const reason = `session id mismatch: expected ${session.id}, got ${msg.payload.status.sessionId}`;
1547
+ this.rejectHandshakeResponse(session, reason, {
1548
+ ...session.loggingMetadata,
1549
+ transportMessage: msg
1550
+ });
1551
+ return;
1552
+ }
1553
+ this.log?.info(`handshake from ${msg.from} ok`, {
1554
+ ...session.loggingMetadata,
1555
+ transportMessage: msg
1556
+ });
1557
+ const connectedSession = ClientSessionStateGraph.transition.HandshakingToConnected(session, {
1558
+ onConnectionErrored: (err) => {
1559
+ const errStr = coerceErrorString(err);
1560
+ this.log?.warn(
1561
+ `connection to ${connectedSession.to} errored: ${errStr}`,
1562
+ connectedSession.loggingMetadata
1563
+ );
1564
+ },
1565
+ onConnectionClosed: () => {
1566
+ this.log?.info(
1567
+ `connection to ${connectedSession.to} closed`,
1568
+ connectedSession.loggingMetadata
1569
+ );
1570
+ this.onConnClosed(connectedSession);
1571
+ },
1572
+ onMessage: (msg2) => {
1573
+ this.handleMsg(msg2);
1574
+ },
1575
+ onInvalidMessage: (reason) => {
1576
+ this.log?.error(`invalid message: ${reason}`, {
1577
+ ...connectedSession.loggingMetadata,
1578
+ transportMessage: msg
1579
+ });
1580
+ this.protocolError({
1581
+ type: ProtocolError.InvalidMessage,
1582
+ message: reason
1583
+ });
1584
+ this.deleteSession(connectedSession, { unhealthy: true });
1585
+ },
1586
+ onMessageSendFailure: (msg2, reason) => {
1587
+ this.log?.error(`failed to send message: ${reason}`, {
1588
+ ...connectedSession.loggingMetadata,
1589
+ transportMessage: msg2
1590
+ });
1591
+ this.protocolError({
1592
+ type: ProtocolError.MessageSendFailure,
1593
+ message: reason
1594
+ });
1595
+ this.deleteSession(connectedSession, { unhealthy: true });
1596
+ }
1597
+ });
1598
+ const res = connectedSession.sendBufferedMessages();
1599
+ if (!res.ok) {
1600
+ this.log?.error(`failed to send buffered messages: ${res.reason}`, {
1601
+ ...connectedSession.loggingMetadata,
1602
+ transportMessage: msg
1603
+ });
1604
+ this.protocolError({
1605
+ type: ProtocolError.MessageSendFailure,
1606
+ message: res.reason
1607
+ });
1608
+ this.deleteSession(connectedSession, { unhealthy: true });
1609
+ return;
1610
+ }
1611
+ this.updateSession(connectedSession);
1612
+ this.retryBudget.startRestoringBudget();
1613
+ }
1614
+ /**
1615
+ * Manually attempts to connect to a client.
1616
+ * @param to The client ID of the node to connect to.
1617
+ */
1618
+ connect(to) {
1619
+ if (this.getStatus() !== "open") {
1620
+ this.log?.info(
1621
+ `transport state is no longer open, cancelling attempt to connect to ${to}`
1622
+ );
1623
+ return;
1624
+ }
1625
+ const session = this.sessions.get(to) ?? this.createUnconnectedSession(to);
1626
+ if (session.state !== "NoConnection" /* NoConnection */) {
1627
+ this.log?.debug(
1628
+ `session to ${to} has state ${session.state}, skipping connect attempt`,
1629
+ session.loggingMetadata
1630
+ );
1631
+ return;
1632
+ }
1633
+ if (!this.retryBudget.hasBudget()) {
1634
+ const budgetConsumed = this.retryBudget.getBudgetConsumed();
1635
+ const errMsg = `tried to connect to ${to} but retry budget exceeded (more than ${budgetConsumed} attempts in the last ${this.retryBudget.totalBudgetRestoreTime}ms)`;
1636
+ this.log?.error(errMsg, session.loggingMetadata);
1637
+ this.protocolError({
1638
+ type: ProtocolError.RetriesExceeded,
1639
+ message: errMsg
1640
+ });
1641
+ return;
1642
+ }
1643
+ const backoffMs = this.retryBudget.getBackoffMs();
1644
+ this.log?.info(
1645
+ `attempting connection to ${to} (${backoffMs}ms backoff)`,
1646
+ session.loggingMetadata
1647
+ );
1648
+ this.retryBudget.consumeBudget();
1649
+ const backingOffSession = ClientSessionStateGraph.transition.NoConnectionToBackingOff(
1650
+ session,
1651
+ backoffMs,
1652
+ {
1653
+ onBackoffFinished: () => {
1654
+ this.onBackoffFinished(backingOffSession);
1655
+ },
1656
+ onSessionGracePeriodElapsed: () => {
1657
+ this.onSessionGracePeriodElapsed(backingOffSession);
1658
+ }
1659
+ }
1660
+ );
1661
+ this.updateSession(backingOffSession);
1662
+ }
1663
+ /**
1664
+ * Manually kills all sessions to the server (including all pending state).
1665
+ * This is useful for when you want to close all connections to a server
1666
+ * and don't want to wait for the grace period to elapse.
1667
+ */
1668
+ hardDisconnect() {
1669
+ const sessions = Array.from(this.sessions.values());
1670
+ for (const session of sessions) {
1671
+ this.deleteSession(session);
1672
+ }
1673
+ }
1674
+ onBackoffFinished(session) {
1675
+ const connPromise = session.tracer.startActiveSpan(
1676
+ "connect",
1677
+ async (span) => {
1678
+ try {
1679
+ return await this.createNewOutgoingConnection(session.to);
1680
+ } catch (err) {
1681
+ const errStr = coerceErrorString(err);
1682
+ span.recordException(errStr);
1683
+ span.setStatus({ code: SpanStatusCode2.ERROR });
1684
+ throw err;
1685
+ } finally {
1686
+ span.end();
1687
+ }
1688
+ }
1689
+ );
1690
+ const connectingSession = ClientSessionStateGraph.transition.BackingOffToConnecting(
1691
+ session,
1692
+ connPromise,
1693
+ {
1694
+ onConnectionEstablished: (conn) => {
1695
+ this.log?.debug(
1696
+ `connection to ${connectingSession.to} established`,
1697
+ {
1698
+ ...conn.loggingMetadata,
1699
+ ...connectingSession.loggingMetadata
1700
+ }
1701
+ );
1702
+ this.onConnectionEstablished(connectingSession, conn);
1703
+ },
1704
+ onConnectionFailed: (error) => {
1705
+ const errStr = coerceErrorString(error);
1706
+ this.log?.error(
1707
+ `error connecting to ${connectingSession.to}: ${errStr}`,
1708
+ connectingSession.loggingMetadata
1709
+ );
1710
+ this.onConnectingFailed(connectingSession);
1711
+ },
1712
+ onConnectionTimeout: () => {
1713
+ this.log?.error(
1714
+ `connection to ${connectingSession.to} timed out`,
1715
+ connectingSession.loggingMetadata
1716
+ );
1717
+ this.onConnectingFailed(connectingSession);
1718
+ },
1719
+ onSessionGracePeriodElapsed: () => {
1720
+ this.onSessionGracePeriodElapsed(connectingSession);
1721
+ }
1722
+ }
1723
+ );
1724
+ this.updateSession(connectingSession);
1725
+ }
1726
+ async sendHandshake(session) {
1727
+ let metadata = void 0;
1728
+ if (this.handshakeExtensions) {
1729
+ metadata = await this.handshakeExtensions.construct();
1730
+ }
1731
+ if (session._isConsumed) {
1732
+ return;
1733
+ }
1734
+ const requestMsg = handshakeRequestMessage({
1735
+ from: this.clientId,
1736
+ to: session.to,
1737
+ sessionId: session.id,
1738
+ expectedSessionState: {
1739
+ nextExpectedSeq: session.ack,
1740
+ nextSentSeq: session.nextSeq()
1741
+ },
1742
+ metadata,
1743
+ tracing: getPropagationContext(session.telemetry.ctx)
1744
+ });
1745
+ this.log?.debug(`sending handshake request to ${session.to}`, {
1746
+ ...session.loggingMetadata,
1747
+ transportMessage: requestMsg
1748
+ });
1749
+ const res = session.sendHandshake(requestMsg);
1750
+ if (!res.ok) {
1751
+ this.log?.error(`failed to send handshake request: ${res.reason}`, {
1752
+ ...session.loggingMetadata,
1753
+ transportMessage: requestMsg
1754
+ });
1755
+ this.protocolError({
1756
+ type: ProtocolError.MessageSendFailure,
1757
+ message: res.reason
1758
+ });
1759
+ this.deleteSession(session, { unhealthy: true });
1760
+ }
1761
+ }
1762
+ close() {
1763
+ this.retryBudget.close();
1764
+ super.close();
1765
+ }
1766
+ };
1767
+
1768
+ // transport/server.ts
1769
+ import { SpanStatusCode as SpanStatusCode3 } from "@opentelemetry/api";
1770
+ import { Value as Value3 } from "@sinclair/typebox/value";
1771
+ var ServerTransport = class extends Transport {
1772
+ /**
1773
+ * The options for this transport.
1774
+ */
1775
+ options;
1776
+ /**
1777
+ * Optional handshake options for the server.
1778
+ */
1779
+ handshakeExtensions;
1780
+ /**
1781
+ * A map of session handshake data for each session.
1782
+ */
1783
+ sessionHandshakeMetadata = /* @__PURE__ */ new Map();
1784
+ sessions = /* @__PURE__ */ new Map();
1785
+ pendingSessions = /* @__PURE__ */ new Set();
1786
+ constructor(clientId, providedOptions) {
1787
+ super(clientId, providedOptions);
1788
+ this.sessions = /* @__PURE__ */ new Map();
1789
+ this.options = {
1790
+ ...defaultServerTransportOptions,
1791
+ ...providedOptions
1792
+ };
1793
+ this.log?.info(`initiated server transport`, {
1794
+ clientId: this.clientId,
1795
+ protocolVersion: currentProtocolVersion
1796
+ });
1797
+ }
1798
+ extendHandshake(options) {
1799
+ this.handshakeExtensions = options;
1800
+ }
1801
+ deletePendingSession(pendingSession) {
1802
+ pendingSession.close();
1803
+ this.pendingSessions.delete(pendingSession);
1804
+ }
1805
+ deleteSession(session, options) {
1806
+ this.sessionHandshakeMetadata.delete(session.to);
1807
+ super.deleteSession(session, options);
1808
+ }
1809
+ handleConnection(conn) {
1810
+ if (this.getStatus() !== "open")
1811
+ return;
1812
+ this.log?.info(`new incoming connection`, {
1813
+ ...conn.loggingMetadata,
1814
+ clientId: this.clientId
1815
+ });
1816
+ let receivedHandshake = false;
1817
+ const pendingSession = ServerSessionStateGraph.entrypoint(
1818
+ this.clientId,
1819
+ conn,
1820
+ {
1821
+ onConnectionClosed: () => {
1822
+ this.log?.warn(
1823
+ `connection from unknown closed before handshake finished`,
1824
+ pendingSession.loggingMetadata
1825
+ );
1826
+ this.deletePendingSession(pendingSession);
1827
+ },
1828
+ onConnectionErrored: (err) => {
1829
+ const errorString = coerceErrorString(err);
1830
+ this.log?.warn(
1831
+ `connection from unknown errored before handshake finished: ${errorString}`,
1832
+ pendingSession.loggingMetadata
1833
+ );
1834
+ this.deletePendingSession(pendingSession);
1835
+ },
1836
+ onHandshakeTimeout: () => {
1837
+ this.log?.warn(
1838
+ `connection from unknown timed out before handshake finished`,
1839
+ pendingSession.loggingMetadata
1840
+ );
1841
+ this.deletePendingSession(pendingSession);
1842
+ },
1843
+ onHandshake: (msg) => {
1844
+ if (receivedHandshake) {
1845
+ this.log?.error(
1846
+ `received multiple handshake messages from pending session`,
1847
+ {
1848
+ ...pendingSession.loggingMetadata,
1849
+ connectedTo: msg.from,
1850
+ transportMessage: msg
1851
+ }
1852
+ );
1853
+ this.deletePendingSession(pendingSession);
1854
+ return;
1855
+ }
1856
+ receivedHandshake = true;
1857
+ void this.onHandshakeRequest(pendingSession, msg);
1858
+ },
1859
+ onInvalidHandshake: (reason, code) => {
1860
+ this.log?.error(
1861
+ `invalid handshake: ${reason}`,
1862
+ pendingSession.loggingMetadata
1863
+ );
1864
+ this.deletePendingSession(pendingSession);
1865
+ this.protocolError({
1866
+ type: ProtocolError.HandshakeFailed,
1867
+ code,
1868
+ message: reason
1869
+ });
1870
+ }
1871
+ },
1872
+ this.options,
1873
+ this.tracer,
1874
+ this.log
1875
+ );
1876
+ this.pendingSessions.add(pendingSession);
1877
+ }
1878
+ rejectHandshakeRequest(session, to, reason, code, metadata) {
1879
+ session.conn.telemetry?.span.setStatus({
1880
+ code: SpanStatusCode3.ERROR,
1881
+ message: reason
1882
+ });
1883
+ this.log?.warn(reason, metadata);
1884
+ const responseMsg = handshakeResponseMessage({
1885
+ from: this.clientId,
1886
+ to,
1887
+ status: {
1888
+ ok: false,
1889
+ code,
1890
+ reason
1891
+ }
1892
+ });
1893
+ const res = session.sendHandshake(responseMsg);
1894
+ if (!res.ok) {
1895
+ this.log?.error(`failed to send handshake response: ${res.reason}`, {
1896
+ ...session.loggingMetadata,
1897
+ transportMessage: responseMsg
1898
+ });
1899
+ this.protocolError({
1900
+ type: ProtocolError.MessageSendFailure,
1901
+ message: res.reason
1902
+ });
1903
+ this.deletePendingSession(session);
1904
+ return;
1905
+ }
1906
+ this.protocolError({
1907
+ type: ProtocolError.HandshakeFailed,
1908
+ code,
1909
+ message: reason
1910
+ });
1911
+ this.deletePendingSession(session);
1912
+ }
1913
+ async onHandshakeRequest(session, msg) {
1914
+ if (!Value3.Check(ControlMessageHandshakeRequestSchema, msg.payload)) {
1915
+ this.rejectHandshakeRequest(
1916
+ session,
1917
+ msg.from,
1918
+ "received invalid handshake request",
1919
+ "MALFORMED_HANDSHAKE",
1920
+ {
1921
+ ...session.loggingMetadata,
1922
+ transportMessage: msg,
1923
+ connectedTo: msg.from,
1924
+ validationErrors: [
1925
+ ...Value3.Errors(ControlMessageHandshakeRequestSchema, msg.payload)
1926
+ ]
1927
+ }
1928
+ );
1929
+ return;
1930
+ }
1931
+ const gotVersion = msg.payload.protocolVersion;
1932
+ if (!isAcceptedProtocolVersion(gotVersion)) {
1933
+ this.rejectHandshakeRequest(
1934
+ session,
1935
+ msg.from,
1936
+ `expected protocol version oneof [${acceptedProtocolVersions.toString()}], got ${gotVersion}`,
1937
+ "PROTOCOL_VERSION_MISMATCH",
1938
+ {
1939
+ ...session.loggingMetadata,
1940
+ connectedTo: msg.from,
1941
+ transportMessage: msg
1942
+ }
1943
+ );
1944
+ return;
1945
+ }
1946
+ let parsedMetadata = {};
1947
+ if (this.handshakeExtensions) {
1948
+ if (!Value3.Check(this.handshakeExtensions.schema, msg.payload.metadata)) {
1949
+ this.rejectHandshakeRequest(
1950
+ session,
1951
+ msg.from,
1952
+ "received malformed handshake metadata",
1953
+ "MALFORMED_HANDSHAKE_META",
1954
+ {
1955
+ ...session.loggingMetadata,
1956
+ connectedTo: msg.from,
1957
+ validationErrors: [
1958
+ ...Value3.Errors(
1959
+ this.handshakeExtensions.schema,
1960
+ msg.payload.metadata
1961
+ )
1962
+ ]
1963
+ }
1964
+ );
1965
+ return;
1966
+ }
1967
+ const previousParsedMetadata = this.sessionHandshakeMetadata.get(
1968
+ msg.from
1969
+ );
1970
+ const parsedMetadataOrFailureCode = await this.handshakeExtensions.validate(
1971
+ msg.payload.metadata,
1972
+ previousParsedMetadata
1973
+ );
1974
+ if (session._isConsumed) {
1975
+ return;
1976
+ }
1977
+ if (Value3.Check(
1978
+ HandshakeErrorCustomHandlerFatalResponseCodes,
1979
+ parsedMetadataOrFailureCode
1980
+ )) {
1981
+ this.rejectHandshakeRequest(
1982
+ session,
1983
+ msg.from,
1984
+ "rejected by handshake handler",
1985
+ parsedMetadataOrFailureCode,
1986
+ {
1987
+ ...session.loggingMetadata,
1988
+ connectedTo: msg.from,
1989
+ clientId: this.clientId
1990
+ }
1991
+ );
1992
+ return;
1993
+ }
1994
+ parsedMetadata = parsedMetadataOrFailureCode;
1995
+ }
1996
+ let connectCase = "new session";
1997
+ const clientNextExpectedSeq = msg.payload.expectedSessionState.nextExpectedSeq;
1998
+ const clientNextSentSeq = msg.payload.expectedSessionState.nextSentSeq;
1999
+ let oldSession = this.sessions.get(msg.from);
2000
+ if (this.options.enableTransparentSessionReconnects && oldSession && oldSession.id === msg.payload.sessionId) {
2001
+ connectCase = "transparent reconnection";
2002
+ const ourNextSeq = oldSession.nextSeq();
2003
+ const ourAck = oldSession.ack;
2004
+ if (clientNextSentSeq > ourAck) {
2005
+ this.rejectHandshakeRequest(
2006
+ session,
2007
+ msg.from,
2008
+ `client is in the future: server wanted next message to be ${ourAck} but client would have sent ${clientNextSentSeq}`,
2009
+ "SESSION_STATE_MISMATCH",
2010
+ {
2011
+ ...session.loggingMetadata,
2012
+ connectedTo: msg.from,
2013
+ transportMessage: msg
2014
+ }
2015
+ );
2016
+ return;
2017
+ }
2018
+ if (ourNextSeq > clientNextExpectedSeq) {
2019
+ this.rejectHandshakeRequest(
2020
+ session,
2021
+ msg.from,
2022
+ `server is in the future: client wanted next message to be ${clientNextExpectedSeq} but server would have sent ${ourNextSeq}`,
2023
+ "SESSION_STATE_MISMATCH",
2024
+ {
2025
+ ...session.loggingMetadata,
2026
+ connectedTo: msg.from,
2027
+ transportMessage: msg
2028
+ }
2029
+ );
2030
+ return;
2031
+ }
2032
+ if (oldSession.state !== "NoConnection" /* NoConnection */) {
2033
+ const noConnectionSession = ServerSessionStateGraph.transition.ConnectedToNoConnection(
2034
+ oldSession,
2035
+ {
2036
+ onSessionGracePeriodElapsed: () => {
2037
+ this.onSessionGracePeriodElapsed(noConnectionSession);
2038
+ }
2039
+ }
2040
+ );
2041
+ oldSession = noConnectionSession;
2042
+ this.updateSession(oldSession);
2043
+ }
2044
+ } else if (oldSession) {
2045
+ connectCase = "hard reconnection";
2046
+ this.log?.info(
2047
+ `client is reconnecting to a new session (${msg.payload.sessionId}) with an old session (${oldSession.id}) already existing, closing old session`,
2048
+ {
2049
+ ...session.loggingMetadata,
2050
+ connectedTo: msg.from,
2051
+ sessionId: msg.payload.sessionId
2052
+ }
2053
+ );
2054
+ this.deleteSession(oldSession);
2055
+ oldSession = void 0;
2056
+ }
2057
+ if (!oldSession && (clientNextSentSeq > 0 || clientNextExpectedSeq > 0)) {
2058
+ connectCase = "unknown session";
2059
+ const rejectionMessage = this.options.enableTransparentSessionReconnects ? `client is trying to reconnect to a session the server don't know about: ${msg.payload.sessionId}` : `client is attempting a transparent reconnect to a session but the server does not support it: ${msg.payload.sessionId}`;
2060
+ this.rejectHandshakeRequest(
2061
+ session,
2062
+ msg.from,
2063
+ rejectionMessage,
2064
+ "SESSION_STATE_MISMATCH",
2065
+ {
2066
+ ...session.loggingMetadata,
2067
+ connectedTo: msg.from,
2068
+ transportMessage: msg
2069
+ }
2070
+ );
2071
+ return;
2072
+ }
2073
+ const sessionId = msg.payload.sessionId;
2074
+ this.log?.info(
2075
+ `handshake from ${msg.from} ok (${connectCase}), responding with handshake success`,
2076
+ {
2077
+ ...session.loggingMetadata,
2078
+ connectedTo: msg.from
2079
+ }
2080
+ );
2081
+ const responseMsg = handshakeResponseMessage({
2082
+ from: this.clientId,
2083
+ to: msg.from,
2084
+ status: {
2085
+ ok: true,
2086
+ sessionId
2087
+ }
2088
+ });
2089
+ const res = session.sendHandshake(responseMsg);
2090
+ if (!res.ok) {
2091
+ this.log?.error(`failed to send handshake response: ${res.reason}`, {
2092
+ ...session.loggingMetadata,
2093
+ transportMessage: responseMsg
2094
+ });
2095
+ this.protocolError({
2096
+ type: ProtocolError.MessageSendFailure,
2097
+ message: res.reason
2098
+ });
2099
+ this.deletePendingSession(session);
2100
+ return;
2101
+ }
2102
+ this.pendingSessions.delete(session);
2103
+ const connectedSession = ServerSessionStateGraph.transition.WaitingForHandshakeToConnected(
2104
+ session,
2105
+ // by this point oldSession is either no connection or we dont have an old session
2106
+ oldSession,
2107
+ sessionId,
2108
+ msg.from,
2109
+ msg.tracing,
2110
+ {
2111
+ onConnectionErrored: (err) => {
2112
+ const errStr = coerceErrorString(err);
2113
+ this.log?.warn(
2114
+ `connection to ${connectedSession.to} errored: ${errStr}`,
2115
+ connectedSession.loggingMetadata
2116
+ );
2117
+ },
2118
+ onConnectionClosed: () => {
2119
+ this.log?.info(
2120
+ `connection to ${connectedSession.to} closed`,
2121
+ connectedSession.loggingMetadata
2122
+ );
2123
+ this.onConnClosed(connectedSession);
2124
+ },
2125
+ onMessage: (msg2) => {
2126
+ this.handleMsg(msg2);
2127
+ },
2128
+ onInvalidMessage: (reason) => {
2129
+ this.log?.error(`invalid message: ${reason}`, {
2130
+ ...connectedSession.loggingMetadata,
2131
+ transportMessage: msg
2132
+ });
2133
+ this.protocolError({
2134
+ type: ProtocolError.InvalidMessage,
2135
+ message: reason
2136
+ });
2137
+ this.deleteSession(connectedSession, { unhealthy: true });
2138
+ },
2139
+ onMessageSendFailure: (msg2, reason) => {
2140
+ this.log?.error(`failed to send message: ${reason}`, {
2141
+ ...connectedSession.loggingMetadata,
2142
+ transportMessage: msg2
2143
+ });
2144
+ this.protocolError({
2145
+ type: ProtocolError.MessageSendFailure,
2146
+ message: reason
2147
+ });
2148
+ this.deleteSession(connectedSession, { unhealthy: true });
2149
+ }
2150
+ },
2151
+ gotVersion
2152
+ );
2153
+ const bufferSendRes = connectedSession.sendBufferedMessages();
2154
+ if (!bufferSendRes.ok) {
2155
+ this.log?.error(
2156
+ `failed to send buffered messages: ${bufferSendRes.reason}`,
2157
+ {
2158
+ ...connectedSession.loggingMetadata,
2159
+ transportMessage: msg
2160
+ }
2161
+ );
2162
+ this.protocolError({
2163
+ type: ProtocolError.MessageSendFailure,
2164
+ message: bufferSendRes.reason
2165
+ });
2166
+ this.deleteSession(connectedSession, { unhealthy: true });
2167
+ return;
2168
+ }
2169
+ this.sessionHandshakeMetadata.set(connectedSession.to, parsedMetadata);
2170
+ if (oldSession) {
2171
+ this.updateSession(connectedSession);
2172
+ } else {
2173
+ this.createSession(connectedSession);
2174
+ }
2175
+ connectedSession.startActiveHeartbeat();
1174
2176
  }
1175
2177
  };
1176
2178
 
@@ -1262,15 +2264,17 @@ var Connection = class {
1262
2264
  };
1263
2265
 
1264
2266
  export {
2267
+ BinaryCodec,
2268
+ NaiveJsonCodec,
2269
+ ProtocolError,
1265
2270
  defaultTransportOptions,
1266
2271
  defaultClientTransportOptions,
1267
- defaultServerTransportOptions,
1268
2272
  SessionState,
1269
2273
  SessionStateGraph,
1270
- ClientSessionStateGraph,
1271
- ServerSessionStateGraph,
1272
- ProtocolError,
1273
2274
  Transport,
1274
- Connection
2275
+ ClientTransport,
2276
+ ServerTransport,
2277
+ Connection,
2278
+ CodecMessageAdapter
1275
2279
  };
1276
- //# sourceMappingURL=chunk-QMAVXV4Z.js.map
2280
+ //# sourceMappingURL=chunk-BO7MFCO6.js.map