@replit/river 0.12.6 → 0.13.1

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 (51) hide show
  1. package/README.md +6 -1
  2. package/dist/{builder-c593de11.d.ts → builder-eef3b061.d.ts} +16 -7
  3. package/dist/{chunk-AFLZ6INU.js → chunk-JXKTY3GQ.js} +77 -49
  4. package/dist/{chunk-IIBVKYDB.js → chunk-JXO2SCQB.js} +39 -1
  5. package/dist/{chunk-4SDJ5VN4.js → chunk-LDUFHGZU.js} +150 -116
  6. package/dist/{chunk-VLBVQX5H.js → chunk-Q7AWJYDQ.js} +1 -1
  7. package/dist/{chunk-XFFS4UOD.js → chunk-SCG5S2EC.js} +9 -9
  8. package/dist/{messageFraming-b200ef25.d.ts → connection-03e650c8.d.ts} +17 -2
  9. package/dist/{connection-ba37d174.d.ts → connection-d052d027.d.ts} +1 -1
  10. package/dist/{index-54e0f99c.d.ts → index-9aa0aabb.d.ts} +29 -39
  11. package/dist/router/index.cjs +83 -56
  12. package/dist/router/index.d.cts +5 -5
  13. package/dist/router/index.d.ts +5 -5
  14. package/dist/router/index.js +2 -2
  15. package/dist/transport/impls/uds/client.cjs +144 -122
  16. package/dist/transport/impls/uds/client.d.cts +2 -3
  17. package/dist/transport/impls/uds/client.d.ts +2 -3
  18. package/dist/transport/impls/uds/client.js +5 -6
  19. package/dist/transport/impls/uds/server.cjs +153 -129
  20. package/dist/transport/impls/uds/server.d.cts +2 -3
  21. package/dist/transport/impls/uds/server.d.ts +2 -3
  22. package/dist/transport/impls/uds/server.js +4 -5
  23. package/dist/transport/impls/ws/client.cjs +151 -134
  24. package/dist/transport/impls/ws/client.d.cts +4 -4
  25. package/dist/transport/impls/ws/client.d.ts +4 -4
  26. package/dist/transport/impls/ws/client.js +10 -17
  27. package/dist/transport/impls/ws/server.cjs +153 -129
  28. package/dist/transport/impls/ws/server.d.cts +2 -2
  29. package/dist/transport/impls/ws/server.d.ts +2 -2
  30. package/dist/transport/impls/ws/server.js +4 -4
  31. package/dist/transport/index.cjs +204 -169
  32. package/dist/transport/index.d.cts +1 -1
  33. package/dist/transport/index.d.ts +1 -1
  34. package/dist/transport/index.js +3 -3
  35. package/dist/util/testHelpers.cjs +294 -16
  36. package/dist/util/testHelpers.d.cts +2 -2
  37. package/dist/util/testHelpers.d.ts +2 -2
  38. package/dist/util/testHelpers.js +30 -8
  39. package/package.json +1 -9
  40. package/dist/chunk-PBPXYLI6.js +0 -44
  41. package/dist/chunk-Q7GL34DZ.js +0 -47
  42. package/dist/connection-1f9971d8.d.ts +0 -17
  43. package/dist/connection-24d878ac.d.ts +0 -18
  44. package/dist/transport/impls/stdio/client.cjs +0 -904
  45. package/dist/transport/impls/stdio/client.d.cts +0 -27
  46. package/dist/transport/impls/stdio/client.d.ts +0 -27
  47. package/dist/transport/impls/stdio/client.js +0 -42
  48. package/dist/transport/impls/stdio/server.cjs +0 -879
  49. package/dist/transport/impls/stdio/server.d.cts +0 -25
  50. package/dist/transport/impls/stdio/server.d.ts +0 -25
  51. package/dist/transport/impls/stdio/server.js +0 -33
package/README.md CHANGED
@@ -8,7 +8,9 @@ It's like tRPC/gRPC but with
8
8
  - result types and error handling
