@replit/river 0.9.3 → 0.10.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 (129) hide show
  1. package/dist/{router/builder.d.ts → builder-3c4485f0.d.ts} +82 -28
  2. package/dist/chunk-7WJ6YLE5.js +683 -0
  3. package/dist/chunk-AJQU4AZG.js +284 -0
  4. package/dist/chunk-ORAG7IAU.js +0 -0
  5. package/dist/chunk-PC65ZFWJ.js +29 -0
  6. package/dist/chunk-R6H2BIMC.js +49 -0
  7. package/dist/chunk-RGMHF6PF.js +65 -0
  8. package/dist/chunk-SLUSVGQH.js +30 -0
  9. package/dist/chunk-UU2Z7LDR.js +113 -0
  10. package/dist/chunk-WVT5QXMZ.js +20 -0
  11. package/dist/chunk-ZE4MX7DF.js +75 -0
  12. package/dist/codec/index.cjs +94 -0
  13. package/dist/codec/index.d.cts +15 -0
  14. package/dist/codec/index.d.ts +15 -4
  15. package/dist/codec/index.js +10 -2
  16. package/dist/connection-8e19874c.d.ts +11 -0
  17. package/dist/connection-f7688cc1.d.ts +11 -0
  18. package/dist/logging/index.cjs +56 -0
  19. package/dist/logging/index.d.cts +28 -0
  20. package/dist/logging/index.d.ts +6 -6
  21. package/dist/logging/index.js +9 -40
  22. package/dist/router/index.cjs +770 -0
  23. package/dist/router/index.d.cts +114 -0
  24. package/dist/router/index.d.ts +114 -10
  25. package/dist/router/index.js +24 -4
  26. package/dist/transport/impls/ws/client.cjs +505 -0
  27. package/dist/transport/impls/ws/client.d.cts +42 -0
  28. package/dist/transport/impls/ws/client.d.ts +9 -12
  29. package/dist/transport/impls/ws/client.js +10 -101
  30. package/dist/transport/impls/ws/server.cjs +457 -0
  31. package/dist/transport/impls/ws/server.d.cts +21 -0
  32. package/dist/transport/impls/ws/server.d.ts +14 -10
  33. package/dist/transport/impls/ws/server.js +11 -47
  34. package/dist/transport/index.cjs +362 -0
  35. package/dist/transport/{transport.d.ts → index.d.cts} +119 -7
  36. package/dist/transport/index.d.ts +273 -13
  37. package/dist/transport/index.js +20 -23
  38. package/dist/{codec/types.d.ts → types-3e5768ec.d.ts} +3 -2
  39. package/dist/util/testHelpers.cjs +731 -0
  40. package/dist/util/testHelpers.d.cts +79 -0
  41. package/dist/util/testHelpers.d.ts +34 -108
  42. package/dist/util/testHelpers.js +137 -320
  43. package/package.json +52 -23
  44. package/dist/__tests__/bandwidth.bench.d.ts +0 -2
  45. package/dist/__tests__/bandwidth.bench.d.ts.map +0 -1
  46. package/dist/__tests__/bandwidth.bench.js +0 -90
  47. package/dist/__tests__/e2e.test.d.ts +0 -2
  48. package/dist/__tests__/e2e.test.d.ts.map +0 -1
  49. package/dist/__tests__/e2e.test.js +0 -316
  50. package/dist/__tests__/fixtures/cleanup.d.ts +0 -12
  51. package/dist/__tests__/fixtures/cleanup.d.ts.map +0 -1
  52. package/dist/__tests__/fixtures/cleanup.js +0 -39
  53. package/dist/__tests__/fixtures/largePayload.json +0 -33
  54. package/dist/__tests__/fixtures/observable.d.ts +0 -26
  55. package/dist/__tests__/fixtures/observable.d.ts.map +0 -1
  56. package/dist/__tests__/fixtures/observable.js +0 -38
  57. package/dist/__tests__/fixtures/observable.test.d.ts +0 -2
  58. package/dist/__tests__/fixtures/observable.test.d.ts.map +0 -1
  59. package/dist/__tests__/fixtures/observable.test.js +0 -39
  60. package/dist/__tests__/fixtures/services.d.ts +0 -288
  61. package/dist/__tests__/fixtures/services.d.ts.map +0 -1
  62. package/dist/__tests__/fixtures/services.js +0 -224
  63. package/dist/__tests__/handler.test.d.ts +0 -2
  64. package/dist/__tests__/handler.test.d.ts.map +0 -1
  65. package/dist/__tests__/handler.test.js +0 -121
  66. package/dist/__tests__/invariants.test.d.ts +0 -2
  67. package/dist/__tests__/invariants.test.d.ts.map +0 -1
  68. package/dist/__tests__/invariants.test.js +0 -138
  69. package/dist/__tests__/serialize.test.d.ts +0 -2
  70. package/dist/__tests__/serialize.test.d.ts.map +0 -1
  71. package/dist/__tests__/serialize.test.js +0 -208
  72. package/dist/__tests__/typescript-stress.test.d.ts +0 -1583
  73. package/dist/__tests__/typescript-stress.test.d.ts.map +0 -1
  74. package/dist/__tests__/typescript-stress.test.js +0 -123
  75. package/dist/codec/binary.d.ts +0 -7
  76. package/dist/codec/binary.d.ts.map +0 -1
  77. package/dist/codec/binary.js +0 -20
  78. package/dist/codec/codec.test.d.ts +0 -5
  79. package/dist/codec/codec.test.d.ts.map +0 -1
  80. package/dist/codec/codec.test.js +0 -41
  81. package/dist/codec/index.d.ts.map +0 -1
  82. package/dist/codec/json.d.ts +0 -7
  83. package/dist/codec/json.d.ts.map +0 -1
  84. package/dist/codec/json.js +0 -51
  85. package/dist/codec/types.d.ts.map +0 -1
  86. package/dist/codec/types.js +0 -1
  87. package/dist/logging/index.d.ts.map +0 -1
  88. package/dist/router/builder.d.ts.map +0 -1
  89. package/dist/router/builder.js +0 -91
  90. package/dist/router/client.d.ts +0 -68
  91. package/dist/router/client.d.ts.map +0 -1
  92. package/dist/router/client.js +0 -159
  93. package/dist/router/context.d.ts +0 -30
  94. package/dist/router/context.d.ts.map +0 -1
  95. package/dist/router/context.js +0 -1
  96. package/dist/router/index.d.ts.map +0 -1
  97. package/dist/router/result.d.ts +0 -25
  98. package/dist/router/result.d.ts.map +0 -1
  99. package/dist/router/result.js +0 -18
  100. package/dist/router/server.d.ts +0 -39
  101. package/dist/router/server.d.ts.map +0 -1
  102. package/dist/router/server.js +0 -217
  103. package/dist/transport/events.d.ts +0 -19
  104. package/dist/transport/events.d.ts.map +0 -1
  105. package/dist/transport/events.js +0 -26
  106. package/dist/transport/impls/stdio/stdio.d.ts +0 -33
  107. package/dist/transport/impls/stdio/stdio.d.ts.map +0 -1
  108. package/dist/transport/impls/stdio/stdio.js +0 -75
  109. package/dist/transport/impls/stdio/stdio.test.d.ts +0 -2
  110. package/dist/transport/impls/stdio/stdio.test.d.ts.map +0 -1
  111. package/dist/transport/impls/stdio/stdio.test.js +0 -25
  112. package/dist/transport/impls/ws/client.d.ts.map +0 -1
  113. package/dist/transport/impls/ws/connection.d.ts +0 -11
  114. package/dist/transport/impls/ws/connection.d.ts.map +0 -1
  115. package/dist/transport/impls/ws/connection.js +0 -23
  116. package/dist/transport/impls/ws/server.d.ts.map +0 -1
  117. package/dist/transport/impls/ws/ws.test.d.ts +0 -2
  118. package/dist/transport/impls/ws/ws.test.d.ts.map +0 -1
  119. package/dist/transport/impls/ws/ws.test.js +0 -117
  120. package/dist/transport/index.d.ts.map +0 -1
  121. package/dist/transport/message.d.ts +0 -143
  122. package/dist/transport/message.d.ts.map +0 -1
  123. package/dist/transport/message.js +0 -113
  124. package/dist/transport/message.test.d.ts +0 -2
  125. package/dist/transport/message.test.d.ts.map +0 -1
  126. package/dist/transport/message.test.js +0 -52
  127. package/dist/transport/transport.d.ts.map +0 -1
  128. package/dist/transport/transport.js +0 -281
  129. package/dist/util/testHelpers.d.ts.map +0 -1
@@ -0,0 +1,770 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // router/index.ts
21
+ var router_exports = {};
22
+ __export(router_exports, {
23
+ Err: () => Err,
24
+ Ok: () => Ok,
25
+ RiverUncaughtSchema: () => RiverUncaughtSchema,
26
+ ServiceBuilder: () => ServiceBuilder,
27
+ UNCAUGHT_ERROR: () => UNCAUGHT_ERROR,
28
+ buildServiceDefs: () => buildServiceDefs,
29
+ createClient: () => createClient,
30
+ createServer: () => createServer,
31
+ serializeService: () => serializeService
32
+ });
33
+ module.exports = __toCommonJS(router_exports);
34
+
35
+ // router/builder.ts
36
+ var import_typebox = require("@sinclair/typebox");
37
+ function serializeService(s) {
38
+ return {
39
+ name: s.name,
40
+ state: s.state,
41
+ procedures: Object.fromEntries(
42
+ Object.entries(s.procedures).map(([procName, procDef]) => [
43
+ procName,
44
+ {
45
+ input: import_typebox.Type.Strict(procDef.input),
46
+ output: import_typebox.Type.Strict(procDef.output),
47
+ // Only add the `errors` field if it is non-never.
48
+ ..."errors" in procDef ? {
49
+ errors: import_typebox.Type.Strict(procDef.errors)
50
+ } : {},
51
+ type: procDef.type,
52
+ // Only add the `init` field if the type declares it.
53
+ ..."init" in procDef ? {
54
+ init: import_typebox.Type.Strict(procDef.init)
55
+ } : {}
56
+ }
57
+ ])
58
+ )
59
+ };
60
+ }
61
+ var ServiceBuilder = class _ServiceBuilder {
62
+ schema;
63
+ constructor(schema) {
64
+ this.schema = schema;
65
+ }
66
+ /**
67
+ * Finalizes the schema for the service.
68
+ * @returns {T} The finalized schema for the service.
69
+ */
70
+ finalize() {
71
+ return this.schema;
72
+ }
73
+ /**
74
+ * Sets the initial state for the service.
75
+ * @template InitState The type of the initial state.
76
+ * @param {InitState} state The initial state for the service.
77
+ * @returns {ServiceBuilder<{ name: T['name']; state: InitState; procedures: T['procedures']; }>} A new ServiceBuilder instance with the updated schema.
78
+ */
79
+ initialState(state) {
80
+ return new _ServiceBuilder({
81
+ ...this.schema,
82
+ state
83
+ });
84
+ }
85
+ /**
86
+ * Defines a new procedure for the service.
87
+ * @param {ProcName} procName The name of the procedure.
88
+ * @param {Procedure<T['state'], Ty, I, O, E, Init>} procDef The definition of the procedure.
89
+ * @returns {ServiceBuilder<{ name: T['name']; state: T['state']; procedures: T['procedures'] & { [k in ProcName]: Procedure<T['state'], Ty, I, O, E, Init>; }; }>} A new ServiceBuilder instance with the updated schema.
90
+ */
91
+ defineProcedure(procName, procDef) {
92
+ const newProcedure = { [procName]: procDef };
93
+ const procedures = {
94
+ ...this.schema.procedures,
95
+ ...newProcedure
96
+ };
97
+ return new _ServiceBuilder({
98
+ ...this.schema,
99
+ procedures
100
+ });
101
+ }
102
+ /**
103
+ * Creates a new instance of ServiceBuilder.
104
+ * @param {Name} name The name of the service.
105
+ * @returns {ServiceBuilder<{ name: Name; state: {}; procedures: {}; }>} A new instance of ServiceBuilder.
106
+ */
107
+ static create(name) {
108
+ return new _ServiceBuilder({
109
+ name,
110
+ state: {},
111
+ procedures: {}
112
+ });
113
+ }
114
+ };
115
+
116
+ // router/defs.ts
117
+ function buildServiceDefs(services) {
118
+ return services.reduce((acc, service) => {
119
+ acc[service.name] = service;
120
+ return acc;
121
+ }, {});
122
+ }
123
+
124
+ // router/client.ts
125
+ var import_it_pushable = require("it-pushable");
126
+
127
+ // transport/message.ts
128
+ var import_typebox2 = require("@sinclair/typebox");
129
+ var import_nanoid = require("nanoid");
130
+ var TransportMessageSchema = (t) => import_typebox2.Type.Object({
131
+ id: import_typebox2.Type.String(),
132
+ from: import_typebox2.Type.String(),
133
+ to: import_typebox2.Type.String(),
134
+ serviceName: import_typebox2.Type.Optional(import_typebox2.Type.Union([import_typebox2.Type.String(), import_typebox2.Type.Null()])),
135
+ procedureName: import_typebox2.Type.Optional(import_typebox2.Type.Union([import_typebox2.Type.String(), import_typebox2.Type.Null()])),
136
+ streamId: import_typebox2.Type.String(),
137
+ controlFlags: import_typebox2.Type.Integer(),
138
+ payload: t
139
+ });
140
+ var TransportAckSchema = TransportMessageSchema(
141
+ import_typebox2.Type.Object({
142
+ ack: import_typebox2.Type.String()
143
+ })
144
+ );
145
+ var ControlMessagePayloadSchema = import_typebox2.Type.Object({
146
+ type: import_typebox2.Type.Literal("CLOSE")
147
+ });
148
+ var OpaqueTransportMessageSchema = TransportMessageSchema(
149
+ import_typebox2.Type.Unknown()
150
+ );
151
+ function msg(from, to, streamId, payload, serviceName, procedureName) {
152
+ return {
153
+ id: (0, import_nanoid.nanoid)(),
154
+ to,
155
+ from,
156
+ serviceName,
157
+ procedureName,
158
+ streamId,
159
+ controlFlags: 0,
160
+ payload
161
+ };
162
+ }
163
+ function reply(msg2, response) {
164
+ return {
165
+ id: (0, import_nanoid.nanoid)(),
166
+ streamId: msg2.streamId,
167
+ controlFlags: 0,
168
+ to: msg2.from,
169
+ from: msg2.to,
170
+ payload: response
171
+ };
172
+ }
173
+ function closeStream(from, to, stream) {
174
+ const closeMessage = msg(from, to, stream, {
175
+ type: "CLOSE"
176
+ });
177
+ closeMessage.controlFlags |= 4 /* StreamClosedBit */;
178
+ return closeMessage;
179
+ }
180
+ function isStreamOpen(controlFlag) {
181
+ return (controlFlag & 2 /* StreamOpenBit */) === 2 /* StreamOpenBit */;
182
+ }
183
+ function isStreamClose(controlFlag) {
184
+ return (controlFlag & 4 /* StreamClosedBit */) === 4 /* StreamClosedBit */;
185
+ }
186
+
187
+ // router/client.ts
188
+ var import_nanoid2 = require("nanoid");
189
+
190
+ // router/result.ts
191
+ var import_typebox3 = require("@sinclair/typebox");
192
+ var UNCAUGHT_ERROR = "UNCAUGHT_ERROR";
193
+ var UNEXPECTED_DISCONNECT = "UNEXPECTED_DISCONNECT";
194
+ var RiverUncaughtSchema = import_typebox3.Type.Object({
195
+ code: import_typebox3.Type.Union([
196
+ import_typebox3.Type.Literal(UNCAUGHT_ERROR),
197
+ import_typebox3.Type.Literal(UNEXPECTED_DISCONNECT)
198
+ ]),
199
+ message: import_typebox3.Type.String()
200
+ });
201
+ function Ok(payload) {
202
+ return {
203
+ ok: true,
204
+ payload
205
+ };
206
+ }
207
+ function Err(error) {
208
+ return {
209
+ ok: false,
210
+ payload: error
211
+ };
212
+ }
213
+
214
+ // router/client.ts
215
+ var noop = () => {
216
+ };
217
+ function _createRecursiveProxy(callback, path) {
218
+ const proxy = new Proxy(noop, {
219
+ // property access, recurse and add field to path
220
+ get(_obj, key) {
221
+ if (typeof key !== "string")
222
+ return void 0;
223
+ return _createRecursiveProxy(callback, [...path, key]);
224
+ },
225
+ // hit the end, let's invoke the handler
226
+ apply(_target, _this, args) {
227
+ return callback({
228
+ path,
229
+ args
230
+ });
231
+ }
232
+ });
233
+ return proxy;
234
+ }
235
+ var createClient = (transport, serverId = "SERVER") => _createRecursiveProxy(async (opts) => {
236
+ const [serviceName, procName, procType] = [...opts.path];
237
+ if (!(serviceName && procName && procType)) {
238
+ throw new Error(
239
+ "invalid river call, ensure the service and procedure you are calling exists"
240
+ );
241
+ }
242
+ const [input] = opts.args;
243
+ if (procType === "rpc") {
244
+ return handleRpc(
245
+ transport,
246
+ serverId,
247
+ input,
248
+ serviceName,
249
+ procName
250
+ );
251
+ } else if (procType === "stream") {
252
+ return handleStream(
253
+ transport,
254
+ serverId,
255
+ input,
256
+ serviceName,
257
+ procName
258
+ );
259
+ } else if (procType === "subscribe") {
260
+ return handleSubscribe(
261
+ transport,
262
+ serverId,
263
+ input,
264
+ serviceName,
265
+ procName
266
+ );
267
+ } else if (procType === "upload") {
268
+ return handleUpload(
269
+ transport,
270
+ serverId,
271
+ input,
272
+ serviceName,
273
+ procName
274
+ );
275
+ } else {
276
+ throw new Error(`invalid river call, unknown procedure type ${procType}`);
277
+ }
278
+ }, []);
279
+ var CONNECTION_GRACE_PERIOD_MS = 5e3;
280
+ function rejectAfterDisconnectGrace(from, cb) {
281
+ let timeout = void 0;
282
+ return (evt) => {
283
+ if (evt.status === "connect" && evt.conn.connectedTo === from) {
284
+ clearTimeout(timeout);
285
+ timeout = void 0;
286
+ }
287
+ if (evt.status === "disconnect" && evt.conn.connectedTo === from) {
288
+ timeout = setTimeout(cb, CONNECTION_GRACE_PERIOD_MS);
289
+ }
290
+ };
291
+ }
292
+ function handleRpc(transport, serverId, input, serviceName, procName) {
293
+ const streamId = (0, import_nanoid2.nanoid)();
294
+ const m = msg(
295
+ transport.clientId,
296
+ serverId,
297
+ streamId,
298
+ input,
299
+ serviceName,
300
+ procName
301
+ );
302
+ m.controlFlags |= 2 /* StreamOpenBit */ | 4 /* StreamClosedBit */;
303
+ transport.send(m);
304
+ const responsePromise = new Promise((resolve) => {
305
+ const onConnectionStatus = rejectAfterDisconnectGrace(serverId, () => {
306
+ cleanup();
307
+ resolve(
308
+ Err({
309
+ code: UNEXPECTED_DISCONNECT,
310
+ message: `${serverId} unexpectedly disconnected`
311
+ })
312
+ );
313
+ });
314
+ function cleanup() {
315
+ transport.removeEventListener("message", onMessage);
316
+ transport.removeEventListener("connectionStatus", onConnectionStatus);
317
+ }
318
+ function onMessage(msg2) {
319
+ if (msg2.streamId === streamId) {
320
+ cleanup();
321
+ resolve(msg2.payload);
322
+ }
323
+ }
324
+ transport.addEventListener("message", onMessage);
325
+ transport.addEventListener("connectionStatus", onConnectionStatus);
326
+ });
327
+ return responsePromise;
328
+ }
329
+ function handleStream(transport, serverId, init, serviceName, procName) {
330
+ const streamId = (0, import_nanoid2.nanoid)();
331
+ const inputStream = (0, import_it_pushable.pushable)({ objectMode: true });
332
+ const outputStream = (0, import_it_pushable.pushable)({ objectMode: true });
333
+ let firstMessage = true;
334
+ if (init) {
335
+ const m = msg(
336
+ transport.clientId,
337
+ serverId,
338
+ streamId,
339
+ init,
340
+ serviceName,
341
+ procName
342
+ );
343
+ m.controlFlags = 2 /* StreamOpenBit */;
344
+ transport.send(m);
345
+ firstMessage = false;
346
+ }
347
+ (async () => {
348
+ for await (const rawIn of inputStream) {
349
+ const m = msg(transport.clientId, serverId, streamId, rawIn);
350
+ if (firstMessage) {
351
+ m.serviceName = serviceName;
352
+ m.procedureName = procName;
353
+ m.controlFlags |= 2 /* StreamOpenBit */;
354
+ firstMessage = false;
355
+ }
356
+ transport.send(m);
357
+ }
358
+ transport.send(closeStream(transport.clientId, serverId, streamId));
359
+ })();
360
+ function onMessage(msg2) {
361
+ if (msg2.streamId !== streamId) {
362
+ return;
363
+ }
364
+ if (isStreamClose(msg2.controlFlags)) {
365
+ cleanup();
366
+ } else {
367
+ outputStream.push(msg2.payload);
368
+ }
369
+ }
370
+ function cleanup() {
371
+ inputStream.end();
372
+ outputStream.end();
373
+ transport.removeEventListener("message", onMessage);
374
+ transport.removeEventListener("connectionStatus", onConnectionStatus);
375
+ }
376
+ const closeHandler = () => {
377
+ cleanup();
378
+ transport.send(closeStream(transport.clientId, serverId, streamId));
379
+ };
380
+ const onConnectionStatus = rejectAfterDisconnectGrace(serverId, () => {
381
+ outputStream.push(
382
+ Err({
383
+ code: UNEXPECTED_DISCONNECT,
384
+ message: `${serverId} unexpectedly disconnected`
385
+ })
386
+ );
387
+ cleanup();
388
+ });
389
+ transport.addEventListener("message", onMessage);
390
+ transport.addEventListener("connectionStatus", onConnectionStatus);
391
+ return [inputStream, outputStream, closeHandler];
392
+ }
393
+ function handleSubscribe(transport, serverId, input, serviceName, procName) {
394
+ const streamId = (0, import_nanoid2.nanoid)();
395
+ const m = msg(
396
+ transport.clientId,
397
+ serverId,
398
+ streamId,
399
+ input,
400
+ serviceName,
401
+ procName
402
+ );
403
+ m.controlFlags |= 2 /* StreamOpenBit */;
404
+ transport.send(m);
405
+ const outputStream = (0, import_it_pushable.pushable)({ objectMode: true });
406
+ function onMessage(msg2) {
407
+ if (msg2.streamId !== streamId) {
408
+ return;
409
+ }
410
+ if (isStreamClose(msg2.controlFlags)) {
411
+ cleanup();
412
+ } else {
413
+ outputStream.push(msg2.payload);
414
+ }
415
+ }
416
+ function cleanup() {
417
+ outputStream.end();
418
+ transport.removeEventListener("message", onMessage);
419
+ transport.removeEventListener("connectionStatus", onConnectionStatus);
420
+ }
421
+ const closeHandler = () => {
422
+ cleanup();
423
+ transport.send(closeStream(transport.clientId, serverId, streamId));
424
+ };
425
+ const onConnectionStatus = rejectAfterDisconnectGrace(serverId, () => {
426
+ outputStream.push(
427
+ Err({
428
+ code: UNEXPECTED_DISCONNECT,
429
+ message: `${serverId} unexpectedly disconnected`
430
+ })
431
+ );
432
+ cleanup();
433
+ });
434
+ transport.addEventListener("message", onMessage);
435
+ transport.addEventListener("connectionStatus", onConnectionStatus);
436
+ return [outputStream, closeHandler];
437
+ }
438
+ function handleUpload(transport, serverId, input, serviceName, procName) {
439
+ const streamId = (0, import_nanoid2.nanoid)();
440
+ const inputStream = (0, import_it_pushable.pushable)({ objectMode: true });
441
+ let firstMessage = true;
442
+ if (input) {
443
+ const m = msg(
444
+ transport.clientId,
445
+ serverId,
446
+ streamId,
447
+ input,
448
+ serviceName,
449
+ procName
450
+ );
451
+ m.controlFlags = 2 /* StreamOpenBit */;
452
+ transport.send(m);
453
+ firstMessage = false;
454
+ }
455
+ (async () => {
456
+ for await (const rawIn of inputStream) {
457
+ const m = msg(transport.clientId, serverId, streamId, rawIn);
458
+ if (firstMessage) {
459
+ m.controlFlags |= 2 /* StreamOpenBit */;
460
+ m.serviceName = serviceName;
461
+ m.procedureName = procName;
462
+ firstMessage = false;
463
+ }
464
+ transport.send(m);
465
+ }
466
+ transport.send(closeStream(transport.clientId, serverId, streamId));
467
+ })();
468
+ const responsePromise = new Promise((resolve) => {
469
+ const onConnectionStatus = rejectAfterDisconnectGrace(serverId, () => {
470
+ cleanup();
471
+ resolve(
472
+ Err({
473
+ code: UNEXPECTED_DISCONNECT,
474
+ message: `${serverId} unexpectedly disconnected`
475
+ })
476
+ );
477
+ });
478
+ function cleanup() {
479
+ inputStream.end();
480
+ transport.removeEventListener("message", onMessage);
481
+ transport.removeEventListener("connectionStatus", onConnectionStatus);
482
+ }
483
+ function onMessage(msg2) {
484
+ if (msg2.streamId === streamId) {
485
+ cleanup();
486
+ resolve(msg2.payload);
487
+ }
488
+ }
489
+ transport.addEventListener("message", onMessage);
490
+ transport.addEventListener("connectionStatus", onConnectionStatus);
491
+ });
492
+ return [inputStream, responsePromise];
493
+ }
494
+
495
+ // router/server.ts
496
+ var import_it_pushable2 = require("it-pushable");
497
+
498
+ // logging/index.ts
499
+ var log;
500
+
501
+ // router/server.ts
502
+ var import_value = require("@sinclair/typebox/value");
503
+ var RiverServer = class {
504
+ transport;
505
+ services;
506
+ contextMap;
507
+ // map of streamId to ProcStream
508
+ streamMap;
509
+ // map of client to their open streams by streamId
510
+ clientStreams;
511
+ constructor(transport, services, extendedContext) {
512
+ this.transport = transport;
513
+ this.services = services;
514
+ this.contextMap = /* @__PURE__ */ new Map();
515
+ for (const service of Object.values(services)) {
516
+ this.contextMap.set(service, {
517
+ ...extendedContext,
518
+ state: service.state
519
+ });
520
+ }
521
+ this.streamMap = /* @__PURE__ */ new Map();
522
+ this.clientStreams = /* @__PURE__ */ new Map();
523
+ this.transport.addEventListener("message", this.handler);
524
+ this.transport.addEventListener("connectionStatus", this.onDisconnect);
525
+ }
526
+ get streams() {
527
+ return this.streamMap;
528
+ }
529
+ handler = async (message) => {
530
+ if (message.to !== this.transport.clientId) {
531
+ log?.info(
532
+ `${this.transport.clientId} -- got msg with destination that isn't the server, ignoring`
533
+ );
534
+ return;
535
+ }
536
+ let procStream = this.streamMap.get(message.streamId);
537
+ const isInitMessage = !procStream;
538
+ procStream ||= this.createNewProcStream(message);
539
+ if (!procStream) {
540
+ return;
541
+ }
542
+ await this.pushToStream(procStream, message, isInitMessage);
543
+ };
544
+ // cleanup streams on unexpected disconnections
545
+ onDisconnect = async (evt) => {
546
+ if (evt.status !== "disconnect") {
547
+ return;
548
+ }
549
+ const disconnectedClientId = evt.conn.connectedTo;
550
+ log?.info(
551
+ `${this.transport.clientId} -- got unexpected disconnect from ${disconnectedClientId}, cleaning up streams`
552
+ );
553
+ const streamsFromThisClient = this.clientStreams.get(disconnectedClientId);
554
+ if (!streamsFromThisClient) {
555
+ return;
556
+ }
557
+ await Promise.all(
558
+ Array.from(streamsFromThisClient).map(this.cleanupStream)
559
+ );
560
+ this.clientStreams.delete(disconnectedClientId);
561
+ };
562
+ async close() {
563
+ this.transport.removeEventListener("message", this.handler);
564
+ this.transport.removeEventListener("connectionStatus", this.onDisconnect);
565
+ await Promise.all([...this.streamMap.keys()].map(this.cleanupStream));
566
+ }
567
+ createNewProcStream(message) {
568
+ if (!isStreamOpen(message.controlFlags)) {
569
+ log?.warn(
570
+ `${this.transport.clientId} -- couldn't find a matching procedure stream for ${message.serviceName}.${message.procedureName}:${message.streamId}`
571
+ );
572
+ return;
573
+ }
574
+ if (!message.serviceName || !(message.serviceName in this.services)) {
575
+ log?.warn(
576
+ `${this.transport.clientId} -- couldn't find service ${message.serviceName}`
577
+ );
578
+ return;
579
+ }
580
+ const service = this.services[message.serviceName];
581
+ const serviceContext = this.getContext(service);
582
+ if (!message.procedureName || !(message.procedureName in service.procedures)) {
583
+ log?.warn(
584
+ `${this.transport.clientId} -- couldn't find a matching procedure for ${message.serviceName}.${message.procedureName}`
585
+ );
586
+ return;
587
+ }
588
+ const procedure = service.procedures[message.procedureName];
589
+ const incoming = (0, import_it_pushable2.pushable)({ objectMode: true });
590
+ const outgoing = (0, import_it_pushable2.pushable)({ objectMode: true });
591
+ const outputHandler = (
592
+ // sending outgoing messages back to client
593
+ (async () => {
594
+ for await (const response of outgoing) {
595
+ this.transport.send(reply(message, response));
596
+ }
597
+ if (procedure.type === "subscription" || procedure.type === "stream") {
598
+ this.transport.send(
599
+ closeStream(
600
+ this.transport.clientId,
601
+ message.from,
602
+ message.streamId
603
+ )
604
+ );
605
+ }
606
+ })()
607
+ );
608
+ const errorHandler = (err) => {
609
+ const errorMsg = err instanceof Error ? err.message : `[coerced to error] ${err}`;
610
+ log?.error(
611
+ `${this.transport.clientId} -- procedure ${message.serviceName}.${message.procedureName}:${message.streamId} threw an error: ${errorMsg}`
612
+ );
613
+ outgoing.push(
614
+ Err({
615
+ code: UNCAUGHT_ERROR,
616
+ message: errorMsg
617
+ })
618
+ );
619
+ };
620
+ let inputHandler;
621
+ const procHasInitMessage = "init" in procedure;
622
+ if (procedure.type === "stream") {
623
+ if (procHasInitMessage) {
624
+ inputHandler = (async () => {
625
+ const initMessage = await incoming.next();
626
+ if (initMessage.done) {
627
+ return;
628
+ }
629
+ return procedure.handler(serviceContext, initMessage.value, incoming, outgoing).catch(errorHandler);
630
+ })();
631
+ } else {
632
+ inputHandler = procedure.handler(serviceContext, incoming, outgoing).catch(errorHandler);
633
+ }
634
+ } else if (procedure.type === "rpc") {
635
+ inputHandler = (async () => {
636
+ const inputMessage = await incoming.next();
637
+ if (inputMessage.done) {
638
+ return;
639
+ }
640
+ try {
641
+ const outputMessage = await procedure.handler(
642
+ serviceContext,
643
+ inputMessage.value
644
+ );
645
+ outgoing.push(outputMessage);
646
+ } catch (err) {
647
+ errorHandler(err);
648
+ }
649
+ })();
650
+ } else if (procedure.type === "subscription") {
651
+ inputHandler = (async () => {
652
+ const inputMessage = await incoming.next();
653
+ if (inputMessage.done) {
654
+ return;
655
+ }
656
+ try {
657
+ await procedure.handler(serviceContext, inputMessage.value, outgoing);
658
+ } catch (err) {
659
+ errorHandler(err);
660
+ }
661
+ })();
662
+ } else if (procedure.type === "upload") {
663
+ if (procHasInitMessage) {
664
+ inputHandler = (async () => {
665
+ const initMessage = await incoming.next();
666
+ if (initMessage.done) {
667
+ return;
668
+ }
669
+ try {
670
+ const outputMessage = await procedure.handler(
671
+ serviceContext,
672
+ initMessage.value,
673
+ incoming
674
+ );
675
+ outgoing.push(outputMessage);
676
+ } catch (err) {
677
+ errorHandler(err);
678
+ }
679
+ })();
680
+ } else {
681
+ inputHandler = (async () => {
682
+ try {
683
+ const outputMessage = await procedure.handler(
684
+ serviceContext,
685
+ incoming
686
+ );
687
+ outgoing.push(outputMessage);
688
+ } catch (err) {
689
+ errorHandler(err);
690
+ }
691
+ })();
692
+ }
693
+ } else {
694
+ log?.warn(
695
+ `${this.transport.clientId} -- got request for invalid procedure type ${procedure.type} at ${message.serviceName}.${message.procedureName}`
696
+ );
697
+ return;
698
+ }
699
+ const procStream = {
700
+ id: message.streamId,
701
+ incoming,
702
+ outgoing,
703
+ serviceName: message.serviceName,
704
+ procedureName: message.procedureName,
705
+ procedure,
706
+ promises: { inputHandler, outputHandler }
707
+ };
708
+ this.streamMap.set(message.streamId, procStream);
709
+ const streamsFromThisClient = this.clientStreams.get(message.from) ?? /* @__PURE__ */ new Set();
710
+ streamsFromThisClient.add(message.streamId);
711
+ this.clientStreams.set(message.from, streamsFromThisClient);
712
+ return procStream;
713
+ }
714
+ async pushToStream(procStream, message, isInit) {
715
+ const procedure = procStream.procedure;
716
+ const procHasInitMessage = "init" in procedure;
717
+ if (isInit && procHasInitMessage && import_value.Value.Check(procedure.init, message.payload) || import_value.Value.Check(procedure.input, message.payload)) {
718
+ procStream.incoming.push(message.payload);
719
+ } else if (!import_value.Value.Check(ControlMessagePayloadSchema, message.payload)) {
720
+ log?.error(
721
+ `${this.transport.clientId} -- procedure ${procStream.serviceName}.${procStream.procedureName} received invalid payload: ${JSON.stringify(message.payload)}`
722
+ );
723
+ }
724
+ if (isStreamClose(message.controlFlags)) {
725
+ await this.cleanupStream(message.streamId);
726
+ const streamsFromThisClient = this.clientStreams.get(message.from);
727
+ if (streamsFromThisClient) {
728
+ streamsFromThisClient.delete(message.streamId);
729
+ if (streamsFromThisClient.size === 0) {
730
+ this.clientStreams.delete(message.from);
731
+ }
732
+ }
733
+ }
734
+ }
735
+ getContext(service) {
736
+ const context = this.contextMap.get(service);
737
+ if (!context) {
738
+ const err = `${this.transport.clientId} -- no context found for ${service.name}`;
739
+ log?.error(err);
740
+ throw new Error(err);
741
+ }
742
+ return context;
743
+ }
744
+ cleanupStream = async (id) => {
745
+ const stream = this.streamMap.get(id);
746
+ if (!stream) {
747
+ return;
748
+ }
749
+ stream.incoming.end();
750
+ await stream.promises.inputHandler;
751
+ stream.outgoing.end();
752
+ await stream.promises.outputHandler;
753
+ this.streamMap.delete(id);
754
+ };
755
+ };
756
+ function createServer(transport, services, extendedContext) {
757
+ return new RiverServer(transport, services, extendedContext);
758
+ }
759
+ // Annotate the CommonJS export names for ESM import in node:
760
+ 0 && (module.exports = {
761
+ Err,
762
+ Ok,
763
+ RiverUncaughtSchema,
764
+ ServiceBuilder,
765
+ UNCAUGHT_ERROR,
766
+ buildServiceDefs,
767
+ createClient,
768
+ createServer,
769
+ serializeService
770
+ });