@replit/river 0.21.1 → 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 (63) 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-NCXUFDVL.js → chunk-GCLEWC26.js} +328 -500
  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-FDLAPYCK.js → chunk-S3YKQT4J.js} +2 -2
  9. package/dist/{chunk-JMXO5L2X.js → chunk-ZPBWKBM5.js} +344 -384
  10. package/dist/chunk-ZPBWKBM5.js.map +1 -0
  11. package/dist/{connection-76c5ed01.d.ts → connection-8b059ac4.d.ts} +6 -4
  12. package/dist/{connection-975b25c9.d.ts → connection-bbfe1147.d.ts} +1 -1
  13. package/dist/{index-dfad460e.d.ts → index-2ece5234.d.ts} +16 -7
  14. package/dist/logging/index.d.cts +2 -1
  15. package/dist/logging/index.d.ts +2 -1
  16. package/dist/router/index.cjs +373 -486
  17. package/dist/router/index.cjs.map +1 -1
  18. package/dist/router/index.d.cts +5 -4
  19. package/dist/router/index.d.ts +5 -4
  20. package/dist/router/index.js +4 -3
  21. package/dist/{services-7b716dcf.d.ts → services-acbcc441.d.ts} +1 -1
  22. package/dist/{services-9c496c6e.d.ts → services-cb01a7a8.d.ts} +1 -1
  23. package/dist/transport/impls/uds/client.cjs +186 -145
  24. package/dist/transport/impls/uds/client.cjs.map +1 -1
  25. package/dist/transport/impls/uds/client.d.cts +3 -2
  26. package/dist/transport/impls/uds/client.d.ts +3 -2
  27. package/dist/transport/impls/uds/client.js +3 -3
  28. package/dist/transport/impls/uds/server.cjs +281 -256
  29. package/dist/transport/impls/uds/server.cjs.map +1 -1
  30. package/dist/transport/impls/uds/server.d.cts +3 -2
  31. package/dist/transport/impls/uds/server.d.ts +3 -2
  32. package/dist/transport/impls/uds/server.js +3 -3
  33. package/dist/transport/impls/ws/client.cjs +240 -204
  34. package/dist/transport/impls/ws/client.cjs.map +1 -1
  35. package/dist/transport/impls/ws/client.d.cts +6 -6
  36. package/dist/transport/impls/ws/client.d.ts +6 -6
  37. package/dist/transport/impls/ws/client.js +33 -48
  38. package/dist/transport/impls/ws/client.js.map +1 -1
  39. package/dist/transport/impls/ws/server.cjs +303 -270
  40. package/dist/transport/impls/ws/server.cjs.map +1 -1
  41. package/dist/transport/impls/ws/server.d.cts +5 -4
  42. package/dist/transport/impls/ws/server.d.ts +5 -4
  43. package/dist/transport/impls/ws/server.js +3 -3
  44. package/dist/transport/impls/ws/server.js.map +1 -1
  45. package/dist/transport/index.cjs +390 -382
  46. package/dist/transport/index.cjs.map +1 -1
  47. package/dist/transport/index.d.cts +5 -5
  48. package/dist/transport/index.d.ts +5 -5
  49. package/dist/transport/index.js +2 -2
  50. package/dist/util/testHelpers.cjs +57 -7
  51. package/dist/util/testHelpers.cjs.map +1 -1
  52. package/dist/util/testHelpers.d.cts +14 -5
  53. package/dist/util/testHelpers.d.ts +14 -5
  54. package/dist/util/testHelpers.js +10 -4
  55. package/dist/util/testHelpers.js.map +1 -1
  56. package/dist/wslike-e0b32dd5.d.ts +40 -0
  57. package/package.json +4 -5
  58. package/dist/chunk-3Y7AB5EB.js +0 -42
  59. package/dist/chunk-3Y7AB5EB.js.map +0 -1
  60. package/dist/chunk-5WFL722S.js.map +0 -1
  61. package/dist/chunk-JMXO5L2X.js.map +0 -1
  62. package/dist/chunk-NCXUFDVL.js.map +0 -1
  63. /package/dist/{chunk-FDLAPYCK.js.map → chunk-S3YKQT4J.js.map} +0 -0
@@ -33,7 +33,6 @@ module.exports = __toCommonJS(transport_exports);
33
33
 
34
34
  // transport/transport.ts
35
35
  var import_value = require("@sinclair/typebox/value");
36
- var import_api2 = require("@opentelemetry/api");
37
36
 
38
37
  // transport/message.ts
39
38
  var import_typebox = require("@sinclair/typebox");
