@replit/river 0.21.1 → 0.23.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-FDLAPYCK.js → chunk-DZOATC6M.js} +2 -2
  3. package/dist/{chunk-JMXO5L2X.js → chunk-MJUFKPBT.js} +354 -398
  4. package/dist/chunk-MJUFKPBT.js.map +1 -0
  5. package/dist/{chunk-5WFL722S.js → chunk-PCKHBAVP.js} +94 -3
  6. package/dist/chunk-PCKHBAVP.js.map +1 -0
  7. package/dist/{chunk-NCXUFDVL.js → chunk-VOJVLWVX.js} +360 -516
  8. package/dist/chunk-VOJVLWVX.js.map +1 -0
  9. package/dist/chunk-ZF2UFTNN.js +60 -0
  10. package/dist/chunk-ZF2UFTNN.js.map +1 -0
  11. package/dist/{connection-76c5ed01.d.ts → connection-5685d817.d.ts} +6 -4
  12. package/dist/{connection-975b25c9.d.ts → connection-7582fb92.d.ts} +1 -1
  13. package/dist/{index-dfad460e.d.ts → index-a6fe0edd.d.ts} +55 -59
  14. package/dist/logging/index.d.cts +2 -1
  15. package/dist/logging/index.d.ts +2 -1
  16. package/dist/router/index.cjs +405 -502
  17. package/dist/router/index.cjs.map +1 -1
  18. package/dist/router/index.d.cts +12 -6
  19. package/dist/router/index.d.ts +12 -6
  20. package/dist/router/index.js +4 -3
  21. package/dist/{services-9c496c6e.d.ts → services-be91b485.d.ts} +21 -52
  22. package/dist/{services-7b716dcf.d.ts → services-eb9326a1.d.ts} +21 -52
  23. package/dist/transport/impls/uds/client.cjs +197 -155
  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 +280 -266
  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 +251 -214
  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 +302 -280
  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 +400 -396
  46. package/dist/transport/index.cjs.map +1 -1
  47. package/dist/transport/index.d.cts +25 -14
  48. package/dist/transport/index.d.ts +25 -14
  49. package/dist/transport/index.js +2 -2
  50. package/dist/util/testHelpers.cjs +59 -14
  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 +12 -5
  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-DZOATC6M.js.map} +0 -0
@@ -1,9 +1,7 @@
1
1
  "use strict";
2
- var __create = Object.create;
3
2
  var __defProp = Object.defineProperty;
4
3
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
4
  var __getOwnPropNames = Object.getOwnPropertyNames;
6
- var __getProtoOf = Object.getPrototypeOf;
7
5
  var __hasOwnProp = Object.prototype.hasOwnProperty;
8
6
  var __export = (target, all) => {
9
7
  for (var name in all)
@@ -17,14 +15,6 @@ var __copyProps = (to, from, except, desc) => {
17
15
  }
18
16
  return to;
19
17
  };
20
- var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
- // If the importer is in node compatibility mode or this is not an ESM
22
- // file that has been converted to a CommonJS file using a Babel-
23
- // compatible transform (i.e. "__esModule" has not been set), then set
24
- // "default" to the CommonJS "module.exports" for node compatibility.
25
- isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
- mod
27
- ));
28
18
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
19
 
30
20
  // transport/impls/ws/client.ts
@@ -33,11 +23,9 @@ __export(client_exports, {
33
23
  WebSocketClientTransport: () => WebSocketClientTransport
34
24
  });
35
25
  module.exports = __toCommonJS(client_exports);
36
- var import_agnostic_ws2 = __toESM(require("agnostic-ws"), 1);
37
26
 
38
27
  // transport/transport.ts
39
28
  var import_value = require("@sinclair/typebox/value");
40
- var import_api2 = require("@opentelemetry/api");
41
29
 
42
30
  // transport/message.ts
43
31
  var import_typebox = require("@sinclair/typebox");
