@replit/river 0.10.0 → 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 (135) hide show
  1. package/dist/{router/builder.d.ts → builder-3c4485f0.d.ts} +76 -21
  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 -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 +731 -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 +41 -13
  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,683 @@
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
+ // router/client.ts
103
+ import { pushable } from "it-pushable";
104
+ import { nanoid } from "nanoid";
105
+
106
+ // router/result.ts
107
+ import {
108
+ Type as Type2
109
+ } from "@sinclair/typebox";
110
+ var UNCAUGHT_ERROR = "UNCAUGHT_ERROR";
111
+ var UNEXPECTED_DISCONNECT = "UNEXPECTED_DISCONNECT";
112
+ var RiverUncaughtSchema = Type2.Object({
113
+ code: Type2.Union([
114
+ Type2.Literal(UNCAUGHT_ERROR),
115
+ Type2.Literal(UNEXPECTED_DISCONNECT)
116
+ ]),
117
+ message: Type2.String()
118
+ });
119
+ function Ok(payload) {
120
+ return {
121
+ ok: true,
122
+ payload
123
+ };
124
+ }
125
+ function Err(error) {
126
+ return {
127
+ ok: false,
128
+ payload: error
129
+ };
130
+ }
131
+
132
+ // router/client.ts
133
+ var noop = () => {
134
+ };
135
+ function _createRecursiveProxy(callback, path) {
136
+ const proxy = new Proxy(noop, {
137
+ // property access, recurse and add field to path
138
+ get(_obj, key) {
139
+ if (typeof key !== "string")
140
+ return void 0;
141
+ return _createRecursiveProxy(callback, [...path, key]);
142
+ },
143
+ // hit the end, let's invoke the handler
144
+ apply(_target, _this, args) {
145
+ return callback({
146
+ path,
147
+ args
148
+ });
149
+ }
150
+ });
151
+ return proxy;
152
+ }
153
+ var createClient = (transport, serverId = "SERVER") => _createRecursiveProxy(async (opts) => {
154
+ const [serviceName, procName, procType] = [...opts.path];
155
+ if (!(serviceName && procName && procType)) {
156
+ throw new Error(
157
+ "invalid river call, ensure the service and procedure you are calling exists"
158
+ );
159
+ }
160
+ const [input] = opts.args;
161
+ if (procType === "rpc") {
162
+ return handleRpc(
163
+ transport,
164
+ serverId,
165
+ input,
166
+ serviceName,
167
+ procName
168
+ );
169
+ } else if (procType === "stream") {
170
+ return handleStream(
171
+ transport,
172
+ serverId,
173
+ input,
174
+ serviceName,
175
+ procName
176
+ );
177
+ } else if (procType === "subscribe") {
178
+ return handleSubscribe(
179
+ transport,
180
+ serverId,
181
+ input,
182
+ serviceName,
183
+ procName
184
+ );
185
+ } else if (procType === "upload") {
186
+ return handleUpload(
187
+ transport,
188
+ serverId,
189
+ input,
190
+ serviceName,
191
+ procName
192
+ );
193
+ } else {
194
+ throw new Error(`invalid river call, unknown procedure type ${procType}`);
195
+ }
196
+ }, []);
197
+ var CONNECTION_GRACE_PERIOD_MS = 5e3;
198
+ function rejectAfterDisconnectGrace(from, cb) {
199
+ let timeout = void 0;
200
+ return (evt) => {
201
+ if (evt.status === "connect" && evt.conn.connectedTo === from) {
202
+ clearTimeout(timeout);
203
+ timeout = void 0;
204
+ }
205
+ if (evt.status === "disconnect" && evt.conn.connectedTo === from) {
206
+ timeout = setTimeout(cb, CONNECTION_GRACE_PERIOD_MS);
207
+ }
208
+ };
209
+ }
210
+ function handleRpc(transport, serverId, input, serviceName, procName) {
211
+ const streamId = nanoid();
212
+ const m = msg(
213
+ transport.clientId,
214
+ serverId,
215
+ streamId,
216
+ input,
217
+ serviceName,
218
+ procName
219
+ );
220
+ m.controlFlags |= 2 /* StreamOpenBit */ | 4 /* StreamClosedBit */;
221
+ transport.send(m);
222
+ const responsePromise = new Promise((resolve) => {
223
+ const onConnectionStatus = rejectAfterDisconnectGrace(serverId, () => {
224
+ cleanup();
225
+ resolve(
226
+ Err({
227
+ code: UNEXPECTED_DISCONNECT,
228
+ message: `${serverId} unexpectedly disconnected`
229
+ })
230
+ );
231
+ });
232
+ function cleanup() {
233
+ transport.removeEventListener("message", onMessage);
234
+ transport.removeEventListener("connectionStatus", onConnectionStatus);
235
+ }
236
+ function onMessage(msg2) {
237
+ if (msg2.streamId === streamId) {
238
+ cleanup();
239
+ resolve(msg2.payload);
240
+ }
241
+ }
242
+ transport.addEventListener("message", onMessage);
243
+ transport.addEventListener("connectionStatus", onConnectionStatus);
244
+ });
245
+ return responsePromise;
246
+ }
247
+ function handleStream(transport, serverId, init, serviceName, procName) {
248
+ const streamId = nanoid();
249
+ const inputStream = pushable({ objectMode: true });
250
+ const outputStream = pushable({ objectMode: true });
251
+ let firstMessage = true;
252
+ if (init) {
253
+ const m = msg(
254
+ transport.clientId,
255
+ serverId,
256
+ streamId,
257
+ init,
258
+ serviceName,
259
+ procName
260
+ );
261
+ m.controlFlags = 2 /* StreamOpenBit */;
262
+ transport.send(m);
263
+ firstMessage = false;
264
+ }
265
+ (async () => {
266
+ for await (const rawIn of inputStream) {
267
+ const m = msg(transport.clientId, serverId, streamId, rawIn);
268
+ if (firstMessage) {
269
+ m.serviceName = serviceName;
270
+ m.procedureName = procName;
271
+ m.controlFlags |= 2 /* StreamOpenBit */;
272
+ firstMessage = false;
273
+ }
274
+ transport.send(m);
275
+ }
276
+ transport.send(closeStream(transport.clientId, serverId, streamId));
277
+ })();
278
+ function onMessage(msg2) {
279
+ if (msg2.streamId !== streamId) {
280
+ return;
281
+ }
282
+ if (isStreamClose(msg2.controlFlags)) {
283
+ cleanup();
284
+ } else {
285
+ outputStream.push(msg2.payload);
286
+ }
287
+ }
288
+ function cleanup() {
289
+ inputStream.end();
290
+ outputStream.end();
291
+ transport.removeEventListener("message", onMessage);
292
+ transport.removeEventListener("connectionStatus", onConnectionStatus);
293
+ }
294
+ const closeHandler = () => {
295
+ cleanup();
296
+ transport.send(closeStream(transport.clientId, serverId, streamId));
297
+ };
298
+ const onConnectionStatus = rejectAfterDisconnectGrace(serverId, () => {
299
+ outputStream.push(
300
+ Err({
301
+ code: UNEXPECTED_DISCONNECT,
302
+ message: `${serverId} unexpectedly disconnected`
303
+ })
304
+ );
305
+ cleanup();
306
+ });
307
+ transport.addEventListener("message", onMessage);
308
+ transport.addEventListener("connectionStatus", onConnectionStatus);
309
+ return [inputStream, outputStream, closeHandler];
310
+ }
311
+ function handleSubscribe(transport, serverId, input, serviceName, procName) {
312
+ const streamId = nanoid();
313
+ const m = msg(
314
+ transport.clientId,
315
+ serverId,
316
+ streamId,
317
+ input,
318
+ serviceName,
319
+ procName
320
+ );
321
+ m.controlFlags |= 2 /* StreamOpenBit */;
322
+ transport.send(m);
323
+ const outputStream = pushable({ objectMode: true });
324
+ function onMessage(msg2) {
325
+ if (msg2.streamId !== streamId) {
326
+ return;
327
+ }
328
+ if (isStreamClose(msg2.controlFlags)) {
329
+ cleanup();
330
+ } else {
331
+ outputStream.push(msg2.payload);
332
+ }
333
+ }
334
+ function cleanup() {
335
+ outputStream.end();
336
+ transport.removeEventListener("message", onMessage);
337
+ transport.removeEventListener("connectionStatus", onConnectionStatus);
338
+ }
339
+ const closeHandler = () => {
340
+ cleanup();
341
+ transport.send(closeStream(transport.clientId, serverId, streamId));
342
+ };
343
+ const onConnectionStatus = rejectAfterDisconnectGrace(serverId, () => {
344
+ outputStream.push(
345
+ Err({
346
+ code: UNEXPECTED_DISCONNECT,
347
+ message: `${serverId} unexpectedly disconnected`
348
+ })
349
+ );
350
+ cleanup();
351
+ });
352
+ transport.addEventListener("message", onMessage);
353
+ transport.addEventListener("connectionStatus", onConnectionStatus);
354
+ return [outputStream, closeHandler];
355
+ }
356
+ function handleUpload(transport, serverId, input, serviceName, procName) {
357
+ const streamId = nanoid();
358
+ const inputStream = pushable({ objectMode: true });
359
+ let firstMessage = true;
360
+ if (input) {
361
+ const m = msg(
362
+ transport.clientId,
363
+ serverId,
364
+ streamId,
365
+ input,
366
+ serviceName,
367
+ procName
368
+ );
369
+ m.controlFlags = 2 /* StreamOpenBit */;
370
+ transport.send(m);
371
+ firstMessage = false;
372
+ }
373
+ (async () => {
374
+ for await (const rawIn of inputStream) {
375
+ const m = msg(transport.clientId, serverId, streamId, rawIn);
376
+ if (firstMessage) {
377
+ m.controlFlags |= 2 /* StreamOpenBit */;
378
+ m.serviceName = serviceName;
379
+ m.procedureName = procName;
380
+ firstMessage = false;
381
+ }
382
+ transport.send(m);
383
+ }
384
+ transport.send(closeStream(transport.clientId, serverId, streamId));
385
+ })();
386
+ const responsePromise = new Promise((resolve) => {
387
+ const onConnectionStatus = rejectAfterDisconnectGrace(serverId, () => {
388
+ cleanup();
389
+ resolve(
390
+ Err({
391
+ code: UNEXPECTED_DISCONNECT,
392
+ message: `${serverId} unexpectedly disconnected`
393
+ })
394
+ );
395
+ });
396
+ function cleanup() {
397
+ inputStream.end();
398
+ transport.removeEventListener("message", onMessage);
399
+ transport.removeEventListener("connectionStatus", onConnectionStatus);
400
+ }
401
+ function onMessage(msg2) {
402
+ if (msg2.streamId === streamId) {
403
+ cleanup();
404
+ resolve(msg2.payload);
405
+ }
406
+ }
407
+ transport.addEventListener("message", onMessage);
408
+ transport.addEventListener("connectionStatus", onConnectionStatus);
409
+ });
410
+ return [inputStream, responsePromise];
411
+ }
412
+
413
+ // router/server.ts
414
+ import { pushable as pushable2 } from "it-pushable";
415
+ import { Value } from "@sinclair/typebox/value";
416
+ var RiverServer = class {
417
+ transport;
418
+ services;
419
+ contextMap;
420
+ // map of streamId to ProcStream
421
+ streamMap;
422
+ // map of client to their open streams by streamId
423
+ clientStreams;
424
+ constructor(transport, services, extendedContext) {
425
+ this.transport = transport;
426
+ this.services = services;
427
+ this.contextMap = /* @__PURE__ */ new Map();
428
+ for (const service of Object.values(services)) {
429
+ this.contextMap.set(service, {
430
+ ...extendedContext,
431
+ state: service.state
432
+ });
433
+ }
434
+ this.streamMap = /* @__PURE__ */ new Map();
435
+ this.clientStreams = /* @__PURE__ */ new Map();
436
+ this.transport.addEventListener("message", this.handler);
437
+ this.transport.addEventListener("connectionStatus", this.onDisconnect);
438
+ }
439
+ get streams() {
440
+ return this.streamMap;
441
+ }
442
+ handler = async (message) => {
443
+ if (message.to !== this.transport.clientId) {
444
+ log?.info(
445
+ `${this.transport.clientId} -- got msg with destination that isn't the server, ignoring`
446
+ );
447
+ return;
448
+ }
449
+ let procStream = this.streamMap.get(message.streamId);
450
+ const isInitMessage = !procStream;
451
+ procStream ||= this.createNewProcStream(message);
452
+ if (!procStream) {
453
+ return;
454
+ }
455
+ await this.pushToStream(procStream, message, isInitMessage);
456
+ };
457
+ // cleanup streams on unexpected disconnections
458
+ onDisconnect = async (evt) => {
459
+ if (evt.status !== "disconnect") {
460
+ return;
461
+ }
462
+ const disconnectedClientId = evt.conn.connectedTo;
463
+ log?.info(
464
+ `${this.transport.clientId} -- got unexpected disconnect from ${disconnectedClientId}, cleaning up streams`
465
+ );
466
+ const streamsFromThisClient = this.clientStreams.get(disconnectedClientId);
467
+ if (!streamsFromThisClient) {
468
+ return;
469
+ }
470
+ await Promise.all(
471
+ Array.from(streamsFromThisClient).map(this.cleanupStream)
472
+ );
473
+ this.clientStreams.delete(disconnectedClientId);
474
+ };
475
+ async close() {
476
+ this.transport.removeEventListener("message", this.handler);
477
+ this.transport.removeEventListener("connectionStatus", this.onDisconnect);
478
+ await Promise.all([...this.streamMap.keys()].map(this.cleanupStream));
479
+ }
480
+ createNewProcStream(message) {
481
+ if (!isStreamOpen(message.controlFlags)) {
482
+ log?.warn(
483
+ `${this.transport.clientId} -- couldn't find a matching procedure stream for ${message.serviceName}.${message.procedureName}:${message.streamId}`
484
+ );
485
+ return;
486
+ }
487
+ if (!message.serviceName || !(message.serviceName in this.services)) {
488
+ log?.warn(
489
+ `${this.transport.clientId} -- couldn't find service ${message.serviceName}`
490
+ );
491
+ return;
492
+ }
493
+ const service = this.services[message.serviceName];
494
+ const serviceContext = this.getContext(service);
495
+ if (!message.procedureName || !(message.procedureName in service.procedures)) {
496
+ log?.warn(
497
+ `${this.transport.clientId} -- couldn't find a matching procedure for ${message.serviceName}.${message.procedureName}`
498
+ );
499
+ return;
500
+ }
501
+ const procedure = service.procedures[message.procedureName];
502
+ const incoming = pushable2({ objectMode: true });
503
+ const outgoing = pushable2({ objectMode: true });
504
+ const outputHandler = (
505
+ // sending outgoing messages back to client
506
+ (async () => {
507
+ for await (const response of outgoing) {
508
+ this.transport.send(reply(message, response));
509
+ }
510
+ if (procedure.type === "subscription" || procedure.type === "stream") {
511
+ this.transport.send(
512
+ closeStream(
513
+ this.transport.clientId,
514
+ message.from,
515
+ message.streamId
516
+ )
517
+ );
518
+ }
519
+ })()
520
+ );
521
+ const errorHandler = (err) => {
522
+ const errorMsg = err instanceof Error ? err.message : `[coerced to error] ${err}`;
523
+ log?.error(
524
+ `${this.transport.clientId} -- procedure ${message.serviceName}.${message.procedureName}:${message.streamId} threw an error: ${errorMsg}`
525
+ );
526
+ outgoing.push(
527
+ Err({
528
+ code: UNCAUGHT_ERROR,
529
+ message: errorMsg
530
+ })
531
+ );
532
+ };
533
+ let inputHandler;
534
+ const procHasInitMessage = "init" in procedure;
535
+ if (procedure.type === "stream") {
536
+ if (procHasInitMessage) {
537
+ inputHandler = (async () => {
538
+ const initMessage = await incoming.next();
539
+ if (initMessage.done) {
540
+ return;
541
+ }
542
+ return procedure.handler(serviceContext, initMessage.value, incoming, outgoing).catch(errorHandler);
543
+ })();
544
+ } else {
545
+ inputHandler = procedure.handler(serviceContext, incoming, outgoing).catch(errorHandler);
546
+ }
547
+ } else if (procedure.type === "rpc") {
548
+ inputHandler = (async () => {
549
+ const inputMessage = await incoming.next();
550
+ if (inputMessage.done) {
551
+ return;
552
+ }
553
+ try {
554
+ const outputMessage = await procedure.handler(
555
+ serviceContext,
556
+ inputMessage.value
557
+ );
558
+ outgoing.push(outputMessage);
559
+ } catch (err) {
560
+ errorHandler(err);
561
+ }
562
+ })();
563
+ } else if (procedure.type === "subscription") {
564
+ inputHandler = (async () => {
565
+ const inputMessage = await incoming.next();
566
+ if (inputMessage.done) {
567
+ return;
568
+ }
569
+ try {
570
+ await procedure.handler(serviceContext, inputMessage.value, outgoing);
571
+ } catch (err) {
572
+ errorHandler(err);
573
+ }
574
+ })();
575
+ } else if (procedure.type === "upload") {
576
+ if (procHasInitMessage) {
577
+ inputHandler = (async () => {
578
+ const initMessage = await incoming.next();
579
+ if (initMessage.done) {
580
+ return;
581
+ }
582
+ try {
583
+ const outputMessage = await procedure.handler(
584
+ serviceContext,
585
+ initMessage.value,
586
+ incoming
587
+ );
588
+ outgoing.push(outputMessage);
589
+ } catch (err) {
590
+ errorHandler(err);
591
+ }
592
+ })();
593
+ } else {
594
+ inputHandler = (async () => {
595
+ try {
596
+ const outputMessage = await procedure.handler(
597
+ serviceContext,
598
+ incoming
599
+ );
600
+ outgoing.push(outputMessage);
601
+ } catch (err) {
602
+ errorHandler(err);
603
+ }
604
+ })();
605
+ }
606
+ } else {
607
+ log?.warn(
608
+ `${this.transport.clientId} -- got request for invalid procedure type ${procedure.type} at ${message.serviceName}.${message.procedureName}`
609
+ );
610
+ return;
611
+ }
612
+ const procStream = {
613
+ id: message.streamId,
614
+ incoming,
615
+ outgoing,
616
+ serviceName: message.serviceName,
617
+ procedureName: message.procedureName,
618
+ procedure,
619
+ promises: { inputHandler, outputHandler }
620
+ };
621
+ this.streamMap.set(message.streamId, procStream);
622
+ const streamsFromThisClient = this.clientStreams.get(message.from) ?? /* @__PURE__ */ new Set();
623
+ streamsFromThisClient.add(message.streamId);
624
+ this.clientStreams.set(message.from, streamsFromThisClient);
625
+ return procStream;
626
+ }
627
+ async pushToStream(procStream, message, isInit) {
628
+ const procedure = procStream.procedure;
629
+ const procHasInitMessage = "init" in procedure;
630
+ if (isInit && procHasInitMessage && Value.Check(procedure.init, message.payload) || Value.Check(procedure.input, message.payload)) {
631
+ procStream.incoming.push(message.payload);
632
+ } else if (!Value.Check(ControlMessagePayloadSchema, message.payload)) {
633
+ log?.error(
634
+ `${this.transport.clientId} -- procedure ${procStream.serviceName}.${procStream.procedureName} received invalid payload: ${JSON.stringify(message.payload)}`
635
+ );
636
+ }
637
+ if (isStreamClose(message.controlFlags)) {
638
+ await this.cleanupStream(message.streamId);
639
+ const streamsFromThisClient = this.clientStreams.get(message.from);
640
+ if (streamsFromThisClient) {
641
+ streamsFromThisClient.delete(message.streamId);
642
+ if (streamsFromThisClient.size === 0) {
643
+ this.clientStreams.delete(message.from);
644
+ }
645
+ }
646
+ }
647
+ }
648
+ getContext(service) {
649
+ const context = this.contextMap.get(service);
650
+ if (!context) {
651
+ const err = `${this.transport.clientId} -- no context found for ${service.name}`;
652
+ log?.error(err);
653
+ throw new Error(err);
654
+ }
655
+ return context;
656
+ }
657
+ cleanupStream = async (id) => {
658
+ const stream = this.streamMap.get(id);
659
+ if (!stream) {
660
+ return;
661
+ }
662
+ stream.incoming.end();
663
+ await stream.promises.inputHandler;
664
+ stream.outgoing.end();
665
+ await stream.promises.outputHandler;
666
+ this.streamMap.delete(id);
667
+ };
668
+ };
669
+ function createServer(transport, services, extendedContext) {
670
+ return new RiverServer(transport, services, extendedContext);
671
+ }
672
+
673
+ export {
674
+ serializeService,
675
+ ServiceBuilder,
676
+ buildServiceDefs,
677
+ UNCAUGHT_ERROR,
678
+ RiverUncaughtSchema,
679
+ Ok,
680
+ Err,
681
+ createClient,
682
+ createServer
683
+ };