9
9
  - snappy DX (no code-generation)
10
10
  - transparent reconnect support for long-lived sessions
11
- - over any transport (WebSockets, stdio, Unix Domain Socket out of the box)
11
+ - over any transport (WebSockets and Unix Domain Socket out of the box)
12
+
13
+ See [PROTOCOL.md](./PROTOCOL.md) for more information on the protocol.
12
14
 
13
15
  ## Installation
14
16
 
@@ -47,6 +49,7 @@ import { Type } from '@sinclair/typebox';
47
49
 
48
50
  export const ExampleServiceConstructor = () =>
49
51
  ServiceBuilder.create('example')
52
+ // initializer for shared state
50
53
  .initialState({
51
54
  count: 0,
52
55
  })
@@ -55,7 +58,9 @@ export const ExampleServiceConstructor = () =>
55
58
  input: Type.Object({ n: Type.Number() }),
56
59
  output: Type.Object({ result: Type.Number() }),
57
60
  errors: Type.Never(),
61
+ // note that a handler is unique per user RPC
58
62
  async handler(ctx, { n }) {
63
+ // access and mutate shared state
59
64
  ctx.state.count += n;
60
65
  return Ok({ result: ctx.state.count });
61
66
  },
@@ -1,5 +1,6 @@
1
1
  import { TObject, TUnion, TString, TSchema, TNever, TLiteral, Static } from '@sinclair/typebox';
2
2
  import { Pushable } from 'it-pushable';
3
+ import { b as TransportClientId, d as Session, C as Connection } from './index-9aa0aabb.js';
3
4
 
4
5
  /**
5
6
  * The context for services/procedures. This is used only on
@@ -21,6 +22,7 @@ import { Pushable } from 'it-pushable';
21
22
  * }
22
23
  * ```
23
24
  */
25
+
24
26
  interface ServiceContext {
25
27
  }
