@replit/river 0.10.0 → 0.10.2

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 (135) hide show
  1. package/dist/{router/builder.d.ts → builder-3c4485f0.d.ts} +76 -21
  2. package/dist/chunk-AJQU4AZG.js +284 -0
  3. package/dist/chunk-IYRPZPSQ.js +964 -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 +1046 -0
  23. package/dist/router/index.d.cts +114 -0
  24. package/dist/router/index.d.ts +114 -12
  25. package/dist/router/index.js +24 -5
  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 +8 -8
  29. package/dist/transport/impls/ws/client.js +10 -100
  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 +11 -10
  33. package/dist/transport/impls/ws/server.js +11 -52
  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 -4
  37. package/dist/transport/index.js +20 -2
  38. package/dist/{codec/types.d.ts → types-3e5768ec.d.ts} +3 -2
  39. package/dist/util/testHelpers.cjs +1010 -0
  40. package/dist/util/testHelpers.d.cts +79 -0
  41. package/dist/util/testHelpers.d.ts +22 -19
  42. package/dist/util/testHelpers.js +135 -163
  43. package/package.json +42 -14
  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__/cleanup.test.d.ts +0 -2
  48. package/dist/__tests__/cleanup.test.d.ts.map +0 -1
  49. package/dist/__tests__/cleanup.test.js +0 -165
  50. package/dist/__tests__/disconnects.test.d.ts +0 -2
  51. package/dist/__tests__/disconnects.test.d.ts.map +0 -1
  52. package/dist/__tests__/disconnects.test.js +0 -163
  53. package/dist/__tests__/e2e.test.d.ts +0 -2
  54. package/dist/__tests__/e2e.test.d.ts.map +0 -1
  55. package/dist/__tests__/e2e.test.js +0 -317
  56. package/dist/__tests__/fixtures/cleanup.d.ts +0 -12
  57. package/dist/__tests__/fixtures/cleanup.d.ts.map +0 -1
  58. package/dist/__tests__/fixtures/cleanup.js +0 -36
  59. package/dist/__tests__/fixtures/largePayload.json +0 -33
  60. package/dist/__tests__/fixtures/observable.d.ts +0 -26
  61. package/dist/__tests__/fixtures/observable.d.ts.map +0 -1
  62. package/dist/__tests__/fixtures/observable.js +0 -38
  63. package/dist/__tests__/fixtures/observable.test.d.ts +0 -2
  64. package/dist/__tests__/fixtures/observable.test.d.ts.map +0 -1
  65. package/dist/__tests__/fixtures/observable.test.js +0 -39
  66. package/dist/__tests__/fixtures/services.d.ts +0 -288
  67. package/dist/__tests__/fixtures/services.d.ts.map +0 -1
  68. package/dist/__tests__/fixtures/services.js +0 -207
  69. package/dist/__tests__/handler.test.d.ts +0 -2
  70. package/dist/__tests__/handler.test.d.ts.map +0 -1
  71. package/dist/__tests__/handler.test.js +0 -120
  72. package/dist/__tests__/serialize.test.d.ts +0 -2
  73. package/dist/__tests__/serialize.test.d.ts.map +0 -1
  74. package/dist/__tests__/serialize.test.js +0 -208
  75. package/dist/__tests__/typescript-stress.test.d.ts +0 -1583
  76. package/dist/__tests__/typescript-stress.test.d.ts.map +0 -1
  77. package/dist/__tests__/typescript-stress.test.js +0 -123
  78. package/dist/codec/binary.d.ts +0 -7
  79. package/dist/codec/binary.d.ts.map +0 -1
  80. package/dist/codec/binary.js +0 -20
  81. package/dist/codec/codec.test.d.ts +0 -5
  82. package/dist/codec/codec.test.d.ts.map +0 -1
  83. package/dist/codec/codec.test.js +0 -41
  84. package/dist/codec/index.d.ts.map +0 -1
  85. package/dist/codec/json.d.ts +0 -7
  86. package/dist/codec/json.d.ts.map +0 -1
  87. package/dist/codec/json.js +0 -51
  88. package/dist/codec/types.d.ts.map +0 -1
  89. package/dist/codec/types.js +0 -1
  90. package/dist/logging/index.d.ts.map +0 -1
  91. package/dist/router/builder.d.ts.map +0 -1
  92. package/dist/router/builder.js +0 -91
  93. package/dist/router/client.d.ts +0 -72
  94. package/dist/router/client.d.ts.map +0 -1
  95. package/dist/router/client.js +0 -257
  96. package/dist/router/context.d.ts +0 -30
  97. package/dist/router/context.d.ts.map +0 -1
  98. package/dist/router/context.js +0 -1
  99. package/dist/router/defs.d.ts +0 -16
  100. package/dist/router/defs.d.ts.map +0 -1
  101. package/dist/router/defs.js +0 -11
  102. package/dist/router/index.d.ts.map +0 -1
  103. package/dist/router/result.d.ts +0 -26
  104. package/dist/router/result.d.ts.map +0 -1
  105. package/dist/router/result.js +0 -22
  106. package/dist/router/server.d.ts +0 -39
  107. package/dist/router/server.d.ts.map +0 -1
  108. package/dist/router/server.js +0 -260
  109. package/dist/transport/events.d.ts +0 -19
  110. package/dist/transport/events.d.ts.map +0 -1
  111. package/dist/transport/events.js +0 -26
  112. package/dist/transport/impls/stdio/stdio.d.ts +0 -33
  113. package/dist/transport/impls/stdio/stdio.d.ts.map +0 -1
  114. package/dist/transport/impls/stdio/stdio.js +0 -75
  115. package/dist/transport/impls/stdio/stdio.test.d.ts +0 -2
  116. package/dist/transport/impls/stdio/stdio.test.d.ts.map +0 -1
  117. package/dist/transport/impls/stdio/stdio.test.js +0 -24
  118. package/dist/transport/impls/ws/client.d.ts.map +0 -1
  119. package/dist/transport/impls/ws/connection.d.ts +0 -11
  120. package/dist/transport/impls/ws/connection.d.ts.map +0 -1
  121. package/dist/transport/impls/ws/connection.js +0 -23
  122. package/dist/transport/impls/ws/server.d.ts.map +0 -1
  123. package/dist/transport/impls/ws/ws.test.d.ts +0 -2
  124. package/dist/transport/impls/ws/ws.test.d.ts.map +0 -1
  125. package/dist/transport/impls/ws/ws.test.js +0 -185
  126. package/dist/transport/index.d.ts.map +0 -1
  127. package/dist/transport/message.d.ts +0 -142
  128. package/dist/transport/message.d.ts.map +0 -1
  129. package/dist/transport/message.js +0 -113
  130. package/dist/transport/message.test.d.ts +0 -2
  131. package/dist/transport/message.test.d.ts.map +0 -1
  132. package/dist/transport/message.test.js +0 -52
  133. package/dist/transport/transport.d.ts.map +0 -1
  134. package/dist/transport/transport.js +0 -281
  135. package/dist/util/testHelpers.d.ts.map +0 -1
@@ -0,0 +1,964 @@
1
+ import {
2
+ ControlMessagePayloadSchema,
3
+ closeStream,
4
+ isStreamClose,
5
+ isStreamOpen,
6
+ msg,
7
+ reply
8
+ } from "./chunk-ZE4MX7DF.js";
9
+ import {
10
+ log
11
+ } from "./chunk-SLUSVGQH.js";
12
+
13
+ // router/builder.ts
14
+ import { Type } from "@sinclair/typebox";
15
+ function serializeService(s) {
16
+ return {
17
+ name: s.name,
18
+ state: s.state,
19
+ procedures: Object.fromEntries(
20
+ Object.entries(s.procedures).map(([procName, procDef]) => [
21
+ procName,
22
+ {
23
+ input: Type.Strict(procDef.input),
24
+ output: Type.Strict(procDef.output),
25
+ // Only add the `errors` field if it is non-never.
26
+ ..."errors" in procDef ? {
27
+ errors: Type.Strict(procDef.errors)
28
+ } : {},
29
+ type: procDef.type,
30
+ // Only add the `init` field if the type declares it.
31
+ ..."init" in procDef ? {
32
+ init: Type.Strict(procDef.init)
33
+ } : {}
34
+ }
35
+ ])
36
+ )
37
+ };
38
+ }
39
+ var ServiceBuilder = class _ServiceBuilder {
40
+ schema;
41
+ constructor(schema) {
42
+ this.schema = schema;
43
+ }
44
+ /**
45
+ * Finalizes the schema for the service.
46
+ * @returns {T} The finalized schema for the service.
47
+ */
48
+ finalize() {
49
+ return this.schema;
50
+ }
51
+ /**
52
+ * Sets the initial state for the service.
53
+ * @template InitState The type of the initial state.
54
+ * @param {InitState} state The initial state for the service.
55
+ * @returns {ServiceBuilder<{ name: T['name']; state: InitState; procedures: T['procedures']; }>} A new ServiceBuilder instance with the updated schema.
56
+ */
57
+ initialState(state) {
58
+ return new _ServiceBuilder({
59
+ ...this.schema,
60
+ state
61
+ });
62
+ }
63
+ /**
64
+ * Defines a new procedure for the service.
65
+ * @param {ProcName} procName The name of the procedure.
66
+ * @param {Procedure<T['state'], Ty, I, O, E, Init>} procDef The definition of the procedure.
67
+ * @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.
68
+ */
69
+ defineProcedure(procName, procDef) {
70
+ const newProcedure = { [procName]: procDef };
71
+ const procedures = {
72
+ ...this.schema.procedures,
73
+ ...newProcedure
74
+ };
75
+ return new _ServiceBuilder({
76
+ ...this.schema,
77
+ procedures
78
+ });
79
+ }
80
+ /**
81
+ * Creates a new instance of ServiceBuilder.
82
+ * @param {Name} name The name of the service.
83
+ * @returns {ServiceBuilder<{ name: Name; state: {}; procedures: {}; }>} A new instance of ServiceBuilder.
84
+ */
85
+ static create(name) {
86
+ return new _ServiceBuilder({
87
+ name,
88
+ state: {},
89
+ procedures: {}
90
+ });
91
+ }
92
+ };
93
+
94
+ // router/defs.ts
95
+ function buildServiceDefs(services) {
96
+ return services.reduce((acc, service) => {
97
+ acc[service.name] = service;
98
+ return acc;
99
+ }, {});
100
+ }
101
+
102
+ // node_modules/p-defer/index.js
103
+ function pDefer() {
104
+ const deferred = {};
105
+ deferred.promise = new Promise((resolve, reject) => {
106
+ deferred.resolve = resolve;
107
+ deferred.reject = reject;
108
+ });
109
+ return deferred;
110
+ }
111
+
112
+ // node_modules/it-pushable/dist/src/fifo.js
113
+ var FixedFIFO = class {
114
+ buffer;
115
+ mask;
116
+ top;
117
+ btm;
118
+ next;
119
+ constructor(hwm) {
120
+ if (!(hwm > 0) || (hwm - 1 & hwm) !== 0) {
121
+ throw new Error("Max size for a FixedFIFO should be a power of two");
122
+ }
123
+ this.buffer = new Array(hwm);
124
+ this.mask = hwm - 1;
125
+ this.top = 0;
126
+ this.btm = 0;
127
+ this.next = null;
128
+ }
129
+ push(data) {
130
+ if (this.buffer[this.top] !== void 0) {
131
+ return false;
132
+ }
133
+ this.buffer[this.top] = data;
134
+ this.top = this.top + 1 & this.mask;
135
+ return true;
136
+ }
137
+ shift() {
138
+ const last = this.buffer[this.btm];
139
+ if (last === void 0) {
140
+ return void 0;
141
+ }
142
+ this.buffer[this.btm] = void 0;
143
+ this.btm = this.btm + 1 & this.mask;
144
+ return last;
145
+ }
146
+ isEmpty() {
147
+ return this.buffer[this.btm] === void 0;
148
+ }
149
+ };
150
+ var FIFO = class {
151
+ size;
152
+ hwm;
153
+ head;
154
+ tail;
155
+ constructor(options = {}) {
156
+ this.hwm = options.splitLimit ?? 16;
157
+ this.head = new FixedFIFO(this.hwm);
158
+ this.tail = this.head;
159
+ this.size = 0;
160
+ }
161
+ calculateSize(obj) {
162
+ if (obj?.byteLength != null) {
163
+ return obj.byteLength;
164
+ }
165
+ return 1;
166
+ }
167
+ push(val) {
168
+ if (val?.value != null) {
169
+ this.size += this.calculateSize(val.value);
170
+ }
171
+ if (!this.head.push(val)) {
172
+ const prev = this.head;
173
+ this.head = prev.next = new FixedFIFO(2 * this.head.buffer.length);
174
+ this.head.push(val);
175
+ }
176
+ }
177
+ shift() {
178
+ let val = this.tail.shift();
179
+ if (val === void 0 && this.tail.next != null) {
180
+ const next = this.tail.next;
181
+ this.tail.next = null;
182
+ this.tail = next;
183
+ val = this.tail.shift();
184
+ }
185
+ if (val?.value != null) {
186
+ this.size -= this.calculateSize(val.value);
187
+ }
188
+ return val;
189
+ }
190
+ isEmpty() {
191
+ return this.head.isEmpty();
192
+ }
193
+ };
194
+
195
+ // node_modules/it-pushable/dist/src/index.js
196
+ var AbortError = class extends Error {
197
+ type;
198
+ code;
199
+ constructor(message, code) {
200
+ super(message ?? "The operation was aborted");
201
+ this.type = "aborted";
202
+ this.code = code ?? "ABORT_ERR";
203
+ }
204
+ };
205
+ function pushable(options = {}) {
206
+ const getNext = (buffer) => {
207
+ const next = buffer.shift();
208
+ if (next == null) {
209
+ return { done: true };
210
+ }
211
+ if (next.error != null) {
212
+ throw next.error;
213
+ }
214
+ return {
215
+ done: next.done === true,
216
+ // @ts-expect-error if done is false, value will be present
217
+ value: next.value
218
+ };
219
+ };
220
+ return _pushable(getNext, options);
221
+ }
222
+ function _pushable(getNext, options) {
223
+ options = options ?? {};
224
+ let onEnd = options.onEnd;
225
+ let buffer = new FIFO();
226
+ let pushable2;
227
+ let onNext;
228
+ let ended;
229
+ let drain = pDefer();
230
+ const waitNext = async () => {
231
+ try {
232
+ if (!buffer.isEmpty()) {
233
+ return getNext(buffer);
234
+ }
235
+ if (ended) {
236
+ return { done: true };
237
+ }
238
+ return await new Promise((resolve, reject) => {
239
+ onNext = (next) => {
240
+ onNext = null;
241
+ buffer.push(next);
242
+ try {
243
+ resolve(getNext(buffer));
244
+ } catch (err) {
245
+ reject(err);
246
+ }
247
+ return pushable2;
248
+ };
249
+ });
250
+ } finally {
251
+ if (buffer.isEmpty()) {
252
+ queueMicrotask(() => {
253
+ drain.resolve();
254
+ drain = pDefer();
255
+ });
256
+ }
257
+ }
258
+ };
259
+ const bufferNext = (next) => {
260
+ if (onNext != null) {
261
+ return onNext(next);
262
+ }
263
+ buffer.push(next);
264
+ return pushable2;
265
+ };
266
+ const bufferError = (err) => {
267
+ buffer = new FIFO();
268
+ if (onNext != null) {
269
+ return onNext({ error: err });
270
+ }
271
+ buffer.push({ error: err });
272
+ return pushable2;
273
+ };
274
+ const push = (value) => {
275
+ if (ended) {
276
+ return pushable2;
277
+ }
278
+ if (options?.objectMode !== true && value?.byteLength == null) {
279
+ throw new Error("objectMode was not true but tried to push non-Uint8Array value");
280
+ }
281
+ return bufferNext({ done: false, value });
282
+ };
283
+ const end = (err) => {
284
+ if (ended)
285
+ return pushable2;
286
+ ended = true;
287
+ return err != null ? bufferError(err) : bufferNext({ done: true });
288
+ };
289
+ const _return = () => {
290
+ buffer = new FIFO();
291
+ end();
292
+ return { done: true };
293
+ };
294
+ const _throw = (err) => {
295
+ end(err);
296
+ return { done: true };
297
+ };
298
+ pushable2 = {
299
+ [Symbol.asyncIterator]() {
300
+ return this;
301
+ },
302
+ next: waitNext,
303
+ return: _return,
304
+ throw: _throw,
305
+ push,
306
+ end,
307
+ get readableLength() {
308
+ return buffer.size;
309
+ },
310
+ onEmpty: async (options2) => {
311
+ const signal = options2?.signal;
312
+ signal?.throwIfAborted();
313
+ if (buffer.isEmpty()) {
314
+ return;
315
+ }
316
+ let cancel;
317
+ let listener;
318
+ if (signal != null) {
319
+ cancel = new Promise((resolve, reject) => {
320
+ listener = () => {
321
+ reject(new AbortError());
322
+ };
323
+ signal.addEventListener("abort", listener);
324
+ });
325
+ }
326
+ try {
327
+ await Promise.race([
328
+ drain.promise,
329
+ cancel
330
+ ]);
331
+ } finally {
332
+ if (listener != null && signal != null) {
333
+ signal?.removeEventListener("abort", listener);
334
+ }
335
+ }
336
+ }
337
+ };
338
+ if (onEnd == null) {
339
+ return pushable2;
340
+ }
341
+ const _pushable2 = pushable2;
342
+ pushable2 = {
343
+ [Symbol.asyncIterator]() {
344
+ return this;
345
+ },
346
+ next() {
347
+ return _pushable2.next();
348
+ },
349
+ throw(err) {
350
+ _pushable2.throw(err);
351
+ if (onEnd != null) {
352
+ onEnd(err);
353
+ onEnd = void 0;
354
+ }
355
+ return { done: true };
356
+ },
357
+ return() {
358
+ _pushable2.return();
359
+ if (onEnd != null) {
360
+ onEnd();
361
+ onEnd = void 0;
362
+ }
363
+ return { done: true };
364
+ },
365
+ push,
366
+ end(err) {
367
+ _pushable2.end(err);
368
+ if (onEnd != null) {
369
+ onEnd(err);
370
+ onEnd = void 0;
371
+ }
372
+ return pushable2;
373
+ },
374
+ get readableLength() {
375
+ return _pushable2.readableLength;
376
+ },
377
+ onEmpty: (opts) => {
378
+ return _pushable2.onEmpty(opts);
379
+ }
380
+ };
381
+ return pushable2;
382
+ }
383
+
384
+ // router/client.ts
385
+ import { nanoid } from "nanoid";
386
+
387
+ // router/result.ts
388
+ import {
389
+ Type as Type2
390
+ } from "@sinclair/typebox";
391
+ var UNCAUGHT_ERROR = "UNCAUGHT_ERROR";
392
+ var UNEXPECTED_DISCONNECT = "UNEXPECTED_DISCONNECT";
393
+ var RiverUncaughtSchema = Type2.Object({
394
+ code: Type2.Union([
395
+ Type2.Literal(UNCAUGHT_ERROR),
396
+ Type2.Literal(UNEXPECTED_DISCONNECT)
397
+ ]),
398
+ message: Type2.String()
399
+ });
400
+ function Ok(payload) {
401
+ return {
402
+ ok: true,
403
+ payload
404
+ };
405
+ }
406
+ function Err(error) {
407
+ return {
408
+ ok: false,
409
+ payload: error
410
+ };
411
+ }
412
+
413
+ // router/client.ts
414
+ var noop = () => {
415
+ };
416
+ function _createRecursiveProxy(callback, path) {
417
+ const proxy = new Proxy(noop, {
418
+ // property access, recurse and add field to path
419
+ get(_obj, key) {
420
+ if (typeof key !== "string")
421
+ return void 0;
422
+ return _createRecursiveProxy(callback, [...path, key]);
423
+ },
424
+ // hit the end, let's invoke the handler
425
+ apply(_target, _this, args) {
426
+ return callback({
427
+ path,
428
+ args
429
+ });
430
+ }
431
+ });
432
+ return proxy;
433
+ }
434
+ var createClient = (transport, serverId = "SERVER") => _createRecursiveProxy(async (opts) => {
435
+ const [serviceName, procName, procType] = [...opts.path];
436
+ if (!(serviceName && procName && procType)) {
437
+ throw new Error(
438
+ "invalid river call, ensure the service and procedure you are calling exists"
439
+ );
440
+ }
441
+ const [input] = opts.args;
442
+ if (procType === "rpc") {
443
+ return handleRpc(
444
+ transport,
445
+ serverId,
446
+ input,
447
+ serviceName,
448
+ procName
449
+ );
450
+ } else if (procType === "stream") {
451
+ return handleStream(
452
+ transport,
453
+ serverId,
454
+ input,
455
+ serviceName,
456
+ procName
457
+ );
458
+ } else if (procType === "subscribe") {
459
+ return handleSubscribe(
460
+ transport,
461
+ serverId,
462
+ input,
463
+ serviceName,
464
+ procName
465
+ );
466
+ } else if (procType === "upload") {
467
+ return handleUpload(
468
+ transport,
469
+ serverId,
470
+ input,
471
+ serviceName,
472
+ procName
473
+ );
474
+ } else {
475
+ throw new Error(`invalid river call, unknown procedure type ${procType}`);
476
+ }
477
+ }, []);
478
+ var CONNECTION_GRACE_PERIOD_MS = 5e3;
479
+ function rejectAfterDisconnectGrace(from, cb) {
480
+ let timeout = void 0;
481
+ return (evt) => {
482
+ if (evt.status === "connect" && evt.conn.connectedTo === from) {
483
+ clearTimeout(timeout);
484
+ timeout = void 0;
485
+ }
486
+ if (evt.status === "disconnect" && evt.conn.connectedTo === from) {
487
+ timeout = setTimeout(cb, CONNECTION_GRACE_PERIOD_MS);
488
+ }
489
+ };
490
+ }
491
+ function handleRpc(transport, serverId, input, serviceName, procName) {
492
+ const streamId = nanoid();
493
+ const m = msg(
494
+ transport.clientId,
495
+ serverId,
496
+ streamId,
497
+ input,
498
+ serviceName,
499
+ procName
500
+ );
501
+ m.controlFlags |= 2 /* StreamOpenBit */ | 4 /* StreamClosedBit */;
502
+ transport.send(m);
503
+ const responsePromise = new Promise((resolve) => {
504
+ const onConnectionStatus = rejectAfterDisconnectGrace(serverId, () => {
505
+ cleanup();
506
+ resolve(
507
+ Err({
508
+ code: UNEXPECTED_DISCONNECT,
509
+ message: `${serverId} unexpectedly disconnected`
510
+ })
511
+ );
512
+ });
513
+ function cleanup() {
514
+ transport.removeEventListener("message", onMessage);
515
+ transport.removeEventListener("connectionStatus", onConnectionStatus);
516
+ }
517
+ function onMessage(msg2) {
518
+ if (msg2.streamId === streamId) {
519
+ cleanup();
520
+ resolve(msg2.payload);
521
+ }
522
+ }
523
+ transport.addEventListener("message", onMessage);
524
+ transport.addEventListener("connectionStatus", onConnectionStatus);
525
+ });
526
+ return responsePromise;
527
+ }
528
+ function handleStream(transport, serverId, init, serviceName, procName) {
529
+ const streamId = nanoid();
530
+ const inputStream = pushable({ objectMode: true });
531
+ const outputStream = pushable({ objectMode: true });
532
+ let firstMessage = true;
533
+ if (init) {
534
+ const m = msg(
535
+ transport.clientId,
536
+ serverId,
537
+ streamId,
538
+ init,
539
+ serviceName,
540
+ procName
541
+ );
542
+ m.controlFlags = 2 /* StreamOpenBit */;
543
+ transport.send(m);
544
+ firstMessage = false;
545
+ }
546
+ (async () => {
547
+ for await (const rawIn of inputStream) {
548
+ const m = msg(transport.clientId, serverId, streamId, rawIn);
549
+ if (firstMessage) {
550
+ m.serviceName = serviceName;
551
+ m.procedureName = procName;
552
+ m.controlFlags |= 2 /* StreamOpenBit */;
553
+ firstMessage = false;
554
+ }
555
+ transport.send(m);
556
+ }
557
+ transport.send(closeStream(transport.clientId, serverId, streamId));
558
+ })();
559
+ function onMessage(msg2) {
560
+ if (msg2.streamId !== streamId) {
561
+ return;
562
+ }
563
+ if (isStreamClose(msg2.controlFlags)) {
564
+ cleanup();
565
+ } else {
566
+ outputStream.push(msg2.payload);
567
+ }
568
+ }
569
+ function cleanup() {
570
+ inputStream.end();
571
+ outputStream.end();
572
+ transport.removeEventListener("message", onMessage);
573
+ transport.removeEventListener("connectionStatus", onConnectionStatus);
574
+ }
575
+ const closeHandler = () => {
576
+ cleanup();
577
+ transport.send(closeStream(transport.clientId, serverId, streamId));
578
+ };
579
+ const onConnectionStatus = rejectAfterDisconnectGrace(serverId, () => {
580
+ outputStream.push(
581
+ Err({
582
+ code: UNEXPECTED_DISCONNECT,
583
+ message: `${serverId} unexpectedly disconnected`
584
+ })
585
+ );
586
+ cleanup();
587
+ });
588
+ transport.addEventListener("message", onMessage);
589
+ transport.addEventListener("connectionStatus", onConnectionStatus);
590
+ return [inputStream, outputStream, closeHandler];
591
+ }
592
+ function handleSubscribe(transport, serverId, input, serviceName, procName) {
593
+ const streamId = nanoid();
594
+ const m = msg(
595
+ transport.clientId,
596
+ serverId,
597
+ streamId,
598
+ input,
599
+ serviceName,
600
+ procName
601
+ );
602
+ m.controlFlags |= 2 /* StreamOpenBit */;
603
+ transport.send(m);
604
+ const outputStream = pushable({ objectMode: true });
605
+ function onMessage(msg2) {
606
+ if (msg2.streamId !== streamId) {
607
+ return;
608
+ }
609
+ if (isStreamClose(msg2.controlFlags)) {
610
+ cleanup();
611
+ } else {
612
+ outputStream.push(msg2.payload);
613
+ }
614
+ }
615
+ function cleanup() {
616
+ outputStream.end();
617
+ transport.removeEventListener("message", onMessage);
618
+ transport.removeEventListener("connectionStatus", onConnectionStatus);
619
+ }
620
+ const closeHandler = () => {
621
+ cleanup();
622
+ transport.send(closeStream(transport.clientId, serverId, streamId));
623
+ };
624
+ const onConnectionStatus = rejectAfterDisconnectGrace(serverId, () => {
625
+ outputStream.push(
626
+ Err({
627
+ code: UNEXPECTED_DISCONNECT,
628
+ message: `${serverId} unexpectedly disconnected`
629
+ })
630
+ );
631
+ cleanup();
632
+ });
633
+ transport.addEventListener("message", onMessage);
634
+ transport.addEventListener("connectionStatus", onConnectionStatus);
635
+ return [outputStream, closeHandler];
636
+ }
637
+ function handleUpload(transport, serverId, input, serviceName, procName) {
638
+ const streamId = nanoid();
639
+ const inputStream = pushable({ objectMode: true });
640
+ let firstMessage = true;
641
+ if (input) {
642
+ const m = msg(
643
+ transport.clientId,
644
+ serverId,
645
+ streamId,
646
+ input,
647
+ serviceName,
648
+ procName
649
+ );
650
+ m.controlFlags = 2 /* StreamOpenBit */;
651
+ transport.send(m);
652
+ firstMessage = false;
653
+ }
654
+ (async () => {
655
+ for await (const rawIn of inputStream) {
656
+ const m = msg(transport.clientId, serverId, streamId, rawIn);
657
+ if (firstMessage) {
658
+ m.controlFlags |= 2 /* StreamOpenBit */;
659
+ m.serviceName = serviceName;
660
+ m.procedureName = procName;
661
+ firstMessage = false;
662
+ }
663
+ transport.send(m);
664
+ }
665
+ transport.send(closeStream(transport.clientId, serverId, streamId));
666
+ })();
667
+ const responsePromise = new Promise((resolve) => {
668
+ const onConnectionStatus = rejectAfterDisconnectGrace(serverId, () => {
669
+ cleanup();
670
+ resolve(
671
+ Err({
672
+ code: UNEXPECTED_DISCONNECT,
673
+ message: `${serverId} unexpectedly disconnected`
674
+ })
675
+ );
676
+ });
677
+ function cleanup() {
678
+ inputStream.end();
679
+ transport.removeEventListener("message", onMessage);
680
+ transport.removeEventListener("connectionStatus", onConnectionStatus);
681
+ }
682
+ function onMessage(msg2) {
683
+ if (msg2.streamId === streamId) {
684
+ cleanup();
685
+ resolve(msg2.payload);
686
+ }
687
+ }
688
+ transport.addEventListener("message", onMessage);
689
+ transport.addEventListener("connectionStatus", onConnectionStatus);
690
+ });
691
+ return [inputStream, responsePromise];
692
+ }
693
+
694
+ // router/server.ts
695
+ import { Value } from "@sinclair/typebox/value";
696
+ var RiverServer = class {
697
+ transport;
698
+ services;
699
+ contextMap;
700
+ // map of streamId to ProcStream
701
+ streamMap;
702
+ // map of client to their open streams by streamId
703
+ clientStreams;
704
+ constructor(transport, services, extendedContext) {
705
+ this.transport = transport;
706
+ this.services = services;
707
+ this.contextMap = /* @__PURE__ */ new Map();
708
+ for (const service of Object.values(services)) {
709
+ this.contextMap.set(service, {
710
+ ...extendedContext,
711
+ state: service.state
712
+ });
713
+ }
714
+ this.streamMap = /* @__PURE__ */ new Map();
715
+ this.clientStreams = /* @__PURE__ */ new Map();
716
+ this.transport.addEventListener("message", this.handler);
717
+ this.transport.addEventListener("connectionStatus", this.onDisconnect);
718
+ }
719
+ get streams() {
720
+ return this.streamMap;
721
+ }
722
+ handler = async (message) => {
723
+ if (message.to !== this.transport.clientId) {
724
+ log?.info(
725
+ `${this.transport.clientId} -- got msg with destination that isn't the server, ignoring`
726
+ );
727
+ return;
728
+ }
729
+ let procStream = this.streamMap.get(message.streamId);
730
+ const isInitMessage = !procStream;
731
+ procStream ||= this.createNewProcStream(message);
732
+ if (!procStream) {
733
+ return;
734
+ }
735
+ await this.pushToStream(procStream, message, isInitMessage);
736
+ };
737
+ // cleanup streams on unexpected disconnections
738
+ onDisconnect = async (evt) => {
739
+ if (evt.status !== "disconnect") {
740
+ return;
741
+ }
742
+ const disconnectedClientId = evt.conn.connectedTo;
743
+ log?.info(
744
+ `${this.transport.clientId} -- got unexpected disconnect from ${disconnectedClientId}, cleaning up streams`
745
+ );
746
+ const streamsFromThisClient = this.clientStreams.get(disconnectedClientId);
747
+ if (!streamsFromThisClient) {
748
+ return;
749
+ }
750
+ await Promise.all(
751
+ Array.from(streamsFromThisClient).map(this.cleanupStream)
752
+ );
753
+ this.clientStreams.delete(disconnectedClientId);
754
+ };
755
+ async close() {
756
+ this.transport.removeEventListener("message", this.handler);
757
+ this.transport.removeEventListener("connectionStatus", this.onDisconnect);
758
+ await Promise.all([...this.streamMap.keys()].map(this.cleanupStream));
759
+ }
760
+ createNewProcStream(message) {
761
+ if (!isStreamOpen(message.controlFlags)) {
762
+ log?.warn(
763
+ `${this.transport.clientId} -- couldn't find a matching procedure stream for ${message.serviceName}.${message.procedureName}:${message.streamId}`
764
+ );
765
+ return;
766
+ }
767
+ if (!message.serviceName || !(message.serviceName in this.services)) {
768
+ log?.warn(
769
+ `${this.transport.clientId} -- couldn't find service ${message.serviceName}`
770
+ );
771
+ return;
772
+ }
773
+ const service = this.services[message.serviceName];
774
+ const serviceContext = this.getContext(service);
775
+ if (!message.procedureName || !(message.procedureName in service.procedures)) {
776
+ log?.warn(
777
+ `${this.transport.clientId} -- couldn't find a matching procedure for ${message.serviceName}.${message.procedureName}`
778
+ );
779
+ return;
780
+ }
781
+ const procedure = service.procedures[message.procedureName];
782
+ const incoming = pushable({ objectMode: true });
783
+ const outgoing = pushable({ objectMode: true });
784
+ const outputHandler = (
785
+ // sending outgoing messages back to client
786
+ (async () => {
787
+ for await (const response of outgoing) {
788
+ this.transport.send(reply(message, response));
789
+ }
790
+ if (procedure.type === "subscription" || procedure.type === "stream") {
791
+ this.transport.send(
792
+ closeStream(
793
+ this.transport.clientId,
794
+ message.from,
795
+ message.streamId
796
+ )
797
+ );
798
+ }
799
+ })()
800
+ );
801
+ const errorHandler = (err) => {
802
+ const errorMsg = err instanceof Error ? err.message : `[coerced to error] ${err}`;
803
+ log?.error(
804
+ `${this.transport.clientId} -- procedure ${message.serviceName}.${message.procedureName}:${message.streamId} threw an error: ${errorMsg}`
805
+ );
806
+ outgoing.push(
807
+ Err({
808
+ code: UNCAUGHT_ERROR,
809
+ message: errorMsg
810
+ })
811
+ );
812
+ };
813
+ let inputHandler;
814
+ const procHasInitMessage = "init" in procedure;
815
+ if (procedure.type === "stream") {
816
+ if (procHasInitMessage) {
817
+ inputHandler = (async () => {
818
+ const initMessage = await incoming.next();
819
+ if (initMessage.done) {
820
+ return;
821
+ }
822
+ return procedure.handler(serviceContext, initMessage.value, incoming, outgoing).catch(errorHandler);
823
+ })();
824
+ } else {
825
+ inputHandler = procedure.handler(serviceContext, incoming, outgoing).catch(errorHandler);
826
+ }
827
+ } else if (procedure.type === "rpc") {
828
+ inputHandler = (async () => {
829
+ const inputMessage = await incoming.next();
830
+ if (inputMessage.done) {
831
+ return;
832
+ }
833
+ try {
834
+ const outputMessage = await procedure.handler(
835
+ serviceContext,
836
+ inputMessage.value
837
+ );
838
+ outgoing.push(outputMessage);
839
+ } catch (err) {
840
+ errorHandler(err);
841
+ }
842
+ })();
843
+ } else if (procedure.type === "subscription") {
844
+ inputHandler = (async () => {
845
+ const inputMessage = await incoming.next();
846
+ if (inputMessage.done) {
847
+ return;
848
+ }
849
+ try {
850
+ await procedure.handler(serviceContext, inputMessage.value, outgoing);
851
+ } catch (err) {
852
+ errorHandler(err);
853
+ }
854
+ })();
855
+ } else if (procedure.type === "upload") {
856
+ if (procHasInitMessage) {
857
+ inputHandler = (async () => {
858
+ const initMessage = await incoming.next();
859
+ if (initMessage.done) {
860
+ return;
861
+ }
862
+ try {
863
+ const outputMessage = await procedure.handler(
864
+ serviceContext,
865
+ initMessage.value,
866
+ incoming
867
+ );
868
+ outgoing.push(outputMessage);
869
+ } catch (err) {
870
+ errorHandler(err);
871
+ }
872
+ })();
873
+ } else {
874
+ inputHandler = (async () => {
875
+ try {
876
+ const outputMessage = await procedure.handler(
877
+ serviceContext,
878
+ incoming
879
+ );
880
+ outgoing.push(outputMessage);
881
+ } catch (err) {
882
+ errorHandler(err);
883
+ }
884
+ })();
885
+ }
886
+ } else {
887
+ log?.warn(
888
+ `${this.transport.clientId} -- got request for invalid procedure type ${procedure.type} at ${message.serviceName}.${message.procedureName}`
889
+ );
890
+ return;
891
+ }
892
+ const procStream = {
893
+ id: message.streamId,
894
+ incoming,
895
+ outgoing,
896
+ serviceName: message.serviceName,
897
+ procedureName: message.procedureName,
898
+ procedure,
899
+ promises: { inputHandler, outputHandler }
900
+ };
901
+ this.streamMap.set(message.streamId, procStream);
902
+ const streamsFromThisClient = this.clientStreams.get(message.from) ?? /* @__PURE__ */ new Set();
903
+ streamsFromThisClient.add(message.streamId);
904
+ this.clientStreams.set(message.from, streamsFromThisClient);
905
+ return procStream;
906
+ }
907
+ async pushToStream(procStream, message, isInit) {
908
+ const procedure = procStream.procedure;
909
+ const procHasInitMessage = "init" in procedure;
910
+ if (isInit && procHasInitMessage && Value.Check(procedure.init, message.payload) || Value.Check(procedure.input, message.payload)) {
911
+ procStream.incoming.push(message.payload);
912
+ } else if (!Value.Check(ControlMessagePayloadSchema, message.payload)) {
913
+ log?.error(
914
+ `${this.transport.clientId} -- procedure ${procStream.serviceName}.${procStream.procedureName} received invalid payload: ${JSON.stringify(message.payload)}`
915
+ );
916
+ }
917
+ if (isStreamClose(message.controlFlags)) {
918
+ await this.cleanupStream(message.streamId);
919
+ const streamsFromThisClient = this.clientStreams.get(message.from);
920
+ if (streamsFromThisClient) {
921
+ streamsFromThisClient.delete(message.streamId);
922
+ if (streamsFromThisClient.size === 0) {
923
+ this.clientStreams.delete(message.from);
924
+ }
925
+ }
926
+ }
927
+ }
928
+ getContext(service) {
929
+ const context = this.contextMap.get(service);
930
+ if (!context) {
931
+ const err = `${this.transport.clientId} -- no context found for ${service.name}`;
932
+ log?.error(err);
933
+ throw new Error(err);
934
+ }
935
+ return context;
936
+ }
937
+ cleanupStream = async (id) => {
938
+ const stream = this.streamMap.get(id);
939
+ if (!stream) {
940
+ return;
941
+ }
942
+ stream.incoming.end();
943
+ await stream.promises.inputHandler;
944
+ stream.outgoing.end();
945
+ await stream.promises.outputHandler;
946
+ this.streamMap.delete(id);
947
+ };
948
+ };
949
+ function createServer(transport, services, extendedContext) {
950
+ return new RiverServer(transport, services, extendedContext);
951
+ }
952
+
953
+ export {
954
+ serializeService,
955
+ ServiceBuilder,
956
+ buildServiceDefs,
957
+ pushable,
958
+ UNCAUGHT_ERROR,
959
+ RiverUncaughtSchema,
960
+ Ok,
961
+ Err,
962
+ createClient,
963
+ createServer
964
+ };