@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
@@ -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,32 +363,38 @@ 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,
385
+ tags: ["invariant-violation"]
326
386
  });
327
- throw new Error(errMsg);
387
+ conn.close();
388
+ return;
328
389
  }
329
390
  }
330
391
  }
331
392
  updateBookkeeping(ack, seq) {
332
393
  if (seq + 1 < this.ack) {
333
- log?.error(
334
- `received stale seq ${seq} + 1 < ${this.ack}`,
335
- this.loggingMetadata
336
- );
394
+ log?.error(`received stale seq ${seq} + 1 < ${this.ack}`, {
395
+ ...this.loggingMetadata,
396
+ tags: ["invariant-violation"]
397
+ });
337
398
  return;
338
399
  }
339
400
  this.sendBuffer = this.sendBuffer.filter((unacked) => unacked.seq >= ack);
@@ -403,11 +464,6 @@ var Session = class {
403
464
  }
404
465
  };
405
466
 
406
- // tracing/index.ts
407
- var import_api = require("@opentelemetry/api");
408
- var tracer = import_api.trace.getTracer("river");
409
- var tracing_default = tracer;
410
-
411
467
  // util/stringify.ts
412
468
  function coerceErrorString(err) {
413
469
  if (err instanceof Error) {
@@ -540,6 +596,7 @@ var NaiveJsonCodec = {
540
596
  };
541
597
 
542
598
  // transport/transport.ts
599
+ var import_api3 = require("@opentelemetry/api");
543
600
  var defaultTransportOptions = {
544
601
  heartbeatIntervalMs: 1e3,
545
602
  heartbeatsUntilDead: 2,
@@ -618,17 +675,22 @@ var Transport = class {
618
675
  status: "connect",
619
676
  conn
620
677
  });
678
+ conn.telemetry = createConnectionTelemetryInfo(
679
+ conn,
680
+ session.telemetry.span
681
+ );
621
682
  if (isReconnect) {
622
683
  session.replaceWithNewConnection(conn);
623
684
  log?.info(`reconnected to ${connectedTo}`, session.loggingMetadata);
624
685
  }
625
686
  }
626
- createSession(to, conn) {
687
+ createSession(to, conn, propagationCtx) {
627
688
  const session = new Session(
628
689
  conn,
629
690
  this.clientId,
630
691
  to,
631
- this.options
692
+ this.options,
693
+ propagationCtx
632
694
  );
633
695
  this.sessions.set(session.to, session);
634
696
  this.eventDispatcher.dispatchEvent("sessionStatus", {
@@ -637,11 +699,11 @@ var Transport = class {
637
699
  });
638
700
  return session;
639
701
  }
640
- getOrCreateSession(to, conn, sessionId) {
702
+ getOrCreateSession(to, conn, sessionId, propagationCtx) {
641
703
  let session = this.sessions.get(to);
642
704
  let isReconnect = session !== void 0;
643
705
  if (session?.advertisedSessionId !== void 0 && sessionId !== void 0 && session.advertisedSessionId !== sessionId) {
644
- log?.warn(
706
+ log?.info(
645
707
  `session for ${to} already exists but has a different session id (expected: ${session.advertisedSessionId}, got: ${sessionId}), creating a new one`,
646
708
  session.loggingMetadata
647
709
  );
@@ -650,7 +712,7 @@ var Transport = class {
650
712
  session = void 0;
651
713
  }
652
714
  if (!session) {
653
- session = this.createSession(to, conn);
715
+ session = this.createSession(to, conn, propagationCtx);
654
716
  log?.info(
655
717
  `no session for ${to}, created a new one`,
656
718
  session.loggingMetadata
@@ -663,6 +725,7 @@ var Transport = class {
663
725
  }
664
726
  deleteSession(session) {
665
727
  session.close();
728
+ session.telemetry.span.end();
666
729
  this.sessions.delete(session.to);
667
730
  log?.info(
668
731
  `session ${session.id} disconnect from ${session.to}`,
@@ -679,12 +742,16 @@ var Transport = class {
679
742
  * @param connectedTo The peer we are connected to.
680
743
  */
681
744
  onDisconnect(conn, session) {
745
+ conn.telemetry?.span.end();
682
746
  this.eventDispatcher.dispatchEvent("connectionStatus", {
683
747
  status: "disconnect",
684
748
  conn
685
749
  });
686
750
  session.connection = void 0;
687
- session.beginGrace(() => this.deleteSession(session));
751
+ session.beginGrace(() => {
752
+ session.telemetry.span.addEvent("session grace period expired");
753
+ this.deleteSession(session);
754
+ });
688
755
  }
689
756
  /**
690
757
  * Parses a message from a Uint8Array into a {@link OpaqueTransportMessage}.
@@ -718,9 +785,10 @@ var Transport = class {
718
785
  return;
719
786
  const session = this.sessions.get(msg.from);
720
787
  if (!session) {
721
- log?.error(`(invariant violation) no existing session for ${msg.from}`, {
788
+ log?.error(`no existing session for ${msg.from}`, {
722
789
  clientId: this.clientId,
723
- fullTransportMessage: msg
790
+ fullTransportMessage: msg,
791
+ tags: ["invariant-violation"]
724
792
  });
725
793
  return;
726
794
  }
@@ -739,9 +807,14 @@ var Transport = class {
739
807
  const errMsg = `received out-of-order msg (got seq: ${msg.seq}, wanted seq: ${session.nextExpectedSeq})`;
740
808
  log?.error(`${errMsg}, marking connection as dead`, {
741
809
  clientId: this.clientId,
742
- fullTransportMessage: msg
810
+ fullTransportMessage: msg,
811
+ tags: ["invariant-violation"]
743
812
  });
744
813
  this.protocolError(ProtocolError.MessageOrderingViolated, errMsg);
814
+ session.telemetry.span.setStatus({
815
+ code: import_api3.SpanStatusCode.ERROR,
816
+ message: "message order violated"
817
+ });
745
818
  session.close();
746
819
  }
747
820
  return;
@@ -783,7 +856,8 @@ var Transport = class {
783
856
  const err = "transport is destroyed, cant send";
784
857
  log?.error(err, {
785
858
  clientId: this.clientId,
786
- partialTransportMessage: msg
859
+ partialTransportMessage: msg,
860
+ tags: ["invariant-violation"]
787
861
  });
788
862
  this.protocolError(ProtocolError.UseAfterDestroy, err);
789
863
  return void 0;
@@ -868,7 +942,7 @@ var ClientTransport = class extends Transport {
868
942
  if (!session) {
869
943
  log?.warn(
870
944
  `connection to ${to} timed out waiting for handshake, closing`,
871
- { clientId: this.clientId, connectedTo: to, connId: conn.debugId }
945
+ { clientId: this.clientId, connectedTo: to, connId: conn.id }
872
946
  );
873
947
  conn.close();
874
948
  }
@@ -886,6 +960,10 @@ var ClientTransport = class extends Transport {
886
960
  conn.addDataListener((data2) => {
887
961
  const parsed = this.parseMsg(data2);
888
962
  if (!parsed) {
963
+ conn.telemetry?.span.setStatus({
964
+ code: import_api3.SpanStatusCode.ERROR,
965
+ message: "message parse failure"
966
+ });
889
967
  conn.close();
890
968
  return;
891
969
  }
@@ -908,6 +986,10 @@ var ClientTransport = class extends Transport {
908
986
  }
909
987
  });
910
988
  conn.addErrorListener((err) => {
989
+ conn.telemetry?.span.setStatus({
990
+ code: import_api3.SpanStatusCode.ERROR,
991
+ message: "connection error"
992
+ });
911
993
  log?.warn(`error in connection to ${to}: ${coerceErrorString(err)}`, {
912
994
  ...session?.loggingMetadata,
913
995
  clientId: this.clientId,
@@ -918,6 +1000,10 @@ var ClientTransport = class extends Transport {
918
1000
  receiveHandshakeResponseMessage(data, conn) {
919
1001
  const parsed = this.parseMsg(data);
920
1002
  if (!parsed) {
1003
+ conn.telemetry?.span.setStatus({
1004
+ code: import_api3.SpanStatusCode.ERROR,
1005
+ message: "non-transport message"
1006
+ });
921
1007
  this.protocolError(
922
1008
  ProtocolError.HandshakeFailed,
923
1009
  "received non-transport message"
@@ -925,6 +1011,10 @@ var ClientTransport = class extends Transport {
925
1011
  return false;
926
1012
  }
927
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
+ });
928
1018
  log?.warn(`received invalid handshake resp`, {
929
1019
  clientId: this.clientId,
930
1020
  connectedTo: parsed.from,
@@ -937,7 +1027,11 @@ var ClientTransport = class extends Transport {
937
1027
  return false;
938
1028
  }
939
1029
  if (!parsed.payload.status.ok) {
940
- 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`, {
941
1035
  clientId: this.clientId,
942
1036
  connectedTo: parsed.from,
943
1037
  fullTransportMessage: parsed
@@ -967,142 +1061,94 @@ var ClientTransport = class extends Transport {
967
1061
  * @param to The client ID of the node to connect to.
968
1062
  */
969
1063
  async connect(to) {
970
- return tracing_default.startActiveSpan(
971
- "connect",
972
- {
973
- attributes: {
974
- component: "river",
975
- "span.kind": "client"
976
- },
977
- kind: import_api2.SpanKind.CLIENT
978
- },
979
- async (span) => {
980
- try {
981
- await this.connectAttempt(to);
982
- } catch (e) {
983
- if (e instanceof Error) {
984
- span.recordException(e);
985
- } else {
986
- span.recordException(coerceErrorString(e));
987
- }
988
- span.setStatus({ code: import_api2.SpanStatusCode.ERROR });
989
- } finally {
990
- span.end();
991
- }
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;
992
1080
  }
993
- );
994
- }
995
- async connectAttempt(to, attempt = 0) {
996
- const retry = await tracing_default.startActiveSpan(
997
- "connect",
998
- {
999
- attributes: {
1000
- component: "river",
1001
- "river.attempt": attempt,
1002
- "span.kind": "client"
1003
- },
1004
- kind: import_api2.SpanKind.CLIENT
1005
- },
1006
- 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) => {
1007
1092
  try {
1008
- const canProceedWithConnection = () => this.state === "open";
1093
+ span.addEvent("backoff", { backoffMs });
1094
+ await sleep;
1009
1095
  if (!canProceedWithConnection()) {
1010
- log?.info(
1011
- `transport state is no longer open, cancelling attempt to connect to ${to}`,
1012
- { clientId: this.clientId, connectedTo: to }
1013
- );
1014
- return false;
1096
+ throw new Error("transport state is no longer open");
1015
1097
  }
1016
- let reconnectPromise = this.inflightConnectionPromises.get(to);
1017
- if (!reconnectPromise) {
1018
- const budgetConsumed = this.retryBudget.getBudgetConsumed(to);
1019
- if (!this.retryBudget.hasBudget(to)) {
1020
- const errMsg = `tried to connect to ${to} but retry budget exceeded (more than ${budgetConsumed} attempts in the last ${this.retryBudget.totalBudgetRestoreTime}ms)`;
1021
- log?.warn(errMsg, { clientId: this.clientId, connectedTo: to });
1022
- this.protocolError(ProtocolError.RetriesExceeded, errMsg);
1023
- return false;
1024
- }
1025
- let sleep = Promise.resolve();
1026
- const backoffMs = this.retryBudget.getBackoffMs(to);
1027
- if (backoffMs > 0) {
1028
- sleep = new Promise((resolve) => setTimeout(resolve, backoffMs));
1029
- }
1030
- log?.info(
1031
- `attempting connection to ${to} (${backoffMs}ms backoff)`,
1032
- {
1033
- clientId: this.clientId,
1034
- connectedTo: to
1035
- }
1036
- );
1037
- this.retryBudget.consumeBudget(to);
1038
- reconnectPromise = sleep.then(() => {
1039
- if (!canProceedWithConnection()) {
1040
- throw new Error("transport state is no longer open");
1041
- }
1042
- }).then(() => this.createNewOutgoingConnection(to)).then((conn) => {
1043
- if (!canProceedWithConnection()) {
1044
- log?.info(
1045
- `transport state is no longer open, closing pre-handshake connection to ${to}`,
1046
- {
1047
- clientId: this.clientId,
1048
- connectedTo: to,
1049
- connId: conn.debugId
1050
- }
1051
- );
1052
- conn.close();
1053
- throw new Error("transport state is no longer open");
1054
- }
1055
- return this.sendHandshake(to, conn).then((ok) => {
1056
- if (!ok) {
1057
- conn.close();
1058
- throw new Error("failed to send handshake");
1059
- }
1060
- return conn;
1061
- });
1062
- });
1063
- this.inflightConnectionPromises.set(to, reconnectPromise);
1064
- } else {
1098
+ span.addEvent("connecting");
1099
+ const conn = await this.createNewOutgoingConnection(to);
1100
+ if (!canProceedWithConnection()) {
1065
1101
  log?.info(
1066
- `attempting connection to ${to} (reusing previous attempt)`,
1102
+ `transport state is no longer open, closing pre-handshake connection to ${to}`,
1067
1103
  {
1068
1104
  clientId: this.clientId,
1069
- connectedTo: to
1105
+ connectedTo: to,
1106
+ connId: conn.id
1070
1107
  }
1071
1108
  );
1109
+ conn.close();
1110
+ throw new Error("transport state is no longer open");
1072
1111
  }
1073
- try {
1074
- await reconnectPromise;
1075
- } catch (error) {
1076
- this.inflightConnectionPromises.delete(to);
1077
- const errStr = coerceErrorString(error);
1078
- if (!this.reconnectOnConnectionDrop || !canProceedWithConnection()) {
1079
- log?.warn(`connection to ${to} failed (${errStr})`, {
1080
- clientId: this.clientId,
1081
- connectedTo: to
1082
- });
1083
- } else {
1084
- log?.warn(`connection to ${to} failed (${errStr}), retrying`, {
1085
- clientId: this.clientId,
1086
- connectedTo: to
1087
- });
1088
- return true;
1089
- }
1090
- }
1091
- } catch (e) {
1092
- if (e instanceof Error) {
1093
- span.recordException(e);
1094
- } else {
1095
- 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");
1096
1117
  }
1097
- 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;
1098
1124
  } finally {
1099
1125
  span.end();
1100
1126
  }
1101
- 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);
1102
1151
  }
1103
- );
1104
- if (retry) {
1105
- return this.connectAttempt(to, attempt + 1);
1106
1152
  }
1107
1153
  }
1108
1154
  deleteSession(session) {
@@ -1110,20 +1156,23 @@ var ClientTransport = class extends Transport {
1110
1156
  super.deleteSession(session);
1111
1157
  }
1112
1158
  async sendHandshake(to, conn) {
1113
- const tracing = { traceparent: "", tracestate: "" };
1114
- import_api2.propagation.inject(import_api2.context.active(), tracing);
1115
1159
  let metadata;
1116
1160
  if (this.options.handshake) {
1117
1161
  metadata = await this.options.handshake.get();
1118
1162
  if (!import_value.Value.Check(this.options.handshake.schema, metadata)) {
1119
1163
  log?.error(`handshake metadata did not match schema`, {
1120
1164
  clientId: this.clientId,
1121
- connectedTo: to
1165
+ connectedTo: to,
1166
+ tags: ["invariant-violation"]
1122
1167
  });
1123
1168
  this.protocolError(
1124
1169
  ProtocolError.HandshakeFailed,
1125
1170
  "handshake metadata did not match schema"
1126
1171
  );
1172
+ conn.telemetry?.span.setStatus({
1173
+ code: import_api3.SpanStatusCode.ERROR,
1174
+ message: "handshake meta mismatch"
1175
+ });
1127
1176
  return false;
1128
1177
  }
1129
1178
  }
@@ -1133,7 +1182,7 @@ var ClientTransport = class extends Transport {
1133
1182
  to,
1134
1183
  session.id,
1135
1184
  metadata,
1136
- tracing
1185
+ getPropagationContext(session.telemetry.ctx)
1137
1186
  );
1138
1187
  log?.debug(`sending handshake request to ${to}`, {
1139
1188
  clientId: this.clientId,
@@ -1164,267 +1213,233 @@ var ServerTransport = class extends Transport {
1164
1213
  });
1165
1214
  }
1166
1215
  handleConnection(conn) {
1167
- tracing_default.startActiveSpan(
1168
- "handleConnection",
1169
- {
1170
- attributes: {
1171
- component: "river",
1172
- "span.kind": "server"
1173
- },
1174
- kind: import_api2.SpanKind.SERVER
1175
- },
1176
- (span) => {
1177
- if (this.state !== "open")
1178
- return;
1179
- log?.info(`new incoming connection`, {
1180
- clientId: this.clientId,
1181
- 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"
1182
1237
  });
1183
- let session = void 0;
1184
- const client = () => session?.to ?? "unknown";
1185
- const handshakeTimeout = setTimeout(() => {
1186
- if (!session) {
1187
- log?.warn(
1188
- `connection to ${client()} timed out waiting for handshake, closing`,
1189
- {
1190
- clientId: this.clientId,
1191
- connectedTo: client(),
1192
- connId: conn.debugId
1193
- }
1194
- );
1195
- span.setStatus({ code: import_api2.SpanStatusCode.ERROR });
1196
- 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) {
1197
1253
  conn.close();
1198
- }
1199
- }, this.options.sessionDisconnectGraceMs);
1200
- const buffer = [];
1201
- let receivedHandshakeMessage = false;
1202
- const handshakeHandler = (data) => {
1203
- if (receivedHandshakeMessage) {
1204
- buffer.push(data);
1205
1254
  return;
1206
1255
  }
1207
- receivedHandshakeMessage = true;
1208
- clearTimeout(handshakeTimeout);
1209
- void this.receiveHandshakeRequestMessage(data, conn).then(
1210
- (maybeSession) => {
1211
- if (!maybeSession) {
1212
- span.setStatus({ code: import_api2.SpanStatusCode.ERROR });
1213
- span.end();
1214
- conn.close();
1215
- return;
1216
- }
1217
- session = maybeSession;
1218
- const dataHandler = (data2) => {
1219
- const parsed = this.parseMsg(data2);
1220
- if (!parsed) {
1221
- conn.close();
1222
- return;
1223
- }
1224
- this.handleMsg(parsed);
1225
- };
1226
- conn.removeDataListener(handshakeHandler);
1227
- conn.addDataListener(dataHandler);
1228
- for (const data2 of buffer) {
1229
- dataHandler(data2);
1230
- }
1231
- buffer.length = 0;
1256
+ session = maybeSession;
1257
+ const dataHandler = (data2) => {
1258
+ const parsed = this.parseMsg(data2);
1259
+ if (!parsed) {
1260
+ conn.close();
1261
+ return;
1232
1262
  }
1233
- );
1234
- };
1235
- conn.addDataListener(handshakeHandler);
1236
- conn.addCloseListener(() => {
1237
- if (session) {
1238
- log?.info(`connection to ${client()} disconnected`, {
1239
- clientId: this.clientId,
1240
- connId: conn.debugId
1241
- });
1242
- this.onDisconnect(conn, session);
1263
+ this.handleMsg(parsed);
1264
+ };
1265
+ for (const data2 of buffer) {
1266
+ dataHandler(data2);
1243
1267
  }
1244
- span.setStatus({ code: import_api2.SpanStatusCode.OK });
1245
- span.end();
1246
- });
1247
- conn.addErrorListener((err) => {
1248
- if (session) {
1249
- log?.warn(
1250
- `connection to ${client()} got an error: ${coerceErrorString(
1251
- err
1252
- )}`,
1253
- { clientId: this.clientId, connId: conn.debugId }
1254
- );
1255
- }
1256
- span.setStatus({ code: import_api2.SpanStatusCode.ERROR });
1257
- span.end();
1258
- });
1259
- }
1260
- );
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
+ });
1261
1296
  }
1262
1297
  async receiveHandshakeRequestMessage(data, conn) {
1263
1298
  const parsed = this.parseMsg(data);
1264
1299
  if (!parsed) {
1300
+ conn.telemetry?.span.setStatus({
1301
+ code: import_api3.SpanStatusCode.ERROR,
1302
+ message: "non-transport message"
1303
+ });
1265
1304
  this.protocolError(
1266
1305
  ProtocolError.HandshakeFailed,
1267
1306
  "received non-transport message"
1268
1307
  );
1269
1308
  return false;
1270
1309
  }
1271
- let activeContext = import_api2.context.active();
1272
- if (parsed.tracing) {
1273
- 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;
1274
1332
  }
1275
- return tracing_default.startActiveSpan(
1276
- "receiveHandshakeRequestMessage",
1277
- {
1278
- attributes: {
1279
- component: "river",
1280
- "span.kind": "server"
1281
- },
1282
- kind: import_api2.SpanKind.SERVER
1283
- },
1284
- activeContext,
1285
- async (span) => {
1286
- if (!import_value.Value.Check(ControlMessageHandshakeRequestSchema, parsed.payload)) {
1287
- const reason = "received invalid handshake msg";
1288
- const responseMsg2 = handshakeResponseMessage(
1289
- this.clientId,
1290
- parsed.from,
1291
- {
1292
- ok: false,
1293
- reason
1294
- }
1295
- );
1296
- conn.send(this.codec.toBuffer(responseMsg2));
1297
- const logData = typeof parsed.payload === "object" ? {
1298
- ...parsed,
1299
- payload: { ...parsed.payload, metadata: "redacted" }
1300
- } : { ...parsed };
1301
- log?.warn(`${reason}: ${JSON.stringify(logData)}`, {
1302
- clientId: this.clientId,
1303
- connId: conn.debugId
1304
- });
1305
- this.protocolError(
1306
- ProtocolError.HandshakeFailed,
1307
- "invalid handshake request"
1308
- );
1309
- span.setStatus({ code: import_api2.SpanStatusCode.ERROR });
1310
- span.end();
1311
- return false;
1312
- }
1313
- const gotVersion = parsed.payload.protocolVersion;
1314
- if (gotVersion !== PROTOCOL_VERSION) {
1315
- const reason = `incorrect version (got: ${gotVersion} wanted ${PROTOCOL_VERSION})`;
1316
- const responseMsg2 = handshakeResponseMessage(
1317
- this.clientId,
1318
- parsed.from,
1319
- {
1320
- ok: false,
1321
- reason
1322
- }
1323
- );
1324
- conn.send(this.codec.toBuffer(responseMsg2));
1325
- log?.warn(
1326
- `received handshake msg with incompatible protocol version (got: ${gotVersion}, expected: ${PROTOCOL_VERSION})`,
1327
- { clientId: this.clientId, connId: conn.debugId }
1328
- );
1329
- this.protocolError(ProtocolError.HandshakeFailed, reason);
1330
- span.setStatus({ code: import_api2.SpanStatusCode.ERROR });
1331
- span.end();
1332
- return false;
1333
- }
1334
- 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,
1335
1371
  parsed.from,
1336
- conn,
1337
- parsed.payload.sessionId
1372
+ { ok: false, reason }
1338
1373
  );
1339
- let handshakeMetadata;
1340
- if (this.options.handshake) {
1341
- if (!import_value.Value.Check(
1342
- this.options.handshake.requestSchema,
1343
- parsed.payload.metadata
1344
- )) {
1345
- const reason = "received malformed handshake metadata";
1346
- const responseMsg2 = handshakeResponseMessage(
1347
- this.clientId,
1348
- parsed.from,
1349
- { ok: false, reason }
1350
- );
1351
- conn.send(this.codec.toBuffer(responseMsg2));
1352
- log?.warn(
1353
- `received malformed handshake metadata from ${parsed.from}`,
1354
- {
1355
- clientId: this.clientId,
1356
- connId: conn.debugId
1357
- }
1358
- );
1359
- this.protocolError(ProtocolError.HandshakeFailed, reason);
1360
- this.deleteSession(session);
1361
- span.setStatus({ code: import_api2.SpanStatusCode.ERROR });
1362
- span.end();
1363
- return false;
1364
- }
1365
- const parsedMetadata = await this.options.handshake.parse(
1366
- parsed.payload.metadata,
1367
- session,
1368
- isReconnect
1369
- );
1370
- if (parsedMetadata === false) {
1371
- const reason = "rejected by server";
1372
- const responseMsg2 = handshakeResponseMessage(
1373
- this.clientId,
1374
- parsed.from,
1375
- { ok: false, reason }
1376
- );
1377
- conn.send(this.codec.toBuffer(responseMsg2));
1378
- log?.warn(`rejected handshake from ${parsed.from}`, {
1379
- clientId: this.clientId,
1380
- connId: conn.debugId
1381
- });
1382
- this.protocolError(ProtocolError.HandshakeFailed, reason);
1383
- this.deleteSession(session);
1384
- span.setStatus({ code: import_api2.SpanStatusCode.ERROR });
1385
- span.end();
1386
- return false;
1387
- }
1388
- if (!import_value.Value.Check(this.options.handshake.parsedSchema, parsedMetadata)) {
1389
- const reason = "failed to parse handshake metadata";
1390
- const responseMsg2 = handshakeResponseMessage(
1391
- this.clientId,
1392
- parsed.from,
1393
- { ok: false, reason }
1394
- );
1395
- conn.send(this.codec.toBuffer(responseMsg2));
1396
- log?.error(`failed to parse handshake metadata`, {
1397
- clientId: this.clientId,
1398
- connId: conn.debugId
1399
- });
1400
- this.protocolError(ProtocolError.HandshakeFailed, reason);
1401
- this.deleteSession(session);
1402
- span.setStatus({ code: import_api2.SpanStatusCode.ERROR });
1403
- span.end();
1404
- return false;
1405
- }
1406
- handshakeMetadata = parsedMetadata;
1407
- }
1408
- handshakeMetadata ??= {};
1409
- session.metadata = handshakeMetadata;
1410
- log?.debug(
1411
- `handshake from ${parsed.from} ok, responding with handshake success`,
1412
- { 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 }
1413
1398
  );
1414
- 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(
1415
1415
  this.clientId,
1416
1416
  parsed.from,
1417
- {
1418
- ok: true,
1419
- sessionId: session.id
1420
- }
1417
+ { ok: false, reason }
1421
1418
  );
1422
- conn.send(this.codec.toBuffer(responseMsg));
1423
- this.onConnect(conn, parsed.from, session, isReconnect);
1424
- span.end();
1425
- 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;
1426
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 }
1427
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;
1428
1443
  }
1429
1444
  };
1430
1445
  // Annotate the CommonJS export names for ESM import in node: