@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.
- package/dist/@trpc/server/http.d.ts +1 -2
- package/dist/@trpc/server/http.d.ts.map +1 -1
- package/dist/@trpc/server/index.d.ts +1 -1
- package/dist/@trpc/server/index.d.ts.map +1 -1
- package/dist/@trpc/server/rpc.d.ts +1 -1
- package/dist/@trpc/server/rpc.d.ts.map +1 -1
- package/dist/adapters/aws-lambda/getPlanner.d.ts.map +1 -1
- package/dist/adapters/aws-lambda/getPlanner.js +19 -2
- package/dist/adapters/aws-lambda/getPlanner.mjs +19 -2
- package/dist/adapters/next-app-dir/nextAppDirCaller.d.ts.map +1 -1
- package/dist/adapters/next-app-dir/nextAppDirCaller.js +1 -1
- package/dist/adapters/next-app-dir/nextAppDirCaller.mjs +1 -1
- package/dist/adapters/next-app-dir/redirect.d.ts.map +1 -1
- package/dist/adapters/next.js +1 -1
- package/dist/adapters/next.mjs +1 -1
- package/dist/adapters/node-http/incomingMessageToRequest.d.ts +0 -1
- package/dist/adapters/node-http/incomingMessageToRequest.d.ts.map +1 -1
- package/dist/adapters/node-http/incomingMessageToRequest.js +3 -1
- package/dist/adapters/node-http/incomingMessageToRequest.mjs +3 -1
- package/dist/adapters/node-http/nodeHTTPRequestHandler.d.ts.map +1 -1
- package/dist/adapters/node-http/nodeHTTPRequestHandler.js +30 -7
- package/dist/adapters/node-http/nodeHTTPRequestHandler.mjs +30 -7
- package/dist/adapters/node-http/types.d.ts +0 -1
- package/dist/adapters/node-http/types.d.ts.map +1 -1
- package/dist/adapters/standalone.d.ts +0 -1
- package/dist/adapters/standalone.d.ts.map +1 -1
- package/dist/adapters/ws.d.ts +3 -4
- package/dist/adapters/ws.d.ts.map +1 -1
- package/dist/adapters/ws.js +172 -114
- package/dist/adapters/ws.mjs +172 -114
- package/dist/bundle-analysis.json +236 -155
- package/dist/http.js +3 -0
- package/dist/http.mjs +1 -0
- package/dist/index.js +7 -5
- package/dist/index.mjs +3 -2
- package/dist/observable/observable.d.ts +1 -0
- package/dist/observable/observable.d.ts.map +1 -1
- package/dist/observable/observable.js +55 -0
- package/dist/observable/observable.mjs +55 -1
- package/dist/unstable-core-do-not-import/createProxy.d.ts +3 -3
- package/dist/unstable-core-do-not-import/createProxy.d.ts.map +1 -1
- package/dist/unstable-core-do-not-import/createProxy.js +15 -6
- package/dist/unstable-core-do-not-import/createProxy.mjs +15 -6
- package/dist/unstable-core-do-not-import/http/contentType.d.ts +7 -4
- package/dist/unstable-core-do-not-import/http/contentType.d.ts.map +1 -1
- package/dist/unstable-core-do-not-import/http/contentType.js +60 -17
- package/dist/unstable-core-do-not-import/http/contentType.mjs +61 -18
- package/dist/unstable-core-do-not-import/http/formDataToObject.d.ts.map +1 -0
- package/dist/unstable-core-do-not-import/http/formDataToObject.js +40 -0
- package/dist/unstable-core-do-not-import/http/formDataToObject.mjs +38 -0
- package/dist/unstable-core-do-not-import/http/getHTTPStatusCode.d.ts.map +1 -1
- package/dist/unstable-core-do-not-import/http/getHTTPStatusCode.js +4 -4
- package/dist/unstable-core-do-not-import/http/getHTTPStatusCode.mjs +4 -4
- package/dist/unstable-core-do-not-import/http/parseConnectionParams.d.ts +4 -0
- package/dist/unstable-core-do-not-import/http/parseConnectionParams.d.ts.map +1 -0
- package/dist/unstable-core-do-not-import/http/parseConnectionParams.js +42 -0
- package/dist/unstable-core-do-not-import/http/parseConnectionParams.mjs +39 -0
- package/dist/unstable-core-do-not-import/http/resolveResponse.d.ts.map +1 -1
- package/dist/unstable-core-do-not-import/http/resolveResponse.js +302 -149
- package/dist/unstable-core-do-not-import/http/resolveResponse.mjs +301 -148
- package/dist/unstable-core-do-not-import/http/types.d.ts +26 -2
- package/dist/unstable-core-do-not-import/http/types.d.ts.map +1 -1
- package/dist/unstable-core-do-not-import/initTRPC.d.ts +12 -12
- package/dist/unstable-core-do-not-import/initTRPC.d.ts.map +1 -1
- package/dist/unstable-core-do-not-import/middleware.d.ts +3 -3
- package/dist/unstable-core-do-not-import/middleware.d.ts.map +1 -1
- package/dist/unstable-core-do-not-import/procedureBuilder.d.ts +3 -1
- package/dist/unstable-core-do-not-import/procedureBuilder.d.ts.map +1 -1
- package/dist/unstable-core-do-not-import/rootConfig.d.ts +12 -0
- package/dist/unstable-core-do-not-import/rootConfig.d.ts.map +1 -1
- package/dist/unstable-core-do-not-import/router.d.ts +2 -2
- package/dist/unstable-core-do-not-import/router.d.ts.map +1 -1
- package/dist/unstable-core-do-not-import/router.js +7 -2
- package/dist/unstable-core-do-not-import/router.mjs +7 -2
- package/dist/unstable-core-do-not-import/rpc/envelopes.d.ts +7 -0
- package/dist/unstable-core-do-not-import/rpc/envelopes.d.ts.map +1 -1
- package/dist/unstable-core-do-not-import/rpc/index.d.ts +1 -1
- package/dist/unstable-core-do-not-import/rpc/index.d.ts.map +1 -1
- package/dist/unstable-core-do-not-import/stream/{stream.d.ts → jsonl.d.ts} +5 -5
- package/dist/unstable-core-do-not-import/stream/jsonl.d.ts.map +1 -0
- package/dist/unstable-core-do-not-import/stream/{stream.js → jsonl.js} +148 -111
- package/dist/unstable-core-do-not-import/stream/{stream.mjs → jsonl.mjs} +147 -110
- package/dist/unstable-core-do-not-import/stream/sse.d.ts +86 -0
- package/dist/unstable-core-do-not-import/stream/sse.d.ts.map +1 -0
- package/dist/unstable-core-do-not-import/stream/sse.js +178 -0
- package/dist/unstable-core-do-not-import/stream/sse.mjs +172 -0
- package/dist/unstable-core-do-not-import/stream/utils/createDeferred.d.ts +18 -0
- package/dist/unstable-core-do-not-import/stream/utils/createDeferred.d.ts.map +1 -0
- package/dist/unstable-core-do-not-import/stream/utils/createDeferred.js +46 -0
- package/dist/unstable-core-do-not-import/stream/utils/createDeferred.mjs +43 -0
- package/dist/unstable-core-do-not-import/stream/utils/createReadableStream.d.ts +10 -0
- package/dist/unstable-core-do-not-import/stream/utils/createReadableStream.d.ts.map +1 -0
- package/dist/unstable-core-do-not-import/stream/utils/createReadableStream.js +31 -0
- package/dist/unstable-core-do-not-import/stream/utils/createReadableStream.mjs +29 -0
- package/dist/unstable-core-do-not-import/stream/utils/createServer.d.ts +7 -0
- package/dist/unstable-core-do-not-import/stream/utils/createServer.d.ts.map +1 -0
- package/dist/unstable-core-do-not-import/transformer.d.ts +5 -5
- package/dist/unstable-core-do-not-import/utils.d.ts +4 -0
- package/dist/unstable-core-do-not-import/utils.d.ts.map +1 -1
- package/dist/unstable-core-do-not-import/utils.js +4 -0
- package/dist/unstable-core-do-not-import/utils.mjs +4 -1
- package/dist/unstable-core-do-not-import.d.ts +5 -2
- package/dist/unstable-core-do-not-import.d.ts.map +1 -1
- package/dist/unstable-core-do-not-import.js +19 -7
- package/dist/unstable-core-do-not-import.mjs +6 -3
- package/package.json +6 -6
- package/src/@trpc/server/http.ts +7 -2
- package/src/@trpc/server/index.ts +1 -0
- package/src/@trpc/server/rpc.ts +1 -0
- package/src/adapters/aws-lambda/getPlanner.ts +21 -2
- package/src/adapters/next-app-dir/nextAppDirCaller.ts +2 -1
- package/src/adapters/node-http/incomingMessageToRequest.ts +3 -2
- package/src/adapters/node-http/nodeHTTPRequestHandler.ts +32 -7
- package/src/adapters/ws.ts +193 -107
- package/src/observable/observable.ts +63 -0
- package/src/unstable-core-do-not-import/createProxy.ts +23 -8
- package/src/unstable-core-do-not-import/http/contentType.ts +83 -21
- package/src/{adapters/next-app-dir → unstable-core-do-not-import/http}/formDataToObject.ts +18 -10
- package/src/unstable-core-do-not-import/http/getHTTPStatusCode.ts +4 -7
- package/src/unstable-core-do-not-import/http/parseConnectionParams.ts +49 -0
- package/src/unstable-core-do-not-import/http/resolveResponse.ts +333 -164
- package/src/unstable-core-do-not-import/http/types.ts +31 -2
- package/src/unstable-core-do-not-import/procedureBuilder.ts +8 -1
- package/src/unstable-core-do-not-import/rootConfig.ts +12 -0
- package/src/unstable-core-do-not-import/router.ts +47 -35
- package/src/unstable-core-do-not-import/rpc/envelopes.ts +9 -0
- package/src/unstable-core-do-not-import/rpc/index.ts +1 -0
- package/src/unstable-core-do-not-import/stream/{stream.ts → jsonl.ts} +163 -110
- package/src/unstable-core-do-not-import/stream/sse.ts +288 -0
- package/src/unstable-core-do-not-import/stream/utils/createDeferred.ts +48 -0
- package/src/unstable-core-do-not-import/stream/utils/createReadableStream.ts +31 -0
- package/src/unstable-core-do-not-import/stream/utils/createServer.ts +44 -0
- package/src/unstable-core-do-not-import/utils.ts +5 -0
- package/src/unstable-core-do-not-import.ts +5 -2
- package/dist/adapters/next-app-dir/formDataToObject.d.ts.map +0 -1
- package/dist/adapters/next-app-dir/formDataToObject.js +0 -34
- package/dist/adapters/next-app-dir/formDataToObject.mjs +0 -32
- package/dist/unstable-core-do-not-import/stream/stream.d.ts.map +0 -1
- /package/dist/{adapters/next-app-dir → unstable-core-do-not-import/http}/formDataToObject.d.ts +0 -0
|
@@ -1,19 +1,8 @@
|
|
|
1
|
-
|
|
2
|
-
import { isAsyncIterable, isFunction, isObject } from '../utils';
|
|
3
|
-
|
|
4
|
-
|
|
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
|
|
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,
|
|
64
|
+
type Head = Record<string, DehydratedValue>;
|
|
76
65
|
type PromiseChunk =
|
|
77
66
|
| [
|
|
78
67
|
chunkIndex: ChunkIndex,
|
|
79
68
|
status: PROMISE_STATUS_FULFILLED,
|
|
80
|
-
value:
|
|
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:
|
|
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<
|
|
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
|
|
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
|
|
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
|
-
|
|
156
|
-
|
|
157
|
-
};
|
|
158
|
-
promise
|
|
145
|
+
|
|
146
|
+
Promise.race([promise, stream.cancelledPromise])
|
|
159
147
|
.then((it) => {
|
|
160
|
-
|
|
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((
|
|
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
|
|
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
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
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
|
-
|
|
196
|
-
|
|
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([
|
|
200
|
-
} catch (error) {
|
|
201
|
-
opts.onError?.({ error, path });
|
|
202
|
-
controller.enqueue([
|
|
212
|
+
stream.controller.enqueue([
|
|
203
213
|
idx,
|
|
204
|
-
|
|
205
|
-
|
|
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
|
|
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,
|
|
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
|
-
|
|
256
|
+
dehydrateAsyncIterable(value, path),
|
|
234
257
|
];
|
|
235
258
|
}
|
|
236
259
|
return null;
|
|
237
260
|
}
|
|
238
|
-
function
|
|
239
|
-
|
|
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 =
|
|
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] =
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
|
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 =
|
|
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(
|
|
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]:
|
|
485
|
-
const reader =
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
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
|
|
565
|
+
function hydrate(value: DehydratedValue): unknown {
|
|
518
566
|
const [[data], ...asyncProps] = value;
|
|
519
567
|
|
|
520
568
|
for (const value of asyncProps) {
|
|
521
|
-
const
|
|
569
|
+
const hydrated = hydrateChunkDefinition(value);
|
|
522
570
|
|
|
523
571
|
const [path] = value;
|
|
524
572
|
if (path === null) {
|
|
525
|
-
return
|
|
573
|
+
return hydrated;
|
|
526
574
|
}
|
|
527
575
|
|
|
528
|
-
(data as any)[path] =
|
|
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 =
|
|
608
|
+
const parsed = hydrate(value as any);
|
|
556
609
|
head[key] = parsed;
|
|
557
610
|
}
|
|
558
611
|
headDeferred.resolve(head as THead);
|