@replit/river 0.200.0-rc.8 → 0.200.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 (72) hide show
  1. package/README.md +8 -8
  2. package/dist/{chunk-Y3JHOIJ7.js → chunk-6VA5DW7N.js} +21 -13
  3. package/dist/chunk-6VA5DW7N.js.map +1 -0
  4. package/dist/{chunk-4PVU7J25.js → chunk-AJGIY2UB.js} +1 -1
  5. package/dist/chunk-AJGIY2UB.js.map +1 -0
  6. package/dist/chunk-MADS7AI5.js +298 -0
  7. package/dist/chunk-MADS7AI5.js.map +1 -0
  8. package/dist/{chunk-FAIV2RO2.js → chunk-SONGYR7A.js} +22 -30
  9. package/dist/chunk-SONGYR7A.js.map +1 -0
  10. package/dist/{chunk-3ROTSXAO.js → chunk-UQHYJZTP.js} +14 -32
  11. package/dist/chunk-UQHYJZTP.js.map +1 -0
  12. package/dist/{chunk-XV5WUEIR.js → chunk-YQPJ3HZK.js} +24 -37
  13. package/dist/chunk-YQPJ3HZK.js.map +1 -0
  14. package/dist/{chunk-VXYHC666.js → chunk-YTMS7OP6.js} +1 -1
  15. package/dist/chunk-YTMS7OP6.js.map +1 -0
  16. package/dist/{chunk-J54ZWTQM.js → chunk-ZDYZ2FCN.js} +82 -20
  17. package/dist/chunk-ZDYZ2FCN.js.map +1 -0
  18. package/dist/{chunk-B5VE44UX.js → chunk-ZNJM2HIE.js} +14 -4
  19. package/dist/chunk-ZNJM2HIE.js.map +1 -0
  20. package/dist/{client-22a47343.d.ts → client-095a929e.d.ts} +3 -4
  21. package/dist/codec/index.cjs.map +1 -1
  22. package/dist/codec/index.js +1 -1
  23. package/dist/connection-623d75e9.d.ts +32 -0
  24. package/dist/{context-b4aff18f.d.ts → context-85b8690e.d.ts} +43 -43
  25. package/dist/logging/index.cjs.map +1 -1
  26. package/dist/logging/index.d.cts +1 -1
  27. package/dist/logging/index.d.ts +1 -1
  28. package/dist/logging/index.js +1 -1
  29. package/dist/{message-7d135e38.d.ts → message-57bb8187.d.ts} +5 -3
  30. package/dist/router/index.cjs +692 -741
  31. package/dist/router/index.cjs.map +1 -1
  32. package/dist/router/index.d.cts +22 -12
  33. package/dist/router/index.d.ts +22 -12
  34. package/dist/router/index.js +1322 -14
  35. package/dist/router/index.js.map +1 -1
  36. package/dist/{server-dd6a9853.d.ts → server-456bf6cb.d.ts} +5 -5
  37. package/dist/{services-bd2c50c0.d.ts → services-e4f28470.d.ts} +191 -179
  38. package/dist/transport/impls/ws/client.cjs +129 -62
  39. package/dist/transport/impls/ws/client.cjs.map +1 -1
  40. package/dist/transport/impls/ws/client.d.cts +4 -4
  41. package/dist/transport/impls/ws/client.d.ts +4 -4
  42. package/dist/transport/impls/ws/client.js +7 -7
  43. package/dist/transport/impls/ws/client.js.map +1 -1
  44. package/dist/transport/impls/ws/server.cjs +146 -70
  45. package/dist/transport/impls/ws/server.cjs.map +1 -1
  46. package/dist/transport/impls/ws/server.d.cts +6 -5
  47. package/dist/transport/impls/ws/server.d.ts +6 -5
  48. package/dist/transport/impls/ws/server.js +21 -9
  49. package/dist/transport/impls/ws/server.js.map +1 -1
  50. package/dist/transport/index.cjs +138 -92
  51. package/dist/transport/index.cjs.map +1 -1
  52. package/dist/transport/index.d.cts +4 -4
  53. package/dist/transport/index.d.ts +4 -4
  54. package/dist/transport/index.js +7 -7
  55. package/dist/util/testHelpers.cjs +342 -385
  56. package/dist/util/testHelpers.cjs.map +1 -1
  57. package/dist/util/testHelpers.d.cts +36 -39
  58. package/dist/util/testHelpers.d.ts +36 -39
  59. package/dist/util/testHelpers.js +82 -52
  60. package/dist/util/testHelpers.js.map +1 -1
  61. package/package.json +4 -3
  62. package/dist/chunk-3ROTSXAO.js.map +0 -1
  63. package/dist/chunk-4PVU7J25.js.map +0 -1
  64. package/dist/chunk-B5VE44UX.js.map +0 -1
  65. package/dist/chunk-FAIV2RO2.js.map +0 -1
  66. package/dist/chunk-J54ZWTQM.js.map +0 -1
  67. package/dist/chunk-VXYHC666.js.map +0 -1
  68. package/dist/chunk-XV5WUEIR.js.map +0 -1
  69. package/dist/chunk-Y3JHOIJ7.js.map +0 -1
  70. package/dist/chunk-Y4DP7WHM.js +0 -1646
  71. package/dist/chunk-Y4DP7WHM.js.map +0 -1
  72. package/dist/connection-260e45a8.d.ts +0 -11