@@ -167,17 +166,71 @@ var EventDispatcher = class {
167
166
 
168
167
  // transport/session.ts
169
168
  var import_nanoid2 = require("nanoid");
169
+
170
+ // tracing/index.ts
171
+ var import_api = require("@opentelemetry/api");
172
+
173
+ // package.json
174
+ var version = "0.22.0";
175
+
176
+ // tracing/index.ts
177
+ function getPropagationContext(ctx) {
178
+ const tracing = {
179
+ traceparent: "",
180
+ tracestate: ""
181
+ };
182
+ import_api.propagation.inject(ctx, tracing);
183
+ return tracing;
184
+ }
185
+ function createSessionTelemetryInfo(session, propagationCtx) {
186
+ const ctx = propagationCtx ? import_api.propagation.extract(import_api.context.active(), propagationCtx) : import_api.context.active();
187
+ const span = tracer.startSpan(
188
+ `session ${session.id}`,
189
+ {
190
+ attributes: {
191
+ component: "river",
192
+ "river.session.id": session.id,
193
+ "river.session.to": session.to,
194
+ "river.session.from": session.from
195
+ }
196
+ },
197
+ ctx
198
+ );
199
+ return { span, ctx };
200
+ }
201
+ function createConnectionTelemetryInfo(connection, sessionSpan) {
202
+ const ctx = import_api.trace.setSpan(import_api.context.active(), sessionSpan);
203
+ const span = tracer.startSpan(
204
+ `connection ${connection.id}`,
205
+ {
206
+ attributes: {
207
+ component: "river",
208
+ "river.connection.id": connection.id
209
+ },
210
+ links: [{ context: sessionSpan.spanContext() }]
211
+ },
212
+ ctx
213
+ );
214
+ return { span, ctx };
215
+ }
216
+ var tracer = import_api.trace.getTracer("river", version);
217
+ var tracing_default = tracer;
218
+
219
+ // transport/session.ts
220
+ var import_api2 = require("@opentelemetry/api");
170
221
  var nanoid2 = (0, import_nanoid2.customAlphabet)("1234567890abcdefghijklmnopqrstuvxyz", 6);
171
222
  var unsafeId = () => nanoid2();
172
223
  var Connection = class {
173
- debugId;
224
+ id;
225
+ telemetry;
174
226
  constructor() {
175
- this.debugId = `conn-${unsafeId()}`;
227
+ this.id = `conn-${nanoid2(12)}`;
176
228
  }
177
229
  };
178
230
  var Session = class {
179
231
  codec;
180
232
  options;
233
+ telemetry;
181
234
  /**
182
235
  * The buffer of messages that have been sent but not yet acknowledged.
183
236
  */
@@ -224,7 +277,7 @@ var Session = class {
224
277
  * The interval for sending heartbeats.
225
278
  */
226
279
  heartbeat;
227
- constructor(conn, from, to, options) {
280
+ constructor(conn, from, to, options, propagationCtx) {
228
281
  this.id = `session-${nanoid2(12)}`;
229
282
  this.options = options;
230
283
  this.from = from;
@@ -236,13 +289,14 @@ var Session = class {
236
289
  () => this.sendHeartbeat(),
237
290
  options.heartbeatIntervalMs
238
291
  );
292
+ this.telemetry = createSessionTelemetryInfo(this, propagationCtx);
239
293
  }
240
294
  get loggingMetadata() {
241
295
  return {
242
296
  clientId: this.from,
243
297
  connectedTo: this.to,
244
298
  sessionId: this.id,
245
- connId: this.connection?.debugId
299
+ connId: this.connection?.id
246
300
  };
247
301
  }
248
302
  /**
@@ -287,6 +341,7 @@ var Session = class {
287
341
  `closing connection to ${this.to} due to inactivity (missed ${misses} heartbeats which is ${missDuration}ms)`,
288
342
  this.loggingMetadata
289
343
  );
344
+ this.telemetry.span.addEvent("closing connection due to inactivity");
290
345
  this.closeStaleConnection();
291
346
  }
292
347
  return;
@@ -308,21 +363,25 @@ var Session = class {
308
363
  sendBufferedMessages(conn) {
309
364
  log?.info(`resending ${this.sendBuffer.length} buffered messages`, {
310
365
  ...this.loggingMetadata,
311
- connId: conn.debugId
366
+ connId: conn.id
312
367
  });
313
368
  for (const msg of this.sendBuffer) {
314
369
  log?.debug(`resending msg`, {
315
370
  ...this.loggingMetadata,
316
371
  fullTransportMessage: msg,
317
- connId: conn.debugId
372
+ connId: conn.id
318
373
  });
319
374
  const ok = conn.send(this.codec.toBuffer(msg));
320
375
  if (!ok) {
321
376
  const errMsg = `failed to send buffered message to ${this.to} (sus, this is a fresh connection)`;
377
+ conn.telemetry?.span.setStatus({
378
+ code: import_api2.SpanStatusCode.ERROR,
379
+ message: errMsg
380
+ });
322
381
  log?.error(errMsg, {
323
382
  ...this.loggingMetadata,
324
383
  fullTransportMessage: msg,
325
- connId: conn.debugId,
384
+ connId: conn.id,
326
385
  tags: ["invariant-violation"]
327
386
  });
328
387
  conn.close();
@@ -405,11 +464,6 @@ var Session = class {
405
464
  }
406
465
  };
407
466
 
408
- // tracing/index.ts
409
- var import_api = require("@opentelemetry/api");
410
- var tracer = import_api.trace.getTracer("river");
411
- var tracing_default = tracer;
412
-
413
467
  // util/stringify.ts
414
468
  function coerceErrorString(err) {
415
469
  if (err instanceof Error) {
@@ -542,6 +596,7 @@ var NaiveJsonCodec = {
542
596
  };
543
597
 
544
598
  // transport/transport.ts
599
+ var import_api3 = require("@opentelemetry/api");
545
600
  var defaultTransportOptions = {
546
601
  heartbeatIntervalMs: 1e3,
547
602
  heartbeatsUntilDead: 2,
@@ -620,17 +675,22 @@ var Transport = class {
620
675
  status: "connect",
621
676
  conn
622
677
  });
678
+ conn.telemetry = createConnectionTelemetryInfo(
679
+ conn,
680
+ session.telemetry.span
681
+ );
623
682
  if (isReconnect) {
624
683
  session.replaceWithNewConnection(conn);
625
684
  log?.info(`reconnected to ${connectedTo}`, session.loggingMetadata);
626
685
  }
627
686
  }
628
- createSession(to, conn) {
687
+ createSession(to, conn, propagationCtx) {
629
688
  const session = new Session(
630
689
  conn,
631
690
  this.clientId,
632
691
  to,
633
- this.options
692
+ this.options,
693
+ propagationCtx
634
694
  );
635
695
  this.sessions.set(session.to, session);
636
696
  this.eventDispatcher.dispatchEvent("sessionStatus", {
@@ -639,11 +699,11 @@ var Transport = class {
639
699
  });
640
700
  return session;
641
701
  }
642
- getOrCreateSession(to, conn, sessionId) {
702
+ getOrCreateSession(to, conn, sessionId, propagationCtx) {
643
703
  let session = this.sessions.get(to);
644
704
  let isReconnect = session !== void 0;
645
705
  if (session?.advertisedSessionId !== void 0 && sessionId !== void 0 && session.advertisedSessionId !== sessionId) {
646
- log?.warn(
706
+ log?.info(
647
707
  `session for ${to} already exists but has a different session id (expected: ${session.advertisedSessionId}, got: ${sessionId}), creating a new one`,
648
708
  session.loggingMetadata
649
709
  );
@@ -652,7 +712,7 @@ var Transport = class {
652
712
  session = void 0;
653
713
  }
654
714
  if (!session) {
655
- session = this.createSession(to, conn);
715
+ session = this.createSession(to, conn, propagationCtx);
656
716
  log?.info(
657
717
  `no session for ${to}, created a new one`,
658
718
  session.loggingMetadata
@@ -665,6 +725,7 @@ var Transport = class {
665
725
  }
666
726
  deleteSession(session) {
667
727
  session.close();
728
+ session.telemetry.span.end();
668
729
  this.sessions.delete(session.to);
669
730
  log?.info(
670
731
  `session ${session.id} disconnect from ${session.to}`,
@@ -681,12 +742,16 @@ var Transport = class {
681
742
  * @param connectedTo The peer we are connected to.
682
743
  */
683
744
  onDisconnect(conn, session) {
745
+ conn.telemetry?.span.end();
684
746
  this.eventDispatcher.dispatchEvent("connectionStatus", {
685
747
  status: "disconnect",
686
748
  conn
687
749
  });
688
750
  session.connection = void 0;
689
- session.beginGrace(() => this.deleteSession(session));
751
+ session.beginGrace(() => {
752
+ session.telemetry.span.addEvent("session grace period expired");
753
+ this.deleteSession(session);
754
+ });
690
755
  }
691
756
  /**
692
757
  * Parses a message from a Uint8Array into a {@link OpaqueTransportMessage}.
@@ -746,6 +811,10 @@ var Transport = class {
746
811
  tags: ["invariant-violation"]
747
812
  });
748
813
  this.protocolError(ProtocolError.MessageOrderingViolated, errMsg);
814
+ session.telemetry.span.setStatus({
815
+ code: import_api3.SpanStatusCode.ERROR,
816
+ message: "message order violated"
817
+ });
749
818
  session.close();
750
819
  }
751
820
  return;
@@ -873,7 +942,7 @@ var ClientTransport = class extends Transport {
873
942
  if (!session) {
874
943
  log?.warn(
875
944
  `connection to ${to} timed out waiting for handshake, closing`,
876
- { clientId: this.clientId, connectedTo: to, connId: conn.debugId }
945
+ { clientId: this.clientId, connectedTo: to, connId: conn.id }
877
946
  );
878
947
  conn.close();
879
948
  }
@@ -891,6 +960,10 @@ var ClientTransport = class extends Transport {
891
960
  conn.addDataListener((data2) => {
892
961
  const parsed = this.parseMsg(data2);
893
962
  if (!parsed) {
963
+ conn.telemetry?.span.setStatus({
964
+ code: import_api3.SpanStatusCode.ERROR,
965
+ message: "message parse failure"
966
+ });
894
967
  conn.close();
895
968
  return;
896
969
  }
@@ -913,6 +986,10 @@ var ClientTransport = class extends Transport {
913
986
  }
914
987
  });
915
988
  conn.addErrorListener((err) => {
989
+ conn.telemetry?.span.setStatus({
990
+ code: import_api3.SpanStatusCode.ERROR,
991
+ message: "connection error"
992
+ });
916
993
  log?.warn(`error in connection to ${to}: ${coerceErrorString(err)}`, {
917
994
  ...session?.loggingMetadata,
918
995
  clientId: this.clientId,
@@ -923,6 +1000,10 @@ var ClientTransport = class extends Transport {
923
1000
  receiveHandshakeResponseMessage(data, conn) {
924
1001
  const parsed = this.parseMsg(data);
925
1002
  if (!parsed) {
1003
+ conn.telemetry?.span.setStatus({
1004
+ code: import_api3.SpanStatusCode.ERROR,
1005
+ message: "non-transport message"
1006
+ });
926
1007
  this.protocolError(
927
1008
  ProtocolError.HandshakeFailed,
928
1009
  "received non-transport message"
@@ -930,6 +1011,10 @@ var ClientTransport = class extends Transport {
930
1011
  return false;
931
1012
  }
932
1013
  if (!import_value.Value.Check(ControlMessageHandshakeResponseSchema, parsed.payload)) {
1014
+ conn.telemetry?.span.setStatus({
1015
+ code: import_api3.SpanStatusCode.ERROR,
1016
+ message: "invalid handshake response"
1017
+ });
933
1018
  log?.warn(`received invalid handshake resp`, {
934
1019
  clientId: this.clientId,
935
1020
  connectedTo: parsed.from,
@@ -942,7 +1027,11 @@ var ClientTransport = class extends Transport {
942
1027
  return false;
943
1028
  }
944
1029
  if (!parsed.payload.status.ok) {
945
- log?.warn(`received invalid handshake resp`, {
1030
+ conn.telemetry?.span.setStatus({
1031
+ code: import_api3.SpanStatusCode.ERROR,
1032
+ message: "handshake rejected"
1033
+ });
1034
+ log?.warn(`received handshake rejection`, {
946
1035
  clientId: this.clientId,
947
1036
  connectedTo: parsed.from,
948
1037
  fullTransportMessage: parsed
@@ -972,142 +1061,94 @@ var ClientTransport = class extends Transport {
972
1061
  * @param to The client ID of the node to connect to.
973
1062
  */
974
1063
  async connect(to) {
975
- return tracing_default.startActiveSpan(
976
- "connect",
977
- {
978
- attributes: {
979
- component: "river",
980
- "span.kind": "client"
981
- },
982
- kind: import_api2.SpanKind.CLIENT
983
- },
984
- async (span) => {
985
- try {
986
- await this.connectAttempt(to);
987
- } catch (e) {
988
- if (e instanceof Error) {
989
- span.recordException(e);
990
- } else {
991
- span.recordException(coerceErrorString(e));
992
- }
993
- span.setStatus({ code: import_api2.SpanStatusCode.ERROR });
994
- } finally {
995
- span.end();
996
- }
1064
+ const canProceedWithConnection = () => this.state === "open";
1065
+ if (!canProceedWithConnection()) {
1066
+ log?.info(
1067
+ `transport state is no longer open, cancelling attempt to connect to ${to}`,
1068
+ { clientId: this.clientId, connectedTo: to }
1069
+ );
1070
+ return;
1071
+ }
1072
+ let reconnectPromise = this.inflightConnectionPromises.get(to);
1073
+ if (!reconnectPromise) {
1074
+ const budgetConsumed = this.retryBudget.getBudgetConsumed(to);
1075
+ if (!this.retryBudget.hasBudget(to)) {
1076
+ const errMsg = `tried to connect to ${to} but retry budget exceeded (more than ${budgetConsumed} attempts in the last ${this.retryBudget.totalBudgetRestoreTime}ms)`;
1077
+ log?.warn(errMsg, { clientId: this.clientId, connectedTo: to });
1078
+ this.protocolError(ProtocolError.RetriesExceeded, errMsg);
1079
+ return;
997
1080
  }
998
- );
999
- }
1000
- async connectAttempt(to, attempt = 0) {
1001
- const retry = await tracing_default.startActiveSpan(
1002
- "connect",
1003
- {
1004
- attributes: {
1005
- component: "river",
1006
- "river.attempt": attempt,
1007
- "span.kind": "client"
1008
- },
1009
- kind: import_api2.SpanKind.CLIENT
1010
- },
1011
- async (span) => {
1081
+ let sleep = Promise.resolve();
1082
+ const backoffMs = this.retryBudget.getBackoffMs(to);
1083
+ if (backoffMs > 0) {
1084
+ sleep = new Promise((resolve) => setTimeout(resolve, backoffMs));
1085
+ }
1086
+ log?.info(`attempting connection to ${to} (${backoffMs}ms backoff)`, {
1087
+ clientId: this.clientId,
1088
+ connectedTo: to
1089
+ });
1090
+ this.retryBudget.consumeBudget(to);
1091
+ reconnectPromise = tracing_default.startActiveSpan("connect", async (span) => {
1012
1092
  try {
1013
- const canProceedWithConnection = () => this.state === "open";
1093
+ span.addEvent("backoff", { backoffMs });
1094
+ await sleep;
1014
1095
  if (!canProceedWithConnection()) {
1015
- log?.info(
1016
- `transport state is no longer open, cancelling attempt to connect to ${to}`,
1017
- { clientId: this.clientId, connectedTo: to }
1018
- );
1019
- return false;
1096
+ throw new Error("transport state is no longer open");
1020
1097
  }
1021
- let reconnectPromise = this.inflightConnectionPromises.get(to);
1022
- if (!reconnectPromise) {
1023
- const budgetConsumed = this.retryBudget.getBudgetConsumed(to);
1024
- if (!this.retryBudget.hasBudget(to)) {
1025
- const errMsg = `tried to connect to ${to} but retry budget exceeded (more than ${budgetConsumed} attempts in the last ${this.retryBudget.totalBudgetRestoreTime}ms)`;
1026
- log?.warn(errMsg, { clientId: this.clientId, connectedTo: to });
1027
- this.protocolError(ProtocolError.RetriesExceeded, errMsg);
1028
- return false;
1029
- }
1030
- let sleep = Promise.resolve();
1031
- const backoffMs = this.retryBudget.getBackoffMs(to);
1032
- if (backoffMs > 0) {
1033
- sleep = new Promise((resolve) => setTimeout(resolve, backoffMs));
1034
- }
1035
- log?.info(
1036
- `attempting connection to ${to} (${backoffMs}ms backoff)`,
1037
- {
1038
- clientId: this.clientId,
1039
- connectedTo: to
1040
- }
1041
- );
1042
- this.retryBudget.consumeBudget(to);
1043
- reconnectPromise = sleep.then(() => {
1044
- if (!canProceedWithConnection()) {
1045
- throw new Error("transport state is no longer open");
1046
- }
1047
- }).then(() => this.createNewOutgoingConnection(to)).then((conn) => {
1048
- if (!canProceedWithConnection()) {
1049
- log?.info(
1050
- `transport state is no longer open, closing pre-handshake connection to ${to}`,
1051
- {
1052
- clientId: this.clientId,
1053
- connectedTo: to,
1054
- connId: conn.debugId
1055
- }
1056
- );
1057
- conn.close();
1058
- throw new Error("transport state is no longer open");
1059
- }
1060
- return this.sendHandshake(to, conn).then((ok) => {
1061
- if (!ok) {
1062
- conn.close();
1063
- throw new Error("failed to send handshake");
1064
- }
1065
- return conn;
1066
- });
1067
- });
1068
- this.inflightConnectionPromises.set(to, reconnectPromise);
1069
- } else {
1098
+ span.addEvent("connecting");
1099
+ const conn = await this.createNewOutgoingConnection(to);
1100
+ if (!canProceedWithConnection()) {
1070
1101
  log?.info(
1071
- `attempting connection to ${to} (reusing previous attempt)`,
1102
+ `transport state is no longer open, closing pre-handshake connection to ${to}`,
1072
1103
  {
1073
1104
  clientId: this.clientId,
1074
- connectedTo: to
1105
+ connectedTo: to,
1106
+ connId: conn.id
1075
1107
  }
1076
1108
  );
1109
+ conn.close();
1110
+ throw new Error("transport state is no longer open");
1077
1111
  }
1078
- try {
1079
- await reconnectPromise;
1080
- } catch (error) {
1081
- this.inflightConnectionPromises.delete(to);
1082
- const errStr = coerceErrorString(error);
1083
- if (!this.reconnectOnConnectionDrop || !canProceedWithConnection()) {
1084
- log?.warn(`connection to ${to} failed (${errStr})`, {
1085
- clientId: this.clientId,
1086
- connectedTo: to
1087
- });
1088
- } else {
1089
- log?.warn(`connection to ${to} failed (${errStr}), retrying`, {
1090
- clientId: this.clientId,
1091
- connectedTo: to
1092
- });
1093
- return true;
1094
- }
1095
- }
1096
- } catch (e) {
1097
- if (e instanceof Error) {
1098
- span.recordException(e);
1099
- } else {
1100
- span.recordException(coerceErrorString(e));
1112
+ span.addEvent("sending handshake");
1113
+ const ok = await this.sendHandshake(to, conn);
1114
+ if (!ok) {
1115
+ conn.close();
1116
+ throw new Error("failed to send handshake");
1101
1117
  }
1102
- span.setStatus({ code: import_api2.SpanStatusCode.ERROR });
1118
+ return conn;
1119
+ } catch (err) {
1120
+ const errStr = coerceErrorString(err);
1121
+ span.recordException(errStr);
1122
+ span.setStatus({ code: import_api3.SpanStatusCode.ERROR });
1123
+ throw err;
1103
1124
  } finally {
1104
1125
  span.end();
1105
1126
  }
1106
- return false;
1127
+ });
1128
+ this.inflightConnectionPromises.set(to, reconnectPromise);
1129
+ } else {
1130
+ log?.info(`attempting connection to ${to} (reusing previous attempt)`, {
1131
+ clientId: this.clientId,
1132
+ connectedTo: to
1133
+ });
1134
+ }
1135
+ try {
1136
+ await reconnectPromise;
1137
+ } catch (error) {
1138
+ this.inflightConnectionPromises.delete(to);
1139
+ const errStr = coerceErrorString(error);
1140
+ if (!this.reconnectOnConnectionDrop || !canProceedWithConnection()) {
1141
+ log?.warn(`connection to ${to} failed (${errStr})`, {
1142
+ clientId: this.clientId,
1143
+ connectedTo: to
1144
+ });
1145
+ } else {
1146
+ log?.warn(`connection to ${to} failed (${errStr}), retrying`, {
1147
+ clientId: this.clientId,
1148
+ connectedTo: to
1149
+ });
1150
+ return this.connect(to);
1107
1151
  }
1108
- );
1109
- if (retry) {
1110
- return this.connectAttempt(to, attempt + 1);
1111
1152
  }
1112
1153
  }
1113
1154
  deleteSession(session) {
@@ -1115,8 +1156,6 @@ var ClientTransport = class extends Transport {
1115
1156
  super.deleteSession(session);
1116
1157
  }
1117
1158
  async sendHandshake(to, conn) {
1118
- const tracing = { traceparent: "", tracestate: "" };
1119
- import_api2.propagation.inject(import_api2.context.active(), tracing);
1120
1159
  let metadata;
1121
1160
  if (this.options.handshake) {
1122
1161
  metadata = await this.options.handshake.get();
@@ -1130,6 +1169,10 @@ var ClientTransport = class extends Transport {
1130
1169
  ProtocolError.HandshakeFailed,
1131
1170
  "handshake metadata did not match schema"
1132
1171
  );
1172
+ conn.telemetry?.span.setStatus({
1173
+ code: import_api3.SpanStatusCode.ERROR,
1174
+ message: "handshake meta mismatch"
1175
+ });
1133
1176
  return false;
1134
1177
  }
1135
1178
  }
@@ -1139,7 +1182,7 @@ var ClientTransport = class extends Transport {
1139
1182
  to,
1140
1183
  session.id,
1141
1184
  metadata,
1142
- tracing
1185
+ getPropagationContext(session.telemetry.ctx)
1143
1186
  );
1144
1187
  log?.debug(`sending handshake request to ${to}`, {
1145
1188
  clientId: this.clientId,
@@ -1170,268 +1213,233 @@ var ServerTransport = class extends Transport {
1170
1213
  });
1171
1214
  }
1172
1215
  handleConnection(conn) {
1173
- tracing_default.startActiveSpan(
1174
- "handleConnection",
1175
- {
1176
- attributes: {
1177
- component: "river",
1178
- "span.kind": "server"
1179
- },
1180
- kind: import_api2.SpanKind.SERVER
1181
- },
1182
- (span) => {
1183
- if (this.state !== "open")
1184
- return;
1185
- log?.info(`new incoming connection`, {
1186
- clientId: this.clientId,
1187
- connId: conn.debugId
1216
+ if (this.state !== "open")
1217
+ return;
1218
+ log?.info(`new incoming connection`, {
1219
+ clientId: this.clientId,
1220
+ connId: conn.id
1221
+ });
1222
+ let session = void 0;
1223
+ const client = () => session?.to ?? "unknown";
1224
+ const handshakeTimeout = setTimeout(() => {
1225
+ if (!session) {
1226
+ log?.warn(
1227
+ `connection to ${client()} timed out waiting for handshake, closing`,
1228
+ {
1229
+ clientId: this.clientId,
1230
+ connectedTo: client(),
1231
+ connId: conn.id
1232
+ }
1233
+ );
1234
+ conn.telemetry?.span.setStatus({
1235
+ code: import_api3.SpanStatusCode.ERROR,
1236
+ message: "handshake timeout"
1188
1237
  });
1189
- let session = void 0;
1190
- const client = () => session?.to ?? "unknown";
1191
- const handshakeTimeout = setTimeout(() => {
1192
- if (!session) {
1193
- log?.warn(
1194
- `connection to ${client()} timed out waiting for handshake, closing`,
1195
- {
1196
- clientId: this.clientId,
1197
- connectedTo: client(),
1198
- connId: conn.debugId
1199
- }
1200
- );
1201
- span.setStatus({ code: import_api2.SpanStatusCode.ERROR });
1202
- span.end();
1238
+ conn.close();
1239
+ }
1240
+ }, this.options.sessionDisconnectGraceMs);
1241
+ const buffer = [];
1242
+ let receivedHandshakeMessage = false;
1243
+ const handshakeHandler = (data) => {
1244
+ if (receivedHandshakeMessage) {
1245
+ buffer.push(data);
1246
+ return;
1247
+ }
1248
+ receivedHandshakeMessage = true;
1249
+ clearTimeout(handshakeTimeout);
1250
+ void this.receiveHandshakeRequestMessage(data, conn).then(
1251
+ (maybeSession) => {
1252
+ if (!maybeSession) {
1203
1253
  conn.close();
1204
- }
1205
- }, this.options.sessionDisconnectGraceMs);
1206
- const buffer = [];
1207
- let receivedHandshakeMessage = false;
1208
- const handshakeHandler = (data) => {
1209
- if (receivedHandshakeMessage) {
1210
- buffer.push(data);
1211
1254
  return;
1212
1255
  }
1213
- receivedHandshakeMessage = true;
1214
- clearTimeout(handshakeTimeout);
1215
- void this.receiveHandshakeRequestMessage(data, conn).then(
1216
- (maybeSession) => {
1217
- if (!maybeSession) {
1218
- span.setStatus({ code: import_api2.SpanStatusCode.ERROR });
1219
- span.end();
1220
- conn.close();
1221
- return;
1222
- }
1223
- session = maybeSession;
1224
- const dataHandler = (data2) => {
1225
- const parsed = this.parseMsg(data2);
1226
- if (!parsed) {
1227
- conn.close();
1228
- return;
1229
- }
1230
- this.handleMsg(parsed);
1231
- };
1232
- conn.removeDataListener(handshakeHandler);
1233
- conn.addDataListener(dataHandler);
1234
- for (const data2 of buffer) {
1235
- dataHandler(data2);
1236
- }
1237
- buffer.length = 0;
1256
+ session = maybeSession;
1257
+ const dataHandler = (data2) => {
1258
+ const parsed = this.parseMsg(data2);
1259
+ if (!parsed) {
1260
+ conn.close();
1261
+ return;
1238
1262
  }
1239
- );
1240
- };
1241
- conn.addDataListener(handshakeHandler);
1242
- conn.addCloseListener(() => {
1243
- if (session) {
1244
- log?.info(`connection to ${client()} disconnected`, {
1245
- clientId: this.clientId,
1246
- connId: conn.debugId
1247
- });
1248
- this.onDisconnect(conn, session);
1263
+ this.handleMsg(parsed);
1264
+ };
1265
+ for (const data2 of buffer) {
1266
+ dataHandler(data2);
1249
1267
  }
1250
- span.setStatus({ code: import_api2.SpanStatusCode.OK });
1251
- span.end();
1252
- });
1253
- conn.addErrorListener((err) => {
1254
- if (session) {
1255
- log?.warn(
1256
- `connection to ${client()} got an error: ${coerceErrorString(
1257
- err
1258
- )}`,
1259
- { clientId: this.clientId, connId: conn.debugId }
1260
- );
1261
- }
1262
- span.setStatus({ code: import_api2.SpanStatusCode.ERROR });
1263
- span.end();
1264
- });
1265
- }
1266
- );
1268
+ conn.removeDataListener(handshakeHandler);
1269
+ conn.addDataListener(dataHandler);
1270
+ buffer.length = 0;
1271
+ }
1272
+ );
1273
+ };
1274
+ conn.addDataListener(handshakeHandler);
1275
+ conn.addCloseListener(() => {
1276
+ if (!session)
1277
+ return;
1278
+ log?.info(`connection to ${client()} disconnected`, {
1279
+ clientId: this.clientId,
1280
+ connId: conn.id
1281
+ });
1282
+ this.onDisconnect(conn, session);
1283
+ });
1284
+ conn.addErrorListener((err) => {
1285
+ conn.telemetry?.span.setStatus({
1286
+ code: import_api3.SpanStatusCode.ERROR,
1287
+ message: "connection error"
1288
+ });
1289
+ if (!session)
1290
+ return;
1291
+ log?.warn(
1292
+ `connection to ${client()} got an error: ${coerceErrorString(err)}`,
1293
+ { clientId: this.clientId, connId: conn.id }
1294
+ );
1295
+ });
1267
1296
  }
1268
1297
  async receiveHandshakeRequestMessage(data, conn) {
1269
1298
  const parsed = this.parseMsg(data);
1270
1299
  if (!parsed) {
1300
+ conn.telemetry?.span.setStatus({
1301
+ code: import_api3.SpanStatusCode.ERROR,
1302
+ message: "non-transport message"
1303
+ });
1271
1304
  this.protocolError(
1272
1305
  ProtocolError.HandshakeFailed,
1273
1306
  "received non-transport message"
1274
1307
  );
1275
1308
  return false;
1276
1309
  }
1277
- let activeContext = import_api2.context.active();
1278
- if (parsed.tracing) {
1279
- activeContext = import_api2.propagation.extract(activeContext, parsed.tracing);
1310
+ if (!import_value.Value.Check(ControlMessageHandshakeRequestSchema, parsed.payload)) {
1311
+ conn.telemetry?.span.setStatus({
1312
+ code: import_api3.SpanStatusCode.ERROR,
1313
+ message: "invalid handshake request"
1314
+ });
1315
+ const reason = "received invalid handshake msg";
1316
+ const responseMsg2 = handshakeResponseMessage(this.clientId, parsed.from, {
1317
+ ok: false,
1318
+ reason
1319
+ });
1320
+ conn.send(this.codec.toBuffer(responseMsg2));
1321
+ const logData = { ...parsed.payload ?? {}, metadata: "redacted" };
1322
+ log?.warn(reason, {
1323
+ clientId: this.clientId,
1324
+ connId: conn.id,
1325
+ partialTransportMessage: { ...parsed, payload: logData }
1326
+ });
1327
+ this.protocolError(
1328
+ ProtocolError.HandshakeFailed,
1329
+ "invalid handshake request"
1330
+ );
1331
+ return false;
1280
1332
  }
1281
- return tracing_default.startActiveSpan(
1282
- "receiveHandshakeRequestMessage",
1283
- {
1284
- attributes: {
1285
- component: "river",
1286
- "span.kind": "server"
1287
- },
1288
- kind: import_api2.SpanKind.SERVER
1289
- },
1290
- activeContext,
1291
- async (span) => {
1292
- if (!import_value.Value.Check(ControlMessageHandshakeRequestSchema, parsed.payload)) {
1293
- const reason = "received invalid handshake msg";
1294
- const responseMsg2 = handshakeResponseMessage(
1295
- this.clientId,
1296
- parsed.from,
1297
- {
1298
- ok: false,
1299
- reason
1300
- }
1301
- );
1302
- conn.send(this.codec.toBuffer(responseMsg2));
1303
- const logData = typeof parsed.payload === "object" ? {
1304
- ...parsed,
1305
- payload: { ...parsed.payload, metadata: "redacted" }
1306
- } : { ...parsed };
1307
- log?.warn(`${reason}: ${JSON.stringify(logData)}`, {
1308
- clientId: this.clientId,
1309
- connId: conn.debugId
1310
- });
1311
- this.protocolError(
1312
- ProtocolError.HandshakeFailed,
1313
- "invalid handshake request"
1314
- );
1315
- span.setStatus({ code: import_api2.SpanStatusCode.ERROR });
1316
- span.end();
1317
- return false;
1318
- }
1319
- const gotVersion = parsed.payload.protocolVersion;
1320
- if (gotVersion !== PROTOCOL_VERSION) {
1321
- const reason = `incorrect version (got: ${gotVersion} wanted ${PROTOCOL_VERSION})`;
1322
- const responseMsg2 = handshakeResponseMessage(
1323
- this.clientId,
1324
- parsed.from,
1325
- {
1326
- ok: false,
1327
- reason
1328
- }
1329
- );
1330
- conn.send(this.codec.toBuffer(responseMsg2));
1331
- log?.warn(
1332
- `received handshake msg with incompatible protocol version (got: ${gotVersion}, expected: ${PROTOCOL_VERSION})`,
1333
- { clientId: this.clientId, connId: conn.debugId }
1334
- );
1335
- this.protocolError(ProtocolError.HandshakeFailed, reason);
1336
- span.setStatus({ code: import_api2.SpanStatusCode.ERROR });
1337
- span.end();
1338
- return false;
1339
- }
1340
- const { session, isReconnect } = this.getOrCreateSession(
1333
+ const gotVersion = parsed.payload.protocolVersion;
1334
+ if (gotVersion !== PROTOCOL_VERSION) {
1335
+ conn.telemetry?.span.setStatus({
1336
+ code: import_api3.SpanStatusCode.ERROR,
1337
+ message: "incorrect protocol version"
1338
+ });
1339
+ const reason = `incorrect version (got: ${gotVersion} wanted ${PROTOCOL_VERSION})`;
1340
+ const responseMsg2 = handshakeResponseMessage(this.clientId, parsed.from, {
1341
+ ok: false,
1342
+ reason
1343
+ });
1344
+ conn.send(this.codec.toBuffer(responseMsg2));
1345
+ log?.warn(
1346
+ `received handshake msg with incompatible protocol version (got: ${gotVersion}, expected: ${PROTOCOL_VERSION})`,
1347
+ { clientId: this.clientId, connId: conn.id }
1348
+ );
1349
+ this.protocolError(ProtocolError.HandshakeFailed, reason);
1350
+ return false;
1351
+ }
1352
+ const { session, isReconnect } = this.getOrCreateSession(
1353
+ parsed.from,
1354
+ conn,
1355
+ parsed.payload.sessionId,
1356
+ parsed.tracing
1357
+ );
1358
+ let handshakeMetadata;
1359
+ if (this.options.handshake) {
1360
+ if (!import_value.Value.Check(
1361
+ this.options.handshake.requestSchema,
1362
+ parsed.payload.metadata
1363
+ )) {
1364
+ conn.telemetry?.span.setStatus({
1365
+ code: import_api3.SpanStatusCode.ERROR,
1366
+ message: "malformed handshake meta"
1367
+ });
1368
+ const reason = "received malformed handshake metadata";
1369
+ const responseMsg2 = handshakeResponseMessage(
1370
+ this.clientId,
1341
1371
  parsed.from,
1342
- conn,
1343
- parsed.payload.sessionId
1372
+ { ok: false, reason }
1344
1373
  );
1345
- let handshakeMetadata;
1346
- if (this.options.handshake) {
1347
- if (!import_value.Value.Check(
1348
- this.options.handshake.requestSchema,
1349
- parsed.payload.metadata
1350
- )) {
1351
- const reason = "received malformed handshake metadata";
1352
- const responseMsg2 = handshakeResponseMessage(
1353
- this.clientId,
1354
- parsed.from,
1355
- { ok: false, reason }
1356
- );
1357
- conn.send(this.codec.toBuffer(responseMsg2));
1358
- log?.warn(
1359
- `received malformed handshake metadata from ${parsed.from}`,
1360
- {
1361
- clientId: this.clientId,
1362
- connId: conn.debugId
1363
- }
1364
- );
1365
- this.protocolError(ProtocolError.HandshakeFailed, reason);
1366
- this.deleteSession(session);
1367
- span.setStatus({ code: import_api2.SpanStatusCode.ERROR });
1368
- span.end();
1369
- return false;
1370
- }
1371
- const parsedMetadata = await this.options.handshake.parse(
1372
- parsed.payload.metadata,
1373
- session,
1374
- isReconnect
1375
- );
1376
- if (parsedMetadata === false) {
1377
- const reason = "rejected by server";
1378
- const responseMsg2 = handshakeResponseMessage(
1379
- this.clientId,
1380
- parsed.from,
1381
- { ok: false, reason }
1382
- );
1383
- conn.send(this.codec.toBuffer(responseMsg2));
1384
- log?.warn(`rejected handshake from ${parsed.from}`, {
1385
- clientId: this.clientId,
1386
- connId: conn.debugId
1387
- });
1388
- this.protocolError(ProtocolError.HandshakeFailed, reason);
1389
- this.deleteSession(session);
1390
- span.setStatus({ code: import_api2.SpanStatusCode.ERROR });
1391
- span.end();
1392
- return false;
1393
- }
1394
- if (!import_value.Value.Check(this.options.handshake.parsedSchema, parsedMetadata)) {
1395
- const reason = "failed to parse handshake metadata";
1396
- const responseMsg2 = handshakeResponseMessage(
1397
- this.clientId,
1398
- parsed.from,
1399
- { ok: false, reason }
1400
- );
1401
- conn.send(this.codec.toBuffer(responseMsg2));
1402
- log?.error(`failed to parse handshake metadata`, {
1403
- clientId: this.clientId,
1404
- connId: conn.debugId,
1405
- tags: ["invariant-violation"]
1406
- });
1407
- this.protocolError(ProtocolError.HandshakeFailed, reason);
1408
- this.deleteSession(session);
1409
- span.setStatus({ code: import_api2.SpanStatusCode.ERROR });
1410
- span.end();
1411
- return false;
1412
- }
1413
- handshakeMetadata = parsedMetadata;
1414
- }
1415
- handshakeMetadata ??= {};
1416
- session.metadata = handshakeMetadata;
1417
- log?.debug(
1418
- `handshake from ${parsed.from} ok, responding with handshake success`,
1419
- { clientId: this.clientId, connId: conn.debugId }
1374
+ conn.send(this.codec.toBuffer(responseMsg2));
1375
+ log?.warn(`received malformed handshake metadata from ${parsed.from}`, {
1376
+ clientId: this.clientId,
1377
+ connId: conn.id
1378
+ });
1379
+ this.protocolError(ProtocolError.HandshakeFailed, reason);
1380
+ this.deleteSession(session);
1381
+ return false;
1382
+ }
1383
+ const parsedMetadata = await this.options.handshake.parse(
1384
+ parsed.payload.metadata,
1385
+ session,
1386
+ isReconnect
1387
+ );
1388
+ if (parsedMetadata === false) {
1389
+ conn.telemetry?.span.setStatus({
1390
+ code: import_api3.SpanStatusCode.ERROR,
1391
+ message: "rejected by handshake handler"
1392
+ });
1393
+ const reason = "rejected by handshake handler";
1394
+ const responseMsg2 = handshakeResponseMessage(
1395
+ this.clientId,
1396
+ parsed.from,
1397
+ { ok: false, reason }
1420
1398
  );
1421
- const responseMsg = handshakeResponseMessage(
1399
+ conn.send(this.codec.toBuffer(responseMsg2));
1400
+ log?.warn(`rejected handshake from ${parsed.from}`, {
1401
+ clientId: this.clientId,
1402
+ connId: conn.id
1403
+ });
1404
+ this.protocolError(ProtocolError.HandshakeFailed, reason);
1405
+ this.deleteSession(session);
1406
+ return false;
1407
+ }
1408
+ if (!import_value.Value.Check(this.options.handshake.parsedSchema, parsedMetadata)) {
1409
+ conn.telemetry?.span.setStatus({
1410
+ code: import_api3.SpanStatusCode.ERROR,
1411
+ message: "malformed handshake meta"
1412
+ });
1413
+ const reason = "failed to parse handshake metadata";
1414
+ const responseMsg2 = handshakeResponseMessage(
1422
1415
  this.clientId,
1423
1416
  parsed.from,
1424
- {
1425
- ok: true,
1426
- sessionId: session.id
1427
- }
1417
+ { ok: false, reason }
1428
1418
  );
1429
- conn.send(this.codec.toBuffer(responseMsg));
1430
- this.onConnect(conn, parsed.from, session, isReconnect);
1431
- span.end();
1432
- return session;
1419
+ conn.send(this.codec.toBuffer(responseMsg2));
1420
+ log?.error(`failed to parse handshake metadata`, {
1421
+ clientId: this.clientId,
1422
+ connId: conn.id
1423
+ });
1424
+ this.protocolError(ProtocolError.HandshakeFailed, reason);
1425
+ this.deleteSession(session);
1426
+ return false;
1433
1427
  }
1428
+ handshakeMetadata = parsedMetadata;
1429
+ }
1430
+ handshakeMetadata ??= {};
1431
+ session.metadata = handshakeMetadata;
1432
+ log?.debug(
1433
+ `handshake from ${parsed.from} ok, responding with handshake success`,
1434
+ { clientId: this.clientId, connId: conn.id }
1434
1435
  );
1436
+ const responseMsg = handshakeResponseMessage(this.clientId, parsed.from, {
1437
+ ok: true,
1438
+ sessionId: session.id
1439
+ });
1440
+ conn.send(this.codec.toBuffer(responseMsg));
1441
+ this.onConnect(conn, parsed.from, session, isReconnect);
1442
+ return session;
1435
1443
  }
1436
1444
  };
1437
1445
  // Annotate the CommonJS export names for ESM import in node: