@replit/river 0.207.2 → 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-4HE7UYRL.js → chunk-B7REV3ZV.js} +6 -5
  3. package/dist/{chunk-4HE7UYRL.js.map → chunk-B7REV3ZV.js.map} +1 -1
  4. package/dist/{chunk-24EWYOGK.js → chunk-BO7MFCO6.js} +1136 -143
  5. package/dist/chunk-BO7MFCO6.js.map +1 -0
  6. package/dist/{chunk-46IVOKJU.js → chunk-QGPYCXV4.js} +2 -2
  7. package/dist/{chunk-46IVOKJU.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-a18e31d5.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-43528f4b.d.ts → services-87887bc5.d.ts} +16 -12
  25. package/dist/testUtil/index.cjs +809 -657
  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 -204
  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 -128
  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 -270
  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-24EWYOGK.js.map +0 -1
  50. package/dist/chunk-A7RGOVRV.js +0 -438
  51. package/dist/chunk-A7RGOVRV.js.map +0 -1
  52. package/dist/chunk-AJGIY2UB.js +0 -56
  53. package/dist/chunk-AJGIY2UB.js.map +0 -1
  54. package/dist/chunk-XV4RQ62N.js +0 -377
  55. package/dist/chunk-XV4RQ62N.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-46IVOKJU.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,25 +520,15 @@ var SessionConnected = class extends IdentifiedSession {
426
520
  conn;
427
521
  listeners;
428
522
  heartbeatHandle;
429
- heartbeatMisses = 0;
430
- isActivelyHeartbeating;
431
- lastConstructedMsgs = [];
432
- pushLastConstructedMsgs = (msg) => {
433
- const trackedMsg = {
434
- id: msg.id,
435
- seq: msg.seq,
436
- streamId: msg.streamId,
437
- stack: new Error().stack
438
- };
439
- this.lastConstructedMsgs.push(trackedMsg);
440
- if (this.lastConstructedMsgs.length > 10) {
441
- this.lastConstructedMsgs.shift();
442
- }
443
- };
523
+ heartbeatMissTimeout;
524
+ isActivelyHeartbeating = false;
444
525
  updateBookkeeping(ack, seq) {
445
526
  this.sendBuffer = this.sendBuffer.filter((unacked) => unacked.seq >= ack);
446
527
  this.ack = seq + 1;
447
- this.heartbeatMisses = 0;
528
+ if (this.heartbeatMissTimeout) {
529
+ clearTimeout(this.heartbeatMissTimeout);
530
+ }
531
+ this.startMissingHeartbeatTimeout();
448
532
  }
449
533
  assertSendOrdering(constructedMsg) {
450
534
  if (constructedMsg.seq > this.seqSent + 1) {
@@ -452,22 +536,22 @@ var SessionConnected = class extends IdentifiedSession {
452
536
  this.log?.error(msg, {
453
537
  ...this.loggingMetadata,
454
538
  transportMessage: constructedMsg,
455
- tags: ["invariant-violation"],
456
- extras: {
457
- lastConstructedMsgs: this.lastConstructedMsgs
458
- }
539
+ tags: ["invariant-violation"]
459
540
  });
460
541
  throw new Error(msg);
461
542
  }
462
543
  }
463
544
  send(msg) {
464
545
  const constructedMsg = this.constructMsg(msg);
465
- this.pushLastConstructedMsgs(constructedMsg);
466
546
  this.assertSendOrdering(constructedMsg);
467
547
  this.sendBuffer.push(constructedMsg);
468
- 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
+ }
469
553
  this.seqSent = constructedMsg.seq;
470
- return constructedMsg.id;
554
+ return res;
471
555
  }
472
556
  constructor(props) {
473
557
  super(props);
@@ -476,6 +560,8 @@ var SessionConnected = class extends IdentifiedSession {
476
560
  this.conn.addDataListener(this.onMessageData);
477
561
  this.conn.addCloseListener(this.listeners.onConnectionClosed);
478
562
  this.conn.addErrorListener(this.listeners.onConnectionErrored);
563
+ }
564
+ sendBufferedMessages() {
479
565
  if (this.sendBuffer.length > 0) {
480
566
  this.log?.info(
481
567
  `sending ${this.sendBuffer.length} buffered messages, starting at seq ${this.nextSeq()}`,
@@ -483,30 +569,15 @@ var SessionConnected = class extends IdentifiedSession {
483
569
  );
484
570
  for (const msg of this.sendBuffer) {
485
571
  this.assertSendOrdering(msg);
486
- 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
+ }
487
577
  this.seqSent = msg.seq;
488
578
  }
489
579
  }
490
- this.isActivelyHeartbeating = false;
491
- this.heartbeatHandle = setInterval(() => {
492
- const misses = this.heartbeatMisses;
493
- const missDuration = misses * this.options.heartbeatIntervalMs;
494
- if (misses >= this.options.heartbeatsUntilDead) {
495
- this.log?.info(
496
- `closing connection to ${this.to} due to inactivity (missed ${misses} heartbeats which is ${missDuration}ms)`,
497
- this.loggingMetadata
498
- );
499
- this.telemetry.span.addEvent("closing connection due to inactivity");
500
- this.conn.close();
501
- clearInterval(this.heartbeatHandle);
502
- this.heartbeatHandle = void 0;
503
- return;
504
- }
505
- if (this.isActivelyHeartbeating) {
506
- this.sendHeartbeat();
507
- }
508
- this.heartbeatMisses++;
509
- }, this.options.heartbeatIntervalMs);
580
+ return { ok: true, value: void 0 };
510
581
  }
511
582
  get loggingMetadata() {
512
583
  return {
@@ -514,25 +585,46 @@ var SessionConnected = class extends IdentifiedSession {
514
585
  ...this.conn.loggingMetadata
515
586
  };
516
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
+ }
517
602
  startActiveHeartbeat() {
518
603
  this.isActivelyHeartbeating = true;
604
+ this.heartbeatHandle = setInterval(() => {
605
+ this.sendHeartbeat();
606
+ }, this.options.heartbeatIntervalMs);
519
607
  }
520
608
  sendHeartbeat() {
521
609
  this.log?.debug("sending heartbeat", this.loggingMetadata);
522
- this.send({
610
+ const heartbeat = {
523
611
  streamId: "heartbeat",
524
612
  controlFlags: 1 /* AckBit */,
525
613
  payload: {
526
614
  type: "ACK"
527
615
  }
528
- });
616
+ };
617
+ this.send(heartbeat);
529
618
  }
530
619
  onMessageData = (msg) => {
531
- const parsedMsg = this.parseMsg(msg);
532
- if (parsedMsg === null) {
533
- 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
+ );
534
625
  return;
535
626
  }
627
+ const parsedMsg = parsedMsgRes.value;
536
628
  if (parsedMsg.seq !== this.ack) {
537
629
  if (parsedMsg.seq < this.ack) {
538
630
  this.log?.debug(
@@ -571,9 +663,7 @@ var SessionConnected = class extends IdentifiedSession {
571
663
  transportMessage: parsedMsg
572
664
  });
573
665
  if (!this.isActivelyHeartbeating) {
574
- void Promise.resolve().then(() => {
575
- this.sendHeartbeat();
576
- });
666
+ this.sendHeartbeat();
577
667
  }
578
668
  };
579
669
  _handleStateExit() {
@@ -585,6 +675,10 @@ var SessionConnected = class extends IdentifiedSession {
585
675
  clearInterval(this.heartbeatHandle);
586
676
  this.heartbeatHandle = void 0;
587
677
  }
678
+ if (this.heartbeatMissTimeout) {
679
+ clearTimeout(this.heartbeatMissTimeout);
680
+ this.heartbeatMissTimeout = void 0;
681
+ }
588
682
  }
589
683
  _handleClose() {
590
684
  super._handleClose();
@@ -616,6 +710,62 @@ var SessionBackingOff = class extends IdentifiedSessionWithGracePeriod {
616
710
  }
617
711
  };
618
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
+
619
769
  // transport/sessionStateMachine/transitions.ts
620
770
  function inheritSharedSession(session) {
621
771
  return {
@@ -630,7 +780,8 @@ function inheritSharedSession(session) {
630
780
  options: session.options,
631
781
  log: session.log,
632
782
  tracer: session.tracer,
633
- protocolVersion: session.protocolVersion
783
+ protocolVersion: session.protocolVersion,
784
+ codec: session.codec
634
785
  };
635
786
  }
636
787
  function inheritSharedSessionWithGrace(session) {
@@ -659,7 +810,8 @@ var SessionStateGraph = {
659
810
  options,
660
811
  protocolVersion,
661
812
  tracer,
662
- log
813
+ log,
814
+ codec: new CodecMessageAdapter(options.codec)
663
815
  });
664
816
  session.log?.info(`session ${session.id} created in NoConnection state`, {
665
817
  ...session.loggingMetadata,
@@ -674,7 +826,8 @@ var SessionStateGraph = {
674
826
  from,
675
827
  options,
676
828
  tracer,
677
- log
829
+ log,
830
+ codec: new CodecMessageAdapter(options.codec)
678
831
  });
679
832
  session.log?.info(`session created in WaitingForHandshake state`, {
680
833
  ...session.loggingMetadata,
@@ -752,6 +905,7 @@ var SessionStateGraph = {
752
905
  listeners,
753
906
  ...carriedState
754
907
  });
908
+ session.startMissingHeartbeatTimeout();
755
909
  session.log?.info(
756
910
  `session ${session.id} transition from Handshaking to Connected`,
757
911
  {
@@ -787,7 +941,8 @@ var SessionStateGraph = {
787
941
  options,
788
942
  tracer: pendingSession.tracer,
789
943
  log: pendingSession.log,
790
- protocolVersion
944
+ protocolVersion,
945
+ codec: new CodecMessageAdapter(options.codec)
791
946
  }
792
947
  );
793
948
  pendingSession._handleStateExit();
@@ -797,6 +952,7 @@ var SessionStateGraph = {
797
952
  listeners,
798
953
  ...carriedState
799
954
  });
955
+ session.startMissingHeartbeatTimeout();
800
956
  conn.telemetry = createConnectionTelemetryInfo(
801
957
  session.tracer,
802
958
  conn,
@@ -927,44 +1083,6 @@ var ServerSessionStateGraph = {
927
1083
  }
928
1084
  };
929
1085
 
930
- // transport/events.ts
931
- var ProtocolError = {
932
- RetriesExceeded: "conn_retry_exceeded",
933
- HandshakeFailed: "handshake_failed",
934
- MessageOrderingViolated: "message_ordering_violated",
935
- InvalidMessage: "invalid_message"
936
- };
937
- var EventDispatcher = class {
938
- eventListeners = {};
939
- removeAllListeners() {
940
- this.eventListeners = {};
941
- }
942
- numberOfListeners(eventType) {
943
- return this.eventListeners[eventType]?.size ?? 0;
944
- }
945
- addEventListener(eventType, handler) {
946
- if (!this.eventListeners[eventType]) {
947
- this.eventListeners[eventType] = /* @__PURE__ */ new Set();
948
- }
949
- this.eventListeners[eventType]?.add(handler);
950
- }
951
- removeEventListener(eventType, handler) {
952
- const handlers = this.eventListeners[eventType];
953
- if (handlers) {
954
- this.eventListeners[eventType]?.delete(handler);
955
- }
956
- }
957
- dispatchEvent(eventType, event) {
958
- const handlers = this.eventListeners[eventType];
959
- if (handlers) {
960
- const copy = [...handlers];
961
- for (const handler of copy) {
962
- handler(event);
963
- }
964
- }
965
- }
966
- };
967
-
968
1086
  // transport/transport.ts
969
1087
  var Transport = class {
970
1088
  /**
@@ -1175,13 +1293,886 @@ var Transport = class {
1175
1293
  );
1176
1294
  }
1177
1295
  const sameSession = session.id === sessionId;
1178
- if (!sameSession) {
1296
+ if (!sameSession || session._isConsumed) {
1179
1297
  throw new Error(
1180
1298
  `session scope for ${sessionId} has ended (transition), can't send`
1181
1299
  );
1182
1300
  }
1183
- 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;
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
1184
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();
1185
2176
  }
1186
2177
  };
1187
2178
 
@@ -1273,15 +2264,17 @@ var Connection = class {
1273
2264
  };
1274
2265
 
1275
2266
  export {
2267
+ BinaryCodec,
2268
+ NaiveJsonCodec,
2269
+ ProtocolError,
1276
2270
  defaultTransportOptions,
1277
2271
  defaultClientTransportOptions,
1278
- defaultServerTransportOptions,
1279
2272
  SessionState,
1280
2273
  SessionStateGraph,
1281
- ClientSessionStateGraph,
1282
- ServerSessionStateGraph,
1283
- ProtocolError,
1284
2274
  Transport,
1285
- Connection
2275
+ ClientTransport,
2276
+ ServerTransport,
2277
+ Connection,
2278
+ CodecMessageAdapter
1286
2279
  };
1287
- //# sourceMappingURL=chunk-24EWYOGK.js.map
2280
+ //# sourceMappingURL=chunk-BO7MFCO6.js.map