@@ -1,30 +1,1335 @@
1
1
  import {
2
+ AnyResultSchema,
3
+ CANCEL_CODE,
2
4
  Err,
5
+ ErrResultSchema,
3
6
  INVALID_REQUEST_CODE,
4
7
  Ok,
5
- Procedure,
6
- RequestReaderErrorSchema,
7
- ResponseReaderErrorSchema,
8
- ServiceSchema,
8
+ ReadableImpl,
9
+ ReaderErrorSchema,
9
10
  UNCAUGHT_ERROR_CODE,
10
11
  UNEXPECTED_DISCONNECT_CODE,
11
- createClient,
12
- createClientHandshakeOptions,
13
- createServer,
14
- createServerHandshakeOptions,
15
- serializeSchema
16
- } from "../chunk-Y4DP7WHM.js";
12
+ WritableImpl,
13
+ flattenErrorType,
14
+ unwrapOrThrow
15
+ } from "../chunk-MADS7AI5.js";
17
16
  import {
17
+ ControlMessageCloseSchema,
18
+ ControlMessagePayloadSchema,
19
+ cancelMessage,
20
+ closeStreamMessage,
21
+ coerceErrorString,
22
+ createHandlerSpan,
23
+ createProcTelemetryInfo,
24
+ generateId,
25
+ getPropagationContext,
26
+ isStreamCancel,
27
+ isStreamClose,
28
+ isStreamOpen,
18
29
  version
19
- } from "../chunk-3ROTSXAO.js";
30
+ } from "../chunk-UQHYJZTP.js";
31
+
32
+ // router/services.ts
33
+ import { Type } from "@sinclair/typebox";
34
+ function serializeSchemaV1Compat(services, handshakeSchema) {
35
+ const serializedServiceObject = Object.entries(services).reduce((acc, [name, value]) => {
36
+ acc[name] = value.serializeV1Compat();
37
+ return acc;
38
+ }, {});
39
+ const schema = {
40
+ services: serializedServiceObject
41
+ };
42
+ if (handshakeSchema) {
43
+ schema.handshakeSchema = Type.Strict(handshakeSchema);
44
+ }
45
+ return schema;
46
+ }
47
+ function serializeSchema(services, handshakeSchema) {
48
+ const serializedServiceObject = Object.entries(services).reduce((acc, [name, value]) => {
49
+ acc[name] = value.serialize();
50
+ return acc;
51
+ }, {});
52
+ const schema = {
53
+ services: serializedServiceObject
54
+ };
55
+ if (handshakeSchema) {
56
+ schema.handshakeSchema = Type.Strict(handshakeSchema);
57
+ }
58
+ return schema;
59
+ }
60
+ var ServiceSchema = class _ServiceSchema {
61
+ /**
62
+ * Factory function for creating a fresh state.
63
+ */
64
+ initializeState;
65
+ /**
66
+ * The procedures for this service.
67
+ */
68
+ procedures;
69
+ /**
70
+ * @param config - The configuration for this service.
71
+ * @param procedures - The procedures for this service.
72
+ */
73
+ constructor(config, procedures) {
74
+ this.initializeState = config.initializeState;
75
+ this.procedures = procedures;
76
+ }
77
+ /**
78
+ * Creates a {@link ServiceScaffold}, which can be used to define procedures
79
+ * that can then be merged into a {@link ServiceSchema}, via the scaffold's
80
+ * `finalize` method.
81
+ *
82
+ * There are two patterns that work well with this method. The first is using
83
+ * it to separate the definition of procedures from the definition of the
84
+ * service's configuration:
85
+ * ```ts
86
+ * const MyServiceScaffold = ServiceSchema.scaffold({
87
+ * initializeState: () => ({ count: 0 }),
88
+ * });
89
+ *
90
+ * const incrementProcedures = MyServiceScaffold.procedures({
91
+ * increment: Procedure.rpc({
92
+ * requestInit: Type.Object({ amount: Type.Number() }),
93
+ * responseData: Type.Object({ current: Type.Number() }),
94
+ * async handler(ctx, init) {
95
+ * ctx.state.count += init.amount;
96
+ * return Ok({ current: ctx.state.count });
97
+ * }
98
+ * }),
99
+ * })
100
+ *
101
+ * const MyService = MyServiceScaffold.finalize({
102
+ * ...incrementProcedures,
103
+ * // you can also directly define procedures here
104
+ * });
105
+ * ```
106
+ * This might be really handy if you have a very large service and you're
107
+ * wanting to split it over multiple files. You can define the scaffold
108
+ * in one file, and then import that scaffold in other files where you
109
+ * define procedures - and then finally import the scaffolds and your
110
+ * procedure objects in a final file where you finalize the scaffold into
111
+ * a service schema.
112
+ *
113
+ * The other way is to use it like in a builder pattern:
114
+ * ```ts
115
+ * const MyService = ServiceSchema
116
+ * .scaffold({ initializeState: () => ({ count: 0 }) })
117
+ * .finalize({
118
+ * increment: Procedure.rpc({
119
+ * requestInit: Type.Object({ amount: Type.Number() }),
120
+ * responseData: Type.Object({ current: Type.Number() }),
121
+ * async handler(ctx, init) {
122
+ * ctx.state.count += init.amount;
123
+ * return Ok({ current: ctx.state.count });
124
+ * }
125
+ * }),
126
+ * })
127
+ * ```
128
+ * Depending on your preferences, this may be a more appealing way to define
129
+ * a schema versus using the {@link ServiceSchema.define} method.
130
+ */
131
+ static scaffold(config) {
132
+ return new ServiceScaffold(config);
133
+ }
134
+ // actual implementation
135
+ static define(configOrProcedures, maybeProcedures) {
136
+ let config;
137
+ let procedures;
138
+ if ("initializeState" in configOrProcedures && typeof configOrProcedures.initializeState === "function") {
139
+ if (!maybeProcedures) {
140
+ throw new Error("Expected procedures to be defined");
141
+ }
142
+ config = configOrProcedures;
143
+ procedures = maybeProcedures;
144
+ } else {
145
+ config = { initializeState: () => ({}) };
146
+ procedures = configOrProcedures;
147
+ }
148
+ return new _ServiceSchema(config, procedures);
149
+ }
150
+ /**
151
+ * Serializes this schema's procedures into a plain object that is JSON compatible.
152
+ */
153
+ serialize() {
154
+ return {
155
+ procedures: Object.fromEntries(
156
+ Object.entries(this.procedures).map(([procName, procDef]) => [
157
+ procName,
158
+ {
159
+ init: Type.Strict(procDef.requestInit),
160
+ output: Type.Strict(procDef.responseData),
161
+ // Only add `description` field if the type declares it.
162
+ ..."description" in procDef ? { description: procDef.description } : {},
163
+ // Only add the `errors` field if the type declares it.
164
+ ..."responseError" in procDef ? {
165
+ errors: Type.Strict(procDef.responseError)
166
+ } : {},
167
+ type: procDef.type,
168
+ // Only add the `input` field if the type declares it.
169
+ ..."requestData" in procDef ? {
170
+ input: Type.Strict(procDef.requestData)
171
+ } : {}
172
+ }
173
+ ])
174
+ )
175
+ };
176
+ }
177
+ // TODO remove once clients migrate to v2
178
+ /**
179
+ * Same as {@link ServiceSchema.serialize}, but with a format that is compatible with
180
+ * protocol v1. This is useful to be able to continue to generate schemas for older
181
+ * clients as they are still supported.
182
+ */
183
+ serializeV1Compat() {
184
+ return {
185
+ procedures: Object.fromEntries(
186
+ Object.entries(this.procedures).map(
187
+ ([procName, procDef]) => {
188
+ if (procDef.type === "rpc" || procDef.type === "subscription") {
189
+ return [
190
+ procName,
191
+ {
192
+ // BACKWARDS COMPAT: map init to input for protocolv1
193
+ // this is the only change needed to make it compatible.
194
+ input: Type.Strict(procDef.requestInit),
195
+ output: Type.Strict(procDef.responseData),
196
+ // Only add `description` field if the type declares it.
197
+ ..."description" in procDef ? { description: procDef.description } : {},
198
+ // Only add the `errors` field if the type declares it.
199
+ ..."responseError" in procDef ? {
200
+ errors: Type.Strict(procDef.responseError)
201
+ } : {},
202
+ type: procDef.type
203
+ }
204
+ ];
205
+ }
206
+ return [
207
+ procName,
208
+ {
209
+ init: Type.Strict(procDef.requestInit),
210
+ output: Type.Strict(procDef.responseData),
211
+ // Only add `description` field if the type declares it.
212
+ ..."description" in procDef ? { description: procDef.description } : {},
213
+ // Only add the `errors` field if the type declares it.
214
+ ..."responseError" in procDef ? {
215
+ errors: Type.Strict(procDef.responseError)
216
+ } : {},
217
+ type: procDef.type,
218
+ input: Type.Strict(procDef.requestData)
219
+ }
220
+ ];
221
+ }
222
+ )
223
+ )
224
+ };
225
+ }
226
+ /**
227
+ * Instantiates this schema into a {@link Service} object.
228
+ *
229
+ * You probably don't need this, usually the River server will handle this
230
+ * for you.
231
+ */
232
+ instantiate(extendedContext) {
233
+ return Object.freeze({
234
+ state: this.initializeState(extendedContext),
235
+ procedures: this.procedures
236
+ });
237
+ }
238
+ };
239
+ var ServiceScaffold = class {
240
+ /**
241
+ * The configuration for this service.
242
+ */
243
+ config;
244
+ /**
245
+ * @param config - The configuration for this service.
246
+ */
247
+ constructor(config) {
248
+ this.config = config;
249
+ }
250
+ /**
251
+ * Define procedures for this service. Use the {@link Procedure} constructors
252
+ * to create them. This returns the procedures object, which can then be
253
+ * passed to {@link ServiceSchema.finalize} to create a {@link ServiceSchema}.
254
+ *
255
+ * @example
256
+ * ```
257
+ * const myProcedures = MyServiceScaffold.procedures({
258
+ * myRPC: Procedure.rpc({
259
+ * // ...
260
+ * }),
261
+ * });
262
+ *
263
+ * const MyService = MyServiceScaffold.finalize({
264
+ * ...myProcedures,
265
+ * });
266
+ * ```
267
+ *
268
+ * @param procedures - The procedures for this service.
269
+ */
270
+ procedures(procedures) {
271
+ return procedures;
272
+ }
273
+ /**
274
+ * Finalizes the scaffold into a {@link ServiceSchema}. This is where you
275
+ * provide the service's procedures and get a {@link ServiceSchema} in return.
276
+ *
277
+ * You can directly define procedures here, or you can define them separately
278
+ * with the {@link ServiceScaffold.procedures} method, and then pass them here.
279
+ *
280
+ * @example
281
+ * ```
282
+ * const MyService = MyServiceScaffold.finalize({
283
+ * myRPC: Procedure.rpc({
284
+ * // ...
285
+ * }),
286
+ * // e.g. from the procedures method
287
+ * ...myOtherProcedures,
288
+ * });
289
+ * ```
290
+ */
291
+ finalize(procedures) {
292
+ return ServiceSchema.define(this.config, procedures);
293
+ }
294
+ };
295
+
296
+ // router/procedures.ts
297
+ import { Type as Type2 } from "@sinclair/typebox";
298
+ function rpc({
299
+ requestInit,
300
+ responseData,
301
+ responseError = Type2.Never(),
302
+ description,
303
+ handler
304
+ }) {
305
+ return {
306
+ ...description ? { description } : {},
307
+ type: "rpc",
308
+ requestInit,
309
+ responseData,
310
+ responseError,
311
+ handler
312
+ };
313
+ }
314
+ function upload({
315
+ requestInit,
316
+ requestData,
317
+ responseData,
318
+ responseError = Type2.Never(),
319
+ description,
320
+ handler
321
+ }) {
322
+ return {
323
+ type: "upload",
324
+ ...description ? { description } : {},
325
+ requestInit,
326
+ requestData,
327
+ responseData,
328
+ responseError,
329
+ handler
330
+ };
331
+ }
332
+ function subscription({
333
+ requestInit,
334
+ responseData,
335
+ responseError = Type2.Never(),
336
+ description,
337
+ handler
338
+ }) {
339
+ return {
340
+ type: "subscription",
341
+ ...description ? { description } : {},
342
+ requestInit,
343
+ responseData,
344
+ responseError,
345
+ handler
346
+ };
347
+ }
348
+ function stream({
349
+ requestInit,
350
+ requestData,
351
+ responseData,
352
+ responseError = Type2.Never(),
353
+ description,
354
+ handler
355
+ }) {
356
+ return {
357
+ type: "stream",
358
+ ...description ? { description } : {},
359
+ requestInit,
360
+ requestData,
361
+ responseData,
362
+ responseError,
363
+ handler
364
+ };
365
+ }
366
+ var Procedure = {
367
+ rpc,
368
+ upload,
369
+ subscription,
370
+ stream
371
+ };
372
+
373
+ // router/client.ts
374
+ import { Value } from "@sinclair/typebox/value";
375
+ var ReaderErrResultSchema = ErrResultSchema(ReaderErrorSchema);
376
+ var noop = () => {
377
+ };
378
+ function _createRecursiveProxy(callback, path) {
379
+ const proxy = new Proxy(noop, {
380
+ // property access, recurse and add field to path
381
+ get(_obj, key) {
382
+ if (typeof key !== "string")
383
+ return void 0;
384
+ return _createRecursiveProxy(callback, [...path, key]);
385
+ },
386
+ // hit the end, let's invoke the handler
387
+ apply(_target, _this, args) {
388
+ return callback({
389
+ path,
390
+ args
391
+ });
392
+ }
393
+ });
394
+ return proxy;
395
+ }
396
+ var defaultClientOptions = {
397
+ connectOnInvoke: true,
398
+ eagerlyConnect: true
399
+ };
400
+ function createClient(transport, serverId, providedClientOptions = {}) {
401
+ if (providedClientOptions.handshakeOptions) {
402
+ transport.extendHandshake(providedClientOptions.handshakeOptions);
403
+ }
404
+ const clientOptions = { ...defaultClientOptions, ...providedClientOptions };
405
+ if (clientOptions.eagerlyConnect) {
406
+ transport.connect(serverId);
407
+ }
408
+ return _createRecursiveProxy((opts) => {
409
+ const [serviceName, procName, procMethod] = [...opts.path];
410
+ if (!(serviceName && procName && procMethod)) {
411
+ throw new Error(
412
+ "invalid river call, ensure the service and procedure you are calling exists"
413
+ );
414
+ }
415
+ const [init, callOptions] = opts.args;
416
+ if (clientOptions.connectOnInvoke && !transport.sessions.has(serverId)) {
417
+ transport.connect(serverId);
418
+ }
419
+ if (procMethod !== "rpc" && procMethod !== "subscribe" && procMethod !== "stream" && procMethod !== "upload") {
420
+ throw new Error(
421
+ `invalid river call, unknown procedure type ${procMethod}`
422
+ );
423
+ }
424
+ return handleProc(
425
+ procMethod === "subscribe" ? "subscription" : procMethod,
426
+ transport,
427
+ serverId,
428
+ init,
429
+ serviceName,
430
+ procName,
431
+ callOptions ? callOptions.signal : void 0
432
+ );
433
+ }, []);
434
+ }
435
+ function handleProc(procType, transport, serverId, init, serviceName, procedureName, abortSignal) {
436
+ const session = transport.sessions.get(serverId) ?? transport.createUnconnectedSession(serverId);
437
+ const sessionScopedSend = transport.getSessionBoundSendFn(
438
+ serverId,
439
+ session.id
440
+ );
441
+ const procClosesWithInit = procType === "rpc" || procType === "subscription";
442
+ const streamId = generateId();
443
+ const { span, ctx } = createProcTelemetryInfo(
444
+ transport,
445
+ procType,
446
+ serviceName,
447
+ procedureName,
448
+ streamId
449
+ );
450
+ let cleanClose = true;
451
+ const reqWritable = new WritableImpl({
452
+ writeCb: (rawIn) => {
453
+ sessionScopedSend({
454
+ streamId,
455
+ payload: rawIn,
456
+ controlFlags: 0
457
+ });
458
+ },
459
+ // close callback
460
+ closeCb: () => {
461
+ span.addEvent("reqWritable closed");
462
+ if (!procClosesWithInit && cleanClose) {
463
+ sessionScopedSend(closeStreamMessage(streamId));
464
+ }
465
+ if (resReadable.isClosed()) {
466
+ cleanup();
467
+ }
468
+ }
469
+ });
470
+ const resReadable = new ReadableImpl();
471
+ const closeReadable = () => {
472
+ resReadable._triggerClose();
473
+ span.addEvent("resReadable closed");
474
+ if (reqWritable.isClosed()) {
475
+ cleanup();
476
+ }
477
+ };
478
+ function cleanup() {
479
+ transport.removeEventListener("message", onMessage);
480
+ transport.removeEventListener("sessionStatus", onSessionStatus);
481
+ abortSignal?.removeEventListener("abort", onClientCancel);
482
+ span.end();
483
+ }
484
+ function onClientCancel() {
485
+ if (resReadable.isClosed() && reqWritable.isClosed()) {
486
+ return;
487
+ }
488
+ span.addEvent("sending cancel");
489
+ cleanClose = false;
490
+ if (!resReadable.isClosed()) {
491
+ resReadable._pushValue(
492
+ Err({
493
+ code: CANCEL_CODE,
494
+ message: "cancelled by client"
495
+ })
496
+ );
497
+ closeReadable();
498
+ }
499
+ reqWritable.close();
500
+ sessionScopedSend(
501
+ cancelMessage(
502
+ streamId,
503
+ Err({
504
+ code: CANCEL_CODE,
505
+ message: "cancelled by client"
506
+ })
507
+ )
508
+ );
509
+ }
510
+ function onMessage(msg) {
511
+ if (msg.streamId !== streamId)
512
+ return;
513
+ if (msg.to !== transport.clientId) {
514
+ transport.log?.error("got stream message from unexpected client", {
515
+ clientId: transport.clientId,
516
+ transportMessage: msg
517
+ });
518
+ return;
519
+ }
520
+ if (isStreamCancel(msg.controlFlags)) {
521
+ cleanClose = false;
522
+ span.addEvent("received cancel");
523
+ let cancelResult;
524
+ if (Value.Check(ReaderErrResultSchema, msg.payload)) {
525
+ cancelResult = msg.payload;
526
+ } else {
527
+ cancelResult = Err({
528
+ code: CANCEL_CODE,
529
+ message: "stream cancelled with invalid payload"
530
+ });
531
+ transport.log?.error(
532
+ "got stream cancel without a valid protocol error",
533
+ {
534
+ clientId: transport.clientId,
535
+ transportMessage: msg,
536
+ validationErrors: [
537
+ ...Value.Errors(ReaderErrResultSchema, msg.payload)
538
+ ]
539
+ }
540
+ );
541
+ }
542
+ if (!resReadable.isClosed()) {
543
+ resReadable._pushValue(cancelResult);
544
+ closeReadable();
545
+ }
546
+ reqWritable.close();
547
+ return;
548
+ }
549
+ if (resReadable.isClosed()) {
550
+ span.recordException("received message after response stream is closed");
551
+ transport.log?.error("received message after response stream is closed", {
552
+ clientId: transport.clientId,
553
+ transportMessage: msg
554
+ });
555
+ return;
556
+ }
557
+ if (!Value.Check(ControlMessageCloseSchema, msg.payload)) {
558
+ if (Value.Check(AnyResultSchema, msg.payload)) {
559
+ resReadable._pushValue(msg.payload);
560
+ } else {
561
+ transport.log?.error(
562
+ "Got non-control payload, but was not a valid result",
563
+ {
564
+ clientId: transport.clientId,
565
+ transportMessage: msg,
566
+ validationErrors: [...Value.Errors(AnyResultSchema, msg.payload)]
567
+ }
568
+ );
569
+ }
570
+ }
571
+ if (isStreamClose(msg.controlFlags)) {
572
+ span.addEvent("received response close");
573
+ if (resReadable.isClosed()) {
574
+ transport.log?.error(
575
+ "received stream close but readable was already closed"
576
+ );
577
+ } else {
578
+ closeReadable();
579
+ }
580
+ }
581
+ }
582
+ function onSessionStatus(evt) {
583
+ if (evt.status !== "disconnect" || evt.session.to !== serverId || session.id !== evt.session.id) {
584
+ return;
585
+ }
586
+ cleanClose = false;
587
+ if (!resReadable.isClosed()) {
588
+ resReadable._pushValue(
589
+ Err({
590
+ code: UNEXPECTED_DISCONNECT_CODE,
591
+ message: `${serverId} unexpectedly disconnected`
592
+ })
593
+ );
594
+ closeReadable();
595
+ }
596
+ reqWritable.close();
597
+ }
598
+ abortSignal?.addEventListener("abort", onClientCancel);
599
+ transport.addEventListener("message", onMessage);
600
+ transport.addEventListener("sessionStatus", onSessionStatus);
601
+ sessionScopedSend({
602
+ streamId,
603
+ serviceName,
604
+ procedureName,
605
+ tracing: getPropagationContext(ctx),
606
+ payload: init,
607
+ controlFlags: procClosesWithInit ? 2 /* StreamOpenBit */ | 8 /* StreamClosedBit */ : 2 /* StreamOpenBit */
608
+ });
609
+ if (procClosesWithInit) {
610
+ reqWritable.close();
611
+ }
612
+ if (procType === "subscription") {
613
+ return {
614
+ resReadable
615
+ };
616
+ }
617
+ if (procType === "rpc") {
618
+ return getSingleMessage(resReadable, transport.log);
619
+ }
620
+ if (procType === "upload") {
621
+ let didFinalize = false;
622
+ return {
623
+ reqWritable,
624
+ finalize: () => {
625
+ if (didFinalize) {
626
+ throw new Error("upload stream already finalized");
627
+ }
628
+ didFinalize = true;
629
+ if (!reqWritable.isClosed()) {
630
+ reqWritable.close();
631
+ }
632
+ return getSingleMessage(resReadable, transport.log);
633
+ }
634
+ };
635
+ }
636
+ return {
637
+ resReadable,
638
+ reqWritable
639
+ };
640
+ }
641
+ async function getSingleMessage(resReadable, log) {
642
+ const ret = await resReadable.collect();
643
+ if (ret.length > 1) {
644
+ log?.error("Expected single message from server, got multiple");
645
+ }
646
+ return ret[0];
647
+ }
648
+
649
+ // router/server.ts
650
+ import { Type as Type3 } from "@sinclair/typebox";
651
+ import { Value as Value2 } from "@sinclair/typebox/value";
652
+ import { SpanStatusCode } from "@opentelemetry/api";
653
+ var CancelResultSchema = ErrResultSchema(
654
+ Type3.Object({
655
+ code: Type3.Literal(CANCEL_CODE),
656
+ message: Type3.String()
657
+ })
658
+ );
659
+ var RiverServer = class {
660
+ transport;
661
+ contextMap;
662
+ log;
663
+ /**
664
+ * We create a tombstones for streams cancelled by the server
665
+ * so that we don't hit errors when the client has inflight
666
+ * requests it sent before it saw the cancel.
667
+ * We track cancelled streams for every client separately, so
668
+ * that bad clients don't affect good clients.
669
+ */
670
+ serverCancelledStreams;
671
+ maxCancelledStreamTombstonesPerSession;
672
+ streams;
673
+ services;
674
+ constructor(transport, services, handshakeOptions, extendedContext, maxCancelledStreamTombstonesPerSession = 200) {
675
+ const instances = {};
676
+ this.services = instances;
677
+ this.contextMap = /* @__PURE__ */ new Map();
678
+ for (const [name, service] of Object.entries(services)) {
679
+ const instance = service.instantiate(extendedContext ?? {});
680
+ instances[name] = instance;
681
+ this.contextMap.set(instance, {
682
+ ...extendedContext,
683
+ state: instance.state
684
+ });
685
+ }
686
+ if (handshakeOptions) {
687
+ transport.extendHandshake(handshakeOptions);
688
+ }
689
+ this.transport = transport;
690
+ this.streams = /* @__PURE__ */ new Map();
691
+ this.serverCancelledStreams = /* @__PURE__ */ new Map();
692
+ this.maxCancelledStreamTombstonesPerSession = maxCancelledStreamTombstonesPerSession;
693
+ this.log = transport.log;
694
+ const handleCreatingNewStreams = (message) => {
695
+ if (message.to !== this.transport.clientId) {
696
+ this.log?.info(
697
+ `got msg with destination that isn't this server, ignoring`,
698
+ {
699
+ clientId: this.transport.clientId,
700
+ transportMessage: message
701
+ }
702
+ );
703
+ return;
704
+ }
705
+ const streamId = message.streamId;
706
+ const stream2 = this.streams.get(streamId);
707
+ if (stream2) {
708
+ stream2.handleMsg(message);
709
+ return;
710
+ }
711
+ if (this.serverCancelledStreams.get(message.from)?.has(streamId)) {
712
+ return;
713
+ }
714
+ const newStreamProps = this.validateNewProcStream(message);
715
+ if (!newStreamProps) {
716
+ return;
717
+ }
718
+ const newStream = this.createNewProcStream({
719
+ ...newStreamProps,
720
+ ...message
721
+ });
722
+ this.streams.set(streamId, newStream);
723
+ };
724
+ const handleSessionStatus = (evt) => {
725
+ if (evt.status !== "disconnect")
726
+ return;
727
+ const disconnectedClientId = evt.session.to;
728
+ this.log?.info(
729
+ `got session disconnect from ${disconnectedClientId}, cleaning up streams`,
730
+ evt.session.loggingMetadata
731
+ );
732
+ for (const stream2 of this.streams.values()) {
733
+ if (stream2.from === disconnectedClientId) {
734
+ stream2.handleSessionDisconnect();
735
+ }
736
+ }
737
+ this.serverCancelledStreams.delete(disconnectedClientId);
738
+ };
739
+ const handleTransportStatus = (evt) => {
740
+ if (evt.status !== "closed")
741
+ return;
742
+ this.transport.removeEventListener("message", handleCreatingNewStreams);
743
+ this.transport.removeEventListener("sessionStatus", handleSessionStatus);
744
+ this.transport.removeEventListener(
745
+ "transportStatus",
746
+ handleTransportStatus
747
+ );
748
+ };
749
+ this.transport.addEventListener("message", handleCreatingNewStreams);
750
+ this.transport.addEventListener("sessionStatus", handleSessionStatus);
751
+ this.transport.addEventListener("transportStatus", handleTransportStatus);
752
+ }
753
+ createNewProcStream(props) {
754
+ const {
755
+ streamId,
756
+ initialSession,
757
+ procedureName,
758
+ serviceName,
759
+ procedure,
760
+ sessionMetadata,
761
+ serviceContext,
762
+ initPayload,
763
+ tracingCtx,
764
+ procClosesWithInit,
765
+ passInitAsDataForBackwardsCompat
766
+ } = props;
767
+ const {
768
+ to: from,
769
+ loggingMetadata,
770
+ protocolVersion,
771
+ id: sessionId
772
+ } = initialSession;
773
+ let cleanClose = true;
774
+ const onMessage = (msg) => {
775
+ if (msg.from !== from) {
776
+ this.log?.error("got stream message from unexpected client", {
777
+ ...loggingMetadata,
778
+ transportMessage: msg,
779
+ tags: ["invariant-violation"]
780
+ });
781
+ return;
782
+ }
783
+ if (isStreamCancelBackwardsCompat(msg.controlFlags, protocolVersion)) {
784
+ let cancelResult;
785
+ if (Value2.Check(CancelResultSchema, msg.payload)) {
786
+ cancelResult = msg.payload;
787
+ } else {
788
+ cancelResult = Err({
789
+ code: CANCEL_CODE,
790
+ message: "stream cancelled, client sent invalid payload"
791
+ });
792
+ this.log?.warn("got stream cancel without a valid protocol error", {
793
+ ...loggingMetadata,
794
+ transportMessage: msg,
795
+ validationErrors: [
796
+ ...Value2.Errors(CancelResultSchema, msg.payload)
797
+ ],
798
+ tags: ["invalid-request"]
799
+ });
800
+ }
801
+ if (!reqReadable.isClosed()) {
802
+ reqReadable._pushValue(cancelResult);
803
+ closeReadable();
804
+ }
805
+ resWritable.close();
806
+ return;
807
+ }
808
+ if (reqReadable.isClosed()) {
809
+ this.log?.warn("received message after request stream is closed", {
810
+ ...loggingMetadata,
811
+ transportMessage: msg,
812
+ tags: ["invalid-request"]
813
+ });
814
+ onServerCancel({
815
+ code: INVALID_REQUEST_CODE,
816
+ message: "received message after request stream is closed"
817
+ });
818
+ return;
819
+ }
820
+ if ("requestData" in procedure && Value2.Check(procedure.requestData, msg.payload)) {
821
+ reqReadable._pushValue(Ok(msg.payload));
822
+ if (isStreamCloseBackwardsCompat(msg.controlFlags, protocolVersion)) {
823
+ closeReadable();
824
+ }
825
+ return;
826
+ }
827
+ if (Value2.Check(ControlMessagePayloadSchema, msg.payload) && isStreamCloseBackwardsCompat(msg.controlFlags, protocolVersion)) {
828
+ closeReadable();
829
+ return;
830
+ }
831
+ let validationErrors;
832
+ let errMessage;
833
+ if ("requestData" in procedure) {
834
+ errMessage = "expected requestData or control payload";
835
+ validationErrors = [
836
+ ...Value2.Errors(procedure.responseData, msg.payload)
837
+ ];
838
+ } else {
839
+ validationErrors = [
840
+ ...Value2.Errors(ControlMessagePayloadSchema, msg.payload)
841
+ ];
842
+ errMessage = "expected control payload";
843
+ }
844
+ this.log?.warn(errMessage, {
845
+ ...loggingMetadata,
846
+ transportMessage: msg,
847
+ validationErrors,
848
+ tags: ["invalid-request"]
849
+ });
850
+ onServerCancel({
851
+ code: INVALID_REQUEST_CODE,
852
+ message: errMessage
853
+ });
854
+ };
855
+ const procStream = {
856
+ from,
857
+ streamId,
858
+ procedureName,
859
+ serviceName,
860
+ sessionMetadata,
861
+ procedure,
862
+ handleMsg: onMessage,
863
+ handleSessionDisconnect: () => {
864
+ cleanClose = false;
865
+ const errPayload = {
866
+ code: UNEXPECTED_DISCONNECT_CODE,
867
+ message: "client unexpectedly disconnected"
868
+ };
869
+ if (!reqReadable.isClosed()) {
870
+ reqReadable._pushValue(Err(errPayload));
871
+ closeReadable();
872
+ }
873
+ resWritable.close();
874
+ }
875
+ };
876
+ const sessionScopedSend = this.transport.getSessionBoundSendFn(
877
+ from,
878
+ sessionId
879
+ );
880
+ const cancelStream = (streamId2, payload) => {
881
+ this.cancelStream(from, sessionScopedSend, streamId2, payload);
882
+ };
883
+ const onServerCancel = (e) => {
884
+ if (reqReadable.isClosed() && resWritable.isClosed()) {
885
+ return;
886
+ }
887
+ cleanClose = false;
888
+ const result = Err(e);
889
+ if (!reqReadable.isClosed()) {
890
+ reqReadable._pushValue(result);
891
+ closeReadable();
892
+ }
893
+ resWritable.close();
894
+ cancelStream(streamId, result);
895
+ };
896
+ const finishedController = new AbortController();
897
+ const cleanup = () => {
898
+ finishedController.abort();
899
+ this.streams.delete(streamId);
900
+ };
901
+ const procClosesWithResponse = procedure.type === "rpc" || procedure.type === "upload";
902
+ const reqReadable = new ReadableImpl();
903
+ const closeReadable = () => {
904
+ reqReadable._triggerClose();
905
+ if (protocolVersion === "v1.1") {
906
+ if (!procClosesWithResponse && !resWritable.isClosed()) {
907
+ resWritable.close();
908
+ }
909
+ }
910
+ if (resWritable.isClosed()) {
911
+ cleanup();
912
+ }
913
+ };
914
+ if (passInitAsDataForBackwardsCompat) {
915
+ reqReadable._pushValue(Ok(initPayload));
916
+ }
917
+ const resWritable = new WritableImpl({
918
+ writeCb: (response) => {
919
+ sessionScopedSend({
920
+ streamId,
921
+ controlFlags: procClosesWithResponse ? getStreamCloseBackwardsCompat(protocolVersion) : 0,
922
+ payload: response
923
+ });
924
+ if (procClosesWithResponse) {
925
+ resWritable.close();
926
+ }
927
+ },
928
+ // close callback
929
+ closeCb: () => {
930
+ if (!procClosesWithResponse && cleanClose) {
931
+ const message = closeStreamMessage(streamId);
932
+ message.controlFlags = getStreamCloseBackwardsCompat(protocolVersion);
933
+ sessionScopedSend(message);
934
+ }
935
+ if (protocolVersion === "v1.1") {
936
+ if (!reqReadable.isClosed()) {
937
+ closeReadable();
938
+ }
939
+ }
940
+ if (reqReadable.isClosed()) {
941
+ cleanup();
942
+ }
943
+ }
944
+ });
945
+ const onHandlerError = (err, span) => {
946
+ const errorMsg = coerceErrorString(err);
947
+ span.recordException(err instanceof Error ? err : new Error(errorMsg));
948
+ span.setStatus({ code: SpanStatusCode.ERROR });
949
+ onServerCancel({
950
+ code: UNCAUGHT_ERROR_CODE,
951
+ message: errorMsg
952
+ });
953
+ };
954
+ if (procClosesWithInit) {
955
+ closeReadable();
956
+ } else if (procedure.type === "rpc" || procedure.type === "subscription") {
957
+ this.log?.warn("sent an init without a stream close", {
958
+ ...loggingMetadata,
959
+ clientId: this.transport.clientId
960
+ });
961
+ }
962
+ const handlerContext = {
963
+ ...serviceContext,
964
+ from,
965
+ sessionId,
966
+ metadata: sessionMetadata,
967
+ cancel: () => {
968
+ onServerCancel({
969
+ code: CANCEL_CODE,
970
+ message: "cancelled by server procedure handler"
971
+ });
972
+ },
973
+ signal: finishedController.signal
974
+ };
975
+ switch (procedure.type) {
976
+ case "rpc":
977
+ void createHandlerSpan(
978
+ procedure.type,
979
+ serviceName,
980
+ procedureName,
981
+ streamId,
982
+ tracingCtx,
983
+ async (span) => {
984
+ try {
985
+ const responsePayload = await procedure.handler({
986
+ ctx: handlerContext,
987
+ reqInit: initPayload
988
+ });
989
+ if (resWritable.isClosed()) {
990
+ return;
991
+ }
992
+ resWritable.write(responsePayload);
993
+ } catch (err) {
994
+ onHandlerError(err, span);
995
+ } finally {
996
+ span.end();
997
+ }
998
+ }
999
+ );
1000
+ break;
1001
+ case "stream":
1002
+ void createHandlerSpan(
1003
+ procedure.type,
1004
+ serviceName,
1005
+ procedureName,
1006
+ streamId,
1007
+ tracingCtx,
1008
+ async (span) => {
1009
+ try {
1010
+ await procedure.handler({
1011
+ ctx: handlerContext,
1012
+ reqInit: initPayload,
1013
+ reqReadable,
1014
+ resWritable
1015
+ });
1016
+ } catch (err) {
1017
+ onHandlerError(err, span);
1018
+ } finally {
1019
+ span.end();
1020
+ }
1021
+ }
1022
+ );
1023
+ break;
1024
+ case "subscription":
1025
+ void createHandlerSpan(
1026
+ procedure.type,
1027
+ serviceName,
1028
+ procedureName,
1029
+ streamId,
1030
+ tracingCtx,
1031
+ async (span) => {
1032
+ try {
1033
+ await procedure.handler({
1034
+ ctx: handlerContext,
1035
+ reqInit: initPayload,
1036
+ resWritable
1037
+ });
1038
+ } catch (err) {
1039
+ onHandlerError(err, span);
1040
+ } finally {
1041
+ span.end();
1042
+ }
1043
+ }
1044
+ );
1045
+ break;
1046
+ case "upload":
1047
+ void createHandlerSpan(
1048
+ procedure.type,
1049
+ serviceName,
1050
+ procedureName,
1051
+ streamId,
1052
+ tracingCtx,
1053
+ async (span) => {
1054
+ try {
1055
+ const responsePayload = await procedure.handler({
1056
+ ctx: handlerContext,
1057
+ reqInit: initPayload,
1058
+ reqReadable
1059
+ });
1060
+ if (resWritable.isClosed()) {
1061
+ return;
1062
+ }
1063
+ resWritable.write(responsePayload);
1064
+ } catch (err) {
1065
+ onHandlerError(err, span);
1066
+ } finally {
1067
+ span.end();
1068
+ }
1069
+ }
1070
+ );
1071
+ break;
1072
+ }
1073
+ return procStream;
1074
+ }
1075
+ getContext(service, serviceName) {
1076
+ const context = this.contextMap.get(service);
1077
+ if (!context) {
1078
+ const err = `no context found for ${serviceName}`;
1079
+ this.log?.error(err, {
1080
+ clientId: this.transport.clientId,
1081
+ tags: ["invariant-violation"]
1082
+ });
1083
+ throw new Error(err);
1084
+ }
1085
+ return context;
1086
+ }
1087
+ validateNewProcStream(initMessage) {
1088
+ const session = this.transport.sessions.get(initMessage.from);
1089
+ if (!session) {
1090
+ this.log?.error(`couldn't find session for ${initMessage.from}`, {
1091
+ clientId: this.transport.clientId,
1092
+ transportMessage: initMessage,
1093
+ tags: ["invariant-violation"]
1094
+ });
1095
+ return null;
1096
+ }
1097
+ const sessionScopedSend = this.transport.getSessionBoundSendFn(
1098
+ initMessage.from,
1099
+ session.id
1100
+ );
1101
+ const cancelStream = (streamId, payload) => {
1102
+ this.cancelStream(initMessage.from, sessionScopedSend, streamId, payload);
1103
+ };
1104
+ const sessionMetadata = this.transport.sessionHandshakeMetadata.get(
1105
+ session.to
1106
+ );
1107
+ if (!sessionMetadata) {
1108
+ const errMessage = `session doesn't have handshake metadata`;
1109
+ this.log?.error(errMessage, {
1110
+ ...session.loggingMetadata,
1111
+ tags: ["invariant-violation"]
1112
+ });
1113
+ cancelStream(
1114
+ initMessage.streamId,
1115
+ Err({
1116
+ code: UNCAUGHT_ERROR_CODE,
1117
+ message: errMessage
1118
+ })
1119
+ );
1120
+ return null;
1121
+ }
1122
+ if (!isStreamOpen(initMessage.controlFlags)) {
1123
+ const errMessage = `can't create a new procedure stream from a message that doesn't have the stream open bit set`;
1124
+ this.log?.warn(errMessage, {
1125
+ ...session.loggingMetadata,
1126
+ clientId: this.transport.clientId,
1127
+ transportMessage: initMessage,
1128
+ tags: ["invalid-request"]
1129
+ });
1130
+ cancelStream(
1131
+ initMessage.streamId,
1132
+ Err({
1133
+ code: INVALID_REQUEST_CODE,
1134
+ message: errMessage
1135
+ })
1136
+ );
1137
+ return null;
1138
+ }
1139
+ if (!initMessage.serviceName) {
1140
+ const errMessage = `missing service name in stream open message`;
1141
+ this.log?.warn(errMessage, {
1142
+ ...session.loggingMetadata,
1143
+ transportMessage: initMessage,
1144
+ tags: ["invalid-request"]
1145
+ });
1146
+ cancelStream(
1147
+ initMessage.streamId,
1148
+ Err({
1149
+ code: INVALID_REQUEST_CODE,
1150
+ message: errMessage
1151
+ })
1152
+ );
1153
+ return null;
1154
+ }
1155
+ if (!initMessage.procedureName) {
1156
+ const errMessage = `missing procedure name in stream open message`;
1157
+ this.log?.warn(errMessage, {
1158
+ ...session.loggingMetadata,
1159
+ transportMessage: initMessage,
1160
+ tags: ["invalid-request"]
1161
+ });
1162
+ cancelStream(
1163
+ initMessage.streamId,
1164
+ Err({
1165
+ code: INVALID_REQUEST_CODE,
1166
+ message: errMessage
1167
+ })
1168
+ );
1169
+ return null;
1170
+ }
1171
+ if (!(initMessage.serviceName in this.services)) {
1172
+ const errMessage = `couldn't find service ${initMessage.serviceName}`;
1173
+ this.log?.warn(errMessage, {
1174
+ ...session.loggingMetadata,
1175
+ clientId: this.transport.clientId,
1176
+ transportMessage: initMessage,
1177
+ tags: ["invalid-request"]
1178
+ });
1179
+ cancelStream(
1180
+ initMessage.streamId,
1181
+ Err({
1182
+ code: INVALID_REQUEST_CODE,
1183
+ message: errMessage
1184
+ })
1185
+ );
1186
+ return null;
1187
+ }
1188
+ const service = this.services[initMessage.serviceName];
1189
+ if (!(initMessage.procedureName in service.procedures)) {
1190
+ const errMessage = `couldn't find a matching procedure for ${initMessage.serviceName}.${initMessage.procedureName}`;
1191
+ this.log?.warn(errMessage, {
1192
+ ...session.loggingMetadata,
1193
+ transportMessage: initMessage,
1194
+ tags: ["invalid-request"]
1195
+ });
1196
+ cancelStream(
1197
+ initMessage.streamId,
1198
+ Err({
1199
+ code: INVALID_REQUEST_CODE,
1200
+ message: errMessage
1201
+ })
1202
+ );
1203
+ return null;
1204
+ }
1205
+ const serviceContext = this.getContext(service, initMessage.serviceName);
1206
+ const procedure = service.procedures[initMessage.procedureName];
1207
+ if (!["rpc", "upload", "stream", "subscription"].includes(procedure.type)) {
1208
+ this.log?.error(
1209
+ `got request for invalid procedure type ${procedure.type} at ${initMessage.serviceName}.${initMessage.procedureName}`,
1210
+ {
1211
+ ...session.loggingMetadata,
1212
+ transportMessage: initMessage,
1213
+ tags: ["invariant-violation"]
1214
+ }
1215
+ );
1216
+ return null;
1217
+ }
1218
+ let passInitAsDataForBackwardsCompat = false;
1219
+ if (session.protocolVersion === "v1.1" && (procedure.type === "upload" || procedure.type === "stream") && Value2.Check(procedure.requestData, initMessage.payload) && Value2.Check(procedure.requestInit, {})) {
1220
+ passInitAsDataForBackwardsCompat = true;
1221
+ } else if (!Value2.Check(procedure.requestInit, initMessage.payload)) {
1222
+ const errMessage = `procedure init failed validation`;
1223
+ this.log?.warn(errMessage, {
1224
+ ...session.loggingMetadata,
1225
+ clientId: this.transport.clientId,
1226
+ transportMessage: initMessage,
1227
+ tags: ["invalid-request"]
1228
+ });
1229
+ cancelStream(
1230
+ initMessage.streamId,
1231
+ Err({
1232
+ code: INVALID_REQUEST_CODE,
1233
+ message: errMessage
1234
+ })
1235
+ );
1236
+ return null;
1237
+ }
1238
+ return {
1239
+ initialSession: session,
1240
+ streamId: initMessage.streamId,
1241
+ procedureName: initMessage.procedureName,
1242
+ serviceName: initMessage.serviceName,
1243
+ tracingCtx: initMessage.tracing,
1244
+ initPayload: initMessage.payload,
1245
+ sessionMetadata,
1246
+ procedure,
1247
+ serviceContext,
1248
+ procClosesWithInit: isStreamCloseBackwardsCompat(
1249
+ initMessage.controlFlags,
1250
+ session.protocolVersion
1251
+ ),
1252
+ passInitAsDataForBackwardsCompat
1253
+ };
1254
+ }
1255
+ cancelStream(to, sessionScopedSend, streamId, payload) {
1256
+ let cancelledStreamsInSession = this.serverCancelledStreams.get(to);
1257
+ if (!cancelledStreamsInSession) {
1258
+ cancelledStreamsInSession = new LRUSet(
1259
+ this.maxCancelledStreamTombstonesPerSession
1260
+ );
1261
+ this.serverCancelledStreams.set(to, cancelledStreamsInSession);
1262
+ }
1263
+ cancelledStreamsInSession.add(streamId);
1264
+ const msg = cancelMessage(streamId, payload);
1265
+ sessionScopedSend(msg);
1266
+ }
1267
+ };
1268
+ var LRUSet = class {
1269
+ items;
1270
+ maxItems;
1271
+ constructor(maxItems) {
1272
+ this.items = /* @__PURE__ */ new Set();
1273
+ this.maxItems = maxItems;
1274
+ }
1275
+ add(item) {
1276
+ if (this.items.has(item)) {
1277
+ this.items.delete(item);
1278
+ } else if (this.items.size >= this.maxItems) {
1279
+ const first = this.items.values().next();
1280
+ if (!first.done) {
1281
+ this.items.delete(first.value);
1282
+ }
1283
+ }
1284
+ this.items.add(item);
1285
+ }
1286
+ has(item) {
1287
+ return this.items.has(item);
1288
+ }
1289
+ };
1290
+ function isStreamCancelBackwardsCompat(controlFlags, protocolVersion) {
1291
+ if (protocolVersion === "v1.1") {
1292
+ return false;
1293
+ }
1294
+ return isStreamCancel(controlFlags);
1295
+ }
1296
+ function isStreamCloseBackwardsCompat(controlFlags, protocolVersion) {
1297
+ if (protocolVersion === "v1.1") {
1298
+ return isStreamCancel(controlFlags);
1299
+ }
1300
+ return isStreamClose(controlFlags);
1301
+ }
1302
+ function getStreamCloseBackwardsCompat(protocolVersion) {
1303
+ if (protocolVersion === "v1.1") {
1304
+ return 4 /* StreamCancelBit */;
1305
+ }
1306
+ return 8 /* StreamClosedBit */;
1307
+ }
1308
+ function createServer(transport, services, providedServerOptions) {
1309
+ return new RiverServer(
1310
+ transport,
1311
+ services,
1312
+ providedServerOptions?.handshakeOptions,
1313
+ providedServerOptions?.extendedContext,
1314
+ providedServerOptions?.maxCancelledStreamTombstonesPerSession
1315
+ );
1316
+ }
1317
+
1318
+ // router/handshake.ts
1319
+ function createClientHandshakeOptions(schema, construct) {
1320
+ return { schema, construct };
1321
+ }
1322
+ function createServerHandshakeOptions(schema, validate) {
1323
+ return { schema, validate };
1324
+ }
20
1325
  export {
1326
+ CANCEL_CODE,
21
1327
  Err,
22
1328
  INVALID_REQUEST_CODE,
23
1329
  Ok,
24
1330
  Procedure,
25
1331
  version as RIVER_VERSION,
26
- RequestReaderErrorSchema,
27
- ResponseReaderErrorSchema,
1332
+ ReaderErrorSchema,
28
1333
  ServiceSchema,
29
1334
  UNCAUGHT_ERROR_CODE,
30
1335
  UNEXPECTED_DISCONNECT_CODE,
@@ -32,6 +1337,9 @@ export {
32
1337
  createClientHandshakeOptions,
33
1338
  createServer,
34
1339
  createServerHandshakeOptions,
35
- serializeSchema
1340
+ flattenErrorType,
1341
+ serializeSchema,
1342
+ serializeSchemaV1Compat,
1343
+ unwrapOrThrow
36
1344
  };
37
1345
  //# sourceMappingURL=index.js.map