26
28
  /**
@@ -29,6 +31,13 @@ interface ServiceContext {
29
31
  type ServiceContextWithState<State> = ServiceContext & {
30
32
  state: State;
31
33
  };
34
+ type ServiceContextWithTransportInfo<State> = ServiceContext & {
35
+ state: State;
36
+ to: TransportClientId;
37
+ from: TransportClientId;
38
+ streamId: string;
39
+ session: Session<Connection>;
40
+ };
32
41
 
33
42
  type TLiteralString = TLiteral<string>;
34
43
  type RiverErrorSchema = TObject<{
@@ -145,39 +154,39 @@ type Procedure<State, Ty extends ValidProcType, I extends PayloadType, O extends
145
154
  input: I;
146
155
  output: O;
147
156
  errors: E;
148
- handler: (context: ServiceContextWithState<State>, input: Static<I>) => Promise<Result<Static<O>, Static<E>>>;
157
+ handler: (context: ServiceContextWithTransportInfo<State>, input: Static<I>) => Promise<Result<Static<O>, Static<E>>>;
149
158
  type: Ty;
150
159
  } : never : Ty extends 'upload' ? Init extends PayloadType ? {
151
160
  init: Init;
152
161
  input: I;
153
162
  output: O;
154
163
  errors: E;
155
- handler: (context: ServiceContextWithState<State>, init: Static<Init>, input: AsyncIterableIterator<Static<I>>) => Promise<Result<Static<O>, Static<E>>>;
164
+ handler: (context: ServiceContextWithTransportInfo<State>, init: Static<Init>, input: AsyncIterableIterator<Static<I>>) => Promise<Result<Static<O>, Static<E>>>;
156
165
  type: Ty;
157
166
  } : {
158
167
  input: I;
159
168
  output: O;
160
169
  errors: E;
161
- handler: (context: ServiceContextWithState<State>, input: AsyncIterableIterator<Static<I>>) => Promise<Result<Static<O>, Static<E>>>;
170
+ handler: (context: ServiceContextWithTransportInfo<State>, input: AsyncIterableIterator<Static<I>>) => Promise<Result<Static<O>, Static<E>>>;
162
171
  type: Ty;
163
172
  } : Ty extends 'subscription' ? Init extends null ? {
164
173
  input: I;
165
174
  output: O;
166
175
  errors: E;
167
- handler: (context: ServiceContextWithState<State>, input: Static<I>, output: Pushable<Result<Static<O>, Static<E>>>) => Promise<void>;
176
+ handler: (context: ServiceContextWithTransportInfo<State>, input: Static<I>, output: Pushable<Result<Static<O>, Static<E>>>) => Promise<void>;
168
177
  type: Ty;
169
178
  } : never : Ty extends 'stream' ? Init extends PayloadType ? {
170
179
  init: Init;
171
180
  input: I;
172
181
  output: O;
173
182
  errors: E;
174
- handler: (context: ServiceContextWithState<State>, init: Static<Init>, input: AsyncIterableIterator<Static<I>>, output: Pushable<Result<Static<O>, Static<E>>>) => Promise<void>;
183
+ handler: (context: ServiceContextWithTransportInfo<State>, init: Static<Init>, input: AsyncIterableIterator<Static<I>>, output: Pushable<Result<Static<O>, Static<E>>>) => Promise<void>;
175
184
  type: Ty;
176
185
  } : {
177
186
  input: I;
178
187
  output: O;
179
188
  errors: E;
180
- handler: (context: ServiceContextWithState<State>, input: AsyncIterableIterator<Static<I>>, output: Pushable<Result<Static<O>, Static<E>>>) => Promise<void>;
189
+ handler: (context: ServiceContextWithTransportInfo<State>, input: AsyncIterableIterator<Static<I>>, output: Pushable<Result<Static<O>, Static<E>>>) => Promise<void>;
181
190
  type: Ty;
182
191
  } : never;
183
192
  type AnyProcedure = Procedure<object, ValidProcType, PayloadType, PayloadType, RiverError, PayloadType | null>;
@@ -230,4 +239,4 @@ declare class ServiceBuilder<T extends Service<string, object, ProcListing>> {
230
239
  }>;
231
240
  }
232
241
 
233
- export { AnyService as A, Err as E, Ok as O, PayloadType as P, RiverError as R, ServiceContext as S, UNCAUGHT_ERROR as U, ValidProcType as V, Procedure as a, Result as b, RiverUncaughtSchema as c, ProcType as d, ProcInput as e, ProcOutput as f, ProcErrors as g, ProcHasInit as h, ProcInit as i, ServiceBuilder as j, ProcListing as k, Service as l, ProcHandler as m, ServiceContextWithState as n, RiverErrorSchema as o, serializeService as s };
242
+ export { AnyService as A, Err as E, Ok as O, PayloadType as P, RiverError as R, ServiceContext as S, UNCAUGHT_ERROR as U, ValidProcType as V, Procedure as a, Result as b, RiverUncaughtSchema as c, ProcType as d, ProcInput as e, ProcOutput as f, ProcErrors as g, ProcHasInit as h, ProcInit as i, ServiceBuilder as j, ProcListing as k, Service as l, ProcHandler as m, ServiceContextWithState as n, ServiceContextWithTransportInfo as o, RiverErrorSchema as p, serializeService as s };
@@ -3,7 +3,7 @@ import {
3
3
  coerceErrorString,
4
4
  isStreamClose,
5
5
  isStreamOpen
6
- } from "./chunk-XFFS4UOD.js";
6
+ } from "./chunk-SCG5S2EC.js";
7
7
  import {
8
8
  log
9
9
  } from "./chunk-H4BYJELI.js";
@@ -429,7 +429,8 @@ function _createRecursiveProxy(callback, path) {
429
429
  });
430
430
  return proxy;
431
431
  }
432
- var createClient = (transport, serverId = "SERVER") => _createRecursiveProxy(async (opts) => {
432
+ var createClient = (transport) => _createRecursiveProxy(async (opts) => {
433
+ const serverId = transport.connectedTo;
433
434
  const [serviceName, procName, procType] = [...opts.path];
434
435
  if (!(serviceName && procName && procType)) {
435
436
  throw new Error(
@@ -437,6 +438,11 @@ var createClient = (transport, serverId = "SERVER") => _createRecursiveProxy(asy
437
438
  );
438
439
  }
439
440
  const [input] = opts.args;
441
+ log?.info(
442
+ `${transport.clientId} -- invoked ${procType}: ${serviceName}.${procName} with args: ${JSON.stringify(
443
+ input
444
+ )}`
445
+ );
440
446
  if (procType === "rpc") {
441
447
  return handleRpc(
442
448
  transport,
@@ -504,16 +510,12 @@ function handleRpc(transport, serverId, input, serviceName, procedureName) {
504
510
  transport.removeEventListener("sessionStatus", onSessionStatus);
505
511
  }
506
512
  function onMessage(msg) {
507
- if (msg.streamId !== streamId) {
513
+ if (msg.streamId !== streamId)
508
514
  return;
509
- }
510
- if (msg.to !== transport.clientId) {
515
+ if (msg.to !== transport.clientId)
511
516
  return;
512
- }
513
- if (msg.streamId === streamId) {
514
- cleanup();
515
- resolve(msg.payload);
516
- }
517
+ cleanup();
518
+ resolve(msg.payload);
517
519
  }
518
520
  transport.addEventListener("message", onMessage);
519
521
  transport.addEventListener("sessionStatus", onSessionStatus);
@@ -557,12 +559,10 @@ function handleStream(transport, serverId, init, serviceName, procedureName) {
557
559
  };
558
560
  void pipeInputToTransport();
559
561
  function onMessage(msg) {
560
- if (msg.streamId !== streamId) {
562
+ if (msg.streamId !== streamId)
561
563
  return;
562
- }
563
- if (msg.to !== transport.clientId) {
564
+ if (msg.to !== transport.clientId)
564
565
  return;
565
- }
566
566
  if (isStreamClose(msg.controlFlags)) {
567
567
  cleanup();
568
568
  } else {
@@ -601,12 +601,10 @@ function handleSubscribe(transport, serverId, input, serviceName, procedureName)
601
601
  let healthyClose = true;
602
602
  const outputStream = pushable({ objectMode: true });
603
603
  function onMessage(msg) {
604
- if (msg.streamId !== streamId) {
604
+ if (msg.streamId !== streamId)
605
605
  return;
606
- }
607
- if (msg.to !== transport.clientId) {
606
+ if (msg.to !== transport.clientId)
608
607
  return;
609
- }
610
608
  if (isStreamClose(msg.controlFlags)) {
611
609
  cleanup();
612
610
  } else {
@@ -690,13 +688,12 @@ function handleUpload(transport, serverId, init, serviceName, procedureName) {
690
688
  transport.removeEventListener("sessionStatus", onSessionStatus);
691
689
  }
692
690
  function onMessage(msg) {
693
- if (msg.to !== transport.clientId) {
691
+ if (msg.streamId !== streamId)
694
692
  return;
695
- }
696
- if (msg.streamId === streamId) {
697
- cleanup();
698
- resolve(msg.payload);
699
- }
693
+ if (msg.to !== transport.clientId)
694
+ return;
695
+ cleanup();
696
+ resolve(msg.payload);
700
697
  }
701
698
  transport.addEventListener("message", onMessage);
702
699
  transport.addEventListener("sessionStatus", onSessionStatus);
@@ -751,17 +748,15 @@ var RiverServer = class {
751
748
  };
752
749
  // cleanup streams on session close
753
750
  onSessionStatus = async (evt) => {
754
- if (evt.status !== "disconnect") {
751
+ if (evt.status !== "disconnect")
755
752
  return;
756
- }
757
753
  const disconnectedClientId = evt.session.to;
758
754
  log?.info(
759
755
  `${this.transport.clientId} -- got session disconnect from ${disconnectedClientId}, cleaning up streams`
760
756
  );
761
757
  const streamsFromThisClient = this.clientStreams.get(disconnectedClientId);
762
- if (!streamsFromThisClient) {
758
+ if (!streamsFromThisClient)
763
759
  return;
764
- }
765
760
  this.disconnectedSessions.add(disconnectedClientId);
766
761
  await Promise.all(
767
762
  Array.from(streamsFromThisClient).map(this.cleanupStream)
@@ -781,7 +776,13 @@ var RiverServer = class {
781
776
  );
782
777
  return;
783
778
  }
784
- if (!message.serviceName || !(message.serviceName in this.services)) {
779
+ if (!message.procedureName || !message.serviceName) {
780
+ log?.warn(
781
+ `${this.transport.clientId} -- missing procedure or service name in stream open message`
782
+ );
783
+ return;
784
+ }
785
+ if (!(message.serviceName in this.services)) {
785
786
  log?.warn(
786
787
  `${this.transport.clientId} -- couldn't find service ${message.serviceName}`
787
788
  );
@@ -789,7 +790,7 @@ var RiverServer = class {
789
790
  }
790
791
  const service = this.services[message.serviceName];
791
792
  const serviceContext = this.getContext(service);
792
- if (!message.procedureName || !(message.procedureName in service.procedures)) {
793
+ if (!(message.procedureName in service.procedures)) {
793
794
  log?.warn(
794
795
  `${this.transport.clientId} -- couldn't find a matching procedure for ${message.serviceName}.${message.procedureName}`
795
796
  );
@@ -805,21 +806,36 @@ var RiverServer = class {
805
806
  const procedure = service.procedures[message.procedureName];
806
807
  const incoming = pushable({ objectMode: true });
807
808
  const outgoing = pushable({ objectMode: true });
809
+ const needsClose = procedure.type === "subscription" || procedure.type === "stream";
808
810
  const outputHandler = (
809
811
  // sending outgoing messages back to client
810
- (async () => {
811
- for await (const response of outgoing) {
812
- this.transport.send(session.to, {
813
- streamId: message.streamId,
814
- controlFlags: 0,
815
- payload: response
816
- });
817
- }
818
- const needsClose = procedure.type === "subscription" || procedure.type === "stream";
819
- if (needsClose && !this.disconnectedSessions.has(message.from)) {
820
- this.transport.sendCloseStream(session.to, message.streamId);
821
- }
822
- })()
812
+ needsClose ? (
813
+ // subscription and stream case, we need to send a close bit after the response stream
814
+ (async () => {
815
+ for await (const response of outgoing) {
816
+ this.transport.send(session.to, {
817
+ streamId: message.streamId,
818
+ controlFlags: 0,
819
+ payload: response
820
+ });
821
+ }
822
+ if (!this.disconnectedSessions.has(message.from)) {
823
+ this.transport.sendCloseStream(session.to, message.streamId);
824
+ }
825
+ })()
826
+ ) : (
827
+ // rpc and upload case, we just send the response back with close bit
828
+ (async () => {
829
+ const response = await outgoing.next().then((res) => res.value);
830
+ if (response) {
831
+ this.transport.send(session.to, {
832
+ streamId: message.streamId,
833
+ controlFlags: 4 /* StreamClosedBit */,
834
+ payload: response
835
+ });
836
+ }
837
+ })()
838
+ )
823
839
  );
