@trpc/server 11.0.0-rc.643 → 11.0.0-rc.648

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 (107) hide show
  1. package/dist/adapters/aws-lambda/index.js +1 -1
  2. package/dist/adapters/aws-lambda/index.mjs +1 -1
  3. package/dist/adapters/express.js +1 -1
  4. package/dist/adapters/express.mjs +1 -1
  5. package/dist/adapters/fastify/fastifyRequestHandler.js +1 -1
  6. package/dist/adapters/fastify/fastifyRequestHandler.mjs +1 -1
  7. package/dist/adapters/fetch/fetchRequestHandler.js +1 -1
  8. package/dist/adapters/fetch/fetchRequestHandler.mjs +1 -1
  9. package/dist/adapters/next-app-dir/nextAppDirCaller.js +1 -1
  10. package/dist/adapters/next-app-dir/nextAppDirCaller.mjs +1 -1
  11. package/dist/adapters/next-app-dir/notFound.js +1 -1
  12. package/dist/adapters/next-app-dir/notFound.mjs +1 -1
  13. package/dist/adapters/next-app-dir/redirect.js +15 -3
  14. package/dist/adapters/next-app-dir/redirect.mjs +14 -2
  15. package/dist/adapters/next.js +1 -1
  16. package/dist/adapters/next.mjs +1 -1
  17. package/dist/adapters/node-http/incomingMessageToRequest.js +1 -1
  18. package/dist/adapters/node-http/incomingMessageToRequest.mjs +1 -1
  19. package/dist/adapters/node-http/nodeHTTPRequestHandler.js +1 -1
  20. package/dist/adapters/node-http/nodeHTTPRequestHandler.mjs +1 -1
  21. package/dist/adapters/node-http/writeResponse.d.ts.map +1 -1
  22. package/dist/adapters/node-http/writeResponse.js +3 -1
  23. package/dist/adapters/node-http/writeResponse.mjs +3 -1
  24. package/dist/adapters/standalone.js +1 -1
  25. package/dist/adapters/standalone.mjs +1 -1
  26. package/dist/adapters/ws.d.ts.map +1 -1
  27. package/dist/adapters/ws.js +145 -66
  28. package/dist/adapters/ws.mjs +145 -66
  29. package/dist/bundle-analysis.json +194 -201
  30. package/dist/http.js +1 -1
  31. package/dist/http.mjs +1 -1
  32. package/dist/index.js +1 -1
  33. package/dist/index.mjs +1 -1
  34. package/dist/node_modules/.pnpm/@rollup_plugin-typescript@12.1.1_rollup@4.27.4_tslib@2.8.1_typescript@5.6.2/node_modules/tslib/tslib.es6.js +73 -0
  35. package/dist/observable/observable.d.ts +1 -1
  36. package/dist/observable/observable.d.ts.map +1 -1
  37. package/dist/observable/observable.js +16 -4
  38. package/dist/observable/observable.mjs +16 -4
  39. package/dist/rpc.js +1 -1
  40. package/dist/rpc.mjs +1 -1
  41. package/dist/shared.js +1 -1
  42. package/dist/shared.mjs +1 -1
  43. package/dist/unstable-core-do-not-import/error/TRPCError.d.ts +1 -1
  44. package/dist/unstable-core-do-not-import/error/TRPCError.js +14 -2
  45. package/dist/unstable-core-do-not-import/error/TRPCError.mjs +13 -1
  46. package/dist/unstable-core-do-not-import/http/resolveResponse.d.ts.map +1 -1
  47. package/dist/unstable-core-do-not-import/http/resolveResponse.js +7 -18
  48. package/dist/unstable-core-do-not-import/http/resolveResponse.mjs +8 -19
  49. package/dist/unstable-core-do-not-import/stream/jsonl.d.ts +15 -4
  50. package/dist/unstable-core-do-not-import/stream/jsonl.d.ts.map +1 -1
  51. package/dist/unstable-core-do-not-import/stream/jsonl.js +430 -291
  52. package/dist/unstable-core-do-not-import/stream/jsonl.mjs +428 -289
  53. package/dist/unstable-core-do-not-import/stream/sse.d.ts +1 -1
  54. package/dist/unstable-core-do-not-import/stream/sse.d.ts.map +1 -1
  55. package/dist/unstable-core-do-not-import/stream/sse.js +183 -122
  56. package/dist/unstable-core-do-not-import/stream/sse.mjs +183 -122
  57. package/dist/unstable-core-do-not-import/stream/utils/asyncIterable.d.ts +1 -0
  58. package/dist/unstable-core-do-not-import/stream/utils/asyncIterable.d.ts.map +1 -1
  59. package/dist/unstable-core-do-not-import/stream/utils/asyncIterable.js +103 -10
  60. package/dist/unstable-core-do-not-import/stream/utils/asyncIterable.mjs +104 -12
  61. package/dist/unstable-core-do-not-import/stream/utils/disposable.d.ts +17 -0
  62. package/dist/unstable-core-do-not-import/stream/utils/disposable.d.ts.map +1 -0
  63. package/dist/unstable-core-do-not-import/stream/utils/disposable.js +44 -0
  64. package/dist/unstable-core-do-not-import/stream/utils/disposable.mjs +41 -0
  65. package/dist/unstable-core-do-not-import/stream/utils/readableStreamFrom.d.ts +8 -0
  66. package/dist/unstable-core-do-not-import/stream/utils/readableStreamFrom.d.ts.map +1 -0
  67. package/dist/unstable-core-do-not-import/stream/utils/readableStreamFrom.js +25 -0
  68. package/dist/unstable-core-do-not-import/stream/utils/readableStreamFrom.mjs +23 -0
  69. package/dist/unstable-core-do-not-import/stream/utils/timerResource.d.ts +1 -2
  70. package/dist/unstable-core-do-not-import/stream/utils/timerResource.d.ts.map +1 -1
  71. package/dist/unstable-core-do-not-import/stream/utils/timerResource.js +8 -10
  72. package/dist/unstable-core-do-not-import/stream/utils/timerResource.mjs +8 -10
  73. package/dist/unstable-core-do-not-import/stream/utils/withPing.d.ts.map +1 -1
  74. package/dist/unstable-core-do-not-import/stream/utils/withPing.js +113 -24
  75. package/dist/unstable-core-do-not-import/stream/utils/withPing.mjs +114 -25
  76. package/dist/unstable-core-do-not-import/transformer.d.ts +1 -1
  77. package/dist/unstable-core-do-not-import.d.ts +1 -0
  78. package/dist/unstable-core-do-not-import.d.ts.map +1 -1
  79. package/dist/unstable-core-do-not-import.js +3 -0
  80. package/dist/unstable-core-do-not-import.mjs +1 -0
  81. package/dist/vendor/unpromise/unpromise.js +17 -6
  82. package/dist/vendor/unpromise/unpromise.mjs +13 -2
  83. package/package.json +2 -3
  84. package/src/adapters/node-http/writeResponse.ts +3 -0
  85. package/src/adapters/ws.ts +8 -9
  86. package/src/observable/observable.ts +17 -2
  87. package/src/unstable-core-do-not-import/http/resolveResponse.ts +8 -21
  88. package/src/unstable-core-do-not-import/stream/jsonl.ts +236 -216
  89. package/src/unstable-core-do-not-import/stream/sse.ts +112 -132
  90. package/src/unstable-core-do-not-import/stream/utils/asyncIterable.ts +50 -48
  91. package/src/unstable-core-do-not-import/stream/utils/disposable.ts +52 -0
  92. package/src/unstable-core-do-not-import/stream/utils/readableStreamFrom.ts +28 -0
  93. package/src/unstable-core-do-not-import/stream/utils/timerResource.ts +17 -15
  94. package/src/unstable-core-do-not-import/stream/utils/withPing.ts +18 -19
  95. package/src/unstable-core-do-not-import.ts +1 -0
  96. package/dist/node_modules/.pnpm/@swc_helpers@0.5.13/node_modules/@swc/helpers/esm/_define_property.js +0 -11
  97. package/dist/node_modules/.pnpm/@swc_helpers@0.5.13/node_modules/@swc/helpers/esm/_define_property.mjs +0 -9
  98. package/dist/unstable-core-do-not-import/stream/utils/createReadableStream.d.ts +0 -9
  99. package/dist/unstable-core-do-not-import/stream/utils/createReadableStream.d.ts.map +0 -1
  100. package/dist/unstable-core-do-not-import/stream/utils/createReadableStream.js +0 -25
  101. package/dist/unstable-core-do-not-import/stream/utils/createReadableStream.mjs +0 -23
  102. package/dist/unstable-core-do-not-import/stream/utils/withRefCount.d.ts +0 -17
  103. package/dist/unstable-core-do-not-import/stream/utils/withRefCount.d.ts.map +0 -1
  104. package/dist/unstable-core-do-not-import/stream/utils/withRefCount.js +0 -59
  105. package/dist/unstable-core-do-not-import/stream/utils/withRefCount.mjs +0 -57
  106. package/src/unstable-core-do-not-import/stream/utils/createReadableStream.ts +0 -25
  107. package/src/unstable-core-do-not-import/stream/utils/withRefCount.ts +0 -93
