@replit/river 0.200.0-rc.2 → 0.200.0-rc.3

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 (84) hide show
  1. package/README.md +21 -20
  2. package/dist/{chunk-S5RL45KH.js → chunk-3CCOX55A.js} +76 -42
  3. package/dist/chunk-3CCOX55A.js.map +1 -0
  4. package/dist/chunk-GWYJZFCW.js +653 -0
  5. package/dist/chunk-GWYJZFCW.js.map +1 -0
  6. package/dist/chunk-O4O2E6DJ.js +277 -0
  7. package/dist/chunk-O4O2E6DJ.js.map +1 -0
  8. package/dist/chunk-QXLXDJD5.js +382 -0
  9. package/dist/chunk-QXLXDJD5.js.map +1 -0
  10. package/dist/{chunk-4VNY34QG.js → chunk-UFMDEG44.js} +24 -18
  11. package/dist/chunk-UFMDEG44.js.map +1 -0
  12. package/dist/{chunk-7CKIN3JT.js → chunk-UIYGPURD.js} +31 -484
  13. package/dist/chunk-UIYGPURD.js.map +1 -0
  14. package/dist/{chunk-QMM35C3H.js → chunk-VXYHC666.js} +1 -1
  15. package/dist/chunk-VXYHC666.js.map +1 -0
  16. package/dist/chunk-WSCAA7VY.js +50 -0
  17. package/dist/chunk-WSCAA7VY.js.map +1 -0
  18. package/dist/chunk-ZQVKFLAB.js +399 -0
  19. package/dist/chunk-ZQVKFLAB.js.map +1 -0
  20. package/dist/client-a84783be.d.ts +49 -0
  21. package/dist/{connection-f900e390.d.ts → connection-320fb130.d.ts} +1 -5
  22. package/dist/connection-d0b488e6.d.ts +11 -0
  23. package/dist/context-c9668e95.d.ts +527 -0
  24. package/dist/logging/index.cjs.map +1 -1
  25. package/dist/logging/index.d.cts +1 -1
  26. package/dist/logging/index.d.ts +1 -1
  27. package/dist/logging/index.js +1 -1
  28. package/dist/{index-10ebd26a.d.ts → message-fd349b27.d.ts} +31 -31
  29. package/dist/router/index.cjs +83 -496
  30. package/dist/router/index.cjs.map +1 -1
  31. package/dist/router/index.d.cts +11 -46
  32. package/dist/router/index.d.ts +11 -46
  33. package/dist/router/index.js +2 -4
  34. package/dist/server-dbad597e.d.ts +42 -0
  35. package/dist/{services-970f97bb.d.ts → services-690e5553.d.ts} +5 -602
  36. package/dist/transport/impls/uds/client.cjs +1240 -1237
  37. package/dist/transport/impls/uds/client.cjs.map +1 -1
  38. package/dist/transport/impls/uds/client.d.cts +4 -3
  39. package/dist/transport/impls/uds/client.d.ts +4 -3
  40. package/dist/transport/impls/uds/client.js +7 -13
  41. package/dist/transport/impls/uds/client.js.map +1 -1
  42. package/dist/transport/impls/uds/server.cjs +1302 -1168
  43. package/dist/transport/impls/uds/server.cjs.map +1 -1
  44. package/dist/transport/impls/uds/server.d.cts +4 -4
  45. package/dist/transport/impls/uds/server.d.ts +4 -4
  46. package/dist/transport/impls/uds/server.js +6 -6
  47. package/dist/transport/impls/ws/client.cjs +981 -986
  48. package/dist/transport/impls/ws/client.cjs.map +1 -1
  49. package/dist/transport/impls/ws/client.d.cts +6 -5
  50. package/dist/transport/impls/ws/client.d.ts +6 -5
  51. package/dist/transport/impls/ws/client.js +6 -7
  52. package/dist/transport/impls/ws/client.js.map +1 -1
  53. package/dist/transport/impls/ws/server.cjs +1183 -1064
  54. package/dist/transport/impls/ws/server.cjs.map +1 -1
  55. package/dist/transport/impls/ws/server.d.cts +4 -4
  56. package/dist/transport/impls/ws/server.d.ts +4 -4
  57. package/dist/transport/impls/ws/server.js +6 -6
  58. package/dist/transport/index.cjs +1435 -1377
  59. package/dist/transport/index.cjs.map +1 -1
  60. package/dist/transport/index.d.cts +4 -26
  61. package/dist/transport/index.d.ts +4 -26
  62. package/dist/transport/index.js +9 -9
  63. package/dist/util/testHelpers.cjs +738 -302
  64. package/dist/util/testHelpers.cjs.map +1 -1
  65. package/dist/util/testHelpers.d.cts +9 -4
  66. package/dist/util/testHelpers.d.ts +9 -4
  67. package/dist/util/testHelpers.js +32 -8
  68. package/dist/util/testHelpers.js.map +1 -1
  69. package/package.json +1 -1
  70. package/dist/chunk-47TFNAY2.js +0 -476
  71. package/dist/chunk-47TFNAY2.js.map +0 -1
  72. package/dist/chunk-4VNY34QG.js.map +0 -1
  73. package/dist/chunk-7CKIN3JT.js.map +0 -1
  74. package/dist/chunk-CZP4LK3F.js +0 -335
  75. package/dist/chunk-CZP4LK3F.js.map +0 -1
  76. package/dist/chunk-DJCW3SKT.js +0 -59
  77. package/dist/chunk-DJCW3SKT.js.map +0 -1
  78. package/dist/chunk-NQWDT6GS.js +0 -347
  79. package/dist/chunk-NQWDT6GS.js.map +0 -1
  80. package/dist/chunk-ONUXWVRC.js +0 -492
  81. package/dist/chunk-ONUXWVRC.js.map +0 -1
  82. package/dist/chunk-QMM35C3H.js.map +0 -1
  83. package/dist/chunk-S5RL45KH.js.map +0 -1
  84. package/dist/connection-3f117047.d.ts +0 -17
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # River
2
2
 
