@replit/river 0.10.13 → 0.12.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 (83) hide show
  1. package/README.md +28 -8
  2. package/dist/{builder-1f26296b.d.ts → builder-c593de11.d.ts} +14 -13
  3. package/dist/{chunk-3JGVFWKQ.js → chunk-24O3BKZA.js} +205 -180
  4. package/dist/chunk-4HOR4NUO.js +40 -0
  5. package/dist/chunk-ANGOKBE5.js +708 -0
  6. package/dist/{chunk-R6H2BIMC.js → chunk-GZ7HCLLM.js} +31 -7
  7. package/dist/{chunk-T7M7OKPE.js → chunk-H4BYJELI.js} +5 -1
  8. package/dist/chunk-IIBVKYDB.js +62 -0
  9. package/dist/chunk-IUDKWAOK.js +47 -0
  10. package/dist/chunk-WTOIOXB7.js +125 -0
  11. package/dist/chunk-XOTIAXAH.js +44 -0
  12. package/dist/codec/index.cjs +13 -7
  13. package/dist/codec/index.js +2 -4
  14. package/dist/connection-2956a1c5.d.ts +17 -0
  15. package/dist/connection-aaea7c88.d.ts +15 -0
  16. package/dist/connection-cd963ed5.d.ts +18 -0
  17. package/dist/index-d91775d9.d.ts +442 -0
  18. package/dist/logging/index.cjs +8 -3
  19. package/dist/logging/index.d.cts +6 -1
  20. package/dist/logging/index.d.ts +6 -1
  21. package/dist/logging/index.js +5 -3
  22. package/dist/messageFraming-b200ef25.d.ts +20 -0
  23. package/dist/router/index.cjs +249 -211
  24. package/dist/router/index.d.cts +6 -7
  25. package/dist/router/index.d.ts +6 -7
  26. package/dist/router/index.js +3 -3
  27. package/dist/transport/impls/stdio/client.cjs +891 -0
  28. package/dist/transport/impls/stdio/client.d.cts +27 -0
  29. package/dist/transport/impls/stdio/client.d.ts +27 -0
  30. package/dist/transport/impls/stdio/client.js +42 -0
  31. package/dist/transport/impls/stdio/server.cjs +861 -0
  32. package/dist/transport/impls/stdio/server.d.cts +25 -0
  33. package/dist/transport/impls/stdio/server.d.ts +25 -0
  34. package/dist/transport/impls/stdio/server.js +33 -0
  35. package/dist/transport/impls/uds/client.cjs +893 -0
  36. package/dist/transport/impls/uds/client.d.cts +16 -0
  37. package/dist/transport/impls/uds/client.d.ts +16 -0
  38. package/dist/transport/impls/uds/client.js +44 -0
  39. package/dist/transport/impls/uds/server.cjs +863 -0
  40. package/dist/transport/impls/uds/server.d.cts +16 -0
  41. package/dist/transport/impls/uds/server.d.ts +16 -0
  42. package/dist/transport/impls/uds/server.js +39 -0
  43. package/dist/transport/impls/ws/client.cjs +587 -248
  44. package/dist/transport/impls/ws/client.d.cts +6 -21
  45. package/dist/transport/impls/ws/client.d.ts +6 -21
  46. package/dist/transport/impls/ws/client.js +77 -7
  47. package/dist/transport/impls/ws/server.cjs +541 -194
  48. package/dist/transport/impls/ws/server.d.cts +6 -10
  49. package/dist/transport/impls/ws/server.d.ts +6 -10
  50. package/dist/transport/impls/ws/server.js +31 -8
  51. package/dist/transport/index.cjs +652 -129
  52. package/dist/transport/index.d.cts +3 -276
  53. package/dist/transport/index.d.ts +3 -276
  54. package/dist/transport/index.js +13 -10
  55. package/dist/util/testHelpers.cjs +40 -602
  56. package/dist/util/testHelpers.d.cts +18 -37
  57. package/dist/util/testHelpers.d.ts +18 -37
  58. package/dist/util/testHelpers.js +27 -47
  59. package/package.json +29 -14
  60. package/dist/chunk-3MQETIGZ.js +0 -80
  61. package/dist/chunk-5IC5XMWK.js +0 -140
  62. package/dist/chunk-L7D75G4K.js +0 -29
  63. package/dist/chunk-LQXPKF3A.js +0 -282
  64. package/dist/chunk-PJ2EUO7O.js +0 -63
  65. package/dist/chunk-WVT5QXMZ.js +0 -20
  66. package/dist/chunk-ZE4MX7DF.js +0 -75
  67. package/dist/connection-2529fc14.d.ts +0 -10
  68. package/dist/connection-316d6e3a.d.ts +0 -10
  69. package/dist/connection-8e19874c.d.ts +0 -11
  70. package/dist/connection-f7688cc1.d.ts +0 -11
  71. package/dist/transport/impls/stdio/stdio.cjs +0 -508
  72. package/dist/transport/impls/stdio/stdio.d.cts +0 -26
  73. package/dist/transport/impls/stdio/stdio.d.ts +0 -26
  74. package/dist/transport/impls/stdio/stdio.js +0 -70
  75. package/dist/transport/impls/unixsocket/client.cjs +0 -506
  76. package/dist/transport/impls/unixsocket/client.d.cts +0 -16
  77. package/dist/transport/impls/unixsocket/client.d.ts +0 -16
  78. package/dist/transport/impls/unixsocket/client.js +0 -67
  79. package/dist/transport/impls/unixsocket/server.cjs +0 -510
  80. package/dist/transport/impls/unixsocket/server.d.cts +0 -18
  81. package/dist/transport/impls/unixsocket/server.d.ts +0 -18
  82. package/dist/transport/impls/unixsocket/server.js +0 -73
  83. /package/dist/{chunk-ORAG7IAU.js → chunk-5IZ2UHWV.js} +0 -0
