@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
@@ -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.22.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
  */
@@ -209,7 +243,7 @@ var Session = class {
209
243
  * The interval for sending heartbeats.
210
244
  */
211
245
  heartbeat;
212
- constructor(conn, from, to, options) {
246
+ constructor(conn, from, to, options, propagationCtx) {
213
247
  this.id = `session-${nanoid2(12)}`;
214
248
  this.options = options;
215
249
  this.from = from;
@@ -221,13 +255,14 @@ var Session = class {
221
255
  () => this.sendHeartbeat(),
222
256
  options.heartbeatIntervalMs
223
257
  );
258
+ this.telemetry = createSessionTelemetryInfo(this, propagationCtx);
224
259
  }
225
260
  get loggingMetadata() {
226
261
  return {
227
262
  clientId: this.from,
228
263
  connectedTo: this.to,
229
264
  sessionId: this.id,
230
- connId: this.connection?.debugId
265
+ connId: this.connection?.id
231
266
  };
232
267
  }
233
268
  /**
@@ -272,6 +307,7 @@ var Session = class {
272
307
  `closing connection to ${this.to} due to inactivity (missed ${misses} heartbeats which is ${missDuration}ms)`,
273
308
  this.loggingMetadata
274
309
  );
310
+ this.telemetry.span.addEvent("closing connection due to inactivity");
275
311
  this.closeStaleConnection();
276
312
  }
277
313
  return;
@@ -293,32 +329,38 @@ var Session = class {
293
329
  sendBufferedMessages(conn) {
294
330
  log?.info(`resending ${this.sendBuffer.length} buffered messages`, {
295
331
  ...this.loggingMetadata,
296
- connId: conn.debugId
332
+ connId: conn.id
297
333
  });
298
334
  for (const msg of this.sendBuffer) {
299
335
  log?.debug(`resending msg`, {
300
336
  ...this.loggingMetadata,
301
337
  fullTransportMessage: msg,
302
- connId: conn.debugId
338
+ connId: conn.id
303
339
  });
304
340
  const ok = conn.send(this.codec.toBuffer(msg));
305
341
  if (!ok) {
306
342
  const errMsg = `failed to send buffered message to ${this.to} (sus, this is a fresh connection)`;
343
+ conn.telemetry?.span.setStatus({
344
+ code: import_api2.SpanStatusCode.ERROR,
345
+ message: errMsg
346
+ });
307
347
  log?.error(errMsg, {
308
348
  ...this.loggingMetadata,
309
349
  fullTransportMessage: msg,
310
- connId: conn.debugId
350
+ connId: conn.id,
351
+ tags: ["invariant-violation"]
311
352
  });
312
- throw new Error(errMsg);
353
+ conn.close();
354
+ return;
313
355
  }
314
356
  }
315
357
  }
316
358
  updateBookkeeping(ack, seq) {
317
359
  if (seq + 1 < this.ack) {
318
- log?.error(
319
- `received stale seq ${seq} + 1 < ${this.ack}`,
320
- this.loggingMetadata
321
- );
360
+ log?.error(`received stale seq ${seq} + 1 < ${this.ack}`, {
361
+ ...this.loggingMetadata,
362
+ tags: ["invariant-violation"]
363
+ });
322
364
  return;
323
365
  }
324
366
  this.sendBuffer = this.sendBuffer.filter((unacked) => unacked.seq >= ack);
@@ -388,11 +430,6 @@ var Session = class {
388
430
  }
389
431
  };
390
432
 
391
- // tracing/index.ts
392
- var import_api = require("@opentelemetry/api");
393
- var tracer = import_api.trace.getTracer("river");
394
- var tracing_default = tracer;
395
-
396
433
  // util/stringify.ts
397
434
  function coerceErrorString(err) {
398
435
  if (err instanceof Error) {
@@ -454,6 +491,7 @@ var NaiveJsonCodec = {
454
491
  };
455
492
 
456
493
  // transport/transport.ts
494
+ var import_api3 = require("@opentelemetry/api");
457
495
  var defaultTransportOptions = {
458
496
  heartbeatIntervalMs: 1e3,
459
497
  heartbeatsUntilDead: 2,
@@ -532,17 +570,22 @@ var Transport = class {
532
570
  status: "connect",
533
571
  conn
534
572
  });
573
+ conn.telemetry = createConnectionTelemetryInfo(
574
+ conn,
575
+ session.telemetry.span
576
+ );
535
577
  if (isReconnect) {
536
578
  session.replaceWithNewConnection(conn);
537
579
  log?.info(`reconnected to ${connectedTo}`, session.loggingMetadata);
538
580
  }
539
581
  }
540
- createSession(to, conn) {
582
+ createSession(to, conn, propagationCtx) {
541
583
  const session = new Session(
542
584
  conn,
543
585
  this.clientId,
544
586
  to,
545
- this.options
587
+ this.options,
588
+ propagationCtx
546
589
  );
547
590
  this.sessions.set(session.to, session);
548
591
  this.eventDispatcher.dispatchEvent("sessionStatus", {
@@ -551,11 +594,11 @@ var Transport = class {
551
594
  });
552
595
  return session;
553
596
  }
554
- getOrCreateSession(to, conn, sessionId) {
597
+ getOrCreateSession(to, conn, sessionId, propagationCtx) {
555
598
  let session = this.sessions.get(to);
556
599
  let isReconnect = session !== void 0;
557
600
  if (session?.advertisedSessionId !== void 0 && sessionId !== void 0 && session.advertisedSessionId !== sessionId) {
558
- log?.warn(
601
+ log?.info(
559
602
  `session for ${to} already exists but has a different session id (expected: ${session.advertisedSessionId}, got: ${sessionId}), creating a new one`,
560
603
  session.loggingMetadata
561
604
  );
@@ -564,7 +607,7 @@ var Transport = class {
564
607
  session = void 0;
565
608
  }
566
609
  if (!session) {
567
- session = this.createSession(to, conn);
610
+ session = this.createSession(to, conn, propagationCtx);
568
611
  log?.info(
569
612
  `no session for ${to}, created a new one`,
570
613
  session.loggingMetadata
@@ -577,6 +620,7 @@ var Transport = class {
577
620
  }
578
621
  deleteSession(session) {
579
622
  session.close();
623
+ session.telemetry.span.end();
580
624
  this.sessions.delete(session.to);
581
625
  log?.info(
582
626
  `session ${session.id} disconnect from ${session.to}`,
@@ -593,12 +637,16 @@ var Transport = class {
593
637
  * @param connectedTo The peer we are connected to.
594
638
  */
595
639
  onDisconnect(conn, session) {
640
+ conn.telemetry?.span.end();
596
641
  this.eventDispatcher.dispatchEvent("connectionStatus", {
597
642
  status: "disconnect",
598
643
  conn
599
644
  });
600
645
  session.connection = void 0;
601
- session.beginGrace(() => this.deleteSession(session));
646
+ session.beginGrace(() => {
647
+ session.telemetry.span.addEvent("session grace period expired");
648
+ this.deleteSession(session);
649
+ });
602
650
  }
603
651
  /**
604
652
  * Parses a message from a Uint8Array into a {@link OpaqueTransportMessage}.
@@ -632,9 +680,10 @@ var Transport = class {
632
680
  return;
633
681
  const session = this.sessions.get(msg.from);
634
682
  if (!session) {
635
- log?.error(`(invariant violation) no existing session for ${msg.from}`, {
683
+ log?.error(`no existing session for ${msg.from}`, {
636
684
  clientId: this.clientId,
637
- fullTransportMessage: msg
685
+ fullTransportMessage: msg,
686
+ tags: ["invariant-violation"]
638
687
  });
639
688
  return;
640
689
  }
@@ -653,9 +702,14 @@ var Transport = class {
653
702
  const errMsg = `received out-of-order msg (got seq: ${msg.seq}, wanted seq: ${session.nextExpectedSeq})`;
654
703
  log?.error(`${errMsg}, marking connection as dead`, {
655
704
  clientId: this.clientId,
656
- fullTransportMessage: msg
705
+ fullTransportMessage: msg,
706
+ tags: ["invariant-violation"]
657
707
  });
658
708
  this.protocolError(ProtocolError.MessageOrderingViolated, errMsg);
709
+ session.telemetry.span.setStatus({
710
+ code: import_api3.SpanStatusCode.ERROR,
711
+ message: "message order violated"
712
+ });
659
713
  session.close();
660
714
  }
661
715
  return;
@@ -697,7 +751,8 @@ var Transport = class {
697
751
  const err = "transport is destroyed, cant send";
698
752
  log?.error(err, {
699
753
  clientId: this.clientId,
700
- partialTransportMessage: msg
754
+ partialTransportMessage: msg,
755
+ tags: ["invariant-violation"]
701
756
  });
702
757
  this.protocolError(ProtocolError.UseAfterDestroy, err);
703
758
  return void 0;
@@ -765,278 +820,262 @@ var ServerTransport = class extends Transport {
765
820
  });
766
821
  }
767
822
  handleConnection(conn) {
768
- tracing_default.startActiveSpan(
769
- "handleConnection",
770
- {
771
- attributes: {
772
- component: "river",
773
- "span.kind": "server"
774
- },
775
- kind: import_api2.SpanKind.SERVER
776
- },
777
- (span) => {
778
- if (this.state !== "open")
779
- return;
780
- log?.info(`new incoming connection`, {
781
- clientId: this.clientId,
782
- connId: conn.debugId
823
+ if (this.state !== "open")
824
+ return;
825
+ log?.info(`new incoming connection`, {
826
+ clientId: this.clientId,
827
+ connId: conn.id
828
+ });
829
+ let session = void 0;
830
+ const client = () => session?.to ?? "unknown";
831
+ const handshakeTimeout = setTimeout(() => {
832
+ if (!session) {
833
+ log?.warn(
834
+ `connection to ${client()} timed out waiting for handshake, closing`,
835
+ {
836
+ clientId: this.clientId,
837
+ connectedTo: client(),
838
+ connId: conn.id
839
+ }
840
+ );
841
+ conn.telemetry?.span.setStatus({
842
+ code: import_api3.SpanStatusCode.ERROR,
843
+ message: "handshake timeout"
783
844
  });
784
- let session = void 0;
785
- const client = () => session?.to ?? "unknown";
786
- const handshakeTimeout = setTimeout(() => {
787
- if (!session) {
788
- log?.warn(
789
- `connection to ${client()} timed out waiting for handshake, closing`,
790
- {
791
- clientId: this.clientId,
792
- connectedTo: client(),
793
- connId: conn.debugId
794
- }
795
- );
796
- span.setStatus({ code: import_api2.SpanStatusCode.ERROR });
797
- span.end();
845
+ conn.close();
846
+ }
847
+ }, this.options.sessionDisconnectGraceMs);
848
+ const buffer = [];
849
+ let receivedHandshakeMessage = false;
850
+ const handshakeHandler = (data) => {
851
+ if (receivedHandshakeMessage) {
852
+ buffer.push(data);
853
+ return;
854
+ }
855
+ receivedHandshakeMessage = true;
856
+ clearTimeout(handshakeTimeout);
857
+ void this.receiveHandshakeRequestMessage(data, conn).then(
858
+ (maybeSession) => {
859
+ if (!maybeSession) {
798
860
  conn.close();
799
- }
800
- }, this.options.sessionDisconnectGraceMs);
801
- const buffer = [];
802
- let receivedHandshakeMessage = false;
803
- const handshakeHandler = (data) => {
804
- if (receivedHandshakeMessage) {
805
- buffer.push(data);
806
861
  return;
807
862
  }
808
- receivedHandshakeMessage = true;
809
- clearTimeout(handshakeTimeout);
810
- void this.receiveHandshakeRequestMessage(data, conn).then(
811
- (maybeSession) => {
812
- if (!maybeSession) {
813
- span.setStatus({ code: import_api2.SpanStatusCode.ERROR });
814
- span.end();
815
- conn.close();
816
- return;
817
- }
818
- session = maybeSession;
819
- const dataHandler = (data2) => {
820
- const parsed = this.parseMsg(data2);
821
- if (!parsed) {
822
- conn.close();
823
- return;
824
- }
825
- this.handleMsg(parsed);
826
- };
827
- conn.removeDataListener(handshakeHandler);
828
- conn.addDataListener(dataHandler);
829
- for (const data2 of buffer) {
830
- dataHandler(data2);
831
- }
832
- buffer.length = 0;
863
+ session = maybeSession;
864
+ const dataHandler = (data2) => {
865
+ const parsed = this.parseMsg(data2);
866
+ if (!parsed) {
867
+ conn.close();
868
+ return;
833
869
  }
834
- );
835
- };
836
- conn.addDataListener(handshakeHandler);
837
- conn.addCloseListener(() => {
838
- if (session) {
839
- log?.info(`connection to ${client()} disconnected`, {
840
- clientId: this.clientId,
841
- connId: conn.debugId
842
- });
843
- this.onDisconnect(conn, session);
870
+ this.handleMsg(parsed);
871
+ };
872
+ for (const data2 of buffer) {
873
+ dataHandler(data2);
844
874
  }
845
- span.setStatus({ code: import_api2.SpanStatusCode.OK });
846
- span.end();
847
- });
848
- conn.addErrorListener((err) => {
849
- if (session) {
850
- log?.warn(
851
- `connection to ${client()} got an error: ${coerceErrorString(
852
- err
853
- )}`,
854
- { clientId: this.clientId, connId: conn.debugId }
855
- );
856
- }
857
- span.setStatus({ code: import_api2.SpanStatusCode.ERROR });
858
- span.end();
859
- });
860
- }
861
- );
875
+ conn.removeDataListener(handshakeHandler);
876
+ conn.addDataListener(dataHandler);
877
+ buffer.length = 0;
878
+ }
879
+ );
880
+ };
881
+ conn.addDataListener(handshakeHandler);
882
+ conn.addCloseListener(() => {
883
+ if (!session)
884
+ return;
885
+ log?.info(`connection to ${client()} disconnected`, {
886
+ clientId: this.clientId,
887
+ connId: conn.id
888
+ });
889
+ this.onDisconnect(conn, session);
890
+ });
891
+ conn.addErrorListener((err) => {
892
+ conn.telemetry?.span.setStatus({
893
+ code: import_api3.SpanStatusCode.ERROR,
894
+ message: "connection error"
895
+ });
896
+ if (!session)
897
+ return;
898
+ log?.warn(
899
+ `connection to ${client()} got an error: ${coerceErrorString(err)}`,
900
+ { clientId: this.clientId, connId: conn.id }
901
+ );
902
+ });
862
903
  }
863
904
  async receiveHandshakeRequestMessage(data, conn) {
864
905
  const parsed = this.parseMsg(data);
865
906
  if (!parsed) {
907
+ conn.telemetry?.span.setStatus({
908
+ code: import_api3.SpanStatusCode.ERROR,
909
+ message: "non-transport message"
910
+ });
866
911
  this.protocolError(
867
912
  ProtocolError.HandshakeFailed,
868
913
  "received non-transport message"
869
914
  );
870
915
  return false;
871
916
  }
872
- let activeContext = import_api2.context.active();
873
- if (parsed.tracing) {
874
- activeContext = import_api2.propagation.extract(activeContext, parsed.tracing);
917
+ if (!import_value.Value.Check(ControlMessageHandshakeRequestSchema, parsed.payload)) {
918
+ conn.telemetry?.span.setStatus({
919
+ code: import_api3.SpanStatusCode.ERROR,
920
+ message: "invalid handshake request"
921
+ });
922
+ const reason = "received invalid handshake msg";
923
+ const responseMsg2 = handshakeResponseMessage(this.clientId, parsed.from, {
924
+ ok: false,
925
+ reason
926
+ });
927
+ conn.send(this.codec.toBuffer(responseMsg2));
928
+ const logData = { ...parsed.payload ?? {}, metadata: "redacted" };
929
+ log?.warn(reason, {
930
+ clientId: this.clientId,
931
+ connId: conn.id,
932
+ partialTransportMessage: { ...parsed, payload: logData }
933
+ });
934
+ this.protocolError(
935
+ ProtocolError.HandshakeFailed,
936
+ "invalid handshake request"
937
+ );
938
+ return false;
875
939
  }
876
- return tracing_default.startActiveSpan(
877
- "receiveHandshakeRequestMessage",
878
- {
879
- attributes: {
880
- component: "river",
881
- "span.kind": "server"
882
- },
883
- kind: import_api2.SpanKind.SERVER
884
- },
885
- activeContext,
886
- async (span) => {
887
- if (!import_value.Value.Check(ControlMessageHandshakeRequestSchema, parsed.payload)) {
888
- const reason = "received invalid handshake msg";
889
- const responseMsg2 = handshakeResponseMessage(
890
- this.clientId,
891
- parsed.from,
892
- {
893
- ok: false,
894
- reason
895
- }
896
- );
897
- conn.send(this.codec.toBuffer(responseMsg2));
898
- const logData = typeof parsed.payload === "object" ? {
899
- ...parsed,
900
- payload: { ...parsed.payload, metadata: "redacted" }
901
- } : { ...parsed };
902
- log?.warn(`${reason}: ${JSON.stringify(logData)}`, {
903
- clientId: this.clientId,
904
- connId: conn.debugId
905
- });
906
- this.protocolError(
907
- ProtocolError.HandshakeFailed,
908
- "invalid handshake request"
909
- );
910
- span.setStatus({ code: import_api2.SpanStatusCode.ERROR });
911
- span.end();
912
- return false;
913
- }
914
- const gotVersion = parsed.payload.protocolVersion;
915
- if (gotVersion !== PROTOCOL_VERSION) {
916
- const reason = `incorrect version (got: ${gotVersion} wanted ${PROTOCOL_VERSION})`;
917
- const responseMsg2 = handshakeResponseMessage(
918
- this.clientId,
919
- parsed.from,
920
- {
921
- ok: false,
922
- reason
923
- }
924
- );
925
- conn.send(this.codec.toBuffer(responseMsg2));
926
- log?.warn(
927
- `received handshake msg with incompatible protocol version (got: ${gotVersion}, expected: ${PROTOCOL_VERSION})`,
928
- { clientId: this.clientId, connId: conn.debugId }
929
- );
930
- this.protocolError(ProtocolError.HandshakeFailed, reason);
931
- span.setStatus({ code: import_api2.SpanStatusCode.ERROR });
932
- span.end();
933
- return false;
934
- }
935
- const { session, isReconnect } = this.getOrCreateSession(
940
+ const gotVersion = parsed.payload.protocolVersion;
941
+ if (gotVersion !== PROTOCOL_VERSION) {
942
+ conn.telemetry?.span.setStatus({
943
+ code: import_api3.SpanStatusCode.ERROR,
944
+ message: "incorrect protocol version"
945
+ });
946
+ const reason = `incorrect version (got: ${gotVersion} wanted ${PROTOCOL_VERSION})`;
947
+ const responseMsg2 = handshakeResponseMessage(this.clientId, parsed.from, {
948
+ ok: false,
949
+ reason
950
+ });
951
+ conn.send(this.codec.toBuffer(responseMsg2));
952
+ log?.warn(
953
+ `received handshake msg with incompatible protocol version (got: ${gotVersion}, expected: ${PROTOCOL_VERSION})`,
954
+ { clientId: this.clientId, connId: conn.id }
955
+ );
956
+ this.protocolError(ProtocolError.HandshakeFailed, reason);
957
+ return false;
958
+ }
959
+ const { session, isReconnect } = this.getOrCreateSession(
960
+ parsed.from,
961
+ conn,
962
+ parsed.payload.sessionId,
963
+ parsed.tracing
964
+ );
965
+ let handshakeMetadata;
966
+ if (this.options.handshake) {
967
+ if (!import_value.Value.Check(
968
+ this.options.handshake.requestSchema,
969
+ parsed.payload.metadata
970
+ )) {
971
+ conn.telemetry?.span.setStatus({
972
+ code: import_api3.SpanStatusCode.ERROR,
973
+ message: "malformed handshake meta"
974
+ });
975
+ const reason = "received malformed handshake metadata";
976
+ const responseMsg2 = handshakeResponseMessage(
977
+ this.clientId,
936
978
  parsed.from,
937
- conn,
938
- parsed.payload.sessionId
979
+ { ok: false, reason }
939
980
  );
940
- let handshakeMetadata;
941
- if (this.options.handshake) {
942
- if (!import_value.Value.Check(
943
- this.options.handshake.requestSchema,
944
- parsed.payload.metadata
945
- )) {
946
- const reason = "received malformed handshake metadata";
947
- const responseMsg2 = handshakeResponseMessage(
948
- this.clientId,
949
- parsed.from,
950
- { ok: false, reason }
951
- );
952
- conn.send(this.codec.toBuffer(responseMsg2));
953
- log?.warn(
954
- `received malformed handshake metadata from ${parsed.from}`,
955
- {
956
- clientId: this.clientId,
957
- connId: conn.debugId
958
- }
959
- );
960
- this.protocolError(ProtocolError.HandshakeFailed, reason);
961
- this.deleteSession(session);
962
- span.setStatus({ code: import_api2.SpanStatusCode.ERROR });
963
- span.end();
964
- return false;
965
- }
966
- const parsedMetadata = await this.options.handshake.parse(
967
- parsed.payload.metadata,
968
- session,
969
- isReconnect
970
- );
971
- if (parsedMetadata === false) {
972
- const reason = "rejected by server";
973
- const responseMsg2 = handshakeResponseMessage(
974
- this.clientId,
975
- parsed.from,
976
- { ok: false, reason }
977
- );
978
- conn.send(this.codec.toBuffer(responseMsg2));
979
- log?.warn(`rejected handshake from ${parsed.from}`, {
980
- clientId: this.clientId,
981
- connId: conn.debugId
982
- });
983
- this.protocolError(ProtocolError.HandshakeFailed, reason);
984
- this.deleteSession(session);
985
- span.setStatus({ code: import_api2.SpanStatusCode.ERROR });
986
- span.end();
987
- return false;
988
- }
989
- if (!import_value.Value.Check(this.options.handshake.parsedSchema, parsedMetadata)) {
990
- const reason = "failed to parse handshake metadata";
991
- const responseMsg2 = handshakeResponseMessage(
992
- this.clientId,
993
- parsed.from,
994
- { ok: false, reason }
995
- );
996
- conn.send(this.codec.toBuffer(responseMsg2));
997
- log?.error(`failed to parse handshake metadata`, {
998
- clientId: this.clientId,
999
- connId: conn.debugId
1000
- });
1001
- this.protocolError(ProtocolError.HandshakeFailed, reason);
1002
- this.deleteSession(session);
1003
- span.setStatus({ code: import_api2.SpanStatusCode.ERROR });
1004
- span.end();
1005
- return false;
1006
- }
1007
- handshakeMetadata = parsedMetadata;
1008
- }
1009
- handshakeMetadata ??= {};
1010
- session.metadata = handshakeMetadata;
1011
- log?.debug(
1012
- `handshake from ${parsed.from} ok, responding with handshake success`,
1013
- { clientId: this.clientId, connId: conn.debugId }
981
+ conn.send(this.codec.toBuffer(responseMsg2));
982
+ log?.warn(`received malformed handshake metadata from ${parsed.from}`, {
983
+ clientId: this.clientId,
984
+ connId: conn.id
985
+ });
986
+ this.protocolError(ProtocolError.HandshakeFailed, reason);
987
+ this.deleteSession(session);
988
+ return false;
989
+ }
990
+ const parsedMetadata = await this.options.handshake.parse(
991
+ parsed.payload.metadata,
992
+ session,
993
+ isReconnect
994
+ );
995
+ if (parsedMetadata === false) {
996
+ conn.telemetry?.span.setStatus({
997
+ code: import_api3.SpanStatusCode.ERROR,
998
+ message: "rejected by handshake handler"
999
+ });
1000
+ const reason = "rejected by handshake handler";
1001
+ const responseMsg2 = handshakeResponseMessage(
1002
+ this.clientId,
1003
+ parsed.from,
1004
+ { ok: false, reason }
1014
1005
  );
1015
- const responseMsg = handshakeResponseMessage(
1006
+ conn.send(this.codec.toBuffer(responseMsg2));
1007
+ log?.warn(`rejected handshake from ${parsed.from}`, {
1008
+ clientId: this.clientId,
1009
+ connId: conn.id
1010
+ });
1011
+ this.protocolError(ProtocolError.HandshakeFailed, reason);
1012
+ this.deleteSession(session);
1013
+ return false;
1014
+ }
1015
+ if (!import_value.Value.Check(this.options.handshake.parsedSchema, parsedMetadata)) {
1016
+ conn.telemetry?.span.setStatus({
1017
+ code: import_api3.SpanStatusCode.ERROR,
1018
+ message: "malformed handshake meta"
1019
+ });
1020
+ const reason = "failed to parse handshake metadata";
1021
+ const responseMsg2 = handshakeResponseMessage(
1016
1022
  this.clientId,
1017
1023
  parsed.from,
1018
- {
1019
- ok: true,
1020
- sessionId: session.id
1021
- }
1024
+ { ok: false, reason }
1022
1025
  );
1023
- conn.send(this.codec.toBuffer(responseMsg));
1024
- this.onConnect(conn, parsed.from, session, isReconnect);
1025
- span.end();
1026
- return session;
1026
+ conn.send(this.codec.toBuffer(responseMsg2));
1027
+ log?.error(`failed to parse handshake metadata`, {
1028
+ clientId: this.clientId,
1029
+ connId: conn.id
1030
+ });
1031
+ this.protocolError(ProtocolError.HandshakeFailed, reason);
1032
+ this.deleteSession(session);
1033
+ return false;
1027
1034
  }
1035
+ handshakeMetadata = parsedMetadata;
1036
+ }
1037
+ handshakeMetadata ??= {};
1038
+ session.metadata = handshakeMetadata;
1039
+ log?.debug(
1040
+ `handshake from ${parsed.from} ok, responding with handshake success`,
1041
+ { clientId: this.clientId, connId: conn.id }
1028
1042
  );
1043
+ const responseMsg = handshakeResponseMessage(this.clientId, parsed.from, {
1044
+ ok: true,
1045
+ sessionId: session.id
1046
+ });
1047
+ conn.send(this.codec.toBuffer(responseMsg));
1048
+ this.onConnect(conn, parsed.from, session, isReconnect);
1049
+ return session;
1029
1050
  }
1030
1051
  };
1031
1052
 
1032
1053
  // transport/impls/ws/connection.ts
1033
- var import_agnostic_ws = __toESM(require("agnostic-ws"), 1);
1034
1054
  var WebSocketConnection = class extends Connection {
1055
+ errorCb = null;
1056
+ closeCb = null;
1035
1057
  ws;
1036
1058
  constructor(ws) {
1037
1059
  super();
1038
1060
  this.ws = ws;
1039
1061
  this.ws.binaryType = "arraybuffer";
1062
+ let didError = false;
1063
+ this.ws.onerror = () => {
1064
+ didError = true;
1065
+ };
1066
+ this.ws.onclose = ({ code, reason }) => {
1067
+ if (didError && this.errorCb) {
1068
+ this.errorCb(
1069
+ new Error(
1070
+ `websocket closed with code and reason: ${code} - ${reason}`
1071
+ )
1072
+ );
1073
+ return;
1074
+ }
1075
+ if (this.closeCb) {
1076
+ this.closeCb();
1077
+ }
1078
+ };
1040
1079
  }
1041
1080
  addDataListener(cb) {
1042
1081
  this.ws.onmessage = (msg) => cb(msg.data);
@@ -1045,13 +1084,13 @@ var WebSocketConnection = class extends Connection {
1045
1084
  this.ws.onmessage = null;
1046
1085
  }
1047
1086
  addCloseListener(cb) {
1048
- this.ws.onclose = cb;
1087
+ this.closeCb = cb;
1049
1088
  }
1050
1089
  addErrorListener(cb) {
1051
- this.ws.onerror = (err) => cb(err.error);
1090
+ this.errorCb = cb;
1052
1091
  }
1053
1092
  send(payload) {
1054
- if (this.ws.readyState === import_agnostic_ws.default.OPEN) {
1093
+ if (this.ws.readyState === this.ws.OPEN) {
1055
1094
  this.ws.send(payload);
1056
1095
  return true;
1057
1096
  } else {