3
- ⚠️ Not production ready, while Replit is using parts of river in production, we are still going through rapid breaking changes. First production ready version will be 1.x.x ⚠️
3
+ ⚠️ Not production ready, while Replit is using parts of River in production, we are still going through rapid breaking changes. First production ready version will be `1.x.x` ⚠️
4
4
 
5
5
  River allows multiple clients to connect to and make remote procedure calls to a remote server as if they were local procedures.
6
6
 
@@ -80,7 +80,7 @@ Before proceeding, ensure you have TypeScript 5 installed and configured appropr
80
80
  First, we create a service using `ServiceSchema`:
81
81
 
82
82
  ```ts
83
- import { ServicaSchema, Procedure, Ok } from '@replit/river';
83
+ import { ServiceSchema, Procedure, Ok } from '@replit/river';
84
84
  import { Type } from '@sinclair/typebox';
85
85
 
86
86
  export const ExampleService = ServiceSchema.define(
@@ -134,17 +134,17 @@ In another file for the client (to create a separate entrypoint),
134
134
  ```ts
135
135
  import { WebSocketClientTransport } from '@replit/river/transport/ws/client';
136
136
  import { createClient } from '@replit/river';
137
- import type ServiceSurface from './server';
137
+ import { WebSocket } from 'ws';
138
138
 
139
139
  const transport = new WebSocketClientTransport(
140
140
  async () => new WebSocket('ws://localhost:3000'),
141
141
  'my-client-id',
142
142
  );
143
143
 