824
840
  const errorHandler = (err) => {
825
841
  const errorMsg = coerceErrorString(err);
@@ -835,6 +851,13 @@ var RiverServer = class {
835
851
  };
836
852
  let inputHandler;
837
853
  const procHasInitMessage = "init" in procedure;
854
+ const serviceContextWithTransportInfo = {
855
+ ...serviceContext,
856
+ to: message.to,
857
+ from: message.from,
858
+ streamId: message.streamId,
859
+ session
860
+ };
838
861
  switch (procedure.type) {
839
862
  case "rpc":
840
863
  inputHandler = (async () => {
@@ -844,7 +867,7 @@ var RiverServer = class {
844
867
  }
845
868
  try {
846
869
  const outputMessage = await procedure.handler(
847
- serviceContext,
870
+ serviceContextWithTransportInfo,
848
871
  inputMessage.value
849
872
  );
850
873
  outgoing.push(outputMessage);
@@ -860,10 +883,15 @@ var RiverServer = class {
860
883
  if (initMessage.done) {
861
884
  return;
862
885
  }
863
- return procedure.handler(serviceContext, initMessage.value, incoming, outgoing).catch(errorHandler);
886
+ return procedure.handler(
887
+ serviceContextWithTransportInfo,
888
+ initMessage.value,
889
+ incoming,
890
+ outgoing
891
+ ).catch(errorHandler);
864
892
  })();
865
893
  } else {
866
- inputHandler = procedure.handler(serviceContext, incoming, outgoing).catch(errorHandler);
894
+ inputHandler = procedure.handler(serviceContextWithTransportInfo, incoming, outgoing).catch(errorHandler);
867
895
  }
868
896
  break;
869
897
  case "subscription":
@@ -874,7 +902,7 @@ var RiverServer = class {
874
902
  }
875
903
  try {
876
904
  await procedure.handler(
877
- serviceContext,
905
+ serviceContextWithTransportInfo,
878
906
  inputMessage.value,
879
907
  outgoing
880
908
  );
@@ -892,7 +920,7 @@ var RiverServer = class {
892
920
  }
893
921
  try {
894
922
  const outputMessage = await procedure.handler(
895
- serviceContext,
923
+ serviceContextWithTransportInfo,
896
924
  initMessage.value,
897
925
  incoming
898
926
  );
@@ -907,7 +935,7 @@ var RiverServer = class {
907
935
  inputHandler = (async () => {
908
936
  try {
909
937
  const outputMessage = await procedure.handler(
910
- serviceContext,
938
+ serviceContextWithTransportInfo,
911
939
  incoming
912
940
  );
913
941
  if (!this.disconnectedSessions.has(message.from)) {
@@ -1,3 +1,7 @@
1
+ import {
2
+ Connection
3
+ } from "./chunk-LDUFHGZU.js";
4
+
1
5
  // transport/transforms/messageFraming.ts
2
6
  import { Transform } from "node:stream";
3
7
  var Uint32LengthPrefixFraming = class extends Transform {
@@ -57,6 +61,40 @@ var MessageFramer = {
57
61
  }
58
62
  };
59
63
 
64
+ // transport/impls/uds/connection.ts
65
+ var UdsConnection = class extends Connection {
66
+ sock;
67
+ input;
68
+ framer;
69
+ constructor(sock) {
70
+ super();
71
+ this.framer = MessageFramer.createFramedStream();
72
+ this.sock = sock;
73
+ this.input = sock.pipe(this.framer);
74
+ }
75
+ addDataListener(cb) {
76
+ this.input.on("data", cb);
77
+ }
78
+ removeDataListener(cb) {
79
+ this.input.off("data", cb);
80
+ }
81
+ addCloseListener(cb) {
82
+ this.sock.on("close", cb);
83
+ }
84
+ addErrorListener(cb) {
85
+ this.sock.on("error", cb);
86
+ }
87
+ send(payload) {
88
+ if (this.framer.destroyed || !this.sock.writable)
89
+ return false;
90
+ return this.sock.write(MessageFramer.write(payload));
91
+ }
92
+ close() {
93
+ this.sock.destroy();
94
+ this.framer.destroy();
95
+ }
96
+ };
97
+
60
98
  export {
61
- MessageFramer
99
+ UdsConnection
62
100
  };