@@ -410,57 +410,58 @@ var TransportMessageSchema = (t) => import_typebox2.Type.Object({
410
410
  id: import_typebox2.Type.String(),
411
411
  from: import_typebox2.Type.String(),
412
412
  to: import_typebox2.Type.String(),
413
+ seq: import_typebox2.Type.Integer(),
414
+ ack: import_typebox2.Type.Integer(),
413
415
  serviceName: import_typebox2.Type.Optional(import_typebox2.Type.Union([import_typebox2.Type.String(), import_typebox2.Type.Null()])),
414
416
  procedureName: import_typebox2.Type.Optional(import_typebox2.Type.Union([import_typebox2.Type.String(), import_typebox2.Type.Null()])),
415
417
  streamId: import_typebox2.Type.String(),
416
418
  controlFlags: import_typebox2.Type.Integer(),
417
419
  payload: t
418
420
  });
419
- var TransportAckSchema = TransportMessageSchema(
420
- import_typebox2.Type.Object({
421
- ack: import_typebox2.Type.String()
422
- })
423
- );
424
- var ControlMessagePayloadSchema = import_typebox2.Type.Object({
421
+ var ControlMessageAckSchema = import_typebox2.Type.Object({
422
+ type: import_typebox2.Type.Literal("ACK")
423
+ });
424
+ var ControlMessageCloseSchema = import_typebox2.Type.Object({
425
425
  type: import_typebox2.Type.Literal("CLOSE")
426
426
  });
427
+ var PROTOCOL_VERSION = "v1";
428
+ var ControlMessageHandshakeRequestSchema = import_typebox2.Type.Object({
429
+ type: import_typebox2.Type.Literal("HANDSHAKE_REQ"),
430
+ protocolVersion: import_typebox2.Type.Literal(PROTOCOL_VERSION)
431
+ });
432
+ var ControlMessageHandshakeResponseSchema = import_typebox2.Type.Object({
433
+ type: import_typebox2.Type.Literal("HANDSHAKE_RESP"),
434
+ status: import_typebox2.Type.Union([
435
+ import_typebox2.Type.Object({
436
+ ok: import_typebox2.Type.Literal(true),
437
+ instanceId: import_typebox2.Type.String()
438
+ }),
439
+ import_typebox2.Type.Object({
440
+ ok: import_typebox2.Type.Literal(false),
441
+ reason: import_typebox2.Type.Union([import_typebox2.Type.Literal("VERSION_MISMATCH")])
442
+ })
443
+ ])
444
+ });
445
+ var ControlMessagePayloadSchema = import_typebox2.Type.Union([
446
+ ControlMessageCloseSchema,
447
+ ControlMessageAckSchema,
448
+ ControlMessageHandshakeRequestSchema,
449
+ ControlMessageHandshakeResponseSchema
450
+ ]);
427
451
  var OpaqueTransportMessageSchema = TransportMessageSchema(
428
452
  import_typebox2.Type.Unknown()
429
453
  );
430
- function msg(from, to, streamId, payload, serviceName, procedureName) {
431
- return {
432
- id: (0, import_nanoid.nanoid)(),
433
- to,
434
- from,
435
- serviceName,
436
- procedureName,
437
- streamId,
438
- controlFlags: 0,
439
- payload
440
- };
441
- }
442
- function reply(msg2, response) {
443
- return {
444
- id: (0, import_nanoid.nanoid)(),
445
- streamId: msg2.streamId,
446
- controlFlags: 0,
447
- to: msg2.from,
448
- from: msg2.to,
449
- payload: response
450
- };
451
- }
452
- function closeStream(from, to, stream) {
453
- const closeMessage = msg(from, to, stream, {
454
- type: "CLOSE"
455
- });
456
- closeMessage.controlFlags |= 4 /* StreamClosedBit */;
457
- return closeMessage;
458
- }
459
454
  function isStreamOpen(controlFlag) {
460
- return (controlFlag & 2 /* StreamOpenBit */) === 2 /* StreamOpenBit */;
455
+ return (
456
+ /* eslint-disable-next-line @typescript-eslint/no-unsafe-enum-comparison */
457
+ (controlFlag & 2 /* StreamOpenBit */) === 2 /* StreamOpenBit */
458
+ );
461
459
  }
462
460
  function isStreamClose(controlFlag) {
463
- return (controlFlag & 4 /* StreamClosedBit */) === 4 /* StreamClosedBit */;
461
+ return (
462
+ /* eslint-disable-next-line @typescript-eslint/no-unsafe-enum-comparison */
463
+ (controlFlag & 4 /* StreamClosedBit */) === 4 /* StreamClosedBit */
464
+ );
464
465
  }
465
466
 
466
467
  // router/client.ts
@@ -555,33 +556,24 @@ var createClient = (transport, serverId = "SERVER") => _createRecursiveProxy(asy
555
556
  throw new Error(`invalid river call, unknown procedure type ${procType}`);
556
557
  }
557
558
  }, []);