@@ -156,17 +144,71 @@ var EventDispatcher = class {
156
144
 
157
145
  // transport/session.ts
158
146
  var import_nanoid2 = require("nanoid");
147
+
148
+ // tracing/index.ts
149
+ var import_api = require("@opentelemetry/api");
150
+
151
+ // package.json
152
+ var version = "0.23.0";
153
+
154
+ // tracing/index.ts
155
+ function getPropagationContext(ctx) {
156
+ const tracing = {
157
+ traceparent: "",
158
+ tracestate: ""
159
+ };
160
+ import_api.propagation.inject(ctx, tracing);
161
+ return tracing;
162
+ }
163
+ function createSessionTelemetryInfo(session, propagationCtx) {
164
+ const ctx = propagationCtx ? import_api.propagation.extract(import_api.context.active(), propagationCtx) : import_api.context.active();
165
+ const span = tracer.startSpan(
166
+ `session ${session.id}`,
167
+ {
168
+ attributes: {
169
+ component: "river",
170
+ "river.session.id": session.id,
171
+ "river.session.to": session.to,
172
+ "river.session.from": session.from
173
+ }
174
+ },
175
+ ctx
176
+ );
177
+ return { span, ctx };
178
+ }
179
+ function createConnectionTelemetryInfo(connection, sessionSpan) {
180
+ const ctx = import_api.trace.setSpan(import_api.context.active(), sessionSpan);
181
+ const span = tracer.startSpan(
182
+ `connection ${connection.id}`,
183
+ {
184
+ attributes: {
185
+ component: "river",
186
+ "river.connection.id": connection.id
187
+ },
188
+ links: [{ context: sessionSpan.spanContext() }]
189
+ },
190
+ ctx
191
+ );
192
+ return { span, ctx };
193
+ }
194
+ var tracer = import_api.trace.getTracer("river", version);
195
+ var tracing_default = tracer;
196
+
197
+ // transport/session.ts
198
+ var import_api2 = require("@opentelemetry/api");
159
199
  var nanoid2 = (0, import_nanoid2.customAlphabet)("1234567890abcdefghijklmnopqrstuvxyz", 6);
160
200
  var unsafeId = () => nanoid2();
161
201
  var Connection = class {
162
- debugId;
202
+ id;
203
+ telemetry;
163
204
  constructor() {
164
- this.debugId = `conn-${unsafeId()}`;
205
+ this.id = `conn-${nanoid2(12)}`;
165
206
  }
166
207
  };
167
208
  var Session = class {
168
209
  codec;
169
210
  options;
211
+ telemetry;
170
212
  /**
171
213
  * The buffer of messages that have been sent but not yet acknowledged.
172
214
  */
@@ -186,12 +228,6 @@ var Session = class {
186
228
  * for this session.
187
229
  */
188
230
  advertisedSessionId;
189
- /**
190
- * The metadata for this session, as parsed from the handshake.
191
- *
192
- * Will only ever be populated on the server side.
193
- */
194
- metadata;
195
231
  /**
196
232
  * Number of messages we've sent along this session (excluding handshake and acks)
197
233
  */
@@ -213,7 +249,7 @@ var Session = class {
213
249
  * The interval for sending heartbeats.
214
250
  */
215
251
  heartbeat;
216
- constructor(conn, from, to, options) {
252
+ constructor(conn, from, to, options, propagationCtx) {
217
253
  this.id = `session-${nanoid2(12)}`;
218
254
  this.options = options;
219
255
  this.from = from;
@@ -225,13 +261,14 @@ var Session = class {
225
261
  () => this.sendHeartbeat(),
226
262
  options.heartbeatIntervalMs
227
263
  );
264
+ this.telemetry = createSessionTelemetryInfo(this, propagationCtx);
228
265
  }
229
266
  get loggingMetadata() {
230
267
  return {
231
268
  clientId: this.from,
232
269
  connectedTo: this.to,
233
270
  sessionId: this.id,
234
- connId: this.connection?.debugId
271
+ connId: this.connection?.id
235
272
  };
236
273
  }
237
274
  /**
@@ -276,6 +313,7 @@ var Session = class {
276
313
  `closing connection to ${this.to} due to inactivity (missed ${misses} heartbeats which is ${missDuration}ms)`,
277
314
  this.loggingMetadata
278
315
  );
316
+ this.telemetry.span.addEvent("closing connection due to inactivity");
279
317
  this.closeStaleConnection();
280
318
  }
281
319
  return;
@@ -297,21 +335,25 @@ var Session = class {
297
335
  sendBufferedMessages(conn) {
298
336
  log?.info(`resending ${this.sendBuffer.length} buffered messages`, {
299
337
  ...this.loggingMetadata,
300
- connId: conn.debugId
338
+ connId: conn.id
301
339
  });
302
340
  for (const msg of this.sendBuffer) {
303
341
  log?.debug(`resending msg`, {
304
342
  ...this.loggingMetadata,
305
343
  fullTransportMessage: msg,
306
- connId: conn.debugId
344
+ connId: conn.id
307
345
  });
308
346
  const ok = conn.send(this.codec.toBuffer(msg));
309
347
  if (!ok) {
310
348
  const errMsg = `failed to send buffered message to ${this.to} (sus, this is a fresh connection)`;
349
+ conn.telemetry?.span.setStatus({
350
+ code: import_api2.SpanStatusCode.ERROR,
351
+ message: errMsg
352
+ });
311
353
  log?.error(errMsg, {
312
354
  ...this.loggingMetadata,
313
355
  fullTransportMessage: msg,
314
- connId: conn.debugId,
356
+ connId: conn.id,
315
357
  tags: ["invariant-violation"]
316
358
  });
317
359
  conn.close();
@@ -394,11 +436,6 @@ var Session = class {
394
436
  }
395
437
  };
396
438
 
397
- // tracing/index.ts
398
- var import_api = require("@opentelemetry/api");
399
- var tracer = import_api.trace.getTracer("river");
400
- var tracing_default = tracer;
401
-
402
439
  // util/stringify.ts
403
440
  function coerceErrorString(err) {
404
441
  if (err instanceof Error) {
@@ -531,6 +568,7 @@ var NaiveJsonCodec = {
531
568
  };
532
569
 
533
570
  // transport/transport.ts
571
+ var import_api3 = require("@opentelemetry/api");
534
572
  var defaultTransportOptions = {
535
573
  heartbeatIntervalMs: 1e3,
536
574
  heartbeatsUntilDead: 2,
@@ -609,17 +647,22 @@ var Transport = class {
609
647
  status: "connect",
610
648
  conn
611
649
  });
650
+ conn.telemetry = createConnectionTelemetryInfo(
651
+ conn,
652
+ session.telemetry.span
653
+ );
612
654
  if (isReconnect) {
613
655
  session.replaceWithNewConnection(conn);
614
656
  log?.info(`reconnected to ${connectedTo}`, session.loggingMetadata);
615
657
  }
616
658
  }
617
- createSession(to, conn) {
659
+ createSession(to, conn, propagationCtx) {
618
660
  const session = new Session(
619
661
  conn,
620
662
  this.clientId,
621
663
  to,
622
- this.options
664
+ this.options,
665
+ propagationCtx
623
666
  );
624
667
  this.sessions.set(session.to, session);
625
668
  this.eventDispatcher.dispatchEvent("sessionStatus", {
@@ -628,11 +671,11 @@ var Transport = class {
628
671
  });
629
672
  return session;
630
673
  }
631
- getOrCreateSession(to, conn, sessionId) {
674
+ getOrCreateSession(to, conn, sessionId, propagationCtx) {
632
675
  let session = this.sessions.get(to);
633
676
  let isReconnect = session !== void 0;
634
677
  if (session?.advertisedSessionId !== void 0 && sessionId !== void 0 && session.advertisedSessionId !== sessionId) {
635
- log?.warn(
678
+ log?.info(
636
679
  `session for ${to} already exists but has a different session id (expected: ${session.advertisedSessionId}, got: ${sessionId}), creating a new one`,
637
680
  session.loggingMetadata
638
681
  );
@@ -641,7 +684,7 @@ var Transport = class {
641
684
  session = void 0;
642
685
  }
643
686
  if (!session) {
644
- session = this.createSession(to, conn);
687
+ session = this.createSession(to, conn, propagationCtx);
645
688
  log?.info(
646
689
  `no session for ${to}, created a new one`,
647
690
  session.loggingMetadata
@@ -654,6 +697,7 @@ var Transport = class {
654
697
  }
655
698
  deleteSession(session) {
656
699
  session.close();
700
+ session.telemetry.span.end();
657
701
  this.sessions.delete(session.to);
658
702
  log?.info(
659
703
  `session ${session.id} disconnect from ${session.to}`,
@@ -670,12 +714,16 @@ var Transport = class {
670
714
  * @param connectedTo The peer we are connected to.
671
715
  */
672
716
  onDisconnect(conn, session) {
717
+ conn.telemetry?.span.end();
673
718
  this.eventDispatcher.dispatchEvent("connectionStatus", {
674
719
  status: "disconnect",
675
720
  conn
676
721
  });
677
722
  session.connection = void 0;
678
- session.beginGrace(() => this.deleteSession(session));
723
+ session.beginGrace(() => {
724
+ session.telemetry.span.addEvent("session grace period expired");
725
+ this.deleteSession(session);
726
+ });
679
727
  }
680
728
  /**
681
729
  * Parses a message from a Uint8Array into a {@link OpaqueTransportMessage}.
@@ -735,6 +783,10 @@ var Transport = class {
735
783
  tags: ["invariant-violation"]
736
784
  });
737
785
  this.protocolError(ProtocolError.MessageOrderingViolated, errMsg);
786
+ session.telemetry.span.setStatus({
787
+ code: import_api3.SpanStatusCode.ERROR,
788
+ message: "message order violated"
789
+ });
738
790
  session.close();
739
791
  }
740
792
  return;
@@ -845,6 +897,10 @@ var ClientTransport = class extends Transport {
845
897
  * tests or a special case where you don't want to reconnect.
846
898
  */
847
899
  reconnectOnConnectionDrop = true;
900
+ /**
901
+ * Optional handshake options for this client.
902
+ */
903
+ handshakeExtensions;
848
904
  constructor(clientId, providedOptions) {
849
905
  super(clientId, providedOptions);
850
906
  this.options = {
@@ -854,6 +910,9 @@ var ClientTransport = class extends Transport {
854
910
  this.inflightConnectionPromises = /* @__PURE__ */ new Map();
855
911
  this.retryBudget = new LeakyBucketRateLimit(this.options);
856
912
  }
913
+ extendHandshake(options) {
914
+ this.handshakeExtensions = options;
915
+ }
857
916
  handleConnection(conn, to) {
858
917
  if (this.state !== "open")
859
918
  return;
@@ -862,7 +921,7 @@ var ClientTransport = class extends Transport {
862
921
  if (!session) {
863
922
  log?.warn(
864
923
  `connection to ${to} timed out waiting for handshake, closing`,
865
- { clientId: this.clientId, connectedTo: to, connId: conn.debugId }
924
+ { clientId: this.clientId, connectedTo: to, connId: conn.id }
866
925
  );
867
926
  conn.close();
868
927
  }
@@ -880,6 +939,10 @@ var ClientTransport = class extends Transport {
880
939
  conn.addDataListener((data2) => {
881
940
  const parsed = this.parseMsg(data2);
882
941
  if (!parsed) {
942
+ conn.telemetry?.span.setStatus({
943
+ code: import_api3.SpanStatusCode.ERROR,
944
+ message: "message parse failure"
945
+ });
883
946
  conn.close();
884
947
  return;
885
948
  }
@@ -902,6 +965,10 @@ var ClientTransport = class extends Transport {
902
965
  }
903
966
  });
904
967
  conn.addErrorListener((err) => {
968
+ conn.telemetry?.span.setStatus({
969
+ code: import_api3.SpanStatusCode.ERROR,
970
+ message: "connection error"
971
+ });
905
972
  log?.warn(`error in connection to ${to}: ${coerceErrorString(err)}`, {
906
973
  ...session?.loggingMetadata,
907
974
  clientId: this.clientId,
@@ -912,6 +979,10 @@ var ClientTransport = class extends Transport {
912
979
  receiveHandshakeResponseMessage(data, conn) {
913
980
  const parsed = this.parseMsg(data);
914
981
  if (!parsed) {
982
+ conn.telemetry?.span.setStatus({
983
+ code: import_api3.SpanStatusCode.ERROR,
984
+ message: "non-transport message"
985
+ });
915
986
  this.protocolError(
916
987
  ProtocolError.HandshakeFailed,
917
988
  "received non-transport message"
@@ -919,6 +990,10 @@ var ClientTransport = class extends Transport {
919
990
  return false;
920
991
  }
921
992
  if (!import_value.Value.Check(ControlMessageHandshakeResponseSchema, parsed.payload)) {
993
+ conn.telemetry?.span.setStatus({
994
+ code: import_api3.SpanStatusCode.ERROR,
995
+ message: "invalid handshake response"
996
+ });
922
997
  log?.warn(`received invalid handshake resp`, {
923
998
  clientId: this.clientId,
924
999
  connectedTo: parsed.from,
@@ -931,7 +1006,11 @@ var ClientTransport = class extends Transport {
931
1006
  return false;
932
1007
  }
933
1008
  if (!parsed.payload.status.ok) {
934
- log?.warn(`received invalid handshake resp`, {
1009
+ conn.telemetry?.span.setStatus({
1010
+ code: import_api3.SpanStatusCode.ERROR,
1011
+ message: "handshake rejected"
1012
+ });
1013
+ log?.warn(`received handshake rejection`, {
935
1014
  clientId: this.clientId,
936
1015
  connectedTo: parsed.from,
937
1016
  fullTransportMessage: parsed
@@ -961,142 +1040,94 @@ var ClientTransport = class extends Transport {
961
1040
  * @param to The client ID of the node to connect to.
962
1041
  */
963
1042
  async connect(to) {
964
- return tracing_default.startActiveSpan(
965
- "connect",
966
- {
967
- attributes: {
968
- component: "river",
969
- "span.kind": "client"
970
- },
971
- kind: import_api2.SpanKind.CLIENT
972
- },
973
- async (span) => {
974
- try {
975
- await this.connectAttempt(to);
976
- } catch (e) {
977
- if (e instanceof Error) {
978
- span.recordException(e);
979
- } else {
980
- span.recordException(coerceErrorString(e));
981
- }
982
- span.setStatus({ code: import_api2.SpanStatusCode.ERROR });
983
- } finally {
984
- span.end();
985
- }
1043
+ const canProceedWithConnection = () => this.state === "open";
1044
+ if (!canProceedWithConnection()) {
1045
+ log?.info(
1046
+ `transport state is no longer open, cancelling attempt to connect to ${to}`,
1047
+ { clientId: this.clientId, connectedTo: to }
1048
+ );
1049
+ return;
1050
+ }
1051
+ let reconnectPromise = this.inflightConnectionPromises.get(to);
1052
+ if (!reconnectPromise) {
1053
+ const budgetConsumed = this.retryBudget.getBudgetConsumed(to);
1054
+ if (!this.retryBudget.hasBudget(to)) {
1055
+ const errMsg = `tried to connect to ${to} but retry budget exceeded (more than ${budgetConsumed} attempts in the last ${this.retryBudget.totalBudgetRestoreTime}ms)`;
1056
+ log?.warn(errMsg, { clientId: this.clientId, connectedTo: to });
1057
+ this.protocolError(ProtocolError.RetriesExceeded, errMsg);
1058
+ return;
986
1059
  }
987
- );
988
- }
989
- async connectAttempt(to, attempt = 0) {
990
- const retry = await tracing_default.startActiveSpan(
991
- "connect",
992
- {
993
- attributes: {
994
- component: "river",
995
- "river.attempt": attempt,
996
- "span.kind": "client"
997
- },
998
- kind: import_api2.SpanKind.CLIENT
999
- },
1000
- async (span) => {
1060
+ let sleep = Promise.resolve();
1061
+ const backoffMs = this.retryBudget.getBackoffMs(to);
1062
+ if (backoffMs > 0) {
1063
+ sleep = new Promise((resolve) => setTimeout(resolve, backoffMs));
1064
+ }
1065
+ log?.info(`attempting connection to ${to} (${backoffMs}ms backoff)`, {
1066
+ clientId: this.clientId,
1067
+ connectedTo: to
1068
+ });
1069
+ this.retryBudget.consumeBudget(to);
1070
+ reconnectPromise = tracing_default.startActiveSpan("connect", async (span) => {
1001
1071
  try {
1002
- const canProceedWithConnection = () => this.state === "open";
1072
+ span.addEvent("backoff", { backoffMs });
1073
+ await sleep;
1003
1074
  if (!canProceedWithConnection()) {
1004
- log?.info(
1005
- `transport state is no longer open, cancelling attempt to connect to ${to}`,
1006
- { clientId: this.clientId, connectedTo: to }
1007
- );
1008
- return false;
1075
+ throw new Error("transport state is no longer open");
1009
1076
  }
1010
- let reconnectPromise = this.inflightConnectionPromises.get(to);
1011
- if (!reconnectPromise) {
1012
- const budgetConsumed = this.retryBudget.getBudgetConsumed(to);
1013
- if (!this.retryBudget.hasBudget(to)) {
1014
- const errMsg = `tried to connect to ${to} but retry budget exceeded (more than ${budgetConsumed} attempts in the last ${this.retryBudget.totalBudgetRestoreTime}ms)`;
1015
- log?.warn(errMsg, { clientId: this.clientId, connectedTo: to });
1016
- this.protocolError(ProtocolError.RetriesExceeded, errMsg);
1017
- return false;
1018
- }
1019
- let sleep = Promise.resolve();
1020
- const backoffMs = this.retryBudget.getBackoffMs(to);
1021
- if (backoffMs > 0) {
1022
- sleep = new Promise((resolve) => setTimeout(resolve, backoffMs));
1023
- }
1024
- log?.info(
1025
- `attempting connection to ${to} (${backoffMs}ms backoff)`,
1026
- {
1027
- clientId: this.clientId,
1028
- connectedTo: to
1029
- }
1030
- );
1031
- this.retryBudget.consumeBudget(to);
1032
- reconnectPromise = sleep.then(() => {
1033
- if (!canProceedWithConnection()) {
1034
- throw new Error("transport state is no longer open");
1035
- }
1036
- }).then(() => this.createNewOutgoingConnection(to)).then((conn) => {
1037
- if (!canProceedWithConnection()) {
1038
- log?.info(
1039
- `transport state is no longer open, closing pre-handshake connection to ${to}`,
1040
- {
1041
- clientId: this.clientId,
1042
- connectedTo: to,
1043
- connId: conn.debugId
1044
- }
1045
- );
1046
- conn.close();
1047
- throw new Error("transport state is no longer open");
1048
- }
1049
- return this.sendHandshake(to, conn).then((ok) => {
1050
- if (!ok) {
1051
- conn.close();
1052
- throw new Error("failed to send handshake");
1053
- }
1054
- return conn;
1055
- });
1056
- });
1057
- this.inflightConnectionPromises.set(to, reconnectPromise);
1058
- } else {
1077
+ span.addEvent("connecting");
1078
+ const conn = await this.createNewOutgoingConnection(to);
1079
+ if (!canProceedWithConnection()) {
1059
1080
  log?.info(
1060
- `attempting connection to ${to} (reusing previous attempt)`,
1081
+ `transport state is no longer open, closing pre-handshake connection to ${to}`,
1061
1082
  {
1062
1083
  clientId: this.clientId,
1063
- connectedTo: to
1084
+ connectedTo: to,
1085
+ connId: conn.id
1064
1086
  }
1065
1087
  );
1088
+ conn.close();
1089
+ throw new Error("transport state is no longer open");
1066
1090
  }
1067
- try {
1068
- await reconnectPromise;
1069
- } catch (error) {
1070
- this.inflightConnectionPromises.delete(to);
1071
- const errStr = coerceErrorString(error);
1072
- if (!this.reconnectOnConnectionDrop || !canProceedWithConnection()) {
1073
- log?.warn(`connection to ${to} failed (${errStr})`, {
1074
- clientId: this.clientId,
1075
- connectedTo: to
1076
- });
1077
- } else {
1078
- log?.warn(`connection to ${to} failed (${errStr}), retrying`, {
1079
- clientId: this.clientId,
1080
- connectedTo: to
1081
- });
1082
- return true;
1083
- }
1091
+ span.addEvent("sending handshake");
1092
+ const ok = await this.sendHandshake(to, conn);
1093
+ if (!ok) {
1094
+ conn.close();
1095
+ throw new Error("failed to send handshake");
1084
1096
  }
1085
- } catch (e) {
1086
- if (e instanceof Error) {
1087
- span.recordException(e);
1088
- } else {
1089
- span.recordException(coerceErrorString(e));
1090
- }
1091
- span.setStatus({ code: import_api2.SpanStatusCode.ERROR });
1097
+ return conn;
1098
+ } catch (err) {
1099
+ const errStr = coerceErrorString(err);
1100
+ span.recordException(errStr);
1101
+ span.setStatus({ code: import_api3.SpanStatusCode.ERROR });
1102
+ throw err;
1092
1103
  } finally {
1093
1104
  span.end();
1094
1105
  }
1095
- return false;
1106
+ });
1107
+ this.inflightConnectionPromises.set(to, reconnectPromise);
1108
+ } else {
1109
+ log?.info(`attempting connection to ${to} (reusing previous attempt)`, {
1110
+ clientId: this.clientId,
1111
+ connectedTo: to
1112
+ });
1113
+ }
1114
+ try {
1115
+ await reconnectPromise;
1116
+ } catch (error) {
1117
+ this.inflightConnectionPromises.delete(to);
1118
+ const errStr = coerceErrorString(error);
1119
+ if (!this.reconnectOnConnectionDrop || !canProceedWithConnection()) {
1120
+ log?.warn(`connection to ${to} failed (${errStr})`, {
1121
+ clientId: this.clientId,
1122
+ connectedTo: to
1123
+ });
1124
+ } else {
1125
+ log?.warn(`connection to ${to} failed (${errStr}), retrying`, {
1126
+ clientId: this.clientId,
1127
+ connectedTo: to
1128
+ });
1129
+ return this.connect(to);
1096
1130
  }
1097
- );
1098
- if (retry) {
1099
- return this.connectAttempt(to, attempt + 1);
1100
1131
  }
1101
1132
  }
1102
1133
  deleteSession(session) {
@@ -1104,13 +1135,11 @@ var ClientTransport = class extends Transport {
1104
1135
  super.deleteSession(session);
1105
1136
  }
1106
1137
  async sendHandshake(to, conn) {
1107
- const tracing = { traceparent: "", tracestate: "" };
1108
- import_api2.propagation.inject(import_api2.context.active(), tracing);
1109
1138
  let metadata;
1110
- if (this.options.handshake) {
1111
- metadata = await this.options.handshake.get();
1112
- if (!import_value.Value.Check(this.options.handshake.schema, metadata)) {
1113
- log?.error(`handshake metadata did not match schema`, {
1139
+ if (this.handshakeExtensions) {
1140
+ metadata = await this.handshakeExtensions.construct();
1141
+ if (!import_value.Value.Check(this.handshakeExtensions.schema, metadata)) {
1142
+ log?.error(`constructed handshake metadata did not match schema`, {
1114
1143
  clientId: this.clientId,
1115
1144
  connectedTo: to,
1116
1145
  tags: ["invariant-violation"]
@@ -1119,6 +1148,10 @@ var ClientTransport = class extends Transport {
1119
1148
  ProtocolError.HandshakeFailed,
1120
1149
  "handshake metadata did not match schema"
1121
1150
  );
1151
+ conn.telemetry?.span.setStatus({
1152
+ code: import_api3.SpanStatusCode.ERROR,
1153
+ message: "handshake meta mismatch"
1154
+ });
1122
1155
  return false;
1123
1156
  }
1124
1157
  }
@@ -1128,7 +1161,7 @@ var ClientTransport = class extends Transport {
1128
1161
  to,
1129
1162
  session.id,
1130
1163
  metadata,
1131
- tracing
1164
+ getPropagationContext(session.telemetry.ctx)
1132
1165
  );
1133
1166
  log?.debug(`sending handshake request to ${to}`, {
1134
1167
  clientId: this.clientId,
@@ -1144,13 +1177,31 @@ var ClientTransport = class extends Transport {
1144
1177
  };
1145
1178
 
1146
1179
  // transport/impls/ws/connection.ts
1147
- var import_agnostic_ws = __toESM(require("agnostic-ws"), 1);
1148
1180
  var WebSocketConnection = class extends Connection {
1181
+ errorCb = null;
1182
+ closeCb = null;
1149
1183
  ws;
1150
1184
  constructor(ws) {
1151
1185
  super();
1152
1186
  this.ws = ws;
1153
1187
  this.ws.binaryType = "arraybuffer";
1188
+ let didError = false;
1189
+ this.ws.onerror = () => {
1190
+ didError = true;
1191
+ };
1192
+ this.ws.onclose = ({ code, reason }) => {
1193
+ if (didError && this.errorCb) {
1194
+ this.errorCb(
1195
+ new Error(
1196
+ `websocket closed with code and reason: ${code} - ${reason}`
1197
+ )
1198
+ );
1199
+ return;
1200
+ }
1201
+ if (this.closeCb) {
1202
+ this.closeCb();
1203
+ }
1204
+ };
1154
1205
  }
1155
1206
  addDataListener(cb) {
1156
1207
  this.ws.onmessage = (msg) => cb(msg.data);
@@ -1159,13 +1210,13 @@ var WebSocketConnection = class extends Connection {
1159
1210
  this.ws.onmessage = null;
1160
1211
  }
1161
1212
  addCloseListener(cb) {
1162
- this.ws.onclose = cb;
1213
+ this.closeCb = cb;
1163
1214
  }
1164
1215
  addErrorListener(cb) {
1165
- this.ws.onerror = (err) => cb(err.error);
1216
+ this.errorCb = cb;
1166
1217
  }
1167
1218
  send(payload) {
1168
- if (this.ws.readyState === import_agnostic_ws.default.OPEN) {
1219
+ if (this.ws.readyState === this.ws.OPEN) {
1169
1220
  this.ws.send(payload);
1170
1221
  return true;
1171
1222
  } else {
@@ -1182,7 +1233,7 @@ var WebSocketClientTransport = class extends ClientTransport {
1182
1233
  /**
1183
1234
  * A function that returns a Promise that resolves to a websocket URL.
1184
1235
  */
1185
- urlGetter;
1236
+ wsGetter;
1186
1237
  /**
1187
1238
  * Creates a new WebSocketClientTransport instance.
1188
1239
  * @param wsGetter A function that returns a Promise that resolves to a WebSocket instance.
@@ -1190,53 +1241,39 @@ var WebSocketClientTransport = class extends ClientTransport {
1190
1241
  * @param serverId The ID of the server this transport is connecting to.
1191
1242
  * @param providedOptions An optional object containing configuration options for the transport.
1192
1243
  */
1193
- constructor(urlGetter, clientId, providedOptions) {
1244
+ constructor(wsGetter, clientId, providedOptions) {
1194
1245
  super(clientId, providedOptions);
1195
- this.urlGetter = urlGetter;
1246
+ this.wsGetter = wsGetter;
1196
1247
  }
1197
1248
  async createNewOutgoingConnection(to) {
1198
- const wsRes = await new Promise((resolve) => {
1199
- log?.info(`establishing a new websocket to ${to}`, {
1200
- clientId: this.clientId,
1201
- connectedTo: to
1202
- });
1203
- Promise.resolve(this.urlGetter(to)).then((url) => new import_agnostic_ws2.default(url)).then((ws) => {
1204
- if (ws.readyState === import_agnostic_ws2.default.OPEN) {
1205
- resolve({ ws });
1206
- return;
1207
- }
1208
- if (ws.readyState === import_agnostic_ws2.default.CLOSING || ws.readyState === import_agnostic_ws2.default.CLOSED) {
1209
- resolve({ err: "ws is closing or closed" });
1210
- return;
1211
- }
1212
- ws.onopen = () => {
1213
- resolve({ ws });
1214
- };
1215
- ws.onclose = (evt) => {
1216
- resolve({ err: evt.reason });
1217
- };
1218
- ws.onerror = (evt) => {
1219
- const err = evt.error;
1220
- resolve({
1221
- err: `${err.name}: ${err.message}`
1222
- });
1223
- };
1224
- }).catch((e) => {
1225
- const reason = e instanceof Error ? e.message : "unknown reason";
1226
- resolve({ err: `couldn't get a new websocket: ${reason}` });
1227
- });
1249
+ log?.info(`establishing a new websocket to ${to}`, {
1250
+ clientId: this.clientId,
1251
+ connectedTo: to
1228
1252
  });
1229
- if ("ws" in wsRes) {
1230
- const conn = new WebSocketConnection(wsRes.ws);
1231
- log?.info(`raw websocket to ${to} ok, starting handshake`, {
1232
- clientId: this.clientId,
1233
- connectedTo: to
1234
- });
1235
- this.handleConnection(conn, to);
1236
- return conn;
1237
- } else {
1238
- throw new Error(wsRes.err);
1239
- }
1253
+ const ws = await this.wsGetter(to);
1254
+ await new Promise((resolve, reject) => {
1255
+ if (ws.readyState === ws.OPEN) {
1256
+ resolve();
1257
+ return;
1258
+ }
1259
+ if (ws.readyState === ws.CLOSING || ws.readyState === ws.CLOSED) {
1260
+ reject(new Error("ws is closing or closed"));
1261
+ return;
1262
+ }
1263
+ ws.onopen = () => {
1264
+ resolve();
1265
+ };
1266
+ ws.onclose = (evt) => {
1267
+ reject(new Error(evt.reason));
1268
+ };
1269
+ });
1270
+ const conn = new WebSocketConnection(ws);
1271
+ log?.info(`raw websocket to ${to} ok, starting handshake`, {
1272
+ clientId: this.clientId,
1273
+ connectedTo: to
1274
+ });
1275
+ this.handleConnection(conn, to);
1276
+ return conn;
1240
1277
  }
1241
1278
  };
1242
1279
  // Annotate the CommonJS export names for ESM import in node: