@trpc/server 11.0.0-rc.417 → 11.0.0-rc.419
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/index.d.ts +1 -1
- package/dist/@trpc/server/index.d.ts.map +1 -1
- package/dist/adapters/aws-lambda/getPlanner.d.ts.map +1 -1
- package/dist/adapters/next-app-dir/redirect.d.ts.map +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 +1 -2
- package/dist/adapters/ws.d.ts.map +1 -1
- package/dist/adapters/ws.js +98 -81
- package/dist/adapters/ws.mjs +98 -81
- package/dist/bundle-analysis.json +196 -137
- package/dist/index.js +5 -3
- package/dist/index.mjs +2 -1
- 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.map +1 -1
- 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 +55 -17
- package/dist/unstable-core-do-not-import/http/contentType.mjs +56 -18
- 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 +34 -5
- 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 +6 -0
- package/dist/unstable-core-do-not-import/router.mjs +6 -0
- 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} +90 -89
- package/dist/unstable-core-do-not-import/stream/{stream.mjs → jsonl.mjs} +89 -88
- package/dist/unstable-core-do-not-import/stream/sse.d.ts +95 -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 +175 -0
- package/dist/unstable-core-do-not-import/stream/sse.mjs +169 -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 +2 -1
- package/dist/unstable-core-do-not-import.d.ts.map +1 -1
- package/dist/unstable-core-do-not-import.js +11 -4
- package/dist/unstable-core-do-not-import.mjs +3 -2
- package/package.json +3 -3
- package/src/@trpc/server/index.ts +1 -0
- package/src/adapters/node-http/incomingMessageToRequest.ts +3 -2
- package/src/adapters/node-http/nodeHTTPRequestHandler.ts +32 -7
- package/src/adapters/ws.ts +101 -75
- package/src/observable/observable.ts +63 -0
- package/src/unstable-core-do-not-import/http/contentType.ts +78 -21
- package/src/unstable-core-do-not-import/http/resolveResponse.ts +331 -164
- package/src/unstable-core-do-not-import/http/types.ts +42 -5
- 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 +12 -0
- package/src/unstable-core-do-not-import/stream/{stream.ts → jsonl.ts} +99 -85
- package/src/unstable-core-do-not-import/stream/sse.ts +291 -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 +46 -0
- package/src/unstable-core-do-not-import/utils.ts +5 -0
- package/src/unstable-core-do-not-import.ts +2 -1
- package/dist/unstable-core-do-not-import/stream/stream.d.ts.map +0 -1
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
import type { TRPCError } from '../error/TRPCError';
|
|
2
|
-
import type {
|
|
2
|
+
import type {
|
|
3
|
+
AnyProcedure,
|
|
4
|
+
ErrorHandlerOptions,
|
|
5
|
+
ProcedureType,
|
|
6
|
+
} from '../procedure';
|
|
3
7
|
import type {
|
|
4
8
|
AnyRouter,
|
|
5
9
|
inferRouterContext,
|
|
@@ -51,6 +55,8 @@ export interface HTTPBaseHandlerOptions<TRouter extends AnyRouter, TRequest>
|
|
|
51
55
|
responseMeta?: ResponseMetaFn<TRouter>;
|
|
52
56
|
}
|
|
53
57
|
|
|
58
|
+
export type TRPCAcceptHeader = 'application/jsonl';
|
|
59
|
+
|
|
54
60
|
interface TRPCRequestInfoProcedureCall {
|
|
55
61
|
path: string;
|
|
56
62
|
/**
|
|
@@ -61,16 +67,47 @@ interface TRPCRequestInfoProcedureCall {
|
|
|
61
67
|
* Get already parsed inputs - won't trigger reading the body or parsing the inputs
|
|
62
68
|
*/
|
|
63
69
|
result: () => unknown;
|
|
70
|
+
/**
|
|
71
|
+
* The procedure being called, `null` if not found
|
|
72
|
+
* @internal
|
|
73
|
+
*/
|
|
74
|
+
procedure: AnyProcedure | null;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export interface TRPCRequestInfoBase {
|
|
78
|
+
/**
|
|
79
|
+
* The `trpc-accept` header
|
|
80
|
+
*/
|
|
81
|
+
accept: TRPCAcceptHeader | null;
|
|
82
|
+
/**
|
|
83
|
+
* The type of the request
|
|
84
|
+
*/
|
|
85
|
+
type: ProcedureType | 'unknown';
|
|
86
|
+
/**
|
|
87
|
+
* If the content type handler has detected that this is a batch call
|
|
88
|
+
*/
|
|
89
|
+
isBatchCall: boolean;
|
|
90
|
+
/**
|
|
91
|
+
* The calls being made
|
|
92
|
+
*/
|
|
93
|
+
calls: TRPCRequestInfoProcedureCall[];
|
|
94
|
+
}
|
|
95
|
+
interface TRPCRequestInfoBatchCall extends TRPCRequestInfoBase {
|
|
96
|
+
isBatchCall: true;
|
|
97
|
+
calls: TRPCRequestInfoProcedureCall[];
|
|
98
|
+
}
|
|
99
|
+
interface TRPCRequestInfoSingleCall extends TRPCRequestInfoBase {
|
|
100
|
+
isBatchCall: false;
|
|
101
|
+
calls: [TRPCRequestInfoProcedureCall];
|
|
64
102
|
}
|
|
65
103
|
|
|
66
104
|
/**
|
|
67
105
|
* Information about the incoming request
|
|
68
106
|
* @public
|
|
69
107
|
*/
|
|
70
|
-
export
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
}
|
|
108
|
+
export type TRPCRequestInfo =
|
|
109
|
+
| TRPCRequestInfoBatchCall
|
|
110
|
+
| TRPCRequestInfoSingleCall;
|
|
74
111
|
|
|
75
112
|
/**
|
|
76
113
|
* Inner createContext function for `resolveResponse` used to forward `TRPCRequestInfo` to `createContext`
|
|
@@ -23,6 +23,7 @@ import type {
|
|
|
23
23
|
QueryProcedure,
|
|
24
24
|
SubscriptionProcedure,
|
|
25
25
|
} from './procedure';
|
|
26
|
+
import type { inferSSEOutput } from './stream/sse';
|
|
26
27
|
import type {
|
|
27
28
|
GetRawInputFn,
|
|
28
29
|
MaybePromise,
|
|
@@ -43,6 +44,12 @@ type DefaultValue<TValue, TFallback> = TValue extends UnsetMarker
|
|
|
43
44
|
? TFallback
|
|
44
45
|
: TValue;
|
|
45
46
|
|
|
47
|
+
type inferSubscriptionOutput<TOutput> = TOutput extends AsyncIterable<
|
|
48
|
+
infer $Output
|
|
49
|
+
>
|
|
50
|
+
? inferSSEOutput<$Output>
|
|
51
|
+
: inferObservableValue<TOutput>;
|
|
52
|
+
|
|
46
53
|
export type CallerOverride<TContext> = (opts: {
|
|
47
54
|
args: unknown[];
|
|
48
55
|
invoke: (opts: ProcedureCallOptions<TContext>) => Promise<unknown>;
|
|
@@ -347,7 +354,7 @@ export interface ProcedureBuilder<
|
|
|
347
354
|
? TypeError<'Not implemented'>
|
|
348
355
|
: SubscriptionProcedure<{
|
|
349
356
|
input: DefaultValue<TInputIn, void>;
|
|
350
|
-
output: DefaultValue<TOutputOut,
|
|
357
|
+
output: DefaultValue<TOutputOut, inferSubscriptionOutput<$Output>>;
|
|
351
358
|
}>;
|
|
352
359
|
|
|
353
360
|
/**
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { CombinedDataTransformer } from '../unstable-core-do-not-import';
|
|
2
2
|
import type { DefaultErrorShape, ErrorFormatter } from './error/formatter';
|
|
3
|
+
import type { SSEStreamProducerOptions } from './stream/sse';
|
|
3
4
|
|
|
4
5
|
/**
|
|
5
6
|
* The initial generics that are used in the init function
|
|
@@ -67,8 +68,19 @@ export interface RootConfig<TTypes extends RootTypes> {
|
|
|
67
68
|
experimental?: {
|
|
68
69
|
/**
|
|
69
70
|
* Enable support for returning async iterables and returning deferred promises when using `httpBatchStreamLink`
|
|
71
|
+
* @default true
|
|
70
72
|
*/
|
|
71
73
|
iterablesAndDeferreds?: boolean;
|
|
74
|
+
/**
|
|
75
|
+
* Enable support for server-sent events (SSE) subscriptions
|
|
76
|
+
*/
|
|
77
|
+
sseSubscriptions?: {
|
|
78
|
+
/**
|
|
79
|
+
* Enable server-sent events (SSE) subscriptions
|
|
80
|
+
* @default true
|
|
81
|
+
*/
|
|
82
|
+
enabled: boolean;
|
|
83
|
+
} & Omit<SSEStreamProducerOptions, 'maxDepth' | 'data' | 'serialize'>;
|
|
72
84
|
};
|
|
73
85
|
}
|
|
74
86
|
|
|
@@ -248,6 +248,18 @@ export function callProcedure(
|
|
|
248
248
|
});
|
|
249
249
|
}
|
|
250
250
|
|
|
251
|
+
/* istanbul ignore if -- @preserve */
|
|
252
|
+
if (
|
|
253
|
+
proc._def.type !== type &&
|
|
254
|
+
opts.allowMethodOverride &&
|
|
255
|
+
proc._def.type === 'subscription'
|
|
256
|
+
) {
|
|
257
|
+
throw new TRPCError({
|
|
258
|
+
code: 'METHOD_NOT_SUPPORTED',
|
|
259
|
+
message: `Method override is not supported for subscriptions`,
|
|
260
|
+
});
|
|
261
|
+
}
|
|
262
|
+
|
|
251
263
|
return proc(opts);
|
|
252
264
|
}
|
|
253
265
|
|
|
@@ -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;
|
|
198
203
|
}
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
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;
|
|
211
|
+
}
|
|
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/
|
|
@@ -430,10 +444,10 @@ export async function jsonlStreamConsumer<THead>(opts: {
|
|
|
430
444
|
const chunkDeferred = new Map<ChunkIndex, Deferred<ChunkController>>();
|
|
431
445
|
const controllers = new Map<ChunkIndex, ChunkController>();
|
|
432
446
|
|
|
433
|
-
function
|
|
447
|
+
function hydrateChunkDefinition(value: ChunkDefinition) {
|
|
434
448
|
const [_path, type, chunkId] = value;
|
|
435
449
|
|
|
436
|
-
const
|
|
450
|
+
const { readable, controller } = createReadableStream<ChunkData>();
|
|
437
451
|
controllers.set(chunkId, controller);
|
|
438
452
|
|
|
439
453
|
// resolve chunk deferred if it exists
|
|
@@ -447,7 +461,7 @@ export async function jsonlStreamConsumer<THead>(opts: {
|
|
|
447
461
|
case CHUNK_VALUE_TYPE_PROMISE: {
|
|
448
462
|
return new Promise((resolve, reject) => {
|
|
449
463
|
// listen for next value in the stream
|
|
450
|
-
const reader =
|
|
464
|
+
const reader = readable.getReader();
|
|
451
465
|
reader
|
|
452
466
|
.read()
|
|
453
467
|
.then((it) => {
|
|
@@ -463,7 +477,7 @@ export async function jsonlStreamConsumer<THead>(opts: {
|
|
|
463
477
|
const [_chunkId, status, data] = value as PromiseChunk;
|
|
464
478
|
switch (status) {
|
|
465
479
|
case PROMISE_STATUS_FULFILLED:
|
|
466
|
-
resolve(
|
|
480
|
+
resolve(hydrate(data));
|
|
467
481
|
break;
|
|
468
482
|
case PROMISE_STATUS_REJECTED:
|
|
469
483
|
reject(
|
|
@@ -482,7 +496,7 @@ export async function jsonlStreamConsumer<THead>(opts: {
|
|
|
482
496
|
case CHUNK_VALUE_TYPE_ASYNC_ITERABLE: {
|
|
483
497
|
return {
|
|
484
498
|
[Symbol.asyncIterator]: async function* () {
|
|
485
|
-
const reader =
|
|
499
|
+
const reader = readable.getReader();
|
|
486
500
|
while (true) {
|
|
487
501
|
const { done, value } = await reader.read();
|
|
488
502
|
if (done) {
|
|
@@ -496,7 +510,7 @@ export async function jsonlStreamConsumer<THead>(opts: {
|
|
|
496
510
|
|
|
497
511
|
switch (status) {
|
|
498
512
|
case ASYNC_ITERABLE_STATUS_VALUE:
|
|
499
|
-
yield
|
|
513
|
+
yield hydrate(data);
|
|
500
514
|
break;
|
|
501
515
|
case ASYNC_ITERABLE_STATUS_DONE:
|
|
502
516
|
controllers.delete(chunkId);
|
|
@@ -514,18 +528,18 @@ export async function jsonlStreamConsumer<THead>(opts: {
|
|
|
514
528
|
}
|
|
515
529
|
}
|
|
516
530
|
|
|
517
|
-
function
|
|
531
|
+
function hydrate(value: DehydratedValue): unknown {
|
|
518
532
|
const [[data], ...asyncProps] = value;
|
|
519
533
|
|
|
520
534
|
for (const value of asyncProps) {
|
|
521
|
-
const
|
|
535
|
+
const hydrated = hydrateChunkDefinition(value);
|
|
522
536
|
|
|
523
537
|
const [path] = value;
|
|
524
538
|
if (path === null) {
|
|
525
|
-
return
|
|
539
|
+
return hydrated;
|
|
526
540
|
}
|
|
527
541
|
|
|
528
|
-
(data as any)[path] =
|
|
542
|
+
(data as any)[path] = hydrated;
|
|
529
543
|
}
|
|
530
544
|
return data;
|
|
531
545
|
}
|
|
@@ -552,7 +566,7 @@ export async function jsonlStreamConsumer<THead>(opts: {
|
|
|
552
566
|
const head = chunkOrHead as Record<number | string, unknown>;
|
|
553
567
|
|
|
554
568
|
for (const [key, value] of Object.entries(chunkOrHead)) {
|
|
555
|
-
const parsed =
|
|
569
|
+
const parsed = hydrate(value as any);
|
|
556
570
|
head[key] = parsed;
|
|
557
571
|
}
|
|
558
572
|
headDeferred.resolve(head as THead);
|