558
- var CONNECTION_GRACE_PERIOD_MS = 5e3;
559
- function rejectAfterDisconnectGrace(from, cb) {
560
- let timeout = void 0;
559
+ function createSessionDisconnectHandler(from, cb) {
561
560
  return (evt) => {
562
- if (evt.status === "connect" && evt.conn.connectedTo === from) {
563
- clearTimeout(timeout);
564
- timeout = void 0;
565
- }
566
- if (evt.status === "disconnect" && evt.conn.connectedTo === from) {
567
- timeout = setTimeout(cb, CONNECTION_GRACE_PERIOD_MS);
561
+ if (evt.status === "disconnect" && evt.session.to === from) {
562
+ cb();
568
563
  }
569
564
  };
570
565
  }
571
- function handleRpc(transport, serverId, input, serviceName, procName) {
566
+ function handleRpc(transport, serverId, input, serviceName, procedureName) {
572
567
  const streamId = (0, import_nanoid2.nanoid)();
573
- const m = msg(
574
- transport.clientId,
575
- serverId,
568
+ transport.send(serverId, {
576
569
  streamId,
577
- input,
578
570
  serviceName,
579
- procName
580
- );
581
- m.controlFlags |= 2 /* StreamOpenBit */ | 4 /* StreamClosedBit */;
582
- transport.send(m);
571
+ procedureName,
572
+ payload: input,
573
+ controlFlags: 2 /* StreamOpenBit */ | 4 /* StreamClosedBit */
574
+ });
583
575
  const responsePromise = new Promise((resolve) => {
584
- const onConnectionStatus = rejectAfterDisconnectGrace(serverId, () => {
576
+ const onSessionStatus = createSessionDisconnectHandler(serverId, () => {
585
577
  cleanup();
586
578
  resolve(
587
579
  Err({
@@ -592,172 +584,181 @@ function handleRpc(transport, serverId, input, serviceName, procName) {
592
584
  });
593
585
  function cleanup() {
594
586
  transport.removeEventListener("message", onMessage);
595
- transport.removeEventListener("connectionStatus", onConnectionStatus);
587
+ transport.removeEventListener("sessionStatus", onSessionStatus);
596
588
  }
597
- function onMessage(msg2) {
598
- if (msg2.streamId !== streamId) {
589
+ function onMessage(msg) {
590
+ if (msg.streamId !== streamId) {
599
591
  return;
600
592
  }
601
- if (msg2.to !== transport.clientId) {
593
+ if (msg.to !== transport.clientId) {
602
594
  return;
603
595
  }
604
- if (msg2.streamId === streamId) {
596
+ if (msg.streamId === streamId) {
605
597
  cleanup();
606
- resolve(msg2.payload);
598
+ resolve(msg.payload);
607
599
  }
608
600
  }
609
601
  transport.addEventListener("message", onMessage);
610
- transport.addEventListener("connectionStatus", onConnectionStatus);
602
+ transport.addEventListener("sessionStatus", onSessionStatus);
611
603
  });
612
604
  return responsePromise;
613
605
  }
614
- function handleStream(transport, serverId, init, serviceName, procName) {
606
+ function handleStream(transport, serverId, init, serviceName, procedureName) {
615
607
  const streamId = (0, import_nanoid2.nanoid)();
616
608
  const inputStream = pushable({ objectMode: true });
617
609
  const outputStream = pushable({ objectMode: true });
618
610
  let firstMessage = true;
611
+ let healthyClose = true;
619
612
  if (init) {
620
- const m = msg(
621
- transport.clientId,
622
- serverId,
613
+ transport.send(serverId, {
623
614
  streamId,
624
- init,
625
615
  serviceName,
626
- procName
627
- );
628
- m.controlFlags = 2 /* StreamOpenBit */;
629
- transport.send(m);
616
+ procedureName,
617
+ payload: init,
618
+ controlFlags: 2 /* StreamOpenBit */
619
+ });
630
620
  firstMessage = false;
631
621
  }
632
- (async () => {
622
+ const pipeInputToTransport = async () => {
633
623
  for await (const rawIn of inputStream) {
634
- const m = msg(transport.clientId, serverId, streamId, rawIn);
624
+ const m = {
625
+ streamId,
626
+ payload: rawIn,
627
+ controlFlags: 0
628
+ };
635
629
  if (firstMessage) {
636
630
  m.serviceName = serviceName;
637
- m.procedureName = procName;
631
+ m.procedureName = procedureName;
638
632
  m.controlFlags |= 2 /* StreamOpenBit */;
639
633
  firstMessage = false;
640
634
  }
641
- transport.send(m);
635
+ transport.send(serverId, m);
642
636
  }
643
- transport.send(closeStream(transport.clientId, serverId, streamId));
644
- })();
645
- function onMessage(msg2) {
646
- if (msg2.streamId !== streamId) {
637
+ if (!healthyClose)
638
+ return;
639
+ transport.sendCloseStream(serverId, streamId);
640
+ };
641
+ void pipeInputToTransport();
642
+ function onMessage(msg) {
643
+ if (msg.streamId !== streamId) {
647
644
  return;
648
645
  }
649
- if (msg2.to !== transport.clientId) {
646
+ if (msg.to !== transport.clientId) {
650
647
  return;
651
648
  }
652
- if (isStreamClose(msg2.controlFlags)) {
649
+ if (isStreamClose(msg.controlFlags)) {
653
650
  cleanup();
654
651
  } else {
655
- outputStream.push(msg2.payload);
652
+ outputStream.push(msg.payload);
656
653
  }
657
654
  }
658
655
  function cleanup() {
659
656
  inputStream.end();
660
657
  outputStream.end();
661
658
  transport.removeEventListener("message", onMessage);
662
- transport.removeEventListener("connectionStatus", onConnectionStatus);
659
+ transport.removeEventListener("sessionStatus", onSessionStatus);
663
660
  }
664
- const closeHandler = () => {
665
- cleanup();
666
- transport.send(closeStream(transport.clientId, serverId, streamId));
667
- };
668
- const onConnectionStatus = rejectAfterDisconnectGrace(serverId, () => {
661
+ const onSessionStatus = createSessionDisconnectHandler(serverId, () => {
669
662
  outputStream.push(
670
663
  Err({
671
664
  code: UNEXPECTED_DISCONNECT,
672
665
  message: `${serverId} unexpectedly disconnected`
673
666
  })
674
667
  );
668
+ healthyClose = false;
675
669
  cleanup();
676
670
  });
677
671
  transport.addEventListener("message", onMessage);
678
- transport.addEventListener("connectionStatus", onConnectionStatus);
679
- return [inputStream, outputStream, closeHandler];
672
+ transport.addEventListener("sessionStatus", onSessionStatus);
673
+ return [inputStream, outputStream, cleanup];
680
674
  }
681
- function handleSubscribe(transport, serverId, input, serviceName, procName) {
675
+ function handleSubscribe(transport, serverId, input, serviceName, procedureName) {
682
676
  const streamId = (0, import_nanoid2.nanoid)();
683
- const m = msg(
684
- transport.clientId,
685
- serverId,
677
+ transport.send(serverId, {
686
678
  streamId,
687
- input,
688
679
  serviceName,
689
- procName
690
- );
691
- m.controlFlags |= 2 /* StreamOpenBit */;
692
- transport.send(m);
680
+ procedureName,
681
+ payload: input,
682
+ controlFlags: 2 /* StreamOpenBit */
683
+ });
684
+ let healthyClose = true;
693
685
  const outputStream = pushable({ objectMode: true });
694
- function onMessage(msg2) {
695
- if (msg2.streamId !== streamId) {
686
+ function onMessage(msg) {
687
+ if (msg.streamId !== streamId) {
696
688
  return;
697
689
  }
698
- if (msg2.to !== transport.clientId) {
690
+ if (msg.to !== transport.clientId) {
699
691
  return;
700
692
  }
701
- if (isStreamClose(msg2.controlFlags)) {
693
+ if (isStreamClose(msg.controlFlags)) {
702
694
  cleanup();
703
695
  } else {
704
- outputStream.push(msg2.payload);
696
+ outputStream.push(msg.payload);
705
697
  }
706
698
  }
707
699
  function cleanup() {
708
700
  outputStream.end();
709
701
  transport.removeEventListener("message", onMessage);
710
- transport.removeEventListener("connectionStatus", onConnectionStatus);
702
+ transport.removeEventListener("sessionStatus", onSessionStatus);
711
703
  }
712
704
  const closeHandler = () => {
713
705
  cleanup();
714
- transport.send(closeStream(transport.clientId, serverId, streamId));
706
+ if (!healthyClose)
707
+ return;
708
+ transport.sendCloseStream(serverId, streamId);
715
709
  };
716
- const onConnectionStatus = rejectAfterDisconnectGrace(serverId, () => {
710
+ const onSessionStatus = createSessionDisconnectHandler(serverId, () => {
717
711
  outputStream.push(
718
712
  Err({
719
713
  code: UNEXPECTED_DISCONNECT,
720
714
  message: `${serverId} unexpectedly disconnected`
721
715
  })
722
716
  );
717
+ healthyClose = false;
723
718
  cleanup();
724
719
  });
725
720
  transport.addEventListener("message", onMessage);
726
- transport.addEventListener("connectionStatus", onConnectionStatus);
721
+ transport.addEventListener("sessionStatus", onSessionStatus);
727
722
  return [outputStream, closeHandler];
728
723
  }
729
- function handleUpload(transport, serverId, input, serviceName, procName) {
724
+ function handleUpload(transport, serverId, init, serviceName, procedureName) {
730
725
  const streamId = (0, import_nanoid2.nanoid)();
731
726
  const inputStream = pushable({ objectMode: true });
732
727
  let firstMessage = true;
733
- if (input) {
734
- const m = msg(
735
- transport.clientId,
736
- serverId,
728
+ let healthyClose = true;
729
+ if (init) {
730
+ transport.send(serverId, {
737
731
  streamId,
738
- input,
739
732
  serviceName,
740
- procName
741
- );
742
- m.controlFlags = 2 /* StreamOpenBit */;
743
- transport.send(m);
733
+ procedureName,
734
+ payload: init,
735
+ controlFlags: 2 /* StreamOpenBit */
736
+ });
744
737
  firstMessage = false;
745
738
  }
746
- (async () => {
739
+ const pipeInputToTransport = async () => {
747
740
  for await (const rawIn of inputStream) {
748
- const m = msg(transport.clientId, serverId, streamId, rawIn);
741
+ const m = {
742
+ streamId,
743
+ payload: rawIn,
744
+ controlFlags: 0
745
+ };
749
746
  if (firstMessage) {
750
- m.controlFlags |= 2 /* StreamOpenBit */;
751
747
  m.serviceName = serviceName;
752
- m.procedureName = procName;
748
+ m.procedureName = procedureName;
749
+ m.controlFlags |= 2 /* StreamOpenBit */;
753
750
  firstMessage = false;
754
751
  }
755
- transport.send(m);
752
+ transport.send(serverId, m);
756
753
  }
757
- transport.send(closeStream(transport.clientId, serverId, streamId));
758
- })();
754
+ if (!healthyClose)
755
+ return;
756
+ transport.sendCloseStream(serverId, streamId);
757
+ };
758
+ void pipeInputToTransport();
759
759
  const responsePromise = new Promise((resolve) => {
760
- const onConnectionStatus = rejectAfterDisconnectGrace(serverId, () => {
760
+ const onSessionStatus = createSessionDisconnectHandler(serverId, () => {
761
+ healthyClose = false;
761
762
  cleanup();
762
763
  resolve(
763
764
  Err({
@@ -769,19 +770,19 @@ function handleUpload(transport, serverId, input, serviceName, procName) {
769
770
  function cleanup() {
770
771
  inputStream.end();
771
772
  transport.removeEventListener("message", onMessage);
772
- transport.removeEventListener("connectionStatus", onConnectionStatus);
773
+ transport.removeEventListener("sessionStatus", onSessionStatus);
773
774
  }
774
- function onMessage(msg2) {
775
- if (msg2.to !== transport.clientId) {
775
+ function onMessage(msg) {
776
+ if (msg.to !== transport.clientId) {
776
777
  return;
777
778
  }
778
- if (msg2.streamId === streamId) {
779
+ if (msg.streamId === streamId) {
779
780
  cleanup();
780
- resolve(msg2.payload);
781
+ resolve(msg.payload);
781
782
  }
782
783
  }
783
784
  transport.addEventListener("message", onMessage);
784
- transport.addEventListener("connectionStatus", onConnectionStatus);
785
+ transport.addEventListener("sessionStatus", onSessionStatus);
785
786
  });
786
787
  return [inputStream, responsePromise];
787
788
  }
@@ -791,6 +792,16 @@ var log;
791
792
 
792
793
  // router/server.ts
793
794
  var import_value = require("@sinclair/typebox/value");
795
+
796
+ // util/stringify.ts
797
+ function coerceErrorString(err) {
798
+ if (err instanceof Error) {
799
+ return err.message;
800
+ }
801
+ return `[coerced to error] ${String(err)}`;
802
+ }
803
+
804
+ // router/server.ts
794
805
  var RiverServer = class {
795
806
  transport;
796
807
  services;
@@ -799,10 +810,12 @@ var RiverServer = class {
799
810
  streamMap;
800
811
  // map of client to their open streams by streamId
801
812
  clientStreams;
813
+ disconnectedSessions;
802
814
  constructor(transport, services, extendedContext) {
803
815
  this.transport = transport;
804
816
  this.services = services;
805
817
  this.contextMap = /* @__PURE__ */ new Map();
818
+ this.disconnectedSessions = /* @__PURE__ */ new Set();
806
819
  for (const service of Object.values(services)) {
807
820
  this.contextMap.set(service, {
808
821
  ...extendedContext,
@@ -811,13 +824,13 @@ var RiverServer = class {
811
824
  }
812
825
  this.streamMap = /* @__PURE__ */ new Map();
813
826
  this.clientStreams = /* @__PURE__ */ new Map();
814
- this.transport.addEventListener("message", this.handler);
815
- this.transport.addEventListener("connectionStatus", this.onDisconnect);
827
+ this.transport.addEventListener("message", this.onMessage);
828
+ this.transport.addEventListener("sessionStatus", this.onSessionStatus);
816
829
  }
817
830
  get streams() {
818
831
  return this.streamMap;
819
832
  }
820
- handler = async (message) => {
833
+ onMessage = async (message) => {
821
834
  if (message.to !== this.transport.clientId) {
822
835
  log?.info(
823
836
  `${this.transport.clientId} -- got msg with destination that isn't the server, ignoring`
@@ -832,12 +845,12 @@ var RiverServer = class {
832
845
  }
833
846
  await this.pushToStream(procStream, message, isInitMessage);
834
847
  };
835
- // cleanup streams on unexpected disconnections
836
- onDisconnect = async (evt) => {
848
+ // cleanup streams on session close
849
+ onSessionStatus = async (evt) => {
837
850
  if (evt.status !== "disconnect") {
838
851
  return;
839
852
  }
840
- const disconnectedClientId = evt.conn.connectedTo;
853
+ const disconnectedClientId = evt.session.to;
841
854
  log?.info(
842
855
  `${this.transport.clientId} -- got unexpected disconnect from ${disconnectedClientId}, cleaning up streams`
843
856
  );
@@ -845,14 +858,16 @@ var RiverServer = class {
845
858
  if (!streamsFromThisClient) {
846
859
  return;
847
860
  }
861
+ this.disconnectedSessions.add(disconnectedClientId);
848
862
  await Promise.all(
849
863
  Array.from(streamsFromThisClient).map(this.cleanupStream)
850
864
  );
865
+ this.disconnectedSessions.delete(disconnectedClientId);
851
866
  this.clientStreams.delete(disconnectedClientId);
852
867
  };
853
868
  async close() {
854
- this.transport.removeEventListener("message", this.handler);
855
- this.transport.removeEventListener("connectionStatus", this.onDisconnect);
869
+ this.transport.removeEventListener("message", this.onMessage);
870
+ this.transport.removeEventListener("sessionStatus", this.onSessionStatus);
856
871
  await Promise.all([...this.streamMap.keys()].map(this.cleanupStream));
857
872
  }
858
873
  createNewProcStream(message) {
@@ -876,6 +891,13 @@ var RiverServer = class {
876
891
  );
877
892
  return;
878
893
  }
894
+ const session = this.transport.sessions.get(message.from);
895
+ if (!session) {
896
+ log?.warn(
897
+ `${this.transport.clientId} -- couldn't find session for ${message.from}`
898
+ );
899
+ return;
900
+ }
879
901
  const procedure = service.procedures[message.procedureName];
880
902
  const incoming = pushable({ objectMode: true });
881
903
  const outgoing = pushable({ objectMode: true });
@@ -883,21 +905,20 @@ var RiverServer = class {
883
905
  // sending outgoing messages back to client
884
906
  (async () => {
885
907
  for await (const response of outgoing) {
886
- this.transport.send(reply(message, response));
908
+ this.transport.send(session.to, {
909
+ streamId: message.streamId,
910
+ controlFlags: 0,
911
+ payload: response
912
+ });
887
913
  }
888
- if (procedure.type === "subscription" || procedure.type === "stream") {
889
- this.transport.send(
890
- closeStream(
891
- this.transport.clientId,
892
- message.from,
893
- message.streamId
894
- )
895
- );
914
+ const needsClose = procedure.type === "subscription" || procedure.type === "stream";
915
+ if (needsClose && !this.disconnectedSessions.has(message.from)) {
916
+ this.transport.sendCloseStream(session.to, message.streamId);
896
917
  }
897
918
  })()
898
919
  );
899
920
  const errorHandler = (err) => {
900
- const errorMsg = err instanceof Error ? err.message : `[coerced to error] ${err}`;
921
+ const errorMsg = coerceErrorString(err);
901
922
  log?.error(
902
923
  `${this.transport.clientId} -- procedure ${message.serviceName}.${message.procedureName}:${message.streamId} threw an error: ${errorMsg}`
903
924
  );
@@ -910,82 +931,95 @@ var RiverServer = class {
910
931
  };
911
932
  let inputHandler;
912
933
  const procHasInitMessage = "init" in procedure;
913
- if (procedure.type === "stream") {
914
- if (procHasInitMessage) {
915
- inputHandler = (async () => {
916
- const initMessage = await incoming.next();
917
- if (initMessage.done) {
918
- return;
919
- }
920
- return procedure.handler(serviceContext, initMessage.value, incoming, outgoing).catch(errorHandler);
921
- })();
922
- } else {
923
- inputHandler = procedure.handler(serviceContext, incoming, outgoing).catch(errorHandler);
924
- }
925
- } else if (procedure.type === "rpc") {
926
- inputHandler = (async () => {
927
- const inputMessage = await incoming.next();
928
- if (inputMessage.done) {
929
- return;
930
- }
931
- try {
932
- const outputMessage = await procedure.handler(
933
- serviceContext,
934
- inputMessage.value
935
- );
936
- outgoing.push(outputMessage);
937
- } catch (err) {
938
- errorHandler(err);
939
- }
940
- })();
941
- } else if (procedure.type === "subscription") {
942
- inputHandler = (async () => {
943
- const inputMessage = await incoming.next();
944
- if (inputMessage.done) {
945
- return;
946
- }
947
- try {
948
- await procedure.handler(serviceContext, inputMessage.value, outgoing);
949
- } catch (err) {
950
- errorHandler(err);
951
- }
952
- })();
953
- } else if (procedure.type === "upload") {
954
- if (procHasInitMessage) {
934
+ switch (procedure.type) {
935
+ case "rpc":
955
936
  inputHandler = (async () => {
956
- const initMessage = await incoming.next();
957
- if (initMessage.done) {
937
+ const inputMessage = await incoming.next();
938
+ if (inputMessage.done) {
958
939
  return;
959
940
  }
960
941
  try {
961
942
  const outputMessage = await procedure.handler(
962
943
  serviceContext,
963
- initMessage.value,
964
- incoming
944
+ inputMessage.value
965
945
  );
966
946
  outgoing.push(outputMessage);
967
947
  } catch (err) {
968
948
  errorHandler(err);
969
949
  }
970
950
  })();
971
- } else {
951
+ break;
952
+ case "stream":
953
+ if (procHasInitMessage) {
954
+ inputHandler = (async () => {
955
+ const initMessage = await incoming.next();
956
+ if (initMessage.done) {
957
+ return;
958
+ }
959
+ return procedure.handler(serviceContext, initMessage.value, incoming, outgoing).catch(errorHandler);
960
+ })();
961
+ } else {
962
+ inputHandler = procedure.handler(serviceContext, incoming, outgoing).catch(errorHandler);
963
+ }
964
+ break;
965
+ case "subscription":
972
966
  inputHandler = (async () => {
967
+ const inputMessage = await incoming.next();
968
+ if (inputMessage.done) {
969
+ return;
970
+ }
973
971
  try {
974
- const outputMessage = await procedure.handler(
972
+ await procedure.handler(
975
973
  serviceContext,
976
- incoming
974
+ inputMessage.value,
975
+ outgoing
977
976
  );
978
- outgoing.push(outputMessage);
979
977
  } catch (err) {
980
978
  errorHandler(err);
981
979
  }
982
980
  })();
983
- }
984
- } else {
985
- log?.warn(
986
- `${this.transport.clientId} -- got request for invalid procedure type ${procedure.type} at ${message.serviceName}.${message.procedureName}`
987
- );
988
- return;
981
+ break;
982
+ case "upload":
983
+ if (procHasInitMessage) {
984
+ inputHandler = (async () => {
985
+ const initMessage = await incoming.next();
986
+ if (initMessage.done) {
987
+ return;
988
+ }
989
+ try {
990
+ const outputMessage = await procedure.handler(
991
+ serviceContext,
992
+ initMessage.value,
993
+ incoming
994
+ );
995
+ if (!this.disconnectedSessions.has(message.from)) {
996
+ outgoing.push(outputMessage);
997
+ }
998
+ } catch (err) {
999
+ errorHandler(err);
1000
+ }
1001
+ })();
1002
+ } else {
1003
+ inputHandler = (async () => {
1004
+ try {
1005
+ const outputMessage = await procedure.handler(
1006
+ serviceContext,
1007
+ incoming
1008
+ );
1009
+ if (!this.disconnectedSessions.has(message.from)) {
1010
+ outgoing.push(outputMessage);
1011
+ }
1012
+ } catch (err) {
1013
+ errorHandler(err);
1014
+ }
1015
+ })();
1016
+ }
1017
+ break;
1018
+ default:
1019
+ log?.warn(
1020
+ `${this.transport.clientId} -- got request for invalid procedure type ${procedure.type} at ${message.serviceName}.${message.procedureName}`
1021
+ );
1022
+ return;
989
1023
  }
990
1024
  const procStream = {
991
1025
  id: message.streamId,
@@ -993,7 +1027,6 @@ var RiverServer = class {
993
1027
  outgoing,
994
1028
  serviceName: message.serviceName,
995
1029
  procedureName: message.procedureName,
996
- procedure,
997
1030
  promises: { inputHandler, outputHandler }
998
1031
  };
999
1032
  this.streamMap.set(message.streamId, procStream);
@@ -1003,13 +1036,18 @@ var RiverServer = class {
1003
1036
  return procStream;
1004
1037
  }
1005
1038
  async pushToStream(procStream, message, isInit) {
1006
- const procedure = procStream.procedure;
1039
+ const { serviceName, procedureName } = procStream;
1040
+ const procedure = this.services[serviceName].procedures[procedureName];
1007
1041
  const procHasInitMessage = "init" in procedure;
1008
- if (isInit && procHasInitMessage && import_value.Value.Check(procedure.init, message.payload) || import_value.Value.Check(procedure.input, message.payload)) {
1042
+ if (isInit && procHasInitMessage && import_value.Value.Check(procedure.init, message.payload)) {
1043
+ procStream.incoming.push(message.payload);
1044
+ } else if (import_value.Value.Check(procedure.input, message.payload)) {
1009
1045
  procStream.incoming.push(message.payload);
1010
1046
  } else if (!import_value.Value.Check(ControlMessagePayloadSchema, message.payload)) {
1011
1047
  log?.error(
1012
- `${this.transport.clientId} -- procedure ${procStream.serviceName}.${procStream.procedureName} received invalid payload: ${JSON.stringify(message.payload)}`
1048
+ `${this.transport.clientId} -- procedure ${serviceName}.${procedureName} received invalid payload: ${JSON.stringify(
1049
+ message.payload
1050
+ )}`
1013
1051
  );
1014
1052
  }
1015
1053
  if (isStreamClose(message.controlFlags)) {