@replit/river 0.21.0 → 0.22.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (68) hide show
  1. package/README.md +1 -1
  2. package/dist/{chunk-5WFL722S.js → chunk-3MFX6NXA.js} +94 -3
  3. package/dist/chunk-3MFX6NXA.js.map +1 -0
  4. package/dist/{chunk-NFV77C2M.js → chunk-GCLEWC26.js} +340 -507
  5. package/dist/chunk-GCLEWC26.js.map +1 -0
  6. package/dist/chunk-HUBFYN37.js +60 -0
  7. package/dist/chunk-HUBFYN37.js.map +1 -0
  8. package/dist/{chunk-DT5JS6TM.js → chunk-OTQNCLFH.js} +1 -1
  9. package/dist/chunk-OTQNCLFH.js.map +1 -0
  10. package/dist/{chunk-MJR36SUY.js → chunk-S3YKQT4J.js} +2 -2
  11. package/dist/{chunk-QU2EE6YU.js → chunk-ZPBWKBM5.js} +361 -394
  12. package/dist/chunk-ZPBWKBM5.js.map +1 -0
  13. package/dist/{connection-8a71dbe2.d.ts → connection-8b059ac4.d.ts} +6 -4
  14. package/dist/{connection-d49d5d56.d.ts → connection-bbfe1147.d.ts} +1 -1
  15. package/dist/{index-3ac92295.d.ts → index-2ece5234.d.ts} +18 -7
  16. package/dist/logging/index.cjs.map +1 -1
  17. package/dist/logging/index.d.cts +2 -1
  18. package/dist/logging/index.d.ts +2 -1
  19. package/dist/logging/index.js +1 -1
  20. package/dist/router/index.cjs +384 -492
  21. package/dist/router/index.cjs.map +1 -1
  22. package/dist/router/index.d.cts +5 -4
  23. package/dist/router/index.d.ts +5 -4
  24. package/dist/router/index.js +5 -4
  25. package/dist/{services-abc077db.d.ts → services-acbcc441.d.ts} +1 -1
  26. package/dist/{services-8496d6e8.d.ts → services-cb01a7a8.d.ts} +1 -1
  27. package/dist/transport/impls/uds/client.cjs +202 -155
  28. package/dist/transport/impls/uds/client.cjs.map +1 -1
  29. package/dist/transport/impls/uds/client.d.cts +3 -2
  30. package/dist/transport/impls/uds/client.d.ts +3 -2
  31. package/dist/transport/impls/uds/client.js +4 -4
  32. package/dist/transport/impls/uds/server.cjs +295 -264
  33. package/dist/transport/impls/uds/server.cjs.map +1 -1
  34. package/dist/transport/impls/uds/server.d.cts +3 -2
  35. package/dist/transport/impls/uds/server.d.ts +3 -2
  36. package/dist/transport/impls/uds/server.js +4 -4
  37. package/dist/transport/impls/ws/client.cjs +256 -214
  38. package/dist/transport/impls/ws/client.cjs.map +1 -1
  39. package/dist/transport/impls/ws/client.d.cts +6 -6
  40. package/dist/transport/impls/ws/client.d.ts +6 -6
  41. package/dist/transport/impls/ws/client.js +34 -49
  42. package/dist/transport/impls/ws/client.js.map +1 -1
  43. package/dist/transport/impls/ws/server.cjs +317 -278
  44. package/dist/transport/impls/ws/server.cjs.map +1 -1
  45. package/dist/transport/impls/ws/server.d.cts +5 -4
  46. package/dist/transport/impls/ws/server.d.ts +5 -4
  47. package/dist/transport/impls/ws/server.js +4 -4
  48. package/dist/transport/impls/ws/server.js.map +1 -1
  49. package/dist/transport/index.cjs +406 -391
  50. package/dist/transport/index.cjs.map +1 -1
  51. package/dist/transport/index.d.cts +5 -5
  52. package/dist/transport/index.d.ts +5 -5
  53. package/dist/transport/index.js +3 -3
  54. package/dist/util/testHelpers.cjs +71 -19
  55. package/dist/util/testHelpers.cjs.map +1 -1
  56. package/dist/util/testHelpers.d.cts +20 -8
  57. package/dist/util/testHelpers.d.ts +20 -8
  58. package/dist/util/testHelpers.js +13 -12
  59. package/dist/util/testHelpers.js.map +1 -1
  60. package/dist/wslike-e0b32dd5.d.ts +40 -0
  61. package/package.json +4 -5
  62. package/dist/chunk-2ERP6FUE.js +0 -42
  63. package/dist/chunk-2ERP6FUE.js.map +0 -1
  64. package/dist/chunk-5WFL722S.js.map +0 -1
  65. package/dist/chunk-DT5JS6TM.js.map +0 -1
  66. package/dist/chunk-NFV77C2M.js.map +0 -1
  67. package/dist/chunk-QU2EE6YU.js.map +0 -1
  68. /package/dist/{chunk-MJR36SUY.js.map → chunk-S3YKQT4J.js.map} +0 -0
@@ -4,31 +4,37 @@ import {
4
4
  OpaqueTransportMessageSchema,
5
5
  PROTOCOL_VERSION,
6
6
  coerceErrorString,
7
+ createConnectionTelemetryInfo,
8
+ createSessionTelemetryInfo,
9
+ getPropagationContext,
7
10
  handshakeRequestMessage,
8
11
  handshakeResponseMessage,
9
12
  isAck,
10
13
  tracing_default
11
- } from "./chunk-5WFL722S.js";
14
+ } from "./chunk-3MFX6NXA.js";
12
15
  import {
13
16
  log
14
- } from "./chunk-DT5JS6TM.js";
17
+ } from "./chunk-OTQNCLFH.js";
15
18
  import {
16
19
  NaiveJsonCodec
17
20
  } from "./chunk-3AW3IXVD.js";
18
21
 
19
22
  // transport/session.ts
20
23
  import { customAlphabet } from "nanoid";