144
- const client = createClient<ServiceSurface>(
144
+ const client = createClient(
145
145
  transport,
146
146
  'SERVER', // transport id of the server in the previous step
147
- true, // whether to eagerly connect to the server on creation (optional argument)
147
+ { eagerlyConnect: true }, // whether to eagerly connect to the server on creation (optional argument)
148
148
  );
149
149
 
150
150
  // we get full type safety on `client`
@@ -157,15 +157,6 @@ if (result.ok) {
157
157
  }
158
158
  ```
159
159
 
160
- You can then access the `ParsedMetadata` in your procedure handlers:
161
-
162
- ```ts
163
- async handler(ctx, ...args) {
164
- // this contains the parsed metadata
165
- console.log(ctx.metadata)
166
- }
167
- ```
168
-
169
160
  ### Logging
170
161
 
171
162
  To add logging, you can bind a logging function to a transport.
@@ -192,12 +183,12 @@ River defines two types of reconnects:
192
183
  1. **Transparent reconnects:** These occur when the connection is temporarily lost and reestablished without losing any messages. From the application's perspective, this process is seamless and does not disrupt ongoing operations.
193
184
  2. **Hard reconnect:** This occurs when all server state is lost, requiring the client to reinitialize anything stateful (e.g. subscriptions).
194
185
 
195
- You can listen for transparent reconnects via the `connectionStatus` events, but realistically, no applications should need to listen for this unless it is for debugging purposes. Hard reconnects are signaled via `sessionStatus` events.
186
+ Hard reconnects are signaled via `sessionStatus` events.
196
187
 
197
188
  If your application is stateful on either the server or the client, the service consumer _should_ wrap all the client-side setup with `transport.addEventListener('sessionStatus', (evt) => ...)` to do appropriate setup and teardown.
198
189
 
199
190
  ```ts
200
- transport.addEventListener('connectionStatus', (evt) => {
191
+ transport.addEventListener('sessionStatus', (evt) => {
201
192
  if (evt.status === 'connect') {
202
193
  // do something
203
194
  } else if (evt.status === 'disconnect') {
@@ -205,11 +196,12 @@ transport.addEventListener('connectionStatus', (evt) => {
205
196
  }
206
197
  });
207
198
 
208
- transport.addEventListener('sessionStatus', (evt) => {
209
- if (evt.status === 'connect') {
199
+ // or, listen for specific session states
200
+ transport.addEventListener('sessionTransition', (evt) => {
201
+ if (evt.state === SessionState.Connected) {
202
+ // switch on various transition states
203
+ } else if (evt.state === SessionState.NoConnection) {
210
204
  // do something
211
- } else if (evt.status === 'disconnect') {
212
- // do something else
213
205
  }
214
206
  });
215
207
  ```
@@ -253,6 +245,15 @@ createServer(new MockServerTransport('SERVER'), services, {
253
245
  });
254
246
  ```
255
247
 
248
+ You can then access the `ParsedMetadata` in your procedure handlers:
249
+
250
+ ```ts
251
+ async handler(ctx, ...args) {
252
+ // this contains the parsed metadata
253
+ console.log(ctx.metadata)
254
+ }
255
+ ```
256
+
256
257
  ### Further examples
257
258
 
258
259
  We've also provided an end-to-end testing environment using `Next.js`, and a simple backend connected with the WebSocket transport that you can [play with on Replit](https://replit.com/@jzhao-replit/riverbed).
@@ -1,6 +1,14 @@
1
1
  // transport/message.ts
2
2
  import { Type } from "@sinclair/typebox";
3
- import { nanoid } from "nanoid";
3
+
4
+ // transport/id.ts
5
+ import { customAlphabet } from "nanoid";
6
+ var alphabet = customAlphabet(
7
+ "1234567890abcdefghijklmnopqrstuvxyzABCDEFGHIJKLMNOPQRSTUVXYZ"
8
+ );
9
+ var generateId = () => alphabet(12);
10
+
11
+ // transport/message.ts
4
12
  var TransportMessageSchema = (t) => Type.Object({
5
13
  id: Type.String(),
6
14
  from: Type.String(),
@@ -25,7 +33,7 @@ var ControlMessageAckSchema = Type.Object({
25
33
  var ControlMessageCloseSchema = Type.Object({
26
34
  type: Type.Literal("CLOSE")
27
35
  });
28
- var PROTOCOL_VERSION = "v2.0";
36
+ var PROTOCOL_VERSION = "v1.1";
29
37
  var ControlMessageHandshakeRequestSchema = Type.Object({
30
38
  type: Type.Literal("HANDSHAKE_REQ"),
31
39
  protocolVersion: Type.String(),
@@ -35,18 +43,29 @@ var ControlMessageHandshakeRequestSchema = Type.Object({
35
43
  * used by the server to know whether this is a new or a reestablished connection, and whether it
36
44
  * is compatible with what it already has.
37
45
  */
38
- expectedSessionState: Type.Optional(
39
- Type.Object({
40
- /**
41
- * reconnect is set to true if the client explicitly wants to reestablish an existing
42
- * connection.
43
- */
44
- reconnect: Type.Boolean(),
45
- nextExpectedSeq: Type.Integer()
46
- })
47
- ),
46
+ expectedSessionState: Type.Object({
47
+ // what the client expects the server to send next
48
+ nextExpectedSeq: Type.Integer(),
49
+ // TODO: remove optional once we know all servers
50
+ // are nextSentSeq here
51
+ // what the server expects the client to send next
52
+ nextSentSeq: Type.Optional(Type.Integer())
53
+ }),
48
54
  metadata: Type.Optional(Type.Unknown())
49
55
  });
56
+ var HandshakeErrorRetriableResponseCodes = Type.Union([
57
+ Type.Literal("SESSION_STATE_MISMATCH")
58
+ ]);
59
+ var HandshakeErrorFatalResponseCodes = Type.Union([
60
+ Type.Literal("MALFORMED_HANDSHAKE_META"),
61
+ Type.Literal("MALFORMED_HANDSHAKE"),
62
+ Type.Literal("PROTOCOL_VERSION_MISMATCH"),
63
+ Type.Literal("REJECTED_BY_CUSTOM_HANDLER")
64
+ ]);
65
+ var HandshakeErrorResponseCodes = Type.Union([
66
+ HandshakeErrorRetriableResponseCodes,
67
+ HandshakeErrorFatalResponseCodes
68
+ ]);
50
69
  var ControlMessageHandshakeResponseSchema = Type.Object({
51
70
  type: Type.Literal("HANDSHAKE_RESP"),
52
71
  status: Type.Union([
@@ -56,7 +75,10 @@ var ControlMessageHandshakeResponseSchema = Type.Object({
56
75
  }),
57
76
  Type.Object({
58
77
  ok: Type.Literal(false),
59
- reason: Type.String()
78
+ reason: Type.String(),
79
+ // TODO: remove optional once we know all servers
80
+ // are sending code here
81
+ code: Type.Optional(HandshakeErrorResponseCodes)
60
82
  })
61
83
  ])
62
84
  });
@@ -78,12 +100,12 @@ function handshakeRequestMessage({
78
100
  tracing
79
101
  }) {
80
102
  return {
81
- id: nanoid(),
103
+ id: generateId(),
82
104
  from,
83
105
  to,
84
106
  seq: 0,
85
107
  ack: 0,
86
- streamId: nanoid(),
108
+ streamId: generateId(),
87
109
  controlFlags: 0,
88
110
  tracing,
89
111
  payload: {
@@ -95,19 +117,18 @@ function handshakeRequestMessage({
95
117
  }
96
118
  };
97
119
  }
98
- var SESSION_STATE_MISMATCH = "session state mismatch";
99
120
  function handshakeResponseMessage({
100
121
  from,
101
122
  to,
102
123
  status
103
124
  }) {
104
125
  return {
105
- id: nanoid(),
126
+ id: generateId(),
106
127
  from,
107
128
  to,
108
129
  seq: 0,
109
130
  ack: 0,
110
- streamId: nanoid(),
131
+ streamId: generateId(),
111
132
  controlFlags: 0,
112
133
  payload: {
113
134
  type: "HANDSHAKE_RESP",
@@ -115,6 +136,31 @@ function handshakeResponseMessage({
115
136
  }
116
137
  };
117
138
  }
139
+ function closeStreamMessage(streamId) {
140
+ return {
141
+ streamId,
142
+ controlFlags: 8 /* StreamClosedBit */,
143
+ payload: {
144
+ type: "CLOSE"
145
+ }
146
+ };
147
+ }
148
+ function requestCloseStreamMessage(streamId) {
149
+ return {
150
+ streamId,
151
+ controlFlags: 16 /* StreamCloseRequestBit */,
152
+ payload: {
153
+ type: "CLOSE"
154
+ }
155
+ };
156
+ }
157
+ function abortMessage(streamId, payload) {
158
+ return {
159
+ streamId,
160
+ controlFlags: 4 /* StreamAbortBit */,
161
+ payload
162
+ };
163
+ }
118
164
  function isAck(controlFlag) {
119
165
  return (controlFlag & 1 /* AckBit */) === 1 /* AckBit */;
120
166
  }
@@ -144,7 +190,7 @@ function isStreamAbort(controlFlag) {
144
190
  }
145
191
 
146
192
  // package.json
147
- var version = "0.200.0-rc.2";
193
+ var version = "0.200.0-rc.3";
148
194
 
149
195
  // util/stringify.ts
150
196
  function coerceErrorString(err) {
@@ -169,16 +215,16 @@ function getPropagationContext(ctx) {
169
215
  propagation.inject(ctx, tracing);
170
216
  return tracing;
171
217
  }
172
- function createSessionTelemetryInfo(session, propagationCtx) {
218
+ function createSessionTelemetryInfo(sessionId, to, from, propagationCtx) {
173
219
  const parentCtx = propagationCtx ? propagation.extract(context.active(), propagationCtx) : context.active();
174
220
  const span = tracer.startSpan(
175
- `session ${session.id}`,
221
+ `session ${sessionId}`,
176
222
  {
177
223
  attributes: {
178
224
  component: "river",
179
- "river.session.id": session.id,
180
- "river.session.to": session.to,
181
- "river.session.from": session.from
225
+ "river.session.id": sessionId,
226
+ "river.session.to": to,
227
+ "river.session.from": from
182
228
  }
183
229
  },
184
230
  parentCtx
@@ -186,21 +232,6 @@ function createSessionTelemetryInfo(session, propagationCtx) {
186
232
  const ctx = trace.setSpan(parentCtx, span);
187
233
  return { span, ctx };
188
234
  }
189
- function createConnectionTelemetryInfo(connection, info) {
190
- const span = tracer.startSpan(
191
- `connection ${connection.id}`,
192
- {
193
- attributes: {
194
- component: "river",
195
- "river.connection.id": connection.id
196
- },
197
- links: [{ context: info.span.spanContext() }]
198
- },
199
- info.ctx
200
- );
201
- const ctx = trace.setSpan(info.ctx, span);
202
- return { span, ctx };
203
- }
204
235
  function createProcTelemetryInfo(transport, kind, serviceName, procedureName, streamId) {
205
236
  const baseCtx = context.active();
206
237
  const span = tracer.startSpan(
@@ -255,16 +286,20 @@ var tracer = trace.getTracer("river", version);
255
286
  var tracing_default = tracer;
256
287
 
257
288
  export {
289
+ generateId,
258
290
  TransportMessageSchema,
259
291
  ControlMessageCloseSchema,
260
292
  PROTOCOL_VERSION,
261
293
  ControlMessageHandshakeRequestSchema,
294
+ HandshakeErrorRetriableResponseCodes,
262
295
  ControlMessageHandshakeResponseSchema,
263
296
  ControlMessagePayloadSchema,
264
297
  OpaqueTransportMessageSchema,
265
298
  handshakeRequestMessage,
266
- SESSION_STATE_MISMATCH,
267
299
  handshakeResponseMessage,
300
+ closeStreamMessage,
301
+ requestCloseStreamMessage,
302
+ abortMessage,
268
303
  isAck,
269
304
  isStreamOpen,
270
305
  isStreamClose,
@@ -273,10 +308,9 @@ export {
273
308
  version,
274
309
  getPropagationContext,
275
310
  createSessionTelemetryInfo,
276
- createConnectionTelemetryInfo,
277
311
  createProcTelemetryInfo,
278
312
  createHandlerSpan,
279
313
  tracing_default,
280
314
  coerceErrorString
281
315
  };
282
- //# sourceMappingURL=chunk-S5RL45KH.js.map
316
+ //# sourceMappingURL=chunk-3CCOX55A.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../transport/message.ts","../transport/id.ts","../package.json","../util/stringify.ts","../tracing/index.ts"],"sourcesContent":["import { Type, TSchema, Static } from '@sinclair/typebox';\nimport { PropagationContext } from '../tracing';\nimport { generateId } from './id';\nimport {\n ErrResult,\n InputReaderErrorSchema,\n OutputReaderErrorSchema,\n} from '../router';\n\n/**\n * Control flags for transport messages.\n */\nexport const enum ControlFlags {\n /**\n * Used in heartbeat messages.\n */\n AckBit = 0b00001,\n /**\n * Used in stream open requests.\n */\n StreamOpenBit = 0b00010,\n /**\n * Used when writer closes the stream.\n */\n StreamClosedBit = 0b01000,\n /**\n * Used when readers no longer wish to receive messages.\n */\n StreamCloseRequestBit = 0b10000,\n /**\n * Used when a stream is aborted due to cancellation or errors\n */\n StreamAbortBit = 0b00100,\n}\n\n/**\n * Generic Typebox schema for a transport message.\n * @template T The type of the payload.\n * @param {T} t The payload schema.\n * @returns The transport message schema.\n */\nexport const TransportMessageSchema = <T extends TSchema>(t: T) =>\n Type.Object({\n id: Type.String(),\n from: Type.String(),\n to: Type.String(),\n seq: Type.Integer(),\n ack: Type.Integer(),\n serviceName: Type.Optional(Type.String()),\n procedureName: Type.Optional(Type.String()),\n streamId: Type.String(),\n controlFlags: Type.Integer(),\n tracing: Type.Optional(\n Type.Object({\n traceparent: Type.String(),\n tracestate: Type.String(),\n }),\n ),\n payload: t,\n });\n\n/**\n * Defines the schema for a transport acknowledgement message. This is never constructed manually\n * and is only used internally by the library for tracking inflight messages.\n * @returns The transport message schema.\n */\nexport const ControlMessageAckSchema = Type.Object({\n type: Type.Literal('ACK'),\n});\n\n/**\n * Defines the schema for a transport close message. This is never constructed manually and is only\n * used internally by the library for closing and cleaning up streams.\n */\nexport const ControlMessageCloseSchema = Type.Object({\n type: Type.Literal('CLOSE'),\n});\n\nexport const PROTOCOL_VERSION = 'v1.1';\nexport const ControlMessageHandshakeRequestSchema = Type.Object({\n type: Type.Literal('HANDSHAKE_REQ'),\n protocolVersion: Type.String(),\n sessionId: Type.String(),\n /**\n * Specifies what the server's expected session state (from the pov of the client). This can be\n * used by the server to know whether this is a new or a reestablished connection, and whether it\n * is compatible with what it already has.\n */\n expectedSessionState: Type.Object({\n // what the client expects the server to send next\n nextExpectedSeq: Type.Integer(),\n // TODO: remove optional once we know all servers\n // are nextSentSeq here\n // what the server expects the client to send next\n nextSentSeq: Type.Optional(Type.Integer()),\n }),\n\n metadata: Type.Optional(Type.Unknown()),\n});\n\nexport const HandshakeErrorRetriableResponseCodes = Type.Union([\n Type.Literal('SESSION_STATE_MISMATCH'),\n]);\n\nexport const HandshakeErrorFatalResponseCodes = Type.Union([\n Type.Literal('MALFORMED_HANDSHAKE_META'),\n Type.Literal('MALFORMED_HANDSHAKE'),\n Type.Literal('PROTOCOL_VERSION_MISMATCH'),\n Type.Literal('REJECTED_BY_CUSTOM_HANDLER'),\n]);\n\nexport const HandshakeErrorResponseCodes = Type.Union([\n HandshakeErrorRetriableResponseCodes,\n HandshakeErrorFatalResponseCodes,\n]);\n\nexport const ControlMessageHandshakeResponseSchema = Type.Object({\n type: Type.Literal('HANDSHAKE_RESP'),\n status: Type.Union([\n Type.Object({\n ok: Type.Literal(true),\n sessionId: Type.String(),\n }),\n Type.Object({\n ok: Type.Literal(false),\n reason: Type.String(),\n // TODO: remove optional once we know all servers\n // are sending code here\n code: Type.Optional(HandshakeErrorResponseCodes),\n }),\n ]),\n});\n\nexport const ControlMessagePayloadSchema = Type.Union([\n ControlMessageCloseSchema,\n ControlMessageAckSchema,\n ControlMessageHandshakeRequestSchema,\n ControlMessageHandshakeResponseSchema,\n]);\n\n/**\n * Defines the schema for an opaque transport message that is agnostic to any\n * procedure/service.\n * @returns The transport message schema.\n */\nexport const OpaqueTransportMessageSchema = TransportMessageSchema(\n Type.Unknown(),\n);\n\n/**\n * Represents a transport message. This is the same type as {@link TransportMessageSchema} but\n * we can't statically infer generics from generic Typebox schemas so we have to define it again here.\n *\n * TypeScript can't enforce types when a bitmask is involved, so these are the semantics of\n * `controlFlags`:\n * * If `controlFlags & StreamOpenBit == StreamOpenBit`, `streamId` must be set to a unique value\n * (suggestion: use `nanoid`).\n * * If `controlFlags & StreamOpenBit == StreamOpenBit`, `serviceName` and `procedureName` must be set.\n * * If `controlFlags & StreamClosedBit == StreamClosedBit` and the kind is `stream` or `subscription`,\n * `payload` should be discarded (usually contains a control message).\n * * If `controlFlags & AckBit == AckBit`, the message is an explicit acknowledgement message and doesn't\n * contain any payload that is relevant to the application so should not be delivered.\n * @template Payload The type of the payload.\n */\nexport interface TransportMessage<Payload = unknown> {\n id: string;\n from: TransportClientId;\n to: TransportClientId;\n seq: number;\n ack: number;\n serviceName?: string;\n procedureName?: string;\n streamId: string;\n controlFlags: number;\n tracing?: PropagationContext;\n payload: Payload;\n}\n\nexport type PartialTransportMessage<Payload = unknown> = Omit<\n TransportMessage<Payload>,\n 'id' | 'from' | 'to' | 'seq' | 'ack'\n>;\n\nexport function handshakeRequestMessage({\n from,\n to,\n sessionId,\n expectedSessionState,\n metadata,\n tracing,\n}: {\n from: TransportClientId;\n to: TransportClientId;\n sessionId: string;\n expectedSessionState: Static<\n typeof ControlMessageHandshakeRequestSchema\n >['expectedSessionState'];\n metadata?: unknown;\n tracing?: PropagationContext;\n}): TransportMessage<Static<typeof ControlMessageHandshakeRequestSchema>> {\n return {\n id: generateId(),\n from,\n to,\n seq: 0,\n ack: 0,\n streamId: generateId(),\n controlFlags: 0,\n tracing,\n payload: {\n type: 'HANDSHAKE_REQ',\n protocolVersion: PROTOCOL_VERSION,\n sessionId,\n expectedSessionState,\n metadata,\n } satisfies Static<typeof ControlMessageHandshakeRequestSchema>,\n };\n}\n\n/**\n * This is a reason that can be given during the handshake to indicate that the peer has the wrong\n * session state.\n */\nexport const SESSION_STATE_MISMATCH = 'session state mismatch';\n\nexport function handshakeResponseMessage({\n from,\n to,\n status,\n}: {\n from: TransportClientId;\n to: TransportClientId;\n status: Static<typeof ControlMessageHandshakeResponseSchema>['status'];\n}): TransportMessage<Static<typeof ControlMessageHandshakeResponseSchema>> {\n return {\n id: generateId(),\n from,\n to,\n seq: 0,\n ack: 0,\n streamId: generateId(),\n controlFlags: 0,\n payload: {\n type: 'HANDSHAKE_RESP',\n status,\n } satisfies Static<typeof ControlMessageHandshakeResponseSchema>,\n };\n}\n\nexport function closeStreamMessage(streamId: string): PartialTransportMessage {\n return {\n streamId,\n controlFlags: ControlFlags.StreamClosedBit,\n payload: {\n type: 'CLOSE' as const,\n } satisfies Static<typeof ControlMessagePayloadSchema>,\n };\n}\n\nexport function requestCloseStreamMessage(\n streamId: string,\n): PartialTransportMessage {\n return {\n streamId,\n controlFlags: ControlFlags.StreamCloseRequestBit,\n payload: {\n type: 'CLOSE' as const,\n } satisfies Static<typeof ControlMessagePayloadSchema>,\n };\n}\n\nexport function abortMessage(\n streamId: string,\n payload: ErrResult<\n Static<typeof OutputReaderErrorSchema | typeof InputReaderErrorSchema>\n >,\n) {\n return {\n streamId,\n controlFlags: ControlFlags.StreamAbortBit,\n payload,\n };\n}\n\n/**\n * A type alias for a transport message with an opaque payload.\n * @template T - The type of the opaque payload.\n */\nexport type OpaqueTransportMessage = TransportMessage;\nexport type TransportClientId = string;\n\n/**\n * Checks if the given control flag (usually found in msg.controlFlag) is an ack message.\n * @param controlFlag - The control flag to check.\n * @returns True if the control flag contains the AckBit, false otherwise.\n */\nexport function isAck(controlFlag: number): boolean {\n /* eslint-disable-next-line @typescript-eslint/no-unsafe-enum-comparison */\n return (controlFlag & ControlFlags.AckBit) === ControlFlags.AckBit;\n}\n\n/**\n * Checks if the given control flag (usually found in msg.controlFlag) is a stream open message.\n * @param controlFlag - The control flag to check.\n * @returns True if the control flag contains the StreamOpenBit, false otherwise.\n */\nexport function isStreamOpen(controlFlag: number): boolean {\n return (\n /* eslint-disable-next-line @typescript-eslint/no-unsafe-enum-comparison */\n (controlFlag & ControlFlags.StreamOpenBit) === ControlFlags.StreamOpenBit\n );\n}\n\n/**\n * Checks if the given control flag (usually found in msg.controlFlag) is a stream close message.\n * @param controlFlag - The control flag to check.\n * @returns True if the control flag contains the StreamCloseBit, false otherwise.\n */\nexport function isStreamClose(controlFlag: number): boolean {\n return (\n /* eslint-disable-next-line @typescript-eslint/no-unsafe-enum-comparison */\n (controlFlag & ControlFlags.StreamClosedBit) ===\n ControlFlags.StreamClosedBit\n );\n}\n\n/**\n * Checks if the given control flag (usually found in msg.controlFlag) is a stream close request message.\n * @param controlFlag - The control flag to check.\n * @returns True if the control flag contains the StreamCloseBit, false otherwise.\n */\nexport function isStreamCloseRequest(controlFlag: number): boolean {\n return (\n /* eslint-disable-next-line @typescript-eslint/no-unsafe-enum-comparison */\n (controlFlag & ControlFlags.StreamCloseRequestBit) ===\n ControlFlags.StreamCloseRequestBit\n );\n}\n\n/**\n * Checks if the given control flag (usually found in msg.controlFlag) is an abort message.\n * @param controlFlag - The control flag to check.\n * @returns True if the control flag contains the AbortBit, false otherwise\n */\nexport function isStreamAbort(controlFlag: number): boolean {\n return (\n /* eslint-disable-next-line @typescript-eslint/no-unsafe-enum-comparison */\n (controlFlag & ControlFlags.StreamAbortBit) === ControlFlags.StreamAbortBit\n );\n}\n","import { customAlphabet } from 'nanoid';\n\nconst alphabet = customAlphabet(\n '1234567890abcdefghijklmnopqrstuvxyzABCDEFGHIJKLMNOPQRSTUVXYZ',\n);\nexport const generateId = () => alphabet(12);\n","{\n \"name\": \"@replit/river\",\n \"description\": \"It's like tRPC but... with JSON Schema Support, duplex streaming and support for service multiplexing. Transport agnostic!\",\n \"version\": \"0.200.0-rc.3\",\n \"type\": \"module\",\n \"exports\": {\n \".\": {\n \"import\": \"./dist/router/index.js\",\n \"require\": \"./dist/router/index.cjs\"\n },\n \"./logging\": {\n \"import\": \"./dist/logging/index.js\",\n \"require\": \"./dist/logging/index.cjs\"\n },\n \"./codec\": {\n \"import\": \"./dist/codec/index.js\",\n \"require\": \"./dist/codec/index.cjs\"\n },\n \"./transport\": {\n \"import\": \"./dist/transport/index.js\",\n \"require\": \"./dist/transport/index.cjs\"\n },\n \"./transport/ws/client\": {\n \"import\": \"./dist/transport/impls/ws/client.js\",\n \"require\": \"./dist/transport/impls/ws/client.cjs\"\n },\n \"./transport/ws/server\": {\n \"import\": \"./dist/transport/impls/ws/server.js\",\n \"require\": \"./dist/transport/impls/ws/server.cjs\"\n },\n \"./transport/uds/client\": {\n \"import\": \"./dist/transport/impls/uds/client.js\",\n \"require\": \"./dist/transport/impls/uds/client.cjs\"\n },\n \"./transport/uds/server\": {\n \"import\": \"./dist/transport/impls/uds/server.js\",\n \"require\": \"./dist/transport/impls/uds/server.cjs\"\n },\n \"./test-util\": {\n \"import\": \"./dist/util/testHelpers.js\",\n \"require\": \"./dist/util/testHelpers.cjs\"\n }\n },\n \"sideEffects\": [\n \"./dist/logging/index.js\"\n ],\n \"files\": [\n \"dist\"\n ],\n \"dependencies\": {\n \"@msgpack/msgpack\": \"^3.0.0-beta2\",\n \"nanoid\": \"^4.0.2\",\n \"ws\": \"^8.17.0\"\n },\n \"peerDependencies\": {\n \"@opentelemetry/api\": \"^1.7.0\",\n \"@sinclair/typebox\": \"~0.32.8\"\n },\n \"devDependencies\": {\n \"@opentelemetry/core\": \"^1.7.0\",\n \"@opentelemetry/sdk-trace-base\": \"^1.24.1\",\n \"@opentelemetry/sdk-trace-web\": \"^1.24.1\",\n \"@types/ws\": \"^8.5.5\",\n \"@typescript-eslint/eslint-plugin\": \"^7.8.0\",\n \"@typescript-eslint/parser\": \"^7.8.0\",\n \"@vitest/ui\": \"^1.3.1\",\n \"eslint\": \"^8.57.0\",\n \"eslint-config-prettier\": \"^9.1.0\",\n \"eslint-plugin-prettier\": \"^5.1.3\",\n \"prettier\": \"^3.0.0\",\n \"tsup\": \"^7.2.0\",\n \"typescript\": \"^5.4.5\",\n \"vitest\": \"^1.3.1\"\n },\n \"scripts\": {\n \"check\": \"tsc --noEmit && npm run format && npm run lint\",\n \"format\": \"npx prettier . --check\",\n \"format:fix\": \"npx prettier . --write\",\n \"lint\": \"eslint .\",\n \"lint:fix\": \"eslint . --fix\",\n \"fix\": \"npm run format:fix && npm run lint:fix\",\n \"build\": \"rm -rf dist && tsup && du -sh dist\",\n \"prepack\": \"npm run build\",\n \"release\": \"npm publish --access public\",\n \"test:ui\": \"echo \\\"remember to go to /__vitest__ in the webview\\\" && vitest --ui --api.host 0.0.0.0 --api.port 3000\",\n \"test\": \"vitest\",\n \"test:single\": \"vitest run --reporter=dot\",\n \"test:flake\": \"./flake.sh\",\n \"bench\": \"vitest bench\"\n },\n \"engines\": {\n \"node\": \">=16\"\n },\n \"keywords\": [\n \"rpc\",\n \"websockets\",\n \"jsonschema\"\n ],\n \"author\": \"Jacky Zhao\",\n \"license\": \"MIT\"\n}\n","export function coerceErrorString(err: unknown): string {\n if (err instanceof Error) {\n return err.message || 'unknown reason';\n }\n\n return `[coerced to error] ${String(err)}`;\n}\n","import {\n Context,\n Span,\n SpanKind,\n context,\n propagation,\n trace,\n} from '@opentelemetry/api';\nimport { version as RIVER_VERSION } from '../package.json';\nimport { ValidProcType } from '../router';\nimport { ClientTransport, Connection } from '../transport';\n\nexport interface PropagationContext {\n traceparent: string;\n tracestate: string;\n}\n\nexport interface TelemetryInfo {\n span: Span;\n ctx: Context;\n}\n\nexport function getPropagationContext(\n ctx: Context,\n): PropagationContext | undefined {\n const tracing = {\n traceparent: '',\n tracestate: '',\n };\n propagation.inject(ctx, tracing);\n return tracing;\n}\n\nexport function createSessionTelemetryInfo(\n sessionId: string,\n to: string,\n from: string,\n propagationCtx?: PropagationContext,\n): TelemetryInfo {\n const parentCtx = propagationCtx\n ? propagation.extract(context.active(), propagationCtx)\n : context.active();\n\n const span = tracer.startSpan(\n `session ${sessionId}`,\n {\n attributes: {\n component: 'river',\n 'river.session.id': sessionId,\n 'river.session.to': to,\n 'river.session.from': from,\n },\n },\n parentCtx,\n );\n\n const ctx = trace.setSpan(parentCtx, span);\n\n return { span, ctx };\n}\n\nexport function createConnectionTelemetryInfo(\n connection: Connection,\n info: TelemetryInfo,\n): TelemetryInfo {\n const span = tracer.startSpan(\n `connection ${connection.id}`,\n {\n attributes: {\n component: 'river',\n 'river.connection.id': connection.id,\n },\n links: [{ context: info.span.spanContext() }],\n },\n info.ctx,\n );\n\n const ctx = trace.setSpan(info.ctx, span);\n\n return { span, ctx };\n}\n\nexport function createProcTelemetryInfo(\n transport: ClientTransport<Connection>,\n kind: ValidProcType,\n serviceName: string,\n procedureName: string,\n streamId: string,\n): TelemetryInfo {\n const baseCtx = context.active();\n const span = tracer.startSpan(\n `procedure call ${serviceName}.${procedureName}`,\n {\n attributes: {\n component: 'river',\n 'river.method.kind': kind,\n 'river.method.service': serviceName,\n 'river.method.name': procedureName,\n 'river.streamId': streamId,\n 'span.kind': 'client',\n },\n kind: SpanKind.CLIENT,\n },\n baseCtx,\n );\n\n const ctx = trace.setSpan(baseCtx, span);\n\n transport.log?.info(`invoked ${serviceName}.${procedureName}`, {\n clientId: transport.clientId,\n transportMessage: {\n procedureName,\n serviceName,\n },\n telemetry: {\n traceId: span.spanContext().traceId,\n spanId: span.spanContext().spanId,\n },\n });\n return { span, ctx };\n}\n\nexport function createHandlerSpan<Fn extends (span: Span) => unknown>(\n kind: ValidProcType,\n serviceName: string,\n procedureName: string,\n streamId: string,\n tracing: PropagationContext | undefined,\n fn: Fn,\n): ReturnType<Fn> {\n const ctx = tracing\n ? propagation.extract(context.active(), tracing)\n : context.active();\n\n return tracer.startActiveSpan<Fn>(\n `procedure handler ${serviceName}.${procedureName}`,\n {\n attributes: {\n component: 'river',\n 'river.method.kind': kind,\n 'river.method.service': serviceName,\n 'river.method.name': procedureName,\n 'river.streamId': streamId,\n 'span.kind': 'server',\n },\n kind: SpanKind.SERVER,\n },\n ctx,\n fn,\n );\n}\n\nconst tracer = trace.getTracer('river', RIVER_VERSION);\nexport default tracer;\n"],"mappings":"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","names":[]}