@@ -1,8 +1,10 @@
1
+ import { Unpromise } from '../../vendor/unpromise';
1
2
  import { isAsyncIterable, isFunction, isObject, run } from '../utils';
3
+ import { iteratorResource } from './utils/asyncIterable';
2
4
  import type { Deferred } from './utils/createDeferred';
3
5
  import { createDeferred } from './utils/createDeferred';
4
- import { createReadableStream } from './utils/createReadableStream';
5
- import { withRefCount } from './utils/withRefCount';
6
+ import { makeAsyncResource, makeResource } from './utils/disposable';
7
+ import { readableStreamFrom } from './utils/readableStreamFrom';
6
8
 
7
9
  /**
8
10
  * A subset of the standard ReadableStream properties needed by tRPC internally.
@@ -19,6 +21,8 @@ export type NodeJSReadableStreamEsque = {
19
21
  ): NodeJSReadableStreamEsque;
20
22
  };
21
23
 
24
+ // ---------- types
25
+
22
26
  // ---------- types
23
27
  const CHUNK_VALUE_TYPE_PROMISE = 0;
24
28
  type CHUNK_VALUE_TYPE_PROMISE = typeof CHUNK_VALUE_TYPE_PROMISE;
@@ -32,8 +36,8 @@ type PROMISE_STATUS_REJECTED = typeof PROMISE_STATUS_REJECTED;
32
36
 
33
37
  const ASYNC_ITERABLE_STATUS_RETURN = 0;
34
38
  type ASYNC_ITERABLE_STATUS_RETURN = typeof ASYNC_ITERABLE_STATUS_RETURN;
35
- const ASYNC_ITERABLE_STATUS_VALUE = 1;
36
- type ASYNC_ITERABLE_STATUS_VALUE = typeof ASYNC_ITERABLE_STATUS_VALUE;
39
+ const ASYNC_ITERABLE_STATUS_YIELD = 1;
40
+ type ASYNC_ITERABLE_STATUS_YIELD = typeof ASYNC_ITERABLE_STATUS_YIELD;
37
41
  const ASYNC_ITERABLE_STATUS_ERROR = 2;
38
42
  type ASYNC_ITERABLE_STATUS_ERROR = typeof ASYNC_ITERABLE_STATUS_ERROR;
39
43
 
@@ -77,7 +81,7 @@ type IterableChunk =
77
81
  ]
78
82
  | [
79
83
  chunkIndex: ChunkIndex,
80
- status: ASYNC_ITERABLE_STATUS_VALUE,
84
+ status: ASYNC_ITERABLE_STATUS_YIELD,
81
85
  value: EncodedValue,
82
86
  ]
83
87
  | [
@@ -117,101 +121,98 @@ class MaxDepthError extends Error {
117
121
  }
118
122
  }
119
123
 
120
- function createBatchStreamProducer(opts: ProducerOptions) {
124
+ async function* createBatchStreamProducer(
125
+ opts: ProducerOptions,
126
+ ): AsyncIterable<Head | ChunkData, void> {
121
127
  const { data } = opts;
122
128
  let counter = 0 as ChunkIndex;
123
129
  const placeholder = 0 as PlaceholderValue;
124
130
 
125
- const stream = createReadableStream<ChunkData>();
126
- const pending = withRefCount(new Set<ChunkIndex>(), () => {
127
- if (!stream.cancelled()) {
128
- stream.controller.close();
129
- }
130
- });
131
+ await using queue = makeAsyncResource(
132
+ new Set<{
133
+ iterator: AsyncIterator<ChunkData, ChunkData>;
134
+ nextPromise: Promise<IteratorResult<ChunkData, ChunkData>>;
135
+ }>(),
136
+ async () => {
137
+ await Promise.all(Array.from(queue).map((it) => it.iterator.return?.()));
138
+ },
139
+ );
140
+ function registerAsync(
141
+ callback: (idx: ChunkIndex) => AsyncIterable<ChunkData, ChunkData>,
142
+ ) {
143
+ const idx = counter++ as ChunkIndex;
131
144
 
132
- const maybeEnqueue = (chunk: ChunkData) => {
133
- if (!stream.cancelled()) {
134
- stream.controller.enqueue(chunk);
135
- }
136
- };
145
+ const iterator = callback(idx)[Symbol.asyncIterator]();
137
146
 
138
- function encodePromise(promise: Promise<unknown>, path: (string | number)[]) {
139
- const error = checkMaxDepth(path);
140
- if (error) {
141
- // Catch any errors from the original promise to ensure they're reported
142
- promise.catch((cause) => {
143
- opts.onError?.({ error: cause, path });
144
- });
145
- // Replace the promise with a rejected one containing the max depth error
146
- promise = Promise.reject(error);
147
- }
148
- const idx = counter++ as ChunkIndex;
149
- pending.add(idx);
147
+ const nextPromise = iterator.next();
150
148
 
151
- promise
152
- .then((it) => {
153
- maybeEnqueue([idx, PROMISE_STATUS_FULFILLED, encode(it, path)]);
154
- })
155
- .catch((cause) => {
149
+ nextPromise.catch(() => {
150
+ // prevent unhandled promise rejection
151
+ });
152
+ queue.add({
153
+ iterator,
154
+ nextPromise,
155
+ });
156
+
157
+ return idx;
158
+ }
159
+
160
+ function encodePromise(promise: Promise<unknown>, path: (string | number)[]) {
161
+ return registerAsync(async function* (idx) {
162
+ const error = checkMaxDepth(path);
163
+ if (error) {
164
+ // Catch any errors from the original promise to ensure they're reported
165
+ promise.catch((cause) => {
166
+ opts.onError?.({ error: cause, path });
167
+ });
168
+ // Replace the promise with a rejected one containing the max depth error
169
+ promise = Promise.reject(error);
170
+ }
171
+ try {
172
+ const next = await promise;
173
+ return [idx, PROMISE_STATUS_FULFILLED, encode(next, path)];
174
+ } catch (cause) {
156
175
  opts.onError?.({ error: cause, path });
157
- maybeEnqueue([
176
+ return [
158
177
  idx,
159
178
  PROMISE_STATUS_REJECTED,
160
179
  opts.formatError?.({ error: cause, path }),
161
- ]);
162
- })
163
- .finally(() => {
164
- pending.delete(idx);
165
- });
166
- return idx;
180
+ ];
181
+ }
182
+ });
167
183
  }
168
184
  function encodeAsyncIterable(
169
185
  iterable: AsyncIterable<unknown>,
170
186
  path: (string | number)[],
171
187
  ) {
172
- const idx = counter++ as ChunkIndex;
173
- pending.add(idx);
174
- run(async () => {
188
+ return registerAsync(async function* (idx) {
175
189
  const error = checkMaxDepth(path);
176
190
  if (error) {
177
191
  throw error;
178
192
  }
179
- const iterator = iterable[Symbol.asyncIterator]();
180
-
181
- while (true) {
182
- if (stream.cancelled()) {
183
- const res = await iterator.return?.();
184
- return res?.value;
185
- }
186
- const next = await iterator.next();
187
-
188
- if (next.done) {
189
- maybeEnqueue([
190
- idx,
191
- ASYNC_ITERABLE_STATUS_RETURN,
192
- encode(next.value, path),
193
- ]);
194
- break;
193
+ await using iterator = iteratorResource(iterable);
194
+
195
+ try {
196
+ while (true) {
197
+ const next = await iterator.next();
198
+ if (next.done) {
199
+ return [
200
+ idx,
201
+ ASYNC_ITERABLE_STATUS_RETURN,
202
+ encode(next.value, path),
203
+ ];
204
+ }
205
+ yield [idx, ASYNC_ITERABLE_STATUS_YIELD, encode(next.value, path)];
195
206
  }
196
- maybeEnqueue([
197
- idx,
198
- ASYNC_ITERABLE_STATUS_VALUE,
199
- encode(next.value, path),
200
- ]);
201
- }
202
- })
203
- .catch((cause) => {
207
+ } catch (cause) {
204
208
  opts.onError?.({ error: cause, path });
205
- maybeEnqueue([
209
+ return [
206
210
  idx,
207
211
  ASYNC_ITERABLE_STATUS_ERROR,
208
212
  opts.formatError?.({ error: cause, path }),
209
- ]);
210
- })
211
- .finally(() => {
212
- pending.delete(idx);
213
- });
214
- return idx;
213
+ ];
214
+ }
215
+ });
215
216
  }
216
217
  function checkMaxDepth(path: (string | number)[]) {
217
218
  if (opts.maxDepth && path.length > opts.maxDepth) {
@@ -266,20 +267,35 @@ function createBatchStreamProducer(opts: ProducerOptions) {
266
267
  for (const [key, item] of Object.entries(data)) {
267
268
  newHead[key] = encode(item, [key]);
268
269
  }
269
- pending.activate();
270
270
 
271
- return [newHead, stream.readable] as const;
271
+ yield newHead;
272
+
273
+ // Process all async iterables in parallel by racing their next values
274
+ while (queue.size > 0) {
275
+ // Race all iterators to get the next value from any of them
276
+ const [entry, res] = await Unpromise.race(
277
+ Array.from(queue).map(async (it) => [it, await it.nextPromise] as const),
278
+ );
279
+
280
+ yield res.value;
281
+
282
+ // Remove current iterator and re-add if not done
283
+ queue.delete(entry);
284
+ if (!res.done) {
285
+ entry.nextPromise = entry.iterator.next();
286
+ queue.add(entry);
287
+ }
288
+ }
272
289
  }
273
290
  /**
274
291
  * JSON Lines stream producer
275
292
  * @see https://jsonlines.org/
276
293
  */
277
294
  export function jsonlStreamProducer(opts: ProducerOptions) {
278
- let [head, stream] = createBatchStreamProducer(opts);
295
+ let stream = readableStreamFrom(createBatchStreamProducer(opts));
279
296
 
280
297
  const { serialize } = opts;
281
298
  if (serialize) {
282
- head = serialize(head);
283
299
  stream = stream.pipeThrough(
284
300
  new TransformStream({
285
301
  transform(chunk, controller) {
@@ -292,9 +308,6 @@ export function jsonlStreamProducer(opts: ProducerOptions) {
292
308
  return stream
293
309
  .pipeThrough(
294
310
  new TransformStream({
295
- start(controller) {
296
- controller.enqueue(JSON.stringify(head) + '\n');
297
- },
298
311
  transform(chunk, controller) {
299
312
  controller.enqueue(JSON.stringify(chunk) + '\n');
300
313
  },
@@ -320,17 +333,20 @@ export type ConsumerOnError = (opts: { error: unknown }) => void;
320
333
  const nodeJsStreamToReaderEsque = (source: NodeJSReadableStreamEsque) => {
321
334
  return {
322
335
  getReader() {
323
- const { readable, controller } = createReadableStream<Uint8Array>();
324
- source.on('data', (chunk) => {
325
- controller.enqueue(chunk);
326
- });
327
- source.on('end', () => {
328
- controller.close();
329
- });
330
- source.on('error', (error) => {
331
- controller.error(error);
336
+ const stream = new ReadableStream<Uint8Array>({
337
+ start(controller) {
338
+ source.on('data', (chunk) => {
339
+ controller.enqueue(chunk);
340
+ });
341
+ source.on('end', () => {
342
+ controller.close();
343
+ });
344
+ source.on('error', (error) => {
345
+ controller.error(error);
346
+ });
347
+ },
332
348
  });
333
- return readable.getReader();
349
+ return stream.getReader();
334
350
  },
335
351
  };
336
352
  };
@@ -394,6 +410,102 @@ function createConsumerStream<THead>(
394
410
  }),
395
411
  );
396
412
  }
413
+ /**
414
+ * Represents a chunk of data or stream interruption error that can be enqueued to a controller
415
+ */
416
+ type ControllerChunk = ChunkData | StreamInterruptedError;
417
+
418
+ /**
419
+ * Creates a handler for managing stream controllers and their lifecycle
420
+ */
421
+ function createStreamsManager(abortController: AbortController) {
422
+ const controllerMap = new Map<
423
+ ChunkIndex,
424
+ ReturnType<typeof createStreamController>
425
+ >();
426
+
427
+ /**
428
+ * Checks if there are no pending controllers or deferred promises
429
+ */
430
+ function isEmpty() {
431
+ return Array.from(controllerMap.values()).every((c) => c.closed);
432
+ }
433
+
434
+ /**
435
+ * Creates a stream controller
436
+ */
437
+ function createStreamController() {
438
+ let originalController: ReadableStreamDefaultController<ControllerChunk>;
439
+ const stream = new ReadableStream<ControllerChunk>({
440
+ start(controller) {
441
+ originalController = controller;
442
+ },
443
+ });
444
+
445
+ const streamController = {
446
+ enqueue: (v: ControllerChunk) => originalController.enqueue(v),
447
+ close: () => {
448
+ originalController.close();
449
+
450
+ // mark as closed and remove methods
451
+ Object.assign(streamController, {
452
+ closed: true,
453
+ close: () => {
454
+ // noop
455
+ },
456
+ enqueue: () => {
457
+ // noop
458
+ },
459
+ getReaderResource: null,
460
+ });
461
+
462
+ if (isEmpty()) {
463
+ abortController.abort();
464
+ }
465
+ },
466
+ closed: false,
467
+ getReaderResource: () => {
468
+ const reader = stream.getReader();
469
+
470
+ return makeResource(reader, () => {
471
+ reader.releaseLock();
472
+ streamController.close();
473
+ });
474
+ },
475
+ };
476
+
477
+ return streamController;
478
+ }
479
+
480
+ /**
481
+ * Gets or creates a stream controller
482
+ */
483
+ function getOrCreate(chunkId: ChunkIndex) {
484
+ let c = controllerMap.get(chunkId);
485
+ if (!c) {
486
+ c = createStreamController();
487
+ controllerMap.set(chunkId, c);
488
+ }
489
+ return c;
490
+ }
491
+
492
+ /**
493
+ * Cancels all pending controllers and rejects deferred promises
494
+ */
495
+ function cancelAll(reason: unknown) {
496
+ const error = new StreamInterruptedError(reason);
497
+ for (const controller of controllerMap.values()) {
498
+ controller.enqueue(error);
499
+ controller.close();
500
+ }
501
+ }
502
+
503
+ return {
504
+ getOrCreate,
505
+ isEmpty,
506
+ cancelAll,
507
+ };
508
+ }
397
509
 
398
510
  /**
399
511
  * JSON Lines stream consumer
@@ -423,130 +535,56 @@ export async function jsonlStreamConsumer<THead>(opts: {
423
535
  }
424
536
  let headDeferred: null | Deferred<THead> = createDeferred();
425
537
 
426
- type ControllerChunk = ChunkData | StreamInterruptedError;
427
- type ChunkController = ReadableStreamDefaultController<ControllerChunk>;
428
- /**
429
- * This is needed as new values can come in before the controller has read the chunk
430
- * Not pretty, could likely be refactored and omitted somehow
431
- */
432
- const chunkDeferred = new Map<ChunkIndex, Deferred<ChunkController>>();
433
-
434
- const controllers = new Map<ChunkIndex, ChunkController>();
435
-
436
- const maybeAbort = () => {
437
- if (chunkDeferred.size === 0 && controllers.size === 0) {
438
- // nothing is listening to the stream anymore
439
- opts.abortController?.abort();
440
- }
441
- };
538
+ const streamManager = createStreamsManager(opts.abortController);
442
539
 
443
540
  function decodeChunkDefinition(value: ChunkDefinition) {
444
541
  const [_path, type, chunkId] = value;
445
542
 
446
- const stream = createReadableStream<ChunkData>();
447
-
448
- controllers.set(chunkId, stream.controller);
449
-
450
- // resolve chunk deferred if it exists
451
- const deferred = chunkDeferred.get(chunkId);
452
- if (deferred) {
453
- deferred.resolve(stream.controller);
454
- chunkDeferred.delete(chunkId);
455
- }
543
+ const controller = streamManager.getOrCreate(chunkId);
456
544
 
457
545
  switch (type) {
458
546
  case CHUNK_VALUE_TYPE_PROMISE: {
459
- return new Promise((resolve, reject) => {
460
- // listen for next value in the stream
461
- const reader = stream.readable.getReader();
462
- reader
463
- .read()
464
- .then((it) => {
465
- if (it.done) {
466
- reject(new Error('Promise chunk ended without value'));
467
- return;
468
- }
469
- if (it.value instanceof StreamInterruptedError) {
470
- reject(it.value);
471
- return;
472
- }
473
- const value = it.value;
474
- const [_chunkId, status, data] = value as PromiseChunk;
475
- switch (status) {
476
- case PROMISE_STATUS_FULFILLED:
477
- resolve(decode(data));
478
-
479
- break;
480
- case PROMISE_STATUS_REJECTED:
481
- reject(
482
- opts.formatError?.({ error: data }) ?? new AsyncError(data),
483
- );
484
- break;
485
- }
486
- })
487
- .catch(reject)
488
- .finally(() => {
489
- controllers.delete(chunkId);
490
- maybeAbort();
491
- });
547
+ return run(async () => {
548
+ using reader = controller.getReaderResource();
549
+
550
+ const { value } = await reader.read();
551
+ if (value instanceof StreamInterruptedError) {
552
+ throw value;
553
+ }
554
+ const [_chunkId, status, data] = value as PromiseChunk;
555
+ switch (status) {
556
+ case PROMISE_STATUS_FULFILLED:
557
+ return decode(data);
558
+ case PROMISE_STATUS_REJECTED:
559
+ throw opts.formatError?.({ error: data }) ?? new AsyncError(data);
560
+ }
492
561
  });
493
562
  }
494
563
  case CHUNK_VALUE_TYPE_ASYNC_ITERABLE: {
495
- const reader = stream.readable.getReader();
496
- const iterator: AsyncIterator<unknown> = {
497
- next: async () => {
498
- const { done, value } = await reader.read();
564
+ return run(async function* () {
565
+ using reader = controller.getReaderResource();
566
+
567
+ while (true) {
568
+ const { value } = await reader.read();
499
569
  if (value instanceof StreamInterruptedError) {
500
570
  throw value;
501
571
  }
502
- if (done) {
503
- controllers.delete(chunkId);
504
- maybeAbort();
505
-
506
- return {
507
- done: true,
508
- value: undefined,
509
- };
510
- }
511
572
 
512
573
  const [_chunkId, status, data] = value as IterableChunk;
513
574
 
514
575
  switch (status) {
515
- case ASYNC_ITERABLE_STATUS_VALUE:
516
- return {
517
- done: false,
518
- value: decode(data),
519
- };
576
+ case ASYNC_ITERABLE_STATUS_YIELD:
577
+ yield decode(data);
578
+ break;
520
579
  case ASYNC_ITERABLE_STATUS_RETURN:
521
- controllers.delete(chunkId);
522
- maybeAbort();
523
-
524
- return {
525
- done: true,
526
- value: decode(data),
527
- };
580
+ return decode(data);
528
581
  case ASYNC_ITERABLE_STATUS_ERROR:
529
- controllers.delete(chunkId);
530
- maybeAbort();
531
-
532
582
  throw (
533
583
  opts.formatError?.({ error: data }) ?? new AsyncError(data)
534
584
  );
535
585
  }
536
- },
537
- return: async () => {
538
- controllers.delete(chunkId);
539
- maybeAbort();
540
-
541
- return {
542
- done: true,
543
- value: undefined,
544
- };
545
- },
546
- };
547
- return {
548
- [Symbol.asyncIterator]: () => iterator,
549
- };
586
+ }
587
+ });
550
588
  }
551
589
  }
552
590
  }
@@ -571,16 +609,12 @@ export async function jsonlStreamConsumer<THead>(opts: {
571
609
  const error = new StreamInterruptedError(reason);
572
610
 
573
611
  headDeferred?.reject(error);
574
- for (const controller of controllers.values()) {
575
- controller.enqueue(error);
576
- controller.close();
577
- }
578
- controllers.clear();
612
+ streamManager.cancelAll(error);
579
613
  };
580
614
  source
581
615
  .pipeTo(
582
616
  new WritableStream({
583
- async write(chunkOrHead) {
617
+ write(chunkOrHead) {
584
618
  if (headDeferred) {
585
619
  const head = chunkOrHead as Record<number | string, unknown>;
586
620
 
@@ -596,17 +630,8 @@ export async function jsonlStreamConsumer<THead>(opts: {
596
630
  const chunk = chunkOrHead as ChunkData;
597
631
  const [idx] = chunk;
598
632
 
599
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
600
- let readController = controllers.get(idx)!;
601
- if (!readController) {
602
- let deferred = chunkDeferred.get(idx);
603
- if (!deferred) {
604
- deferred = createDeferred();
605
- chunkDeferred.set(idx, deferred);
606
- }
607
- readController = await deferred.promise;
608
- }
609
- readController.enqueue(chunk);
633
+ const controller = streamManager.getOrCreate(idx);
634
+ controller.enqueue(chunk);
610
635
  },
611
636
  close: closeOrAbort,
612
637
  abort: closeOrAbort,
@@ -620,10 +645,5 @@ export async function jsonlStreamConsumer<THead>(opts: {
620
645
  closeOrAbort(error);
621
646
  });
622
647
 
623
- return [
624
- await headDeferred.promise,
625
- {
626
- controllers,
627
- },
628
- ] as const;
648
+ return [await headDeferred.promise, streamManager] as const;
629
649
  }