24
+ import { SpanStatusCode } from "@opentelemetry/api";
21
25
  var nanoid = customAlphabet("1234567890abcdefghijklmnopqrstuvxyz", 6);
22
26
  var unsafeId = () => nanoid();
23
27
  var Connection = class {
24
- debugId;
28
+ id;
29
+ telemetry;
25
30
  constructor() {
26
- this.debugId = `conn-${unsafeId()}`;
31
+ this.id = `conn-${nanoid(12)}`;
27
32
  }
28
33
  };
29
34
  var Session = class {
30
35
  codec;
31
36
  options;
37
+ telemetry;
32
38
  /**
33
39
  * The buffer of messages that have been sent but not yet acknowledged.
34
40
  */
@@ -75,7 +81,7 @@ var Session = class {
75
81
  * The interval for sending heartbeats.
76
82
  */
77
83
  heartbeat;
78
- constructor(conn, from, to, options) {
84
+ constructor(conn, from, to, options, propagationCtx) {
79
85
  this.id = `session-${nanoid(12)}`;
80
86
  this.options = options;
81
87
  this.from = from;
@@ -87,13 +93,14 @@ var Session = class {
87
93
  () => this.sendHeartbeat(),
88
94
  options.heartbeatIntervalMs
89
95
  );
96
+ this.telemetry = createSessionTelemetryInfo(this, propagationCtx);
90
97
  }
91
98
  get loggingMetadata() {
92
99
  return {
93
100
  clientId: this.from,
94
101
  connectedTo: this.to,
95
102
  sessionId: this.id,
96
- connId: this.connection?.debugId
103
+ connId: this.connection?.id
97
104
  };
98
105
  }
99
106
  /**
@@ -138,6 +145,7 @@ var Session = class {
138
145
  `closing connection to ${this.to} due to inactivity (missed ${misses} heartbeats which is ${missDuration}ms)`,
139
146
  this.loggingMetadata
140
147
  );
148
+ this.telemetry.span.addEvent("closing connection due to inactivity");
141
149
  this.closeStaleConnection();
142
150
  }
143
151
  return;
@@ -159,32 +167,38 @@ var Session = class {
159
167
  sendBufferedMessages(conn) {
160
168
  log?.info(`resending ${this.sendBuffer.length} buffered messages`, {
161
169
  ...this.loggingMetadata,
162
- connId: conn.debugId
170
+ connId: conn.id
163
171
  });
164
172
  for (const msg of this.sendBuffer) {
165
173
  log?.debug(`resending msg`, {
166
174
  ...this.loggingMetadata,
167
175
  fullTransportMessage: msg,
168
- connId: conn.debugId
176
+ connId: conn.id
169
177
  });
170
178
  const ok = conn.send(this.codec.toBuffer(msg));
171
179
  if (!ok) {
172
180
  const errMsg = `failed to send buffered message to ${this.to} (sus, this is a fresh connection)`;
181
+ conn.telemetry?.span.setStatus({
182
+ code: SpanStatusCode.ERROR,
183
+ message: errMsg
184
+ });
173
185
  log?.error(errMsg, {
174
186
  ...this.loggingMetadata,
175
187
  fullTransportMessage: msg,
176
- connId: conn.debugId
188
+ connId: conn.id,
189
+ tags: ["invariant-violation"]
177
190
  });
178
- throw new Error(errMsg);
191
+ conn.close();
192
+ return;
179
193
  }
180
194
  }
181
195
  }
182
196
  updateBookkeeping(ack, seq) {
183
197
  if (seq + 1 < this.ack) {
184
- log?.error(
185
- `received stale seq ${seq} + 1 < ${this.ack}`,
186
- this.loggingMetadata
187
- );
198
+ log?.error(`received stale seq ${seq} + 1 < ${this.ack}`, {
199
+ ...this.loggingMetadata,
200
+ tags: ["invariant-violation"]
201
+ });
188
202
  return;
189
203
  }
190
204
  this.sendBuffer = this.sendBuffer.filter((unacked) => unacked.seq >= ack);
@@ -290,12 +304,6 @@ var EventDispatcher = class {
290
304
 
291
305
  // transport/transport.ts
292
306
  import { Value } from "@sinclair/typebox/value";
293
- import {
294
- context,
295
- propagation,
296
- SpanKind,
297
- SpanStatusCode
298
- } from "@opentelemetry/api";
299
307
 
300
308
  // transport/rateLimit.ts
301
309
  var LeakyBucketRateLimit = class {
@@ -369,6 +377,7 @@ var LeakyBucketRateLimit = class {
369
377
  };
370
378
 
371
379
  // transport/transport.ts
380
+ import { SpanStatusCode as SpanStatusCode2 } from "@opentelemetry/api";
372
381
  var defaultTransportOptions = {
373
382
  heartbeatIntervalMs: 1e3,
374
383
  heartbeatsUntilDead: 2,
@@ -447,17 +456,22 @@ var Transport = class {
447
456
  status: "connect",
448
457
  conn
449
458
  });
459
+ conn.telemetry = createConnectionTelemetryInfo(
460
+ conn,
461
+ session.telemetry.span
462
+ );
450
463
  if (isReconnect) {
451
464
  session.replaceWithNewConnection(conn);
452
465
  log?.info(`reconnected to ${connectedTo}`, session.loggingMetadata);
453
466
  }
454
467
  }
455
- createSession(to, conn) {
468
+ createSession(to, conn, propagationCtx) {
456
469
  const session = new Session(
457
470
  conn,
458
471
  this.clientId,
459
472
  to,
460
- this.options
473
+ this.options,
474
+ propagationCtx
461
475
  );
462
476
  this.sessions.set(session.to, session);
463
477
  this.eventDispatcher.dispatchEvent("sessionStatus", {
@@ -466,11 +480,11 @@ var Transport = class {
466
480
  });
467
481
  return session;
468
482
  }
469
- getOrCreateSession(to, conn, sessionId) {
483
+ getOrCreateSession(to, conn, sessionId, propagationCtx) {
470
484
  let session = this.sessions.get(to);
471
485
  let isReconnect = session !== void 0;
472
486
  if (session?.advertisedSessionId !== void 0 && sessionId !== void 0 && session.advertisedSessionId !== sessionId) {
473
- log?.warn(
487
+ log?.info(
474
488
  `session for ${to} already exists but has a different session id (expected: ${session.advertisedSessionId}, got: ${sessionId}), creating a new one`,
475
489
  session.loggingMetadata
476
490
  );
@@ -479,7 +493,7 @@ var Transport = class {
479
493
  session = void 0;
480
494
  }
481
495
  if (!session) {
482
- session = this.createSession(to, conn);
496
+ session = this.createSession(to, conn, propagationCtx);
483
497
  log?.info(
484
498
  `no session for ${to}, created a new one`,
485
499
  session.loggingMetadata
@@ -492,6 +506,7 @@ var Transport = class {
492
506
  }
493
507
  deleteSession(session) {
494
508
  session.close();
509
+ session.telemetry.span.end();
495
510
  this.sessions.delete(session.to);
496
511
  log?.info(
497
512
  `session ${session.id} disconnect from ${session.to}`,
@@ -508,12 +523,16 @@ var Transport = class {
508
523
  * @param connectedTo The peer we are connected to.
509
524
  */
510
525
  onDisconnect(conn, session) {
526
+ conn.telemetry?.span.end();
511
527
  this.eventDispatcher.dispatchEvent("connectionStatus", {
512
528
  status: "disconnect",
513
529
  conn
514
530
  });
515
531
  session.connection = void 0;
516
- session.beginGrace(() => this.deleteSession(session));
532
+ session.beginGrace(() => {
533
+ session.telemetry.span.addEvent("session grace period expired");
534
+ this.deleteSession(session);
535
+ });
517
536
  }
518
537
  /**
519
538
  * Parses a message from a Uint8Array into a {@link OpaqueTransportMessage}.
@@ -547,9 +566,10 @@ var Transport = class {
547
566
  return;
548
567
  const session = this.sessions.get(msg.from);
549
568
  if (!session) {
550
- log?.error(`(invariant violation) no existing session for ${msg.from}`, {
569
+ log?.error(`no existing session for ${msg.from}`, {
551
570
  clientId: this.clientId,
552
- fullTransportMessage: msg
571
+ fullTransportMessage: msg,
572
+ tags: ["invariant-violation"]
553
573
  });
554
574
  return;
555
575
  }
@@ -568,9 +588,14 @@ var Transport = class {
568
588
  const errMsg = `received out-of-order msg (got seq: ${msg.seq}, wanted seq: ${session.nextExpectedSeq})`;
569
589
  log?.error(`${errMsg}, marking connection as dead`, {
570
590
  clientId: this.clientId,
571
- fullTransportMessage: msg
591
+ fullTransportMessage: msg,
592
+ tags: ["invariant-violation"]
572
593
  });
573
594
  this.protocolError(ProtocolError.MessageOrderingViolated, errMsg);
595
+ session.telemetry.span.setStatus({
596
+ code: SpanStatusCode2.ERROR,
597
+ message: "message order violated"
598
+ });
574
599
  session.close();
575
600
  }
576
601
  return;
@@ -612,7 +637,8 @@ var Transport = class {
612
637
  const err = "transport is destroyed, cant send";
613
638
  log?.error(err, {
614
639
  clientId: this.clientId,
615
- partialTransportMessage: msg
640
+ partialTransportMessage: msg,
641
+ tags: ["invariant-violation"]
616
642
  });
617
643
  this.protocolError(ProtocolError.UseAfterDestroy, err);
618
644
  return void 0;
@@ -697,7 +723,7 @@ var ClientTransport = class extends Transport {
697
723
  if (!session) {
698
724
  log?.warn(
699
725
  `connection to ${to} timed out waiting for handshake, closing`,
700
- { clientId: this.clientId, connectedTo: to, connId: conn.debugId }
726
+ { clientId: this.clientId, connectedTo: to, connId: conn.id }
701
727
  );
702
728
  conn.close();
703
729
  }
@@ -715,6 +741,10 @@ var ClientTransport = class extends Transport {
715
741
  conn.addDataListener((data2) => {
716
742
  const parsed = this.parseMsg(data2);
717
743
  if (!parsed) {
744
+ conn.telemetry?.span.setStatus({
745
+ code: SpanStatusCode2.ERROR,
746
+ message: "message parse failure"
747
+ });
718
748
  conn.close();
719
749
  return;
720
750
  }
@@ -737,6 +767,10 @@ var ClientTransport = class extends Transport {
737
767
  }
738
768
  });
739
769
  conn.addErrorListener((err) => {
770
+ conn.telemetry?.span.setStatus({
771
+ code: SpanStatusCode2.ERROR,
772
+ message: "connection error"
773
+ });
740
774
  log?.warn(`error in connection to ${to}: ${coerceErrorString(err)}`, {
741
775
  ...session?.loggingMetadata,
742
776
  clientId: this.clientId,
@@ -747,6 +781,10 @@ var ClientTransport = class extends Transport {
747
781
  receiveHandshakeResponseMessage(data, conn) {
748
782
  const parsed = this.parseMsg(data);
749
783
  if (!parsed) {
784
+ conn.telemetry?.span.setStatus({
785
+ code: SpanStatusCode2.ERROR,
786
+ message: "non-transport message"
787
+ });
750
788
  this.protocolError(
751
789
  ProtocolError.HandshakeFailed,
752
790
  "received non-transport message"
@@ -754,6 +792,10 @@ var ClientTransport = class extends Transport {
754
792
  return false;
755
793
  }
756
794
  if (!Value.Check(ControlMessageHandshakeResponseSchema, parsed.payload)) {
795
+ conn.telemetry?.span.setStatus({
796
+ code: SpanStatusCode2.ERROR,
797
+ message: "invalid handshake response"
798
+ });
757
799
  log?.warn(`received invalid handshake resp`, {
758
800
  clientId: this.clientId,
759
801
  connectedTo: parsed.from,
@@ -766,7 +808,11 @@ var ClientTransport = class extends Transport {
766
808
  return false;
767
809
  }
768
810
  if (!parsed.payload.status.ok) {
769
- log?.warn(`received invalid handshake resp`, {
811
+ conn.telemetry?.span.setStatus({
812
+ code: SpanStatusCode2.ERROR,
813
+ message: "handshake rejected"
814
+ });
815
+ log?.warn(`received handshake rejection`, {
770
816
  clientId: this.clientId,
771
817
  connectedTo: parsed.from,
772
818
  fullTransportMessage: parsed
@@ -796,142 +842,94 @@ var ClientTransport = class extends Transport {
796
842
  * @param to The client ID of the node to connect to.
797
843
  */
798
844
  async connect(to) {
799
- return tracing_default.startActiveSpan(
800
- "connect",
801
- {
802
- attributes: {
803
- component: "river",
804
- "span.kind": "client"
805
- },
806
- kind: SpanKind.CLIENT
807
- },
808
- async (span) => {
809
- try {
810
- await this.connectAttempt(to);
811
- } catch (e) {
812
- if (e instanceof Error) {
813
- span.recordException(e);
814
- } else {
815
- span.recordException(coerceErrorString(e));
816
- }
817
- span.setStatus({ code: SpanStatusCode.ERROR });
818
- } finally {
819
- span.end();
820
- }
845
+ const canProceedWithConnection = () => this.state === "open";
846
+ if (!canProceedWithConnection()) {
847
+ log?.info(
848
+ `transport state is no longer open, cancelling attempt to connect to ${to}`,
849
+ { clientId: this.clientId, connectedTo: to }
850
+ );
851
+ return;
852
+ }
853
+ let reconnectPromise = this.inflightConnectionPromises.get(to);
854
+ if (!reconnectPromise) {
855
+ const budgetConsumed = this.retryBudget.getBudgetConsumed(to);
856
+ if (!this.retryBudget.hasBudget(to)) {
857
+ const errMsg = `tried to connect to ${to} but retry budget exceeded (more than ${budgetConsumed} attempts in the last ${this.retryBudget.totalBudgetRestoreTime}ms)`;
858
+ log?.warn(errMsg, { clientId: this.clientId, connectedTo: to });
859
+ this.protocolError(ProtocolError.RetriesExceeded, errMsg);
860
+ return;
821
861
  }
822
- );
823
- }
824
- async connectAttempt(to, attempt = 0) {
825
- const retry = await tracing_default.startActiveSpan(
826
- "connect",
827
- {
828
- attributes: {
829
- component: "river",
830
- "river.attempt": attempt,
831
- "span.kind": "client"
832
- },
833
- kind: SpanKind.CLIENT
834
- },
835
- async (span) => {
862
+ let sleep = Promise.resolve();
863
+ const backoffMs = this.retryBudget.getBackoffMs(to);
864
+ if (backoffMs > 0) {
865
+ sleep = new Promise((resolve) => setTimeout(resolve, backoffMs));
866
+ }
867
+ log?.info(`attempting connection to ${to} (${backoffMs}ms backoff)`, {
868
+ clientId: this.clientId,
869
+ connectedTo: to
870
+ });
871
+ this.retryBudget.consumeBudget(to);
872
+ reconnectPromise = tracing_default.startActiveSpan("connect", async (span) => {
836
873
  try {
837
- const canProceedWithConnection = () => this.state === "open";
874
+ span.addEvent("backoff", { backoffMs });
875
+ await sleep;
838
876
  if (!canProceedWithConnection()) {
839
- log?.info(
840
- `transport state is no longer open, cancelling attempt to connect to ${to}`,
841
- { clientId: this.clientId, connectedTo: to }
842
- );
843
- return false;
877
+ throw new Error("transport state is no longer open");
844
878
  }
845
- let reconnectPromise = this.inflightConnectionPromises.get(to);
846
- if (!reconnectPromise) {
847
- const budgetConsumed = this.retryBudget.getBudgetConsumed(to);
848
- if (!this.retryBudget.hasBudget(to)) {
849
- const errMsg = `tried to connect to ${to} but retry budget exceeded (more than ${budgetConsumed} attempts in the last ${this.retryBudget.totalBudgetRestoreTime}ms)`;
850
- log?.warn(errMsg, { clientId: this.clientId, connectedTo: to });
851
- this.protocolError(ProtocolError.RetriesExceeded, errMsg);
852
- return false;
853
- }
854
- let sleep = Promise.resolve();
855
- const backoffMs = this.retryBudget.getBackoffMs(to);
856
- if (backoffMs > 0) {
857
- sleep = new Promise((resolve) => setTimeout(resolve, backoffMs));
858
- }
859
- log?.info(
860
- `attempting connection to ${to} (${backoffMs}ms backoff)`,
861
- {
862
- clientId: this.clientId,
863
- connectedTo: to
864
- }
865
- );
866
- this.retryBudget.consumeBudget(to);
867
- reconnectPromise = sleep.then(() => {
868
- if (!canProceedWithConnection()) {
869
- throw new Error("transport state is no longer open");
870
- }
871
- }).then(() => this.createNewOutgoingConnection(to)).then((conn) => {
872
- if (!canProceedWithConnection()) {
873
- log?.info(
874
- `transport state is no longer open, closing pre-handshake connection to ${to}`,
875
- {
876
- clientId: this.clientId,
877
- connectedTo: to,
878
- connId: conn.debugId
879
- }
880
- );
881
- conn.close();
882
- throw new Error("transport state is no longer open");
883
- }
884
- return this.sendHandshake(to, conn).then((ok) => {
885
- if (!ok) {
886
- conn.close();
887
- throw new Error("failed to send handshake");
888
- }
889
- return conn;
890
- });
891
- });
892
- this.inflightConnectionPromises.set(to, reconnectPromise);
893
- } else {
879
+ span.addEvent("connecting");
880
+ const conn = await this.createNewOutgoingConnection(to);
881
+ if (!canProceedWithConnection()) {
894
882
  log?.info(
895
- `attempting connection to ${to} (reusing previous attempt)`,
883
+ `transport state is no longer open, closing pre-handshake connection to ${to}`,
896
884
  {
897
885
  clientId: this.clientId,
898
- connectedTo: to
886
+ connectedTo: to,
887
+ connId: conn.id
899
888
  }
900
889
  );
890
+ conn.close();
891
+ throw new Error("transport state is no longer open");
901
892
  }
902
- try {
903
- await reconnectPromise;
904
- } catch (error) {
905
- this.inflightConnectionPromises.delete(to);
906
- const errStr = coerceErrorString(error);
907
- if (!this.reconnectOnConnectionDrop || !canProceedWithConnection()) {
908
- log?.warn(`connection to ${to} failed (${errStr})`, {
909
- clientId: this.clientId,
910
- connectedTo: to
911
- });
912
- } else {
913
- log?.warn(`connection to ${to} failed (${errStr}), retrying`, {
914
- clientId: this.clientId,
915
- connectedTo: to
916
- });
917
- return true;
918
- }
919
- }
920
- } catch (e) {
921
- if (e instanceof Error) {
922
- span.recordException(e);
923
- } else {
924
- span.recordException(coerceErrorString(e));
893
+ span.addEvent("sending handshake");
894
+ const ok = await this.sendHandshake(to, conn);
895
+ if (!ok) {
896
+ conn.close();
897
+ throw new Error("failed to send handshake");
925
898
  }
926
- span.setStatus({ code: SpanStatusCode.ERROR });
899
+ return conn;
900
+ } catch (err) {
901
+ const errStr = coerceErrorString(err);
902
+ span.recordException(errStr);
903
+ span.setStatus({ code: SpanStatusCode2.ERROR });
904
+ throw err;
927
905
  } finally {
928
906
  span.end();
929
907
  }
930
- return false;
908
+ });
909
+ this.inflightConnectionPromises.set(to, reconnectPromise);
910
+ } else {
911
+ log?.info(`attempting connection to ${to} (reusing previous attempt)`, {
912
+ clientId: this.clientId,
913
+ connectedTo: to
914
+ });
915
+ }
916
+ try {
917
+ await reconnectPromise;
918
+ } catch (error) {
919
+ this.inflightConnectionPromises.delete(to);
920
+ const errStr = coerceErrorString(error);
921
+ if (!this.reconnectOnConnectionDrop || !canProceedWithConnection()) {
922
+ log?.warn(`connection to ${to} failed (${errStr})`, {
923
+ clientId: this.clientId,
924
+ connectedTo: to
925
+ });
926
+ } else {
927
+ log?.warn(`connection to ${to} failed (${errStr}), retrying`, {
928
+ clientId: this.clientId,
929
+ connectedTo: to
930
+ });
931
+ return this.connect(to);
931
932
  }
932
- );
933
- if (retry) {
934
- return this.connectAttempt(to, attempt + 1);
935
933
  }
936
934
  }
937
935
  deleteSession(session) {
@@ -939,20 +937,23 @@ var ClientTransport = class extends Transport {
939
937
  super.deleteSession(session);
940
938
  }
941
939
  async sendHandshake(to, conn) {
942
- const tracing = { traceparent: "", tracestate: "" };
943
- propagation.inject(context.active(), tracing);
944
940
  let metadata;
945
941
  if (this.options.handshake) {
946
942
  metadata = await this.options.handshake.get();
947
943
  if (!Value.Check(this.options.handshake.schema, metadata)) {
948
944
  log?.error(`handshake metadata did not match schema`, {
949
945
  clientId: this.clientId,
950
- connectedTo: to
946
+ connectedTo: to,
947
+ tags: ["invariant-violation"]
951
948
  });
952
949
  this.protocolError(
953
950
  ProtocolError.HandshakeFailed,
954
951
  "handshake metadata did not match schema"
955
952
  );
953
+ conn.telemetry?.span.setStatus({
954
+ code: SpanStatusCode2.ERROR,
955
+ message: "handshake meta mismatch"
956
+ });
956
957
  return false;
957
958
  }
958
959
  }
@@ -962,7 +963,7 @@ var ClientTransport = class extends Transport {
962
963
  to,
963
964
  session.id,
964
965
  metadata,
965
- tracing
966
+ getPropagationContext(session.telemetry.ctx)
966
967
  );
967
968
  log?.debug(`sending handshake request to ${to}`, {
968
969
  clientId: this.clientId,
@@ -993,267 +994,233 @@ var ServerTransport = class extends Transport {
993
994
  });
994
995
  }
995
996
  handleConnection(conn) {
996
- tracing_default.startActiveSpan(
997
- "handleConnection",
998
- {
999
- attributes: {
1000
- component: "river",
1001
- "span.kind": "server"
1002
- },
1003
- kind: SpanKind.SERVER
1004
- },
1005
- (span) => {
1006
- if (this.state !== "open")
1007
- return;
1008
- log?.info(`new incoming connection`, {
1009
- clientId: this.clientId,
1010
- connId: conn.debugId
997
+ if (this.state !== "open")
998
+ return;
999
+ log?.info(`new incoming connection`, {
1000
+ clientId: this.clientId,
1001
+ connId: conn.id
1002
+ });
1003
+ let session = void 0;
1004
+ const client = () => session?.to ?? "unknown";
1005
+ const handshakeTimeout = setTimeout(() => {
1006
+ if (!session) {
1007
+ log?.warn(
1008
+ `connection to ${client()} timed out waiting for handshake, closing`,
1009
+ {
1010
+ clientId: this.clientId,
1011
+ connectedTo: client(),
1012
+ connId: conn.id
1013
+ }
1014
+ );
1015
+ conn.telemetry?.span.setStatus({
1016
+ code: SpanStatusCode2.ERROR,
1017
+ message: "handshake timeout"
1011
1018
  });
1012
- let session = void 0;
1013
- const client = () => session?.to ?? "unknown";
1014
- const handshakeTimeout = setTimeout(() => {
1015
- if (!session) {
1016
- log?.warn(
1017
- `connection to ${client()} timed out waiting for handshake, closing`,
1018
- {
1019
- clientId: this.clientId,
1020
- connectedTo: client(),
1021
- connId: conn.debugId
1022
- }
1023
- );
1024
- span.setStatus({ code: SpanStatusCode.ERROR });
1025
- span.end();
1019
+ conn.close();
1020
+ }
1021
+ }, this.options.sessionDisconnectGraceMs);
1022
+ const buffer = [];
1023
+ let receivedHandshakeMessage = false;
1024
+ const handshakeHandler = (data) => {
1025
+ if (receivedHandshakeMessage) {
1026
+ buffer.push(data);
1027
+ return;
1028
+ }
1029
+ receivedHandshakeMessage = true;
1030
+ clearTimeout(handshakeTimeout);
1031
+ void this.receiveHandshakeRequestMessage(data, conn).then(
1032
+ (maybeSession) => {
1033
+ if (!maybeSession) {
1026
1034
  conn.close();
1027
- }
1028
- }, this.options.sessionDisconnectGraceMs);
1029
- const buffer = [];
1030
- let receivedHandshakeMessage = false;
1031
- const handshakeHandler = (data) => {
1032
- if (receivedHandshakeMessage) {
1033
- buffer.push(data);
1034
1035
  return;
1035
1036
  }
1036
- receivedHandshakeMessage = true;
1037
- clearTimeout(handshakeTimeout);
1038
- void this.receiveHandshakeRequestMessage(data, conn).then(
1039
- (maybeSession) => {
1040
- if (!maybeSession) {
1041
- span.setStatus({ code: SpanStatusCode.ERROR });
1042
- span.end();
1043
- conn.close();
1044
- return;
1045
- }
1046
- session = maybeSession;
1047
- const dataHandler = (data2) => {
1048
- const parsed = this.parseMsg(data2);
1049
- if (!parsed) {
1050
- conn.close();
1051
- return;
1052
- }
1053
- this.handleMsg(parsed);
1054
- };
1055
- conn.removeDataListener(handshakeHandler);
1056
- conn.addDataListener(dataHandler);
1057
- for (const data2 of buffer) {
1058
- dataHandler(data2);
1059
- }
1060
- buffer.length = 0;
1037
+ session = maybeSession;
1038
+ const dataHandler = (data2) => {
1039
+ const parsed = this.parseMsg(data2);
1040
+ if (!parsed) {
1041
+ conn.close();
1042
+ return;
1061
1043
  }
1062
- );
1063
- };
1064
- conn.addDataListener(handshakeHandler);
1065
- conn.addCloseListener(() => {
1066
- if (session) {
1067
- log?.info(`connection to ${client()} disconnected`, {
1068
- clientId: this.clientId,
1069
- connId: conn.debugId
1070
- });
1071
- this.onDisconnect(conn, session);
1072
- }
1073
- span.setStatus({ code: SpanStatusCode.OK });
1074
- span.end();
1075
- });
1076
- conn.addErrorListener((err) => {
1077
- if (session) {
1078
- log?.warn(
1079
- `connection to ${client()} got an error: ${coerceErrorString(
1080
- err
1081
- )}`,
1082
- { clientId: this.clientId, connId: conn.debugId }
1083
- );
1044
+ this.handleMsg(parsed);
1045
+ };
1046
+ for (const data2 of buffer) {
1047
+ dataHandler(data2);
1084
1048
  }
1085
- span.setStatus({ code: SpanStatusCode.ERROR });
1086
- span.end();
1087
- });
1088
- }
1089
- );
1049
+ conn.removeDataListener(handshakeHandler);
1050
+ conn.addDataListener(dataHandler);
1051
+ buffer.length = 0;
1052
+ }
1053
+ );
1054
+ };
1055
+ conn.addDataListener(handshakeHandler);
1056
+ conn.addCloseListener(() => {
1057
+ if (!session)
1058
+ return;
1059
+ log?.info(`connection to ${client()} disconnected`, {
1060
+ clientId: this.clientId,
1061
+ connId: conn.id
1062
+ });
1063
+ this.onDisconnect(conn, session);
1064
+ });
1065
+ conn.addErrorListener((err) => {
1066
+ conn.telemetry?.span.setStatus({
1067
+ code: SpanStatusCode2.ERROR,
1068
+ message: "connection error"
1069
+ });
1070
+ if (!session)
1071
+ return;
1072
+ log?.warn(
1073
+ `connection to ${client()} got an error: ${coerceErrorString(err)}`,
1074
+ { clientId: this.clientId, connId: conn.id }
1075
+ );
1076
+ });
1090
1077
  }
1091
1078
  async receiveHandshakeRequestMessage(data, conn) {
1092
1079
  const parsed = this.parseMsg(data);
1093
1080
  if (!parsed) {
1081
+ conn.telemetry?.span.setStatus({
1082
+ code: SpanStatusCode2.ERROR,
1083
+ message: "non-transport message"
1084
+ });
1094
1085
  this.protocolError(
1095
1086
  ProtocolError.HandshakeFailed,
1096
1087
  "received non-transport message"
1097
1088
  );
1098
1089
  return false;
1099
1090
  }
1100
- let activeContext = context.active();
1101
- if (parsed.tracing) {
1102
- activeContext = propagation.extract(activeContext, parsed.tracing);
1091
+ if (!Value.Check(ControlMessageHandshakeRequestSchema, parsed.payload)) {
1092
+ conn.telemetry?.span.setStatus({
1093
+ code: SpanStatusCode2.ERROR,
1094
+ message: "invalid handshake request"
1095
+ });
1096
+ const reason = "received invalid handshake msg";
1097
+ const responseMsg2 = handshakeResponseMessage(this.clientId, parsed.from, {
1098
+ ok: false,
1099
+ reason
1100
+ });
1101
+ conn.send(this.codec.toBuffer(responseMsg2));
1102
+ const logData = { ...parsed.payload ?? {}, metadata: "redacted" };
1103
+ log?.warn(reason, {
1104
+ clientId: this.clientId,
1105
+ connId: conn.id,
1106
+ partialTransportMessage: { ...parsed, payload: logData }
1107
+ });
1108
+ this.protocolError(
1109
+ ProtocolError.HandshakeFailed,
1110
+ "invalid handshake request"
1111
+ );
1112
+ return false;
1103
1113
  }
1104
- return tracing_default.startActiveSpan(
1105
- "receiveHandshakeRequestMessage",
1106
- {
1107
- attributes: {
1108
- component: "river",
1109
- "span.kind": "server"
1110
- },
1111
- kind: SpanKind.SERVER
1112
- },
1113
- activeContext,
1114
- async (span) => {
1115
- if (!Value.Check(ControlMessageHandshakeRequestSchema, parsed.payload)) {
1116
- const reason = "received invalid handshake msg";
1117
- const responseMsg2 = handshakeResponseMessage(
1118
- this.clientId,
1119
- parsed.from,
1120
- {
1121
- ok: false,
1122
- reason
1123
- }
1124
- );
1125
- conn.send(this.codec.toBuffer(responseMsg2));
1126
- const logData = typeof parsed.payload === "object" ? {
1127
- ...parsed,
1128
- payload: { ...parsed.payload, metadata: "redacted" }
1129
- } : { ...parsed };
1130
- log?.warn(`${reason}: ${JSON.stringify(logData)}`, {
1131
- clientId: this.clientId,
1132
- connId: conn.debugId
1133
- });
1134
- this.protocolError(
1135
- ProtocolError.HandshakeFailed,
1136
- "invalid handshake request"
1137
- );
1138
- span.setStatus({ code: SpanStatusCode.ERROR });
1139
- span.end();
1140
- return false;
1141
- }
1142
- const gotVersion = parsed.payload.protocolVersion;
1143
- if (gotVersion !== PROTOCOL_VERSION) {
1144
- const reason = `incorrect version (got: ${gotVersion} wanted ${PROTOCOL_VERSION})`;
1145
- const responseMsg2 = handshakeResponseMessage(
1146
- this.clientId,
1147
- parsed.from,
1148
- {
1149
- ok: false,
1150
- reason
1151
- }
1152
- );
1153
- conn.send(this.codec.toBuffer(responseMsg2));
1154
- log?.warn(
1155
- `received handshake msg with incompatible protocol version (got: ${gotVersion}, expected: ${PROTOCOL_VERSION})`,
1156
- { clientId: this.clientId, connId: conn.debugId }
1157
- );
1158
- this.protocolError(ProtocolError.HandshakeFailed, reason);
1159
- span.setStatus({ code: SpanStatusCode.ERROR });
1160
- span.end();
1161
- return false;
1162
- }
1163
- const { session, isReconnect } = this.getOrCreateSession(
1114
+ const gotVersion = parsed.payload.protocolVersion;
1115
+ if (gotVersion !== PROTOCOL_VERSION) {
1116
+ conn.telemetry?.span.setStatus({
1117
+ code: SpanStatusCode2.ERROR,
1118
+ message: "incorrect protocol version"
1119
+ });
1120
+ const reason = `incorrect version (got: ${gotVersion} wanted ${PROTOCOL_VERSION})`;
1121
+ const responseMsg2 = handshakeResponseMessage(this.clientId, parsed.from, {
1122
+ ok: false,
1123
+ reason
1124
+ });
1125
+ conn.send(this.codec.toBuffer(responseMsg2));
1126
+ log?.warn(
1127
+ `received handshake msg with incompatible protocol version (got: ${gotVersion}, expected: ${PROTOCOL_VERSION})`,
1128
+ { clientId: this.clientId, connId: conn.id }
1129
+ );
1130
+ this.protocolError(ProtocolError.HandshakeFailed, reason);
1131
+ return false;
1132
+ }
1133
+ const { session, isReconnect } = this.getOrCreateSession(
1134
+ parsed.from,
1135
+ conn,
1136
+ parsed.payload.sessionId,
1137
+ parsed.tracing
1138
+ );
1139
+ let handshakeMetadata;
1140
+ if (this.options.handshake) {
1141
+ if (!Value.Check(
1142
+ this.options.handshake.requestSchema,
1143
+ parsed.payload.metadata
1144
+ )) {
1145
+ conn.telemetry?.span.setStatus({
1146
+ code: SpanStatusCode2.ERROR,
1147
+ message: "malformed handshake meta"
1148
+ });
1149
+ const reason = "received malformed handshake metadata";
1150
+ const responseMsg2 = handshakeResponseMessage(
1151
+ this.clientId,
1164
1152
  parsed.from,
1165
- conn,
1166
- parsed.payload.sessionId
1153
+ { ok: false, reason }
1167
1154
  );
1168
- let handshakeMetadata;
1169
- if (this.options.handshake) {
1170
- if (!Value.Check(
1171
- this.options.handshake.requestSchema,
1172
- parsed.payload.metadata
1173
- )) {
1174
- const reason = "received malformed handshake metadata";
1175
- const responseMsg2 = handshakeResponseMessage(
1176
- this.clientId,
1177
- parsed.from,
1178
- { ok: false, reason }
1179
- );
1180
- conn.send(this.codec.toBuffer(responseMsg2));
1181
- log?.warn(
1182
- `received malformed handshake metadata from ${parsed.from}`,
1183
- {
1184
- clientId: this.clientId,
1185
- connId: conn.debugId
1186
- }
1187
- );
1188
- this.protocolError(ProtocolError.HandshakeFailed, reason);
1189
- this.deleteSession(session);
1190
- span.setStatus({ code: SpanStatusCode.ERROR });
1191
- span.end();
1192
- return false;
1193
- }
1194
- const parsedMetadata = await this.options.handshake.parse(
1195
- parsed.payload.metadata,
1196
- session,
1197
- isReconnect
1198
- );
1199
- if (parsedMetadata === false) {
1200
- const reason = "rejected by server";
1201
- const responseMsg2 = handshakeResponseMessage(
1202
- this.clientId,
1203
- parsed.from,
1204
- { ok: false, reason }
1205
- );
1206
- conn.send(this.codec.toBuffer(responseMsg2));
1207
- log?.warn(`rejected handshake from ${parsed.from}`, {
1208
- clientId: this.clientId,
1209
- connId: conn.debugId
1210
- });
1211
- this.protocolError(ProtocolError.HandshakeFailed, reason);
1212
- this.deleteSession(session);
1213
- span.setStatus({ code: SpanStatusCode.ERROR });
1214
- span.end();
1215
- return false;
1216
- }
1217
- if (!Value.Check(this.options.handshake.parsedSchema, parsedMetadata)) {
1218
- const reason = "failed to parse handshake metadata";
1219
- const responseMsg2 = handshakeResponseMessage(
1220
- this.clientId,
1221
- parsed.from,
1222
- { ok: false, reason }
1223
- );
1224
- conn.send(this.codec.toBuffer(responseMsg2));
1225
- log?.error(`failed to parse handshake metadata`, {
1226
- clientId: this.clientId,
1227
- connId: conn.debugId
1228
- });
1229
- this.protocolError(ProtocolError.HandshakeFailed, reason);
1230
- this.deleteSession(session);
1231
- span.setStatus({ code: SpanStatusCode.ERROR });
1232
- span.end();
1233
- return false;
1234
- }
1235
- handshakeMetadata = parsedMetadata;
1236
- }
1237
- handshakeMetadata ??= {};
1238
- session.metadata = handshakeMetadata;
1239
- log?.debug(
1240
- `handshake from ${parsed.from} ok, responding with handshake success`,
1241
- { clientId: this.clientId, connId: conn.debugId }
1155
+ conn.send(this.codec.toBuffer(responseMsg2));
1156
+ log?.warn(`received malformed handshake metadata from ${parsed.from}`, {
1157
+ clientId: this.clientId,
1158
+ connId: conn.id
1159
+ });
1160
+ this.protocolError(ProtocolError.HandshakeFailed, reason);
1161
+ this.deleteSession(session);
1162
+ return false;
1163
+ }
1164
+ const parsedMetadata = await this.options.handshake.parse(
1165
+ parsed.payload.metadata,
1166
+ session,
1167
+ isReconnect
1168
+ );
1169
+ if (parsedMetadata === false) {
1170
+ conn.telemetry?.span.setStatus({
1171
+ code: SpanStatusCode2.ERROR,
1172
+ message: "rejected by handshake handler"
1173
+ });
1174
+ const reason = "rejected by handshake handler";
1175
+ const responseMsg2 = handshakeResponseMessage(
1176
+ this.clientId,
1177
+ parsed.from,
1178
+ { ok: false, reason }
1242
1179
  );
1243
- const responseMsg = handshakeResponseMessage(
1180
+ conn.send(this.codec.toBuffer(responseMsg2));
1181
+ log?.warn(`rejected handshake from ${parsed.from}`, {
1182
+ clientId: this.clientId,
1183
+ connId: conn.id
1184
+ });
1185
+ this.protocolError(ProtocolError.HandshakeFailed, reason);
1186
+ this.deleteSession(session);
1187
+ return false;
1188
+ }
1189
+ if (!Value.Check(this.options.handshake.parsedSchema, parsedMetadata)) {
1190
+ conn.telemetry?.span.setStatus({
1191
+ code: SpanStatusCode2.ERROR,
1192
+ message: "malformed handshake meta"
1193
+ });
1194
+ const reason = "failed to parse handshake metadata";
1195
+ const responseMsg2 = handshakeResponseMessage(
1244
1196
  this.clientId,
1245
1197
  parsed.from,
1246
- {
1247
- ok: true,
1248
- sessionId: session.id
1249
- }
1198
+ { ok: false, reason }
1250
1199
  );
1251
- conn.send(this.codec.toBuffer(responseMsg));
1252
- this.onConnect(conn, parsed.from, session, isReconnect);
1253
- span.end();
1254
- return session;
1200
+ conn.send(this.codec.toBuffer(responseMsg2));
1201
+ log?.error(`failed to parse handshake metadata`, {
1202
+ clientId: this.clientId,
1203
+ connId: conn.id
1204
+ });
1205
+ this.protocolError(ProtocolError.HandshakeFailed, reason);
1206
+ this.deleteSession(session);
1207
+ return false;
1255
1208
  }
1209
+ handshakeMetadata = parsedMetadata;
1210
+ }
1211
+ handshakeMetadata ??= {};
1212
+ session.metadata = handshakeMetadata;
1213
+ log?.debug(
1214
+ `handshake from ${parsed.from} ok, responding with handshake success`,
1215
+ { clientId: this.clientId, connId: conn.id }
1256
1216
  );
1217
+ const responseMsg = handshakeResponseMessage(this.clientId, parsed.from, {
1218
+ ok: true,
1219
+ sessionId: session.id
1220
+ });
1221
+ conn.send(this.codec.toBuffer(responseMsg));
1222
+ this.onConnect(conn, parsed.from, session, isReconnect);
1223
+ return session;
1257
1224
  }
1258
1225
  };
1259
1226
 
@@ -1266,4 +1233,4 @@ export {
1266
1233
  ClientTransport,
1267
1234
  ServerTransport
1268
1235
  };
1269
- //# sourceMappingURL=chunk-QU2EE6YU.js.map
1236
+ //# sourceMappingURL=chunk-ZPBWKBM5.js.map