@trpc/server 11.0.0-alpha-tmp-app-router-example.388 → 11.0.0-alpha-tmp-issues-5851-take-two.451
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 +235 -154
- 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} +12 -8
- 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} +158 -119
- package/dist/unstable-core-do-not-import/stream/{stream.mjs → jsonl.mjs} +157 -118
- 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} +186 -127
- 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,
|
|
@@ -105,18 +94,16 @@ export function isPromise(value: unknown): value is Promise<unknown> {
|
|
|
105
94
|
type Serialize = (value: any) => any;
|
|
106
95
|
type Deserialize = (value: any) => any;
|
|
107
96
|
|
|
97
|
+
type PathArray = readonly (string | number)[];
|
|
108
98
|
export type ProducerOnError = (opts: {
|
|
109
99
|
error: unknown;
|
|
110
|
-
path:
|
|
100
|
+
path: PathArray;
|
|
111
101
|
}) => void;
|
|
112
102
|
export interface ProducerOptions {
|
|
113
103
|
serialize?: Serialize;
|
|
114
|
-
data: Record<
|
|
104
|
+
data: Record<string, unknown> | unknown[];
|
|
115
105
|
onError?: ProducerOnError;
|
|
116
|
-
formatError?: (opts: {
|
|
117
|
-
error: unknown;
|
|
118
|
-
path: (string | number)[];
|
|
119
|
-
}) => unknown;
|
|
106
|
+
formatError?: (opts: { error: unknown; path: PathArray }) => unknown;
|
|
120
107
|
maxDepth?: number;
|
|
121
108
|
}
|
|
122
109
|
|
|
@@ -131,14 +118,15 @@ function createBatchStreamProducer(opts: ProducerOptions) {
|
|
|
131
118
|
let counter = 0 as ChunkIndex;
|
|
132
119
|
const placeholder = 0 as PlaceholderValue;
|
|
133
120
|
|
|
134
|
-
const
|
|
121
|
+
const stream = createReadableStream<ChunkData>();
|
|
135
122
|
const pending = new Set<ChunkIndex>();
|
|
123
|
+
|
|
136
124
|
function maybeClose() {
|
|
137
|
-
if (pending.size === 0) {
|
|
138
|
-
controller.close();
|
|
125
|
+
if (pending.size === 0 && !stream.cancelled()) {
|
|
126
|
+
stream.controller.close();
|
|
139
127
|
}
|
|
140
128
|
}
|
|
141
|
-
function
|
|
129
|
+
function dehydratePromise(
|
|
142
130
|
promise: Promise<unknown>,
|
|
143
131
|
path: (string | number)[],
|
|
144
132
|
) {
|
|
@@ -152,19 +140,24 @@ function createBatchStreamProducer(opts: ProducerOptions) {
|
|
|
152
140
|
}
|
|
153
141
|
const idx = counter++ as ChunkIndex;
|
|
154
142
|
pending.add(idx);
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
};
|
|
158
|
-
promise
|
|
143
|
+
|
|
144
|
+
Promise.race([promise, stream.cancelledPromise])
|
|
159
145
|
.then((it) => {
|
|
160
|
-
|
|
146
|
+
if (it === null) {
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
stream.controller.enqueue([
|
|
150
|
+
idx,
|
|
151
|
+
PROMISE_STATUS_FULFILLED,
|
|
152
|
+
dehydrate(it, path),
|
|
153
|
+
]);
|
|
161
154
|
})
|
|
162
|
-
.catch((
|
|
163
|
-
opts.onError?.({ error, path });
|
|
164
|
-
enqueue([
|
|
155
|
+
.catch((cause) => {
|
|
156
|
+
opts.onError?.({ error: cause, path });
|
|
157
|
+
stream.controller.enqueue([
|
|
165
158
|
idx,
|
|
166
159
|
PROMISE_STATUS_REJECTED,
|
|
167
|
-
opts.formatError?.({ error, path }),
|
|
160
|
+
opts.formatError?.({ error: cause, path }),
|
|
168
161
|
]);
|
|
169
162
|
})
|
|
170
163
|
.finally(() => {
|
|
@@ -173,7 +166,7 @@ function createBatchStreamProducer(opts: ProducerOptions) {
|
|
|
173
166
|
});
|
|
174
167
|
return idx;
|
|
175
168
|
}
|
|
176
|
-
function
|
|
169
|
+
function dehydrateAsyncIterable(
|
|
177
170
|
iterable: AsyncIterable<unknown>,
|
|
178
171
|
path: (string | number)[],
|
|
179
172
|
) {
|
|
@@ -187,28 +180,56 @@ function createBatchStreamProducer(opts: ProducerOptions) {
|
|
|
187
180
|
}
|
|
188
181
|
const idx = counter++ as ChunkIndex;
|
|
189
182
|
pending.add(idx);
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
183
|
+
run(async () => {
|
|
184
|
+
const iterator = iterable[Symbol.asyncIterator]();
|
|
185
|
+
|
|
186
|
+
while (true) {
|
|
187
|
+
const next = await Promise.race([
|
|
188
|
+
iterator.next().catch(getTRPCErrorFromUnknown),
|
|
189
|
+
stream.cancelledPromise,
|
|
190
|
+
]);
|
|
191
|
+
|
|
192
|
+
if (next instanceof Error) {
|
|
193
|
+
opts.onError?.({ error: next, path });
|
|
194
|
+
|
|
195
|
+
stream.controller.enqueue([
|
|
194
196
|
idx,
|
|
195
|
-
|
|
196
|
-
|
|
197
|
+
ASYNC_ITERABLE_STATUS_ERROR,
|
|
198
|
+
opts.formatError?.({ error: next, path }),
|
|
197
199
|
]);
|
|
200
|
+
return;
|
|
201
|
+
}
|
|
202
|
+
if (next === 'cancelled') {
|
|
203
|
+
await iterator.return?.();
|
|
204
|
+
break;
|
|
205
|
+
}
|
|
206
|
+
if (next.done) {
|
|
207
|
+
stream.controller.enqueue([idx, ASYNC_ITERABLE_STATUS_DONE]);
|
|
208
|
+
break;
|
|
198
209
|
}
|
|
199
|
-
controller.enqueue([
|
|
200
|
-
} catch (error) {
|
|
201
|
-
opts.onError?.({ error, path });
|
|
202
|
-
controller.enqueue([
|
|
210
|
+
stream.controller.enqueue([
|
|
203
211
|
idx,
|
|
204
|
-
|
|
205
|
-
|
|
212
|
+
ASYNC_ITERABLE_STATUS_VALUE,
|
|
213
|
+
dehydrate(next.value, path),
|
|
206
214
|
]);
|
|
207
|
-
} finally {
|
|
208
|
-
pending.delete(idx);
|
|
209
|
-
maybeClose();
|
|
210
215
|
}
|
|
211
|
-
|
|
216
|
+
|
|
217
|
+
pending.delete(idx);
|
|
218
|
+
maybeClose();
|
|
219
|
+
}).catch((cause) => {
|
|
220
|
+
// this shouldn't happen, but node crashes if we don't catch it
|
|
221
|
+
opts.onError?.({
|
|
222
|
+
error: new Error(
|
|
223
|
+
'You found a bug - please report it on https://github.com/trpc/trpc',
|
|
224
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
225
|
+
// @ts-ignore https://github.com/tc39/proposal-error-cause
|
|
226
|
+
{
|
|
227
|
+
cause,
|
|
228
|
+
},
|
|
229
|
+
),
|
|
230
|
+
path,
|
|
231
|
+
});
|
|
232
|
+
});
|
|
212
233
|
return idx;
|
|
213
234
|
}
|
|
214
235
|
function checkMaxDepth(path: (string | number)[]) {
|
|
@@ -217,12 +238,12 @@ function createBatchStreamProducer(opts: ProducerOptions) {
|
|
|
217
238
|
}
|
|
218
239
|
return null;
|
|
219
240
|
}
|
|
220
|
-
function
|
|
241
|
+
function dehydrateChunk(
|
|
221
242
|
value: unknown,
|
|
222
243
|
path: (string | number)[],
|
|
223
244
|
): null | [type: ChunkValueType, chunkId: ChunkIndex] {
|
|
224
245
|
if (isPromise(value)) {
|
|
225
|
-
return [CHUNK_VALUE_TYPE_PROMISE,
|
|
246
|
+
return [CHUNK_VALUE_TYPE_PROMISE, dehydratePromise(value, path)];
|
|
226
247
|
}
|
|
227
248
|
if (isAsyncIterable(value)) {
|
|
228
249
|
if (opts.maxDepth && path.length >= opts.maxDepth) {
|
|
@@ -230,13 +251,16 @@ function createBatchStreamProducer(opts: ProducerOptions) {
|
|
|
230
251
|
}
|
|
231
252
|
return [
|
|
232
253
|
CHUNK_VALUE_TYPE_ASYNC_ITERABLE,
|
|
233
|
-
|
|
254
|
+
dehydrateAsyncIterable(value, path),
|
|
234
255
|
];
|
|
235
256
|
}
|
|
236
257
|
return null;
|
|
237
258
|
}
|
|
238
|
-
function
|
|
239
|
-
|
|
259
|
+
function dehydrate(
|
|
260
|
+
value: unknown,
|
|
261
|
+
path: (string | number)[],
|
|
262
|
+
): DehydratedValue {
|
|
263
|
+
const reg = dehydrateChunk(value, path);
|
|
240
264
|
if (reg) {
|
|
241
265
|
return [[placeholder], [null, ...reg]];
|
|
242
266
|
}
|
|
@@ -246,7 +270,7 @@ function createBatchStreamProducer(opts: ProducerOptions) {
|
|
|
246
270
|
const newObj = {} as Record<string, unknown>;
|
|
247
271
|
const asyncValues: ChunkDefinition[] = [];
|
|
248
272
|
for (const [key, item] of Object.entries(value)) {
|
|
249
|
-
const transformed =
|
|
273
|
+
const transformed = dehydrateChunk(item, [...path, key]);
|
|
250
274
|
if (!transformed) {
|
|
251
275
|
newObj[key] = item;
|
|
252
276
|
continue;
|
|
@@ -259,10 +283,10 @@ function createBatchStreamProducer(opts: ProducerOptions) {
|
|
|
259
283
|
|
|
260
284
|
const newHead: Head = {};
|
|
261
285
|
for (const [key, item] of Object.entries(data)) {
|
|
262
|
-
newHead[key] =
|
|
286
|
+
newHead[key] = dehydrate(item, [key]);
|
|
263
287
|
}
|
|
264
288
|
|
|
265
|
-
return [newHead, stream] as const;
|
|
289
|
+
return [newHead, stream.readable] as const;
|
|
266
290
|
}
|
|
267
291
|
/**
|
|
268
292
|
* JSON Lines stream producer
|
|
@@ -296,6 +320,7 @@ export function jsonlStreamProducer(opts: ProducerOptions) {
|
|
|
296
320
|
)
|
|
297
321
|
.pipeThrough(new TextEncoderStream());
|
|
298
322
|
}
|
|
323
|
+
|
|
299
324
|
class StreamInterruptedError extends Error {
|
|
300
325
|
constructor(cause?: unknown) {
|
|
301
326
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
@@ -313,7 +338,7 @@ export type ConsumerOnError = (opts: { error: unknown }) => void;
|
|
|
313
338
|
const nodeJsStreamToReaderEsque = (source: NodeJSReadableStreamEsque) => {
|
|
314
339
|
return {
|
|
315
340
|
getReader() {
|
|
316
|
-
const
|
|
341
|
+
const { readable, controller } = createReadableStream<Uint8Array>();
|
|
317
342
|
source.on('data', (chunk) => {
|
|
318
343
|
controller.enqueue(chunk);
|
|
319
344
|
});
|
|
@@ -323,7 +348,7 @@ const nodeJsStreamToReaderEsque = (source: NodeJSReadableStreamEsque) => {
|
|
|
323
348
|
source.on('error', (error) => {
|
|
324
349
|
controller.error(error);
|
|
325
350
|
});
|
|
326
|
-
return
|
|
351
|
+
return readable.getReader();
|
|
327
352
|
},
|
|
328
353
|
};
|
|
329
354
|
};
|
|
@@ -388,19 +413,6 @@ function createConsumerStream<THead>(
|
|
|
388
413
|
);
|
|
389
414
|
}
|
|
390
415
|
|
|
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
416
|
/**
|
|
405
417
|
* JSON Lines stream consumer
|
|
406
418
|
* @see https://jsonlines.org/
|
|
@@ -412,6 +424,7 @@ export async function jsonlStreamConsumer<THead>(opts: {
|
|
|
412
424
|
formatError?: (opts: { error: unknown }) => Error;
|
|
413
425
|
}) {
|
|
414
426
|
const { deserialize = (v) => v } = opts;
|
|
427
|
+
const streamAbortController = new AbortController();
|
|
415
428
|
|
|
416
429
|
let source = createConsumerStream<Head>(opts.from);
|
|
417
430
|
if (deserialize) {
|
|
@@ -427,19 +440,28 @@ export async function jsonlStreamConsumer<THead>(opts: {
|
|
|
427
440
|
|
|
428
441
|
type ControllerChunk = ChunkData | StreamInterruptedError;
|
|
429
442
|
type ChunkController = ReadableStreamDefaultController<ControllerChunk>;
|
|
430
|
-
|
|
431
|
-
|
|
443
|
+
type ControllerWrapper = {
|
|
444
|
+
controller: ChunkController;
|
|
445
|
+
abortController: AbortController;
|
|
446
|
+
};
|
|
447
|
+
const chunkDeferred = new Map<ChunkIndex, Deferred<ControllerWrapper>>();
|
|
448
|
+
const controllers = new Map<ChunkIndex, ControllerWrapper>();
|
|
432
449
|
|
|
433
|
-
function
|
|
450
|
+
function hydrateChunkDefinition(value: ChunkDefinition, path: PathArray) {
|
|
434
451
|
const [_path, type, chunkId] = value;
|
|
435
452
|
|
|
436
|
-
const
|
|
437
|
-
|
|
453
|
+
const { readable, controller } = createReadableStream<ChunkData>();
|
|
454
|
+
|
|
455
|
+
const wrapper: ControllerWrapper = {
|
|
456
|
+
controller,
|
|
457
|
+
abortController: new AbortController(),
|
|
458
|
+
};
|
|
459
|
+
controllers.set(chunkId, wrapper);
|
|
438
460
|
|
|
439
461
|
// resolve chunk deferred if it exists
|
|
440
462
|
const deferred = chunkDeferred.get(chunkId);
|
|
441
463
|
if (deferred) {
|
|
442
|
-
deferred.resolve(
|
|
464
|
+
deferred.resolve(wrapper);
|
|
443
465
|
chunkDeferred.delete(chunkId);
|
|
444
466
|
}
|
|
445
467
|
|
|
@@ -447,7 +469,7 @@ export async function jsonlStreamConsumer<THead>(opts: {
|
|
|
447
469
|
case CHUNK_VALUE_TYPE_PROMISE: {
|
|
448
470
|
return new Promise((resolve, reject) => {
|
|
449
471
|
// listen for next value in the stream
|
|
450
|
-
const reader =
|
|
472
|
+
const reader = readable.getReader();
|
|
451
473
|
reader
|
|
452
474
|
.read()
|
|
453
475
|
.then((it) => {
|
|
@@ -463,7 +485,7 @@ export async function jsonlStreamConsumer<THead>(opts: {
|
|
|
463
485
|
const [_chunkId, status, data] = value as PromiseChunk;
|
|
464
486
|
switch (status) {
|
|
465
487
|
case PROMISE_STATUS_FULFILLED:
|
|
466
|
-
resolve(
|
|
488
|
+
resolve(hydrate(data, path));
|
|
467
489
|
break;
|
|
468
490
|
case PROMISE_STATUS_REJECTED:
|
|
469
491
|
reject(
|
|
@@ -481,51 +503,83 @@ export async function jsonlStreamConsumer<THead>(opts: {
|
|
|
481
503
|
}
|
|
482
504
|
case CHUNK_VALUE_TYPE_ASYNC_ITERABLE: {
|
|
483
505
|
return {
|
|
484
|
-
[Symbol.asyncIterator]:
|
|
485
|
-
const reader =
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
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:
|
|
506
|
+
[Symbol.asyncIterator]: () => {
|
|
507
|
+
const reader = readable.getReader();
|
|
508
|
+
const iterator: AsyncIterator<unknown> = {
|
|
509
|
+
next: async () => {
|
|
510
|
+
const { done, value } = await reader.read();
|
|
511
|
+
if (value instanceof StreamInterruptedError) {
|
|
512
|
+
throw value;
|
|
513
|
+
}
|
|
514
|
+
if (done) {
|
|
505
515
|
controllers.delete(chunkId);
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
516
|
+
return {
|
|
517
|
+
done: true,
|
|
518
|
+
value: undefined,
|
|
519
|
+
};
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
const [_chunkId, status, data] = value as IterableChunk;
|
|
523
|
+
|
|
524
|
+
switch (status) {
|
|
525
|
+
case ASYNC_ITERABLE_STATUS_VALUE:
|
|
526
|
+
return {
|
|
527
|
+
done: false,
|
|
528
|
+
value: hydrate(data, path),
|
|
529
|
+
};
|
|
530
|
+
case ASYNC_ITERABLE_STATUS_DONE:
|
|
531
|
+
controllers.delete(chunkId);
|
|
532
|
+
return {
|
|
533
|
+
done: true,
|
|
534
|
+
value: undefined,
|
|
535
|
+
};
|
|
536
|
+
case ASYNC_ITERABLE_STATUS_ERROR:
|
|
537
|
+
controllers.delete(chunkId);
|
|
538
|
+
throw (
|
|
539
|
+
opts.formatError?.({ error: data }) ??
|
|
540
|
+
new AsyncError(data)
|
|
541
|
+
);
|
|
542
|
+
}
|
|
543
|
+
},
|
|
544
|
+
return: async () => {
|
|
545
|
+
wrapper.abortController.abort();
|
|
546
|
+
if (
|
|
547
|
+
chunkDeferred.size === 0 &&
|
|
548
|
+
Array.from(controllers.values()).every(
|
|
549
|
+
(it) => it.abortController.signal.aborted,
|
|
550
|
+
)
|
|
551
|
+
) {
|
|
552
|
+
// nothing is listening to the stream anymore
|
|
553
|
+
streamAbortController.abort();
|
|
554
|
+
}
|
|
555
|
+
return {
|
|
556
|
+
done: true,
|
|
557
|
+
value: undefined,
|
|
558
|
+
};
|
|
559
|
+
},
|
|
560
|
+
};
|
|
561
|
+
return iterator;
|
|
511
562
|
},
|
|
512
563
|
};
|
|
513
564
|
}
|
|
514
565
|
}
|
|
515
566
|
}
|
|
516
567
|
|
|
517
|
-
function
|
|
568
|
+
function hydrate(value: DehydratedValue, path: PathArray): unknown {
|
|
518
569
|
const [[data], ...asyncProps] = value;
|
|
519
570
|
|
|
520
571
|
for (const value of asyncProps) {
|
|
521
|
-
const
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
572
|
+
const [key] = value;
|
|
573
|
+
const hydrated = hydrateChunkDefinition(
|
|
574
|
+
value,
|
|
575
|
+
key === null ? path : [...path, key],
|
|
576
|
+
);
|
|
577
|
+
|
|
578
|
+
if (key === null) {
|
|
579
|
+
return hydrated;
|
|
526
580
|
}
|
|
527
581
|
|
|
528
|
-
(data as any)[
|
|
582
|
+
(data as any)[key] = hydrated;
|
|
529
583
|
}
|
|
530
584
|
return data;
|
|
531
585
|
}
|
|
@@ -538,7 +592,7 @@ export async function jsonlStreamConsumer<THead>(opts: {
|
|
|
538
592
|
deferred.reject(error);
|
|
539
593
|
}
|
|
540
594
|
chunkDeferred.clear();
|
|
541
|
-
for (const controller of controllers.values()) {
|
|
595
|
+
for (const { controller } of controllers.values()) {
|
|
542
596
|
controller.enqueue(error);
|
|
543
597
|
controller.close();
|
|
544
598
|
}
|
|
@@ -547,12 +601,17 @@ export async function jsonlStreamConsumer<THead>(opts: {
|
|
|
547
601
|
source
|
|
548
602
|
.pipeTo(
|
|
549
603
|
new WritableStream({
|
|
604
|
+
start(controller) {
|
|
605
|
+
streamAbortController.signal.addEventListener('abort', () => {
|
|
606
|
+
controller.error(streamAbortController.signal.reason);
|
|
607
|
+
});
|
|
608
|
+
},
|
|
550
609
|
async write(chunkOrHead) {
|
|
551
610
|
if (headDeferred) {
|
|
552
611
|
const head = chunkOrHead as Record<number | string, unknown>;
|
|
553
612
|
|
|
554
613
|
for (const [key, value] of Object.entries(chunkOrHead)) {
|
|
555
|
-
const parsed =
|
|
614
|
+
const parsed = hydrate(value as any, [key]);
|
|
556
615
|
head[key] = parsed;
|
|
557
616
|
}
|
|
558
617
|
headDeferred.resolve(head as THead);
|
|
@@ -561,17 +620,17 @@ export async function jsonlStreamConsumer<THead>(opts: {
|
|
|
561
620
|
}
|
|
562
621
|
const chunk = chunkOrHead as ChunkData;
|
|
563
622
|
const [idx] = chunk;
|
|
564
|
-
let
|
|
565
|
-
if (!
|
|
623
|
+
let wrapper = controllers.get(idx);
|
|
624
|
+
if (!wrapper) {
|
|
566
625
|
let deferred = chunkDeferred.get(idx);
|
|
567
626
|
if (!deferred) {
|
|
568
|
-
deferred = createDeferred
|
|
627
|
+
deferred = createDeferred();
|
|
569
628
|
chunkDeferred.set(idx, deferred);
|
|
570
629
|
}
|
|
571
630
|
|
|
572
|
-
|
|
631
|
+
wrapper = await deferred.promise;
|
|
573
632
|
}
|
|
574
|
-
controller.enqueue(chunk);
|
|
633
|
+
wrapper.controller.enqueue(chunk);
|
|
575
634
|
},
|
|
576
635
|
close: closeOrAbort,
|
|
577
636
|
abort: closeOrAbort,
|