@trpc/server 11.0.0-alpha-tmp-app-router-example.388 → 11.0.0-alpha-tmp-issues-5851-take-two.448

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 (139) hide show
  1. package/dist/@trpc/server/http.d.ts +1 -2
  2. package/dist/@trpc/server/http.d.ts.map +1 -1
  3. package/dist/@trpc/server/index.d.ts +1 -1
  4. package/dist/@trpc/server/index.d.ts.map +1 -1
  5. package/dist/@trpc/server/rpc.d.ts +1 -1
  6. package/dist/@trpc/server/rpc.d.ts.map +1 -1
  7. package/dist/adapters/aws-lambda/getPlanner.d.ts.map +1 -1
  8. package/dist/adapters/aws-lambda/getPlanner.js +19 -2
  9. package/dist/adapters/aws-lambda/getPlanner.mjs +19 -2
  10. package/dist/adapters/next-app-dir/nextAppDirCaller.d.ts.map +1 -1
  11. package/dist/adapters/next-app-dir/nextAppDirCaller.js +1 -1
  12. package/dist/adapters/next-app-dir/nextAppDirCaller.mjs +1 -1
  13. package/dist/adapters/next-app-dir/redirect.d.ts.map +1 -1
  14. package/dist/adapters/next.js +1 -1
  15. package/dist/adapters/next.mjs +1 -1
  16. package/dist/adapters/node-http/incomingMessageToRequest.d.ts +0 -1
  17. package/dist/adapters/node-http/incomingMessageToRequest.d.ts.map +1 -1
  18. package/dist/adapters/node-http/incomingMessageToRequest.js +3 -1
  19. package/dist/adapters/node-http/incomingMessageToRequest.mjs +3 -1
  20. package/dist/adapters/node-http/nodeHTTPRequestHandler.d.ts.map +1 -1
  21. package/dist/adapters/node-http/nodeHTTPRequestHandler.js +30 -7
  22. package/dist/adapters/node-http/nodeHTTPRequestHandler.mjs +30 -7
  23. package/dist/adapters/node-http/types.d.ts +0 -1
  24. package/dist/adapters/node-http/types.d.ts.map +1 -1
  25. package/dist/adapters/standalone.d.ts +0 -1
  26. package/dist/adapters/standalone.d.ts.map +1 -1
  27. package/dist/adapters/ws.d.ts +3 -4
  28. package/dist/adapters/ws.d.ts.map +1 -1
  29. package/dist/adapters/ws.js +172 -114
  30. package/dist/adapters/ws.mjs +172 -114
  31. package/dist/bundle-analysis.json +236 -155
  32. package/dist/http.js +3 -0
  33. package/dist/http.mjs +1 -0
  34. package/dist/index.js +7 -5
  35. package/dist/index.mjs +3 -2
  36. package/dist/observable/observable.d.ts +1 -0
  37. package/dist/observable/observable.d.ts.map +1 -1
  38. package/dist/observable/observable.js +55 -0
  39. package/dist/observable/observable.mjs +55 -1
  40. package/dist/unstable-core-do-not-import/createProxy.d.ts +3 -3
  41. package/dist/unstable-core-do-not-import/createProxy.d.ts.map +1 -1
  42. package/dist/unstable-core-do-not-import/createProxy.js +15 -6
  43. package/dist/unstable-core-do-not-import/createProxy.mjs +15 -6
  44. package/dist/unstable-core-do-not-import/http/contentType.d.ts +7 -4
  45. package/dist/unstable-core-do-not-import/http/contentType.d.ts.map +1 -1
  46. package/dist/unstable-core-do-not-import/http/contentType.js +60 -17
  47. package/dist/unstable-core-do-not-import/http/contentType.mjs +61 -18
  48. package/dist/unstable-core-do-not-import/http/formDataToObject.d.ts.map +1 -0
  49. package/dist/unstable-core-do-not-import/http/formDataToObject.js +40 -0
  50. package/dist/unstable-core-do-not-import/http/formDataToObject.mjs +38 -0
  51. package/dist/unstable-core-do-not-import/http/getHTTPStatusCode.d.ts.map +1 -1
  52. package/dist/unstable-core-do-not-import/http/getHTTPStatusCode.js +4 -4
  53. package/dist/unstable-core-do-not-import/http/getHTTPStatusCode.mjs +4 -4
  54. package/dist/unstable-core-do-not-import/http/parseConnectionParams.d.ts +4 -0
  55. package/dist/unstable-core-do-not-import/http/parseConnectionParams.d.ts.map +1 -0
  56. package/dist/unstable-core-do-not-import/http/parseConnectionParams.js +42 -0
  57. package/dist/unstable-core-do-not-import/http/parseConnectionParams.mjs +39 -0
  58. package/dist/unstable-core-do-not-import/http/resolveResponse.d.ts.map +1 -1
  59. package/dist/unstable-core-do-not-import/http/resolveResponse.js +302 -149
  60. package/dist/unstable-core-do-not-import/http/resolveResponse.mjs +301 -148
  61. package/dist/unstable-core-do-not-import/http/types.d.ts +26 -2
  62. package/dist/unstable-core-do-not-import/http/types.d.ts.map +1 -1
  63. package/dist/unstable-core-do-not-import/initTRPC.d.ts +12 -12
  64. package/dist/unstable-core-do-not-import/initTRPC.d.ts.map +1 -1
  65. package/dist/unstable-core-do-not-import/middleware.d.ts +3 -3
  66. package/dist/unstable-core-do-not-import/middleware.d.ts.map +1 -1
  67. package/dist/unstable-core-do-not-import/procedureBuilder.d.ts +3 -1
  68. package/dist/unstable-core-do-not-import/procedureBuilder.d.ts.map +1 -1
  69. package/dist/unstable-core-do-not-import/rootConfig.d.ts +12 -0
  70. package/dist/unstable-core-do-not-import/rootConfig.d.ts.map +1 -1
  71. package/dist/unstable-core-do-not-import/router.d.ts +2 -2
  72. package/dist/unstable-core-do-not-import/router.d.ts.map +1 -1
  73. package/dist/unstable-core-do-not-import/router.js +7 -2
  74. package/dist/unstable-core-do-not-import/router.mjs +7 -2
  75. package/dist/unstable-core-do-not-import/rpc/envelopes.d.ts +7 -0
  76. package/dist/unstable-core-do-not-import/rpc/envelopes.d.ts.map +1 -1
  77. package/dist/unstable-core-do-not-import/rpc/index.d.ts +1 -1
  78. package/dist/unstable-core-do-not-import/rpc/index.d.ts.map +1 -1
  79. package/dist/unstable-core-do-not-import/stream/{stream.d.ts → jsonl.d.ts} +5 -5
  80. package/dist/unstable-core-do-not-import/stream/jsonl.d.ts.map +1 -0
  81. package/dist/unstable-core-do-not-import/stream/{stream.js → jsonl.js} +148 -111
  82. package/dist/unstable-core-do-not-import/stream/{stream.mjs → jsonl.mjs} +147 -110
  83. package/dist/unstable-core-do-not-import/stream/sse.d.ts +86 -0
  84. package/dist/unstable-core-do-not-import/stream/sse.d.ts.map +1 -0
  85. package/dist/unstable-core-do-not-import/stream/sse.js +178 -0
  86. package/dist/unstable-core-do-not-import/stream/sse.mjs +172 -0
  87. package/dist/unstable-core-do-not-import/stream/utils/createDeferred.d.ts +18 -0
  88. package/dist/unstable-core-do-not-import/stream/utils/createDeferred.d.ts.map +1 -0
  89. package/dist/unstable-core-do-not-import/stream/utils/createDeferred.js +46 -0
  90. package/dist/unstable-core-do-not-import/stream/utils/createDeferred.mjs +43 -0
  91. package/dist/unstable-core-do-not-import/stream/utils/createReadableStream.d.ts +10 -0
  92. package/dist/unstable-core-do-not-import/stream/utils/createReadableStream.d.ts.map +1 -0
  93. package/dist/unstable-core-do-not-import/stream/utils/createReadableStream.js +31 -0
  94. package/dist/unstable-core-do-not-import/stream/utils/createReadableStream.mjs +29 -0
  95. package/dist/unstable-core-do-not-import/stream/utils/createServer.d.ts +7 -0
  96. package/dist/unstable-core-do-not-import/stream/utils/createServer.d.ts.map +1 -0
  97. package/dist/unstable-core-do-not-import/transformer.d.ts +5 -5
  98. package/dist/unstable-core-do-not-import/utils.d.ts +4 -0
  99. package/dist/unstable-core-do-not-import/utils.d.ts.map +1 -1
  100. package/dist/unstable-core-do-not-import/utils.js +4 -0
  101. package/dist/unstable-core-do-not-import/utils.mjs +4 -1
  102. package/dist/unstable-core-do-not-import.d.ts +5 -2
  103. package/dist/unstable-core-do-not-import.d.ts.map +1 -1
  104. package/dist/unstable-core-do-not-import.js +19 -7
  105. package/dist/unstable-core-do-not-import.mjs +6 -3
  106. package/package.json +6 -6
  107. package/src/@trpc/server/http.ts +7 -2
  108. package/src/@trpc/server/index.ts +1 -0
  109. package/src/@trpc/server/rpc.ts +1 -0
  110. package/src/adapters/aws-lambda/getPlanner.ts +21 -2
  111. package/src/adapters/next-app-dir/nextAppDirCaller.ts +2 -1
  112. package/src/adapters/node-http/incomingMessageToRequest.ts +3 -2
  113. package/src/adapters/node-http/nodeHTTPRequestHandler.ts +32 -7
  114. package/src/adapters/ws.ts +193 -107
  115. package/src/observable/observable.ts +63 -0
  116. package/src/unstable-core-do-not-import/createProxy.ts +23 -8
  117. package/src/unstable-core-do-not-import/http/contentType.ts +83 -21
  118. package/src/{adapters/next-app-dir → unstable-core-do-not-import/http}/formDataToObject.ts +18 -10
  119. package/src/unstable-core-do-not-import/http/getHTTPStatusCode.ts +4 -7
  120. package/src/unstable-core-do-not-import/http/parseConnectionParams.ts +49 -0
  121. package/src/unstable-core-do-not-import/http/resolveResponse.ts +333 -164
  122. package/src/unstable-core-do-not-import/http/types.ts +31 -2
  123. package/src/unstable-core-do-not-import/procedureBuilder.ts +8 -1
  124. package/src/unstable-core-do-not-import/rootConfig.ts +12 -0
  125. package/src/unstable-core-do-not-import/router.ts +47 -35
  126. package/src/unstable-core-do-not-import/rpc/envelopes.ts +9 -0
  127. package/src/unstable-core-do-not-import/rpc/index.ts +1 -0
  128. package/src/unstable-core-do-not-import/stream/{stream.ts → jsonl.ts} +163 -110
  129. package/src/unstable-core-do-not-import/stream/sse.ts +288 -0
  130. package/src/unstable-core-do-not-import/stream/utils/createDeferred.ts +48 -0
  131. package/src/unstable-core-do-not-import/stream/utils/createReadableStream.ts +31 -0
  132. package/src/unstable-core-do-not-import/stream/utils/createServer.ts +44 -0
  133. package/src/unstable-core-do-not-import/utils.ts +5 -0
  134. package/src/unstable-core-do-not-import.ts +5 -2
  135. package/dist/adapters/next-app-dir/formDataToObject.d.ts.map +0 -1
  136. package/dist/adapters/next-app-dir/formDataToObject.js +0 -34
  137. package/dist/adapters/next-app-dir/formDataToObject.mjs +0 -32
  138. package/dist/unstable-core-do-not-import/stream/stream.d.ts.map +0 -1
  139. /package/dist/{adapters/next-app-dir → unstable-core-do-not-import/http}/formDataToObject.d.ts +0 -0
@@ -1,19 +1,8 @@
1
- /* eslint-disable @typescript-eslint/no-non-null-assertion */
2
- import { isAsyncIterable, isFunction, isObject } from '../utils';
3
-
4
- // ---------- utils
5
-
6
- function createReadableStream<TValue = unknown>() {
7
- let controller: ReadableStreamDefaultController<TValue> =
8
- null as unknown as ReadableStreamDefaultController<TValue>;
9
- const stream = new ReadableStream<TValue>({
10
- start(c) {
11
- controller = c;
12
- },
13
- });
14
-
15
- return [stream, controller] as const;
16
- }
1
+ import { getTRPCErrorFromUnknown } from '../error/TRPCError';
2
+ import { isAsyncIterable, isFunction, isObject, run } from '../utils';
3
+ import type { Deferred } from './utils/createDeferred';
4
+ import { createDeferred } from './utils/createDeferred';
5
+ import { createReadableStream } from './utils/createReadableStream';
17
6
 
18
7
  /**
19
8
  * A subset of the standard ReadableStream properties needed by tRPC internally.
@@ -65,19 +54,19 @@ type ChunkDefinition = [
65
54
  type: ChunkValueType,
66
55
  chunkId: ChunkIndex,
67
56
  ];
68
- type HydratedValue = [
57
+ type DehydratedValue = [
69
58
  // data
70
59
  [unknown],
71
60
  // chunk descriptions
72
61
  ...ChunkDefinition[],
73
62
  ];
74
63
 
75
- type Head = Record<string, HydratedValue>;
64
+ type Head = Record<string, DehydratedValue>;
76
65
  type PromiseChunk =
77
66
  | [
78
67
  chunkIndex: ChunkIndex,
79
68
  status: PROMISE_STATUS_FULFILLED,
80
- value: HydratedValue,
69
+ value: DehydratedValue,
81
70
  ]
82
71
  | [chunkIndex: ChunkIndex, status: PROMISE_STATUS_REJECTED, error: unknown];
83
72
  type IterableChunk =
@@ -85,7 +74,7 @@ type IterableChunk =
85
74
  | [
86
75
  chunkIndex: ChunkIndex,
87
76
  status: ASYNC_ITERABLE_STATUS_VALUE,
88
- value: HydratedValue,
77
+ value: DehydratedValue,
89
78
  ]
90
79
  | [
91
80
  chunkIndex: ChunkIndex,
@@ -111,7 +100,7 @@ export type ProducerOnError = (opts: {
111
100
  }) => void;
112
101
  export interface ProducerOptions {
113
102
  serialize?: Serialize;
114
- data: Record<number, unknown>;
103
+ data: Record<string, unknown> | unknown[];
115
104
  onError?: ProducerOnError;
116
105
  formatError?: (opts: {
117
106
  error: unknown;
@@ -131,14 +120,15 @@ function createBatchStreamProducer(opts: ProducerOptions) {
131
120
  let counter = 0 as ChunkIndex;
132
121
  const placeholder = 0 as PlaceholderValue;
133
122
 
134
- const [stream, controller] = createReadableStream<ChunkData>();
123
+ const stream = createReadableStream<ChunkData>();
135
124
  const pending = new Set<ChunkIndex>();
125
+
136
126
  function maybeClose() {
137
- if (pending.size === 0) {
138
- controller.close();
127
+ if (pending.size === 0 && !stream.cancelled()) {
128
+ stream.controller.close();
139
129
  }
140
130
  }
141
- function hydratePromise(
131
+ function dehydratePromise(
142
132
  promise: Promise<unknown>,
143
133
  path: (string | number)[],
144
134
  ) {
@@ -152,19 +142,24 @@ function createBatchStreamProducer(opts: ProducerOptions) {
152
142
  }
153
143
  const idx = counter++ as ChunkIndex;
154
144
  pending.add(idx);
155
- const enqueue = (value: PromiseChunk) => {
156
- controller.enqueue(value);
157
- };
158
- promise
145
+
146
+ Promise.race([promise, stream.cancelledPromise])
159
147
  .then((it) => {
160
- enqueue([idx, PROMISE_STATUS_FULFILLED, hydrate(it, path)]);
148
+ if (it === null) {
149
+ return;
150
+ }
151
+ stream.controller.enqueue([
152
+ idx,
153
+ PROMISE_STATUS_FULFILLED,
154
+ dehydrate(it, path),
155
+ ]);
161
156
  })
162
- .catch((error) => {
163
- opts.onError?.({ error, path });
164
- enqueue([
157
+ .catch((cause) => {
158
+ opts.onError?.({ error: cause, path });
159
+ stream.controller.enqueue([
165
160
  idx,
166
161
  PROMISE_STATUS_REJECTED,
167
- opts.formatError?.({ error, path }),
162
+ opts.formatError?.({ error: cause, path }),
168
163
  ]);
169
164
  })
170
165
  .finally(() => {
@@ -173,7 +168,7 @@ function createBatchStreamProducer(opts: ProducerOptions) {
173
168
  });
174
169
  return idx;
175
170
  }
176
- function hydrateAsyncIterable(
171
+ function dehydrateAsyncIterable(
177
172
  iterable: AsyncIterable<unknown>,
178
173
  path: (string | number)[],
179
174
  ) {
@@ -187,28 +182,56 @@ function createBatchStreamProducer(opts: ProducerOptions) {
187
182
  }
188
183
  const idx = counter++ as ChunkIndex;
189
184
  pending.add(idx);
190
- void (async () => {
191
- try {
192
- for await (const item of iterable) {
193
- controller.enqueue([
185
+ run(async () => {
186
+ const iterator = iterable[Symbol.asyncIterator]();
187
+
188
+ while (true) {
189
+ const next = await Promise.race([
190
+ iterator.next().catch(getTRPCErrorFromUnknown),
191
+ stream.cancelledPromise,
192
+ ]);
193
+
194
+ if (next instanceof Error) {
195
+ opts.onError?.({ error: next, path });
196
+
197
+ stream.controller.enqueue([
194
198
  idx,
195
- ASYNC_ITERABLE_STATUS_VALUE,
196
- hydrate(item, path),
199
+ ASYNC_ITERABLE_STATUS_ERROR,
200
+ opts.formatError?.({ error: next, path }),
197
201
  ]);
202
+ return;
203
+ }
204
+ if (next === 'cancelled') {
205
+ await iterator.return?.();
206
+ break;
207
+ }
208
+ if (next.done) {
209
+ stream.controller.enqueue([idx, ASYNC_ITERABLE_STATUS_DONE]);
210
+ break;
198
211
  }
199
- controller.enqueue([idx, ASYNC_ITERABLE_STATUS_DONE]);
200
- } catch (error) {
201
- opts.onError?.({ error, path });
202
- controller.enqueue([
212
+ stream.controller.enqueue([
203
213
  idx,
204
- ASYNC_ITERABLE_STATUS_ERROR,
205
- opts.formatError?.({ error, path }),
214
+ ASYNC_ITERABLE_STATUS_VALUE,
215
+ dehydrate(next.value, path),
206
216
  ]);
207
- } finally {
208
- pending.delete(idx);
209
- maybeClose();
210
217
  }
211
- })();
218
+
219
+ pending.delete(idx);
220
+ maybeClose();
221
+ }).catch((cause) => {
222
+ // this shouldn't happen, but node crashes if we don't catch it
223
+ opts.onError?.({
224
+ error: new Error(
225
+ 'You found a bug - please report it on https://github.com/trpc/trpc',
226
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
227
+ // @ts-ignore https://github.com/tc39/proposal-error-cause
228
+ {
229
+ cause,
230
+ },
231
+ ),
232
+ path,
233
+ });
234
+ });
212
235
  return idx;
213
236
  }
214
237
  function checkMaxDepth(path: (string | number)[]) {
@@ -217,12 +240,12 @@ function createBatchStreamProducer(opts: ProducerOptions) {
217
240
  }
218
241
  return null;
219
242
  }
220
- function hydrateChunk(
243
+ function dehydrateChunk(
221
244
  value: unknown,
222
245
  path: (string | number)[],
223
246
  ): null | [type: ChunkValueType, chunkId: ChunkIndex] {
224
247
  if (isPromise(value)) {
225
- return [CHUNK_VALUE_TYPE_PROMISE, hydratePromise(value, path)];
248
+ return [CHUNK_VALUE_TYPE_PROMISE, dehydratePromise(value, path)];
226
249
  }
227
250
  if (isAsyncIterable(value)) {
228
251
  if (opts.maxDepth && path.length >= opts.maxDepth) {
@@ -230,13 +253,16 @@ function createBatchStreamProducer(opts: ProducerOptions) {
230
253
  }
231
254
  return [
232
255
  CHUNK_VALUE_TYPE_ASYNC_ITERABLE,
233
- hydrateAsyncIterable(value, path),
256
+ dehydrateAsyncIterable(value, path),
234
257
  ];
235
258
  }
236
259
  return null;
237
260
  }
238
- function hydrate(value: unknown, path: (string | number)[]): HydratedValue {
239
- const reg = hydrateChunk(value, path);
261
+ function dehydrate(
262
+ value: unknown,
263
+ path: (string | number)[],
264
+ ): DehydratedValue {
265
+ const reg = dehydrateChunk(value, path);
240
266
  if (reg) {
241
267
  return [[placeholder], [null, ...reg]];
242
268
  }
@@ -246,7 +272,7 @@ function createBatchStreamProducer(opts: ProducerOptions) {
246
272
  const newObj = {} as Record<string, unknown>;
247
273
  const asyncValues: ChunkDefinition[] = [];
248
274
  for (const [key, item] of Object.entries(value)) {
249
- const transformed = hydrateChunk(item, [...path, key]);
275
+ const transformed = dehydrateChunk(item, [...path, key]);
250
276
  if (!transformed) {
251
277
  newObj[key] = item;
252
278
  continue;
@@ -259,10 +285,10 @@ function createBatchStreamProducer(opts: ProducerOptions) {
259
285
 
260
286
  const newHead: Head = {};
261
287
  for (const [key, item] of Object.entries(data)) {
262
- newHead[key] = hydrate(item, [key]);
288
+ newHead[key] = dehydrate(item, [key]);
263
289
  }
264
290
 
265
- return [newHead, stream] as const;
291
+ return [newHead, stream.readable] as const;
266
292
  }
267
293
  /**
268
294
  * JSON Lines stream producer
@@ -296,6 +322,7 @@ export function jsonlStreamProducer(opts: ProducerOptions) {
296
322
  )
297
323
  .pipeThrough(new TextEncoderStream());
298
324
  }
325
+
299
326
  class StreamInterruptedError extends Error {
300
327
  constructor(cause?: unknown) {
301
328
  // eslint-disable-next-line @typescript-eslint/ban-ts-comment
@@ -313,7 +340,7 @@ export type ConsumerOnError = (opts: { error: unknown }) => void;
313
340
  const nodeJsStreamToReaderEsque = (source: NodeJSReadableStreamEsque) => {
314
341
  return {
315
342
  getReader() {
316
- const [stream, controller] = createReadableStream<Uint8Array>();
343
+ const { readable, controller } = createReadableStream<Uint8Array>();
317
344
  source.on('data', (chunk) => {
318
345
  controller.enqueue(chunk);
319
346
  });
@@ -323,7 +350,7 @@ const nodeJsStreamToReaderEsque = (source: NodeJSReadableStreamEsque) => {
323
350
  source.on('error', (error) => {
324
351
  controller.error(error);
325
352
  });
326
- return stream.getReader();
353
+ return readable.getReader();
327
354
  },
328
355
  };
329
356
  };
@@ -388,19 +415,6 @@ function createConsumerStream<THead>(
388
415
  );
389
416
  }
390
417
 
391
- function createDeferred<TValue>() {
392
- let resolve: (value: TValue) => void;
393
- let reject: (error: unknown) => void;
394
- const promise = new Promise<TValue>((res, rej) => {
395
- resolve = res;
396
- reject = rej;
397
- });
398
-
399
- return { promise, resolve: resolve!, reject: reject! };
400
- }
401
-
402
- type Deferred<TValue> = ReturnType<typeof createDeferred<TValue>>;
403
-
404
418
  /**
405
419
  * JSON Lines stream consumer
406
420
  * @see https://jsonlines.org/
@@ -412,6 +426,7 @@ export async function jsonlStreamConsumer<THead>(opts: {
412
426
  formatError?: (opts: { error: unknown }) => Error;
413
427
  }) {
414
428
  const { deserialize = (v) => v } = opts;
429
+ const abortController = new AbortController();
415
430
 
416
431
  let source = createConsumerStream<Head>(opts.from);
417
432
  if (deserialize) {
@@ -430,10 +445,18 @@ export async function jsonlStreamConsumer<THead>(opts: {
430
445
  const chunkDeferred = new Map<ChunkIndex, Deferred<ChunkController>>();
431
446
  const controllers = new Map<ChunkIndex, ChunkController>();
432
447
 
433
- function dehydrateChunkDefinition(value: ChunkDefinition) {
448
+ const deleteController = (idx: ChunkIndex) => {
449
+ controllers.delete(idx);
450
+ if (controllers.size === 0 && chunkDeferred.size === 0) {
451
+ // nothing can be listening to the stream anymore
452
+ abortController.abort();
453
+ }
454
+ };
455
+
456
+ function hydrateChunkDefinition(value: ChunkDefinition) {
434
457
  const [_path, type, chunkId] = value;
435
458
 
436
- const [stream, controller] = createReadableStream<ChunkData>();
459
+ const { readable, controller } = createReadableStream<ChunkData>();
437
460
  controllers.set(chunkId, controller);
438
461
 
439
462
  // resolve chunk deferred if it exists
@@ -447,7 +470,7 @@ export async function jsonlStreamConsumer<THead>(opts: {
447
470
  case CHUNK_VALUE_TYPE_PROMISE: {
448
471
  return new Promise((resolve, reject) => {
449
472
  // listen for next value in the stream
450
- const reader = stream.getReader();
473
+ const reader = readable.getReader();
451
474
  reader
452
475
  .read()
453
476
  .then((it) => {
@@ -463,7 +486,7 @@ export async function jsonlStreamConsumer<THead>(opts: {
463
486
  const [_chunkId, status, data] = value as PromiseChunk;
464
487
  switch (status) {
465
488
  case PROMISE_STATUS_FULFILLED:
466
- resolve(dehydrate(data));
489
+ resolve(hydrate(data));
467
490
  break;
468
491
  case PROMISE_STATUS_REJECTED:
469
492
  reject(
@@ -481,51 +504,76 @@ export async function jsonlStreamConsumer<THead>(opts: {
481
504
  }
482
505
  case CHUNK_VALUE_TYPE_ASYNC_ITERABLE: {
483
506
  return {
484
- [Symbol.asyncIterator]: async function* () {
485
- const reader = stream.getReader();
486
- while (true) {
487
- const { done, value } = await reader.read();
488
- if (done) {
489
- break;
490
- }
491
- if (value instanceof StreamInterruptedError) {
492
- throw value;
493
- }
494
-
495
- const [_chunkId, status, data] = value as IterableChunk;
496
-
497
- switch (status) {
498
- case ASYNC_ITERABLE_STATUS_VALUE:
499
- yield dehydrate(data);
500
- break;
501
- case ASYNC_ITERABLE_STATUS_DONE:
502
- controllers.delete(chunkId);
503
- return;
504
- case ASYNC_ITERABLE_STATUS_ERROR:
505
- controllers.delete(chunkId);
506
- throw (
507
- opts.formatError?.({ error: data }) ?? new AsyncError(data)
508
- );
509
- }
510
- }
507
+ [Symbol.asyncIterator]: () => {
508
+ const reader = readable.getReader();
509
+ const iterator: AsyncIterator<unknown> = {
510
+ next: async () => {
511
+ const { done, value } = await reader.read();
512
+ if (value instanceof StreamInterruptedError) {
513
+ throw value;
514
+ }
515
+ if (done) {
516
+ deleteController(chunkId);
517
+ return {
518
+ done: true,
519
+ value: undefined,
520
+ };
521
+ }
522
+
523
+ const [_chunkId, status, data] = value as IterableChunk;
524
+
525
+ switch (status) {
526
+ case ASYNC_ITERABLE_STATUS_VALUE:
527
+ return {
528
+ done: false,
529
+ value: hydrate(data),
530
+ };
531
+ case ASYNC_ITERABLE_STATUS_DONE:
532
+ deleteController(chunkId);
533
+ return {
534
+ done: true,
535
+ value: undefined,
536
+ };
537
+ case ASYNC_ITERABLE_STATUS_ERROR:
538
+ deleteController(chunkId);
539
+ throw (
540
+ opts.formatError?.({ error: data }) ??
541
+ new AsyncError(data)
542
+ );
543
+ }
544
+ },
545
+ return: async () => {
546
+ deleteController(chunkId);
547
+
548
+ if (controllers.size === 0) {
549
+ // nothing can be listening to the stream anymore
550
+ abortController.abort();
551
+ }
552
+ return {
553
+ done: true,
554
+ value: undefined,
555
+ };
556
+ },
557
+ };
558
+ return iterator;
511
559
  },
512
560
  };
513
561
  }
514
562
  }
515
563
  }
516
564
 
517
- function dehydrate(value: HydratedValue): unknown {
565
+ function hydrate(value: DehydratedValue): unknown {
518
566
  const [[data], ...asyncProps] = value;
519
567
 
520
568
  for (const value of asyncProps) {
521
- const dehydrated = dehydrateChunkDefinition(value);
569
+ const hydrated = hydrateChunkDefinition(value);
522
570
 
523
571
  const [path] = value;
524
572
  if (path === null) {
525
- return dehydrated;
573
+ return hydrated;
526
574
  }
527
575
 
528
- (data as any)[path] = dehydrated;
576
+ (data as any)[path] = hydrated;
529
577
  }
530
578
  return data;
531
579
  }
@@ -547,12 +595,17 @@ export async function jsonlStreamConsumer<THead>(opts: {
547
595
  source
548
596
  .pipeTo(
549
597
  new WritableStream({
598
+ start(controller) {
599
+ abortController.signal.addEventListener('abort', () => {
600
+ controller.error(abortController.signal.reason);
601
+ });
602
+ },
550
603
  async write(chunkOrHead) {
551
604
  if (headDeferred) {
552
605
  const head = chunkOrHead as Record<number | string, unknown>;
553
606
 
554
607
  for (const [key, value] of Object.entries(chunkOrHead)) {
555
- const parsed = dehydrate(value as any);
608
+ const parsed = hydrate(value as any);
556
609
  head[key] = parsed;
557
610
  }
558
611
  headDeferred.resolve(head as THead);