@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/server.ts
@@ -36,7 +26,6 @@ module.exports = __toCommonJS(server_exports);
36
26
 
37
27
  // transport/transport.ts
38
28
  var import_value = require("@sinclair/typebox/value");
39
- var import_api2 = require("@opentelemetry/api");
40
29
 
41
30
  // transport/message.ts
42
31
  var import_typebox = require("@sinclair/typebox");
@@ -152,17 +141,62 @@ var EventDispatcher = class {
152
141
 
153
142
  // transport/session.ts
154
143
  var import_nanoid2 = require("nanoid");
144
+
145
+ // tracing/index.ts
146
+ var import_api = require("@opentelemetry/api");
147
+
148
+ // package.json
149
+ var version = "0.23.0";
150
+
151
+ // tracing/index.ts
152
+ function createSessionTelemetryInfo(session, propagationCtx) {
153
+ const ctx = propagationCtx ? import_api.propagation.extract(import_api.context.active(), propagationCtx) : import_api.context.active();
154
+ const span = tracer.startSpan(
155
+ `session ${session.id}`,
156
+ {
157
+ attributes: {
158
+ component: "river",
159
+ "river.session.id": session.id,
160
+ "river.session.to": session.to,
161
+ "river.session.from": session.from
162
+ }
163
+ },
164
+ ctx
165
+ );
166
+ return { span, ctx };
167
+ }
168
+ function createConnectionTelemetryInfo(connection, sessionSpan) {
169
+ const ctx = import_api.trace.setSpan(import_api.context.active(), sessionSpan);
170
+ const span = tracer.startSpan(
171
+ `connection ${connection.id}`,
172
+ {
173
+ attributes: {
174
+ component: "river",
175
+ "river.connection.id": connection.id
176
+ },
177
+ links: [{ context: sessionSpan.spanContext() }]
178
+ },
179
+ ctx
180
+ );
181
+ return { span, ctx };
182
+ }
183
+ var tracer = import_api.trace.getTracer("river", version);
184
+
185
+ // transport/session.ts
186
+ var import_api2 = require("@opentelemetry/api");
155
187
  var nanoid2 = (0, import_nanoid2.customAlphabet)("1234567890abcdefghijklmnopqrstuvxyz", 6);
156
188
  var unsafeId = () => nanoid2();
157
189
  var Connection = class {
158
- debugId;
190
+ id;
191
+ telemetry;
159
192
  constructor() {
160
- this.debugId = `conn-${unsafeId()}`;
193
+ this.id = `conn-${nanoid2(12)}`;
161
194
  }
162
195
  };
163
196
  var Session = class {
164
197
  codec;
165
198
  options;
199
+ telemetry;
166
200
  /**
167
201
  * The buffer of messages that have been sent but not yet acknowledged.
168
202
  */
@@ -182,12 +216,6 @@ var Session = class {
182
216
  * for this session.
183
217
  */
184
218
  advertisedSessionId;
185
- /**
186
- * The metadata for this session, as parsed from the handshake.
187
- *
188
- * Will only ever be populated on the server side.
189
- */
190
- metadata;
191
219
  /**
192
220
  * Number of messages we've sent along this session (excluding handshake and acks)
193
221
  */
@@ -209,7 +237,7 @@ var Session = class {
209
237
  * The interval for sending heartbeats.
210
238
  */
211
239
  heartbeat;
212
- constructor(conn, from, to, options) {
240
+ constructor(conn, from, to, options, propagationCtx) {
213
241
  this.id = `session-${nanoid2(12)}`;
214
242
  this.options = options;
215
243
  this.from = from;
@@ -221,13 +249,14 @@ var Session = class {
221
249
  () => this.sendHeartbeat(),
222
250
  options.heartbeatIntervalMs
223
251
  );
252
+ this.telemetry = createSessionTelemetryInfo(this, propagationCtx);
224
253
  }
225
254
  get loggingMetadata() {
226
255
  return {
227
256
  clientId: this.from,
228
257
  connectedTo: this.to,
229
258
  sessionId: this.id,
230
- connId: this.connection?.debugId
259
+ connId: this.connection?.id
231
260
  };
232
261
  }
233
262
  /**
@@ -272,6 +301,7 @@ var Session = class {
272
301
  `closing connection to ${this.to} due to inactivity (missed ${misses} heartbeats which is ${missDuration}ms)`,
273
302
  this.loggingMetadata
274
303
  );
304
+ this.telemetry.span.addEvent("closing connection due to inactivity");
275
305
  this.closeStaleConnection();
276
306
  }
277
307
  return;
@@ -293,21 +323,25 @@ var Session = class {
293
323
  sendBufferedMessages(conn) {
294
324
  log?.info(`resending ${this.sendBuffer.length} buffered messages`, {
295
325
  ...this.loggingMetadata,
296
- connId: conn.debugId
326
+ connId: conn.id
297
327
  });
298
328
  for (const msg of this.sendBuffer) {
299
329
  log?.debug(`resending msg`, {
300
330
  ...this.loggingMetadata,
301
331
  fullTransportMessage: msg,
302
- connId: conn.debugId
332
+ connId: conn.id
303
333
  });
304
334
  const ok = conn.send(this.codec.toBuffer(msg));
305
335
  if (!ok) {
306
336
  const errMsg = `failed to send buffered message to ${this.to} (sus, this is a fresh connection)`;
337
+ conn.telemetry?.span.setStatus({
338
+ code: import_api2.SpanStatusCode.ERROR,
339
+ message: errMsg
340
+ });
307
341
  log?.error(errMsg, {
308
342
  ...this.loggingMetadata,
309
343
  fullTransportMessage: msg,
310
- connId: conn.debugId,
344
+ connId: conn.id,
311
345
  tags: ["invariant-violation"]
312
346
  });
313
347
  conn.close();
@@ -390,11 +424,6 @@ var Session = class {
390
424
  }
391
425
  };
392
426
 
393
- // tracing/index.ts
394
- var import_api = require("@opentelemetry/api");
395
- var tracer = import_api.trace.getTracer("river");
396
- var tracing_default = tracer;
397
-
398
427
  // util/stringify.ts
399
428
  function coerceErrorString(err) {
400
429
  if (err instanceof Error) {
@@ -456,6 +485,7 @@ var NaiveJsonCodec = {
456
485
  };
457
486
 
458
487
  // transport/transport.ts
488
+ var import_api3 = require("@opentelemetry/api");
459
489
  var defaultTransportOptions = {
460
490
  heartbeatIntervalMs: 1e3,
461
491
  heartbeatsUntilDead: 2,
@@ -534,17 +564,22 @@ var Transport = class {
534
564
  status: "connect",
535
565
  conn
536
566
  });
567
+ conn.telemetry = createConnectionTelemetryInfo(
568
+ conn,
569
+ session.telemetry.span
570
+ );
537
571
  if (isReconnect) {
538
572
  session.replaceWithNewConnection(conn);
539
573
  log?.info(`reconnected to ${connectedTo}`, session.loggingMetadata);
540
574
  }
541
575
  }
542
- createSession(to, conn) {
576
+ createSession(to, conn, propagationCtx) {
543
577
  const session = new Session(
544
578
  conn,
545
579
  this.clientId,
546
580
  to,
547
- this.options
581
+ this.options,
582
+ propagationCtx
548
583
  );
549
584
  this.sessions.set(session.to, session);
550
585
  this.eventDispatcher.dispatchEvent("sessionStatus", {
@@ -553,11 +588,11 @@ var Transport = class {
553
588
  });
554
589
  return session;
555
590
  }
556
- getOrCreateSession(to, conn, sessionId) {
591
+ getOrCreateSession(to, conn, sessionId, propagationCtx) {
557
592
  let session = this.sessions.get(to);
558
593
  let isReconnect = session !== void 0;
559
594
  if (session?.advertisedSessionId !== void 0 && sessionId !== void 0 && session.advertisedSessionId !== sessionId) {
560
- log?.warn(
595
+ log?.info(
561
596
  `session for ${to} already exists but has a different session id (expected: ${session.advertisedSessionId}, got: ${sessionId}), creating a new one`,
562
597
  session.loggingMetadata
563
598
  );
@@ -566,7 +601,7 @@ var Transport = class {
566
601
  session = void 0;
567
602
  }
568
603
  if (!session) {
569
- session = this.createSession(to, conn);
604
+ session = this.createSession(to, conn, propagationCtx);
570
605
  log?.info(
571
606
  `no session for ${to}, created a new one`,
572
607
  session.loggingMetadata
@@ -579,6 +614,7 @@ var Transport = class {
579
614
  }
580
615
  deleteSession(session) {
581
616
  session.close();
617
+ session.telemetry.span.end();
582
618
  this.sessions.delete(session.to);
583
619
  log?.info(
584
620
  `session ${session.id} disconnect from ${session.to}`,
@@ -595,12 +631,16 @@ var Transport = class {
595
631
  * @param connectedTo The peer we are connected to.
596
632
  */
597
633
  onDisconnect(conn, session) {
634
+ conn.telemetry?.span.end();
598
635
  this.eventDispatcher.dispatchEvent("connectionStatus", {
599
636
  status: "disconnect",
600
637
  conn
601
638
  });
602
639
  session.connection = void 0;
603
- session.beginGrace(() => this.deleteSession(session));
640
+ session.beginGrace(() => {
641
+ session.telemetry.span.addEvent("session grace period expired");
642
+ this.deleteSession(session);
643
+ });
604
644
  }
605
645
  /**
606
646
  * Parses a message from a Uint8Array into a {@link OpaqueTransportMessage}.
@@ -660,6 +700,10 @@ var Transport = class {
660
700
  tags: ["invariant-violation"]
661
701
  });
662
702
  this.protocolError(ProtocolError.MessageOrderingViolated, errMsg);
703
+ session.telemetry.span.setStatus({
704
+ code: import_api3.SpanStatusCode.ERROR,
705
+ message: "message order violated"
706
+ });
663
707
  session.close();
664
708
  }
665
709
  return;
@@ -758,291 +802,269 @@ var ServerTransport = class extends Transport {
758
802
  * The options for this transport.
759
803
  */
760
804
  options;
805
+ /**
806
+ * Optional handshake options for the server.
807
+ */
808
+ handshakeExtensions;
809
+ /**
810
+ * A map of session handshake data for each session.
811
+ */
812
+ sessionHandshakeMetadata;
761
813
  constructor(clientId, providedOptions) {
762
814
  super(clientId, providedOptions);
763
815
  this.options = {
764
816
  ...defaultServerTransportOptions,
765
817
  ...providedOptions
766
818
  };
819
+ this.sessionHandshakeMetadata = /* @__PURE__ */ new WeakMap();
767
820
  log?.info(`initiated server transport`, {
768
821
  clientId: this.clientId,
769
822
  protocolVersion: PROTOCOL_VERSION
770
823
  });
771
824
  }
825
+ extendHandshake(options) {
826
+ this.handshakeExtensions = options;
827
+ }
772
828
  handleConnection(conn) {
773
- tracing_default.startActiveSpan(
774
- "handleConnection",
775
- {
776
- attributes: {
777
- component: "river",
778
- "span.kind": "server"
779
- },
780
- kind: import_api2.SpanKind.SERVER
781
- },
782
- (span) => {
783
- if (this.state !== "open")
784
- return;
785
- log?.info(`new incoming connection`, {
786
- clientId: this.clientId,
787
- connId: conn.debugId
829
+ if (this.state !== "open")
830
+ return;
831
+ log?.info(`new incoming connection`, {
832
+ clientId: this.clientId,
833
+ connId: conn.id
834
+ });
835
+ let session = void 0;
836
+ const client = () => session?.to ?? "unknown";
837
+ const handshakeTimeout = setTimeout(() => {
838
+ if (!session) {
839
+ log?.warn(
840
+ `connection to ${client()} timed out waiting for handshake, closing`,
841
+ {
842
+ clientId: this.clientId,
843
+ connectedTo: client(),
844
+ connId: conn.id
845
+ }
846
+ );
847
+ conn.telemetry?.span.setStatus({
848
+ code: import_api3.SpanStatusCode.ERROR,
849
+ message: "handshake timeout"
788
850
  });
789
- let session = void 0;
790
- const client = () => session?.to ?? "unknown";
791
- const handshakeTimeout = setTimeout(() => {
792
- if (!session) {
793
- log?.warn(
794
- `connection to ${client()} timed out waiting for handshake, closing`,
795
- {
796
- clientId: this.clientId,
797
- connectedTo: client(),
798
- connId: conn.debugId
799
- }
800
- );
801
- span.setStatus({ code: import_api2.SpanStatusCode.ERROR });
802
- span.end();
851
+ conn.close();
852
+ }
853
+ }, this.options.sessionDisconnectGraceMs);
854
+ const buffer = [];
855
+ let receivedHandshakeMessage = false;
856
+ const handshakeHandler = (data) => {
857
+ if (receivedHandshakeMessage) {
858
+ buffer.push(data);
859
+ return;
860
+ }
861
+ receivedHandshakeMessage = true;
862
+ clearTimeout(handshakeTimeout);
863
+ void this.receiveHandshakeRequestMessage(data, conn).then(
864
+ (maybeSession) => {
865
+ if (!maybeSession) {
803
866
  conn.close();
804
- }
805
- }, this.options.sessionDisconnectGraceMs);
806
- const buffer = [];
807
- let receivedHandshakeMessage = false;
808
- const handshakeHandler = (data) => {
809
- if (receivedHandshakeMessage) {
810
- buffer.push(data);
811
867
  return;
812
868
  }
813
- receivedHandshakeMessage = true;
814
- clearTimeout(handshakeTimeout);
815
- void this.receiveHandshakeRequestMessage(data, conn).then(
816
- (maybeSession) => {
817
- if (!maybeSession) {
818
- span.setStatus({ code: import_api2.SpanStatusCode.ERROR });
819
- span.end();
820
- conn.close();
821
- return;
822
- }
823
- session = maybeSession;
824
- const dataHandler = (data2) => {
825
- const parsed = this.parseMsg(data2);
826
- if (!parsed) {
827
- conn.close();
828
- return;
829
- }
830
- this.handleMsg(parsed);
831
- };
832
- conn.removeDataListener(handshakeHandler);
833
- conn.addDataListener(dataHandler);
834
- for (const data2 of buffer) {
835
- dataHandler(data2);
836
- }
837
- buffer.length = 0;
869
+ session = maybeSession;
870
+ const dataHandler = (data2) => {
871
+ const parsed = this.parseMsg(data2);
872
+ if (!parsed) {
873
+ conn.close();
874
+ return;
838
875
  }
839
- );
840
- };
841
- conn.addDataListener(handshakeHandler);
842
- conn.addCloseListener(() => {
843
- if (session) {
844
- log?.info(`connection to ${client()} disconnected`, {
845
- clientId: this.clientId,
846
- connId: conn.debugId
847
- });
848
- this.onDisconnect(conn, session);
876
+ this.handleMsg(parsed);
877
+ };
878
+ for (const data2 of buffer) {
879
+ dataHandler(data2);
849
880
  }
850
- span.setStatus({ code: import_api2.SpanStatusCode.OK });
851
- span.end();
881
+ conn.removeDataListener(handshakeHandler);
882
+ conn.addDataListener(dataHandler);
883
+ buffer.length = 0;
884
+ }
885
+ );
886
+ };
887
+ conn.addDataListener(handshakeHandler);
888
+ conn.addCloseListener(() => {
889
+ if (!session)
890
+ return;
891
+ log?.info(`connection to ${client()} disconnected`, {
892
+ clientId: this.clientId,
893
+ connId: conn.id
894
+ });
895
+ this.onDisconnect(conn, session);
896
+ });
897
+ conn.addErrorListener((err) => {
898
+ conn.telemetry?.span.setStatus({
899
+ code: import_api3.SpanStatusCode.ERROR,
900
+ message: "connection error"
901
+ });
902
+ if (!session)
903
+ return;
904
+ log?.warn(
905
+ `connection to ${client()} got an error: ${coerceErrorString(err)}`,
906
+ { clientId: this.clientId, connId: conn.id }
907
+ );
908
+ });
909
+ }
910
+ async validateHandshakeMetadata(conn, session, rawMetadata, from) {
911
+ let parsedMetadata = {};
912
+ if (this.handshakeExtensions) {
913
+ if (!import_value.Value.Check(this.handshakeExtensions.schema, rawMetadata)) {
914
+ conn.telemetry?.span.setStatus({
915
+ code: import_api3.SpanStatusCode.ERROR,
916
+ message: "malformed handshake meta"
852
917
  });
853
- conn.addErrorListener((err) => {
854
- if (session) {
855
- log?.warn(
856
- `connection to ${client()} got an error: ${coerceErrorString(
857
- err
858
- )}`,
859
- { clientId: this.clientId, connId: conn.debugId }
860
- );
861
- }
862
- span.setStatus({ code: import_api2.SpanStatusCode.ERROR });
863
- span.end();
918
+ const reason = "received malformed handshake metadata";
919
+ const responseMsg = handshakeResponseMessage(this.clientId, from, {
920
+ ok: false,
921
+ reason
922
+ });
923
+ conn.send(this.codec.toBuffer(responseMsg));
924
+ log?.warn(`received malformed handshake metadata from ${from}`, {
925
+ clientId: this.clientId,
926
+ connId: conn.id
864
927
  });
928
+ this.protocolError(ProtocolError.HandshakeFailed, reason);
929
+ return false;
865
930
  }
866
- );
931
+ parsedMetadata = await this.handshakeExtensions.validate(
932
+ rawMetadata,
933
+ session
934
+ );
935
+ if (parsedMetadata === false) {
936
+ const reason = "rejected by handshake handler";
937
+ conn.telemetry?.span.setStatus({
938
+ code: import_api3.SpanStatusCode.ERROR,
939
+ message: reason
940
+ });
941
+ const responseMsg = handshakeResponseMessage(this.clientId, from, {
942
+ ok: false,
943
+ reason
944
+ });
945
+ conn.send(this.codec.toBuffer(responseMsg));
946
+ log?.warn(`rejected handshake from ${from}`, {
947
+ clientId: this.clientId,
948
+ connId: conn.id
949
+ });
950
+ this.protocolError(ProtocolError.HandshakeFailed, reason);
951
+ return false;
952
+ }
953
+ }
954
+ return parsedMetadata;
867
955
  }
868
956
  async receiveHandshakeRequestMessage(data, conn) {
869
957
  const parsed = this.parseMsg(data);
870
958
  if (!parsed) {
959
+ conn.telemetry?.span.setStatus({
960
+ code: import_api3.SpanStatusCode.ERROR,
961
+ message: "non-transport message"
962
+ });
871
963
  this.protocolError(
872
964
  ProtocolError.HandshakeFailed,
873
965
  "received non-transport message"
874
966
  );
875
967
  return false;
876
968
  }
877
- let activeContext = import_api2.context.active();
878
- if (parsed.tracing) {
879
- activeContext = import_api2.propagation.extract(activeContext, parsed.tracing);
969
+ if (!import_value.Value.Check(ControlMessageHandshakeRequestSchema, parsed.payload)) {
970
+ conn.telemetry?.span.setStatus({
971
+ code: import_api3.SpanStatusCode.ERROR,
972
+ message: "invalid handshake request"
973
+ });
974
+ const reason = "received invalid handshake msg";
975
+ const responseMsg2 = handshakeResponseMessage(this.clientId, parsed.from, {
976
+ ok: false,
977
+ reason
978
+ });
979
+ conn.send(this.codec.toBuffer(responseMsg2));
980
+ const logData = { ...parsed.payload ?? {}, metadata: "redacted" };
981
+ log?.warn(reason, {
982
+ clientId: this.clientId,
983
+ connId: conn.id,
984
+ partialTransportMessage: { ...parsed, payload: logData }
985
+ });
986
+ this.protocolError(
987
+ ProtocolError.HandshakeFailed,
988
+ "invalid handshake request"
989
+ );
990
+ return false;
880
991
  }
881
- return tracing_default.startActiveSpan(
882
- "receiveHandshakeRequestMessage",
883
- {
884
- attributes: {
885
- component: "river",
886
- "span.kind": "server"
887
- },
888
- kind: import_api2.SpanKind.SERVER
889
- },
890
- activeContext,
891
- async (span) => {
892
- if (!import_value.Value.Check(ControlMessageHandshakeRequestSchema, parsed.payload)) {
893
- const reason = "received invalid handshake msg";
894
- const responseMsg2 = handshakeResponseMessage(
895
- this.clientId,
896
- parsed.from,
897
- {
898
- ok: false,
899
- reason
900
- }
901
- );
902
- conn.send(this.codec.toBuffer(responseMsg2));
903
- const logData = typeof parsed.payload === "object" ? {
904
- ...parsed,
905
- payload: { ...parsed.payload, metadata: "redacted" }
906
- } : { ...parsed };
907
- log?.warn(`${reason}: ${JSON.stringify(logData)}`, {
908
- clientId: this.clientId,
909
- connId: conn.debugId
910
- });
911
- this.protocolError(
912
- ProtocolError.HandshakeFailed,
913
- "invalid handshake request"
914
- );
915
- span.setStatus({ code: import_api2.SpanStatusCode.ERROR });
916
- span.end();
917
- return false;
918
- }
919
- const gotVersion = parsed.payload.protocolVersion;
920
- if (gotVersion !== PROTOCOL_VERSION) {
921
- const reason = `incorrect version (got: ${gotVersion} wanted ${PROTOCOL_VERSION})`;
922
- const responseMsg2 = handshakeResponseMessage(
923
- this.clientId,
924
- parsed.from,
925
- {
926
- ok: false,
927
- reason
928
- }
929
- );
930
- conn.send(this.codec.toBuffer(responseMsg2));
931
- log?.warn(
932
- `received handshake msg with incompatible protocol version (got: ${gotVersion}, expected: ${PROTOCOL_VERSION})`,
933
- { clientId: this.clientId, connId: conn.debugId }
934
- );
935
- this.protocolError(ProtocolError.HandshakeFailed, reason);
936
- span.setStatus({ code: import_api2.SpanStatusCode.ERROR });
937
- span.end();
938
- return false;
939
- }
940
- const { session, isReconnect } = this.getOrCreateSession(
941
- parsed.from,
942
- conn,
943
- parsed.payload.sessionId
944
- );
945
- let handshakeMetadata;
946
- if (this.options.handshake) {
947
- if (!import_value.Value.Check(
948
- this.options.handshake.requestSchema,
949
- parsed.payload.metadata
950
- )) {
951
- const reason = "received malformed handshake metadata";
952
- const responseMsg2 = handshakeResponseMessage(
953
- this.clientId,
954
- parsed.from,
955
- { ok: false, reason }
956
- );
957
- conn.send(this.codec.toBuffer(responseMsg2));
958
- log?.warn(
959
- `received malformed handshake metadata from ${parsed.from}`,
960
- {
961
- clientId: this.clientId,
962
- connId: conn.debugId
963
- }
964
- );
965
- this.protocolError(ProtocolError.HandshakeFailed, reason);
966
- this.deleteSession(session);
967
- span.setStatus({ code: import_api2.SpanStatusCode.ERROR });
968
- span.end();
969
- return false;
970
- }
971
- const parsedMetadata = await this.options.handshake.parse(
972
- parsed.payload.metadata,
973
- session,
974
- isReconnect
975
- );
976
- if (parsedMetadata === false) {
977
- const reason = "rejected by server";
978
- const responseMsg2 = handshakeResponseMessage(
979
- this.clientId,
980
- parsed.from,
981
- { ok: false, reason }
982
- );
983
- conn.send(this.codec.toBuffer(responseMsg2));
984
- log?.warn(`rejected handshake from ${parsed.from}`, {
985
- clientId: this.clientId,
986
- connId: conn.debugId
987
- });
988
- this.protocolError(ProtocolError.HandshakeFailed, reason);
989
- this.deleteSession(session);
990
- span.setStatus({ code: import_api2.SpanStatusCode.ERROR });
991
- span.end();
992
- return false;
993
- }
994
- if (!import_value.Value.Check(this.options.handshake.parsedSchema, parsedMetadata)) {
995
- const reason = "failed to parse handshake metadata";
996
- const responseMsg2 = handshakeResponseMessage(
997
- this.clientId,
998
- parsed.from,
999
- { ok: false, reason }
1000
- );
1001
- conn.send(this.codec.toBuffer(responseMsg2));
1002
- log?.error(`failed to parse handshake metadata`, {
1003
- clientId: this.clientId,
1004
- connId: conn.debugId,
1005
- tags: ["invariant-violation"]
1006
- });
1007
- this.protocolError(ProtocolError.HandshakeFailed, reason);
1008
- this.deleteSession(session);
1009
- span.setStatus({ code: import_api2.SpanStatusCode.ERROR });
1010
- span.end();
1011
- return false;
1012
- }
1013
- handshakeMetadata = parsedMetadata;
1014
- }
1015
- handshakeMetadata ??= {};
1016
- session.metadata = handshakeMetadata;
1017
- log?.debug(
1018
- `handshake from ${parsed.from} ok, responding with handshake success`,
1019
- { clientId: this.clientId, connId: conn.debugId }
1020
- );
1021
- const responseMsg = handshakeResponseMessage(
1022
- this.clientId,
1023
- parsed.from,
1024
- {
1025
- ok: true,
1026
- sessionId: session.id
1027
- }
1028
- );
1029
- conn.send(this.codec.toBuffer(responseMsg));
1030
- this.onConnect(conn, parsed.from, session, isReconnect);
1031
- span.end();
1032
- return session;
1033
- }
992
+ const gotVersion = parsed.payload.protocolVersion;
993
+ if (gotVersion !== PROTOCOL_VERSION) {
994
+ conn.telemetry?.span.setStatus({
995
+ code: import_api3.SpanStatusCode.ERROR,
996
+ message: "incorrect protocol version"
997
+ });
998
+ const reason = `incorrect version (got: ${gotVersion} wanted ${PROTOCOL_VERSION})`;
999
+ const responseMsg2 = handshakeResponseMessage(this.clientId, parsed.from, {
1000
+ ok: false,
1001
+ reason
1002
+ });
1003
+ conn.send(this.codec.toBuffer(responseMsg2));
1004
+ log?.warn(
1005
+ `received handshake msg with incompatible protocol version (got: ${gotVersion}, expected: ${PROTOCOL_VERSION})`,
1006
+ { clientId: this.clientId, connId: conn.id }
1007
+ );
1008
+ this.protocolError(ProtocolError.HandshakeFailed, reason);
1009
+ return false;
1010
+ }
1011
+ const oldSession = this.sessions.get(parsed.from);
1012
+ const parsedMetadata = await this.validateHandshakeMetadata(
1013
+ conn,
1014
+ oldSession,
1015
+ parsed.payload.metadata,
1016
+ parsed.from
1017
+ );
1018
+ if (parsedMetadata === false) {
1019
+ return false;
1020
+ }
1021
+ const { session, isReconnect } = this.getOrCreateSession(
1022
+ parsed.from,
1023
+ conn,
1024
+ parsed.payload.sessionId,
1025
+ parsed.tracing
1034
1026
  );
1027
+ this.sessionHandshakeMetadata.set(session, parsedMetadata);
1028
+ log?.debug(
1029
+ `handshake from ${parsed.from} ok, responding with handshake success`,
1030
+ { clientId: this.clientId, connId: conn.id }
1031
+ );
1032
+ const responseMsg = handshakeResponseMessage(this.clientId, parsed.from, {
1033
+ ok: true,
1034
+ sessionId: session.id
1035
+ });
1036
+ conn.send(this.codec.toBuffer(responseMsg));
1037
+ this.onConnect(conn, parsed.from, session, isReconnect);
1038
+ return session;
1035
1039
  }
1036
1040
  };
1037
1041
 
1038
1042
  // transport/impls/ws/connection.ts
1039
- var import_agnostic_ws = __toESM(require("agnostic-ws"), 1);
1040
1043
  var WebSocketConnection = class extends Connection {
1044
+ errorCb = null;
1045
+ closeCb = null;
1041
1046
  ws;
1042
1047
  constructor(ws) {
1043
1048
  super();
1044
1049
  this.ws = ws;
1045
1050
  this.ws.binaryType = "arraybuffer";
1051
+ let didError = false;
1052
+ this.ws.onerror = () => {
1053
+ didError = true;
1054
+ };
1055
+ this.ws.onclose = ({ code, reason }) => {
1056
+ if (didError && this.errorCb) {
1057
+ this.errorCb(
1058
+ new Error(
1059
+ `websocket closed with code and reason: ${code} - ${reason}`
1060
+ )
1061
+ );
1062
+ return;
1063
+ }
1064
+ if (this.closeCb) {
1065
+ this.closeCb();
1066
+ }
1067
+ };
1046
1068
  }
1047
1069
  addDataListener(cb) {
1048
1070
  this.ws.onmessage = (msg) => cb(msg.data);
@@ -1051,13 +1073,13 @@ var WebSocketConnection = class extends Connection {
1051
1073
  this.ws.onmessage = null;
1052
1074
  }
1053
1075
  addCloseListener(cb) {
1054
- this.ws.onclose = cb;
1076
+ this.closeCb = cb;
1055
1077
  }
1056
1078
  addErrorListener(cb) {
1057
- this.ws.onerror = (err) => cb(err.error);
1079
+ this.errorCb = cb;
1058
1080
  }
1059
1081
  send(payload) {
1060
- if (this.ws.readyState === import_agnostic_ws.default.OPEN) {
1082
+ if (this.ws.readyState === this.ws.OPEN) {
1061
1083
  this.ws.send(payload);
1062
1084
  return true;
1063
1085
  } else {