@trpc/server 11.0.0-rc.589 → 11.0.0-rc.590
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/bundle-analysis.json +114 -111
- package/dist/unstable-core-do-not-import/http/resolveResponse.d.ts.map +1 -1
- package/dist/unstable-core-do-not-import/http/resolveResponse.js +30 -16
- package/dist/unstable-core-do-not-import/http/resolveResponse.mjs +31 -17
- package/dist/unstable-core-do-not-import/stream/sse.d.ts +1 -0
- package/dist/unstable-core-do-not-import/stream/sse.d.ts.map +1 -1
- package/dist/unstable-core-do-not-import/stream/sse.js +3 -2
- package/dist/unstable-core-do-not-import/stream/sse.mjs +3 -2
- package/dist/unstable-core-do-not-import/stream/utils/asyncIterable.d.ts +2 -1
- package/dist/unstable-core-do-not-import/stream/utils/asyncIterable.d.ts.map +1 -1
- package/dist/unstable-core-do-not-import/stream/utils/asyncIterable.js +2 -2
- package/dist/unstable-core-do-not-import/stream/utils/asyncIterable.mjs +2 -2
- package/dist/unstable-core-do-not-import/stream/utils/promiseTimer.js +1 -1
- package/dist/unstable-core-do-not-import/stream/utils/promiseTimer.mjs +1 -1
- package/dist/unstable-core-do-not-import/utils.d.ts +13 -0
- package/dist/unstable-core-do-not-import/utils.d.ts.map +1 -1
- package/dist/unstable-core-do-not-import/utils.js +41 -0
- package/dist/unstable-core-do-not-import/utils.mjs +39 -1
- package/dist/unstable-core-do-not-import.js +3 -0
- package/dist/unstable-core-do-not-import.mjs +1 -1
- package/package.json +2 -2
- package/src/unstable-core-do-not-import/http/resolveResponse.ts +39 -21
- package/src/unstable-core-do-not-import/stream/sse.ts +14 -4
- package/src/unstable-core-do-not-import/stream/utils/asyncIterable.ts +3 -2
- package/src/unstable-core-do-not-import/stream/utils/promiseTimer.ts +1 -1
- package/src/unstable-core-do-not-import/utils.ts +48 -0
|
@@ -4,7 +4,7 @@ import { TRPCError, getTRPCErrorFromUnknown } from '../error/TRPCError.mjs';
|
|
|
4
4
|
import { jsonlStreamProducer, isPromise } from '../stream/jsonl.mjs';
|
|
5
5
|
import { sseStreamProducer, sseHeaders } from '../stream/sse.mjs';
|
|
6
6
|
import { transformTRPCResponse } from '../transformer.mjs';
|
|
7
|
-
import { isAsyncIterable, isObject } from '../utils.mjs';
|
|
7
|
+
import { abortSignalsAnyPonyfill, assert, isAsyncIterable, isObject } from '../utils.mjs';
|
|
8
8
|
import { getRequestInfo } from './contentType.mjs';
|
|
9
9
|
import { getHTTPStatusCode } from './getHTTPStatusCode.mjs';
|
|
10
10
|
|
|
@@ -185,21 +185,31 @@ async function resolveResponse(opts) {
|
|
|
185
185
|
message: `Unsupported ${req.method}-request to ${proc._def.type} procedure at path "${call.path}"`
|
|
186
186
|
});
|
|
187
187
|
}
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
188
|
+
let abortCtrl;
|
|
189
|
+
if (proc._def.type === 'subscription') {
|
|
190
|
+
/* istanbul ignore if -- @preserve */ if (info.isBatchCall) {
|
|
191
|
+
throw new TRPCError({
|
|
192
|
+
code: 'BAD_REQUEST',
|
|
193
|
+
message: `Cannot batch subscription calls`
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
abortCtrl = new AbortController();
|
|
193
197
|
}
|
|
194
198
|
const data = await proc({
|
|
195
199
|
path: call.path,
|
|
196
200
|
getRawInput: call.getRawInput,
|
|
197
201
|
ctx,
|
|
198
202
|
type: proc._def.type,
|
|
199
|
-
signal:
|
|
203
|
+
signal: abortCtrl ? abortSignalsAnyPonyfill([
|
|
204
|
+
opts.req.signal,
|
|
205
|
+
abortCtrl.signal
|
|
206
|
+
]) : opts.req.signal
|
|
200
207
|
});
|
|
201
208
|
return [
|
|
202
|
-
|
|
209
|
+
{
|
|
210
|
+
data,
|
|
211
|
+
abortCtrl
|
|
212
|
+
}
|
|
203
213
|
];
|
|
204
214
|
} catch (cause) {
|
|
205
215
|
const error = getTRPCErrorFromUnknown(cause);
|
|
@@ -221,7 +231,7 @@ async function resolveResponse(opts) {
|
|
|
221
231
|
// ----------- response handlers -----------
|
|
222
232
|
if (!info.isBatchCall) {
|
|
223
233
|
const [call] = info.calls;
|
|
224
|
-
const [
|
|
234
|
+
const [result, error] = await rpcCalls[0];
|
|
225
235
|
switch(info.type){
|
|
226
236
|
case 'unknown':
|
|
227
237
|
case 'mutation':
|
|
@@ -229,7 +239,7 @@ async function resolveResponse(opts) {
|
|
|
229
239
|
{
|
|
230
240
|
// httpLink
|
|
231
241
|
headers.set('content-type', 'application/json');
|
|
232
|
-
if (isDataStream(data)) {
|
|
242
|
+
if (isDataStream(result?.data)) {
|
|
233
243
|
throw new TRPCError({
|
|
234
244
|
code: 'UNSUPPORTED_MEDIA_TYPE',
|
|
235
245
|
message: 'Cannot use stream-like response in non-streaming request - use httpBatchStreamLink'
|
|
@@ -246,7 +256,7 @@ async function resolveResponse(opts) {
|
|
|
246
256
|
})
|
|
247
257
|
} : {
|
|
248
258
|
result: {
|
|
249
|
-
data
|
|
259
|
+
data: result.data
|
|
250
260
|
}
|
|
251
261
|
};
|
|
252
262
|
const headResponse = initResponse({
|
|
@@ -278,6 +288,8 @@ async function resolveResponse(opts) {
|
|
|
278
288
|
if (error) {
|
|
279
289
|
throw error;
|
|
280
290
|
}
|
|
291
|
+
const { data , abortCtrl } = result;
|
|
292
|
+
assert(abortCtrl !== undefined, 'subscription type must have an AbortController');
|
|
281
293
|
if (!isObservable(data) && !isAsyncIterable(data)) {
|
|
282
294
|
throw new TRPCError({
|
|
283
295
|
message: `Subscription ${call.path} did not return an observable or a AsyncGenerator`,
|
|
@@ -288,6 +300,7 @@ async function resolveResponse(opts) {
|
|
|
288
300
|
const stream = sseStreamProducer({
|
|
289
301
|
...config.experimental?.sseSubscriptions,
|
|
290
302
|
data: dataAsIterable,
|
|
303
|
+
abortCtrl,
|
|
291
304
|
serialize: (v)=>config.transformer.output.serialize(v),
|
|
292
305
|
formatError (errorOpts) {
|
|
293
306
|
const error = getTRPCErrorFromUnknown(errorOpts.error);
|
|
@@ -373,13 +386,14 @@ async function resolveResponse(opts) {
|
|
|
373
386
|
})
|
|
374
387
|
};
|
|
375
388
|
}
|
|
389
|
+
const { data } = result;
|
|
376
390
|
/**
|
|
377
391
|
* Not very pretty, but we need to wrap nested data in promises
|
|
378
392
|
* Our stream producer will only resolve top-level async values or async values that are directly nested in another async value
|
|
379
|
-
*/ const
|
|
393
|
+
*/ const dataAsPromiseOrIterable = isObservable(data) ? observableToAsyncIterable(data) : Promise.resolve(data);
|
|
380
394
|
return {
|
|
381
395
|
result: Promise.resolve({
|
|
382
|
-
data
|
|
396
|
+
data: dataAsPromiseOrIterable
|
|
383
397
|
})
|
|
384
398
|
};
|
|
385
399
|
}),
|
|
@@ -420,11 +434,11 @@ async function resolveResponse(opts) {
|
|
|
420
434
|
* - return a complete HTTPResponse
|
|
421
435
|
*/ headers.set('content-type', 'application/json');
|
|
422
436
|
const results = (await Promise.all(rpcCalls)).map((res)=>{
|
|
423
|
-
const [
|
|
437
|
+
const [result, error] = res;
|
|
424
438
|
if (error) {
|
|
425
439
|
return res;
|
|
426
440
|
}
|
|
427
|
-
if (isDataStream(data)) {
|
|
441
|
+
if (isDataStream(result.data)) {
|
|
428
442
|
return [
|
|
429
443
|
null,
|
|
430
444
|
new TRPCError({
|
|
@@ -435,7 +449,7 @@ async function resolveResponse(opts) {
|
|
|
435
449
|
}
|
|
436
450
|
return res;
|
|
437
451
|
});
|
|
438
|
-
const resultAsRPCResponse = results.map(([
|
|
452
|
+
const resultAsRPCResponse = results.map(([result, error], index)=>{
|
|
439
453
|
const call = info.calls[index];
|
|
440
454
|
if (error) {
|
|
441
455
|
return {
|
|
@@ -451,7 +465,7 @@ async function resolveResponse(opts) {
|
|
|
451
465
|
}
|
|
452
466
|
return {
|
|
453
467
|
result: {
|
|
454
|
-
data
|
|
468
|
+
data: result.data
|
|
455
469
|
}
|
|
456
470
|
};
|
|
457
471
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sse.d.ts","sourceRoot":"","sources":["../../../src/unstable-core-do-not-import/stream/sse.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAE7C,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,WAAW,CAAC;AASpD,KAAK,SAAS,GAAG,CAAC,KAAK,EAAE,GAAG,KAAK,GAAG,CAAC;AACrC,KAAK,WAAW,GAAG,CAAC,KAAK,EAAE,GAAG,KAAK,GAAG,CAAC;AAEvC;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B;;;OAGG;IACH,OAAO,EAAE,OAAO,CAAC;IACjB;;;OAGG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,wBAAwB,CAAC,MAAM,GAAG,OAAO;IACxD,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB,IAAI,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;IAC5B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,WAAW,CAAC;IACnB;;;;OAIG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB;;;;OAIG;IACH,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAChC,WAAW,CAAC,EAAE,CAAC,IAAI,EAAE;QAAE,KAAK,EAAE,OAAO,CAAA;KAAE,KAAK,OAAO,CAAC;CACrD;AAUD;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,GAAG,OAAO,
|
|
1
|
+
{"version":3,"file":"sse.d.ts","sourceRoot":"","sources":["../../../src/unstable-core-do-not-import/stream/sse.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAE7C,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,WAAW,CAAC;AASpD,KAAK,SAAS,GAAG,CAAC,KAAK,EAAE,GAAG,KAAK,GAAG,CAAC;AACrC,KAAK,WAAW,GAAG,CAAC,KAAK,EAAE,GAAG,KAAK,GAAG,CAAC;AAEvC;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B;;;OAGG;IACH,OAAO,EAAE,OAAO,CAAC;IACjB;;;OAGG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,wBAAwB,CAAC,MAAM,GAAG,OAAO;IACxD,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB,IAAI,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;IAC5B,SAAS,EAAE,eAAe,CAAC;IAC3B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,WAAW,CAAC;IACnB;;;;OAIG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB;;;;OAIG;IACH,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAChC,WAAW,CAAC,EAAE,CAAC,IAAI,EAAE;QAAE,KAAK,EAAE,OAAO,CAAA;KAAE,KAAK,OAAO,CAAC;CACrD;AAUD;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,GAAG,OAAO,EAChD,IAAI,EAAE,wBAAwB,CAAC,MAAM,CAAC,0BAkGvC;AAED,UAAU,wBAAwB;IAChC,WAAW,EAAE,WAAW,CAAC;CAC1B;AAED,UAAU,wBAAwB,CAAC,KAAK,CAAE,SAAQ,wBAAwB;IACxE,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,kBAAkB,CAAC,KAAK,CAAC,CAAC;CACjC;AAED,UAAU,yBAA0B,SAAQ,wBAAwB;IAClE,IAAI,EAAE,OAAO,CAAC;IACd,KAAK,EAAE,OAAO,CAAC;CAChB;AAED,UAAU,0BAA2B,SAAQ,wBAAwB;IACnE,IAAI,EAAE,QAAQ,CAAC;CAChB;AAED,UAAU,8BAA+B,SAAQ,wBAAwB;IACvE,IAAI,EAAE,YAAY,CAAC;CACpB;AAED,KAAK,oBAAoB,CAAC,KAAK,IAC3B,wBAAwB,CAAC,KAAK,CAAC,GAC/B,yBAAyB,GACzB,0BAA0B,GAC1B,8BAA8B,CAAC;AAEnC,MAAM,WAAW,wBAAwB;IACvC,GAAG,EAAE,MAAM,YAAY,CAAC,MAAM,CAAC,CAAC;IAChC,IAAI,EAAE,MAAM,YAAY,CAAC,eAAe,CAAC,GAAG,SAAS,CAAC;IACtD,MAAM,EAAE,WAAW,CAAC;IACpB,qBAAqB,CAAC,EAAE,CACtB,IAAI,EACA;QACE,IAAI,EAAE,OAAO,CAAC;QACd,KAAK,EAAE,KAAK,CAAC;KACd,GACD;QACE,IAAI,EAAE,kBAAkB,CAAC;QACzB,KAAK,EAAE,OAAO,CAAC;KAChB,KACF,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAChC,WAAW,CAAC,EAAE,WAAW,CAAC;CAC3B;AACD;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EACrC,IAAI,EAAE,wBAAwB,GAC7B,aAAa,CAAC,oBAAoB,CAAC,KAAK,CAAC,CAAC,CAkL5C;AAED,eAAO,MAAM,UAAU;;;;;CAKb,CAAC"}
|
|
@@ -29,13 +29,14 @@ const SERIALIZED_ERROR_EVENT = 'serialized-error';
|
|
|
29
29
|
if (opts.emitAndEndImmediately) {
|
|
30
30
|
iterable = asyncIterable.takeWithGrace(iterable, {
|
|
31
31
|
count: 1,
|
|
32
|
-
gracePeriodMs: 1
|
|
32
|
+
gracePeriodMs: 1,
|
|
33
|
+
onCancel: ()=>opts.abortCtrl.abort()
|
|
33
34
|
});
|
|
34
35
|
}
|
|
35
36
|
let maxDurationTimer = null;
|
|
36
37
|
if (opts.maxDurationMs != null && opts.maxDurationMs > 0 && opts.maxDurationMs !== Infinity) {
|
|
37
38
|
maxDurationTimer = promiseTimer.createPromiseTimer(opts.maxDurationMs).start();
|
|
38
|
-
iterable = asyncIterable.withCancel(iterable, maxDurationTimer.promise);
|
|
39
|
+
iterable = asyncIterable.withCancel(iterable, maxDurationTimer.promise.then(()=>opts.abortCtrl.abort()));
|
|
39
40
|
}
|
|
40
41
|
if (ping.enabled && ping.intervalMs !== Infinity && ping.intervalMs > 0) {
|
|
41
42
|
iterable = withPing.withPing(iterable, ping.intervalMs);
|
|
@@ -27,13 +27,14 @@ const SERIALIZED_ERROR_EVENT = 'serialized-error';
|
|
|
27
27
|
if (opts.emitAndEndImmediately) {
|
|
28
28
|
iterable = takeWithGrace(iterable, {
|
|
29
29
|
count: 1,
|
|
30
|
-
gracePeriodMs: 1
|
|
30
|
+
gracePeriodMs: 1,
|
|
31
|
+
onCancel: ()=>opts.abortCtrl.abort()
|
|
31
32
|
});
|
|
32
33
|
}
|
|
33
34
|
let maxDurationTimer = null;
|
|
34
35
|
if (opts.maxDurationMs != null && opts.maxDurationMs > 0 && opts.maxDurationMs !== Infinity) {
|
|
35
36
|
maxDurationTimer = createPromiseTimer(opts.maxDurationMs).start();
|
|
36
|
-
iterable = withCancel(iterable, maxDurationTimer.promise);
|
|
37
|
+
iterable = withCancel(iterable, maxDurationTimer.promise.then(()=>opts.abortCtrl.abort()));
|
|
37
38
|
}
|
|
38
39
|
if (ping.enabled && ping.intervalMs !== Infinity && ping.intervalMs > 0) {
|
|
39
40
|
iterable = withPing(iterable, ping.intervalMs);
|
|
@@ -6,12 +6,13 @@ export declare function withCancel<T>(iterable: AsyncIterable<T>, cancel: Promis
|
|
|
6
6
|
interface TakeWithGraceOptions {
|
|
7
7
|
count: number;
|
|
8
8
|
gracePeriodMs: number;
|
|
9
|
+
onCancel?: () => void;
|
|
9
10
|
}
|
|
10
11
|
/**
|
|
11
12
|
* Derives a new {@link AsyncGenerator} based of {@link iterable}, that yields its first
|
|
12
13
|
* {@link count} values. Then, a grace period of {@link gracePeriodMs} is started in which further
|
|
13
14
|
* values may still come through. After this period, the generator stops.
|
|
14
15
|
*/
|
|
15
|
-
export declare function takeWithGrace<T>(iterable: AsyncIterable<T>, { count, gracePeriodMs }: TakeWithGraceOptions): AsyncGenerator<T>;
|
|
16
|
+
export declare function takeWithGrace<T>(iterable: AsyncIterable<T>, { count, gracePeriodMs, onCancel }: TakeWithGraceOptions): AsyncGenerator<T>;
|
|
16
17
|
export {};
|
|
17
18
|
//# sourceMappingURL=asyncIterable.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"asyncIterable.d.ts","sourceRoot":"","sources":["../../../../src/unstable-core-do-not-import/stream/utils/asyncIterable.ts"],"names":[],"mappings":"AAGA;;;GAGG;AACH,wBAAuB,UAAU,CAAC,CAAC,EACjC,QAAQ,EAAE,aAAa,CAAC,CAAC,CAAC,EAC1B,MAAM,EAAE,OAAO,CAAC,OAAO,CAAC,GACvB,cAAc,CAAC,CAAC,CAAC,CAcnB;AAED,UAAU,oBAAoB;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,aAAa,EAAE,MAAM,CAAC;CACvB;AAED;;;;GAIG;AACH,wBAAuB,aAAa,CAAC,CAAC,EACpC,QAAQ,EAAE,aAAa,CAAC,CAAC,CAAC,EAC1B,EAAE,KAAK,EAAE,aAAa,EAAE,EAAE,oBAAoB,
|
|
1
|
+
{"version":3,"file":"asyncIterable.d.ts","sourceRoot":"","sources":["../../../../src/unstable-core-do-not-import/stream/utils/asyncIterable.ts"],"names":[],"mappings":"AAGA;;;GAGG;AACH,wBAAuB,UAAU,CAAC,CAAC,EACjC,QAAQ,EAAE,aAAa,CAAC,CAAC,CAAC,EAC1B,MAAM,EAAE,OAAO,CAAC,OAAO,CAAC,GACvB,cAAc,CAAC,CAAC,CAAC,CAcnB;AAED,UAAU,oBAAoB;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,aAAa,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAC;CACvB;AAED;;;;GAIG;AACH,wBAAuB,aAAa,CAAC,CAAC,EACpC,QAAQ,EAAE,aAAa,CAAC,CAAC,CAAC,EAC1B,EAAE,KAAK,EAAE,aAAa,EAAE,QAAQ,EAAE,EAAE,oBAAoB,GACvD,cAAc,CAAC,CAAC,CAAC,CAsBnB"}
|
|
@@ -28,7 +28,7 @@ var promiseTimer = require('./promiseTimer.js');
|
|
|
28
28
|
* Derives a new {@link AsyncGenerator} based of {@link iterable}, that yields its first
|
|
29
29
|
* {@link count} values. Then, a grace period of {@link gracePeriodMs} is started in which further
|
|
30
30
|
* values may still come through. After this period, the generator stops.
|
|
31
|
-
*/ async function* takeWithGrace(iterable, { count , gracePeriodMs }) {
|
|
31
|
+
*/ async function* takeWithGrace(iterable, { count , gracePeriodMs , onCancel }) {
|
|
32
32
|
const iterator = iterable[Symbol.asyncIterator]();
|
|
33
33
|
const timer = promiseTimer.createPromiseTimer(gracePeriodMs);
|
|
34
34
|
try {
|
|
@@ -47,7 +47,7 @@ var promiseTimer = require('./promiseTimer.js');
|
|
|
47
47
|
}
|
|
48
48
|
yield result.value;
|
|
49
49
|
if (--count === 0) {
|
|
50
|
-
timer.start();
|
|
50
|
+
timer.start().promise.then(onCancel, utils.noop);
|
|
51
51
|
}
|
|
52
52
|
}
|
|
53
53
|
} finally{
|
|
@@ -26,7 +26,7 @@ import { createPromiseTimer } from './promiseTimer.mjs';
|
|
|
26
26
|
* Derives a new {@link AsyncGenerator} based of {@link iterable}, that yields its first
|
|
27
27
|
* {@link count} values. Then, a grace period of {@link gracePeriodMs} is started in which further
|
|
28
28
|
* values may still come through. After this period, the generator stops.
|
|
29
|
-
*/ async function* takeWithGrace(iterable, { count , gracePeriodMs }) {
|
|
29
|
+
*/ async function* takeWithGrace(iterable, { count , gracePeriodMs , onCancel }) {
|
|
30
30
|
const iterator = iterable[Symbol.asyncIterator]();
|
|
31
31
|
const timer = createPromiseTimer(gracePeriodMs);
|
|
32
32
|
try {
|
|
@@ -45,7 +45,7 @@ import { createPromiseTimer } from './promiseTimer.mjs';
|
|
|
45
45
|
}
|
|
46
46
|
yield result.value;
|
|
47
47
|
if (--count === 0) {
|
|
48
|
-
timer.start();
|
|
48
|
+
timer.start().promise.then(onCancel, noop);
|
|
49
49
|
}
|
|
50
50
|
}
|
|
51
51
|
} finally{
|
|
@@ -16,7 +16,7 @@ function createPromiseTimer(ms) {
|
|
|
16
16
|
return timer;
|
|
17
17
|
function start() {
|
|
18
18
|
if (timeout != null) {
|
|
19
|
-
throw new Error(
|
|
19
|
+
throw new Error('PromiseTimer already started.');
|
|
20
20
|
}
|
|
21
21
|
timeout = setTimeout(deferred.resolve, ms);
|
|
22
22
|
return timer;
|
|
@@ -14,7 +14,7 @@ function createPromiseTimer(ms) {
|
|
|
14
14
|
return timer;
|
|
15
15
|
function start() {
|
|
16
16
|
if (timeout != null) {
|
|
17
|
-
throw new Error(
|
|
17
|
+
throw new Error('PromiseTimer already started.');
|
|
18
18
|
}
|
|
19
19
|
timeout = setTimeout(deferred.resolve, ms);
|
|
20
20
|
return timer;
|
|
@@ -25,5 +25,18 @@ export declare function isAsyncIterable<TValue>(value: unknown): value is AsyncI
|
|
|
25
25
|
export declare const run: <TValue>(fn: () => TValue) => TValue;
|
|
26
26
|
export declare function noop(): void;
|
|
27
27
|
export declare function identity<T>(it: T): T;
|
|
28
|
+
/**
|
|
29
|
+
* Generic runtime assertion function. Throws, if the condition is not `true`.
|
|
30
|
+
*
|
|
31
|
+
* Can be used as a slightly less dangerous variant of type assertions. Code
|
|
32
|
+
* mistakes would be revealed at runtime then (hopefully during testing).
|
|
33
|
+
*/
|
|
34
|
+
export declare function assert(condition: boolean, msg?: string): asserts condition;
|
|
35
|
+
export declare function sleep(ms?: number): Promise<void>;
|
|
36
|
+
/**
|
|
37
|
+
* Ponyfill for
|
|
38
|
+
* [`AbortSignal.any`](https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal/any_static).
|
|
39
|
+
*/
|
|
40
|
+
export declare function abortSignalsAnyPonyfill(signals: AbortSignal[]): AbortSignal;
|
|
28
41
|
export {};
|
|
29
42
|
//# sourceMappingURL=utils.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/unstable-core-do-not-import/utils.ts"],"names":[],"mappings":"AAAA,gBAAgB;AAChB,eAAO,MAAM,WAAW,eAAW,CAAC;AACpC,MAAM,MAAM,WAAW,GAAG,OAAO,WAAW,CAAC;AAE7C;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACzE,IAAI,EAAE,KAAK,EACX,GAAG,IAAI,EAAE,OAAO,CAAC,KAAK,CAAC,EAAE,GACxB,KAAK,CAYP;AAED;;;GAGG;AACH,wBAAgB,QAAQ,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAEzE;AAED,KAAK,KAAK,GAAG,CAAC,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,GAAG,EAAE,OAAO,CAAC,CAAC;AACxE,wBAAgB,UAAU,CAAC,EAAE,EAAE,OAAO,GAAG,EAAE,IAAI,KAAK,CAEnD;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,IAAI,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAChE,GAAG,EAAE,IAAI,GACR,IAAI,CAEN;AAKD,wBAAgB,eAAe,CAAC,MAAM,EACpC,KAAK,EAAE,OAAO,GACb,KAAK,IAAI,aAAa,CAAC,MAAM,CAAC,CAIhC;AAED;;GAEG;AACH,eAAO,MAAM,GAAG,GAAI,MAAM,MAAM,MAAM,MAAM,KAAG,MAAc,CAAC;AAG9D,wBAAgB,IAAI,IAAI,IAAI,CAAG;AAE/B,wBAAgB,QAAQ,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,GAAG,CAAC,CAEpC"}
|
|
1
|
+
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/unstable-core-do-not-import/utils.ts"],"names":[],"mappings":"AAAA,gBAAgB;AAChB,eAAO,MAAM,WAAW,eAAW,CAAC;AACpC,MAAM,MAAM,WAAW,GAAG,OAAO,WAAW,CAAC;AAE7C;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACzE,IAAI,EAAE,KAAK,EACX,GAAG,IAAI,EAAE,OAAO,CAAC,KAAK,CAAC,EAAE,GACxB,KAAK,CAYP;AAED;;;GAGG;AACH,wBAAgB,QAAQ,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAEzE;AAED,KAAK,KAAK,GAAG,CAAC,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,GAAG,EAAE,OAAO,CAAC,CAAC;AACxE,wBAAgB,UAAU,CAAC,EAAE,EAAE,OAAO,GAAG,EAAE,IAAI,KAAK,CAEnD;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,IAAI,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAChE,GAAG,EAAE,IAAI,GACR,IAAI,CAEN;AAKD,wBAAgB,eAAe,CAAC,MAAM,EACpC,KAAK,EAAE,OAAO,GACb,KAAK,IAAI,aAAa,CAAC,MAAM,CAAC,CAIhC;AAED;;GAEG;AACH,eAAO,MAAM,GAAG,GAAI,MAAM,MAAM,MAAM,MAAM,KAAG,MAAc,CAAC;AAG9D,wBAAgB,IAAI,IAAI,IAAI,CAAG;AAE/B,wBAAgB,QAAQ,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,GAAG,CAAC,CAEpC;AAED;;;;;GAKG;AACH,wBAAgB,MAAM,CACpB,SAAS,EAAE,OAAO,EAClB,GAAG,SAAuB,GACzB,OAAO,CAAC,SAAS,CAInB;AAED,wBAAgB,KAAK,CAAC,EAAE,SAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAE3C;AAED;;;GAGG;AACH,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,WAAW,EAAE,GAAG,WAAW,CAuB3E"}
|
|
@@ -43,7 +43,47 @@ function noop() {}
|
|
|
43
43
|
function identity(it) {
|
|
44
44
|
return it;
|
|
45
45
|
}
|
|
46
|
+
/**
|
|
47
|
+
* Generic runtime assertion function. Throws, if the condition is not `true`.
|
|
48
|
+
*
|
|
49
|
+
* Can be used as a slightly less dangerous variant of type assertions. Code
|
|
50
|
+
* mistakes would be revealed at runtime then (hopefully during testing).
|
|
51
|
+
*/ function assert(condition, msg = 'no additional info') {
|
|
52
|
+
if (!condition) {
|
|
53
|
+
throw new Error(`AssertionError: ${msg}`);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
function sleep(ms = 0) {
|
|
57
|
+
return new Promise((res)=>setTimeout(res, ms));
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Ponyfill for
|
|
61
|
+
* [`AbortSignal.any`](https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal/any_static).
|
|
62
|
+
*/ function abortSignalsAnyPonyfill(signals) {
|
|
63
|
+
if (typeof AbortSignal.any === 'function') {
|
|
64
|
+
return AbortSignal.any(signals);
|
|
65
|
+
}
|
|
66
|
+
const ac = new AbortController();
|
|
67
|
+
for (const signal of signals){
|
|
68
|
+
if (signal.aborted) {
|
|
69
|
+
trigger();
|
|
70
|
+
} else if (!ac.signal.aborted) {
|
|
71
|
+
signal.addEventListener('abort', trigger, {
|
|
72
|
+
once: true
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
return ac.signal;
|
|
77
|
+
function trigger() {
|
|
78
|
+
ac.abort();
|
|
79
|
+
for (const signal of signals){
|
|
80
|
+
signal.removeEventListener('abort', trigger);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
46
84
|
|
|
85
|
+
exports.abortSignalsAnyPonyfill = abortSignalsAnyPonyfill;
|
|
86
|
+
exports.assert = assert;
|
|
47
87
|
exports.identity = identity;
|
|
48
88
|
exports.isAsyncIterable = isAsyncIterable;
|
|
49
89
|
exports.isFunction = isFunction;
|
|
@@ -52,4 +92,5 @@ exports.mergeWithoutOverrides = mergeWithoutOverrides;
|
|
|
52
92
|
exports.noop = noop;
|
|
53
93
|
exports.omitPrototype = omitPrototype;
|
|
54
94
|
exports.run = run;
|
|
95
|
+
exports.sleep = sleep;
|
|
55
96
|
exports.unsetMarker = unsetMarker;
|
|
@@ -41,5 +41,43 @@ function noop() {}
|
|
|
41
41
|
function identity(it) {
|
|
42
42
|
return it;
|
|
43
43
|
}
|
|
44
|
+
/**
|
|
45
|
+
* Generic runtime assertion function. Throws, if the condition is not `true`.
|
|
46
|
+
*
|
|
47
|
+
* Can be used as a slightly less dangerous variant of type assertions. Code
|
|
48
|
+
* mistakes would be revealed at runtime then (hopefully during testing).
|
|
49
|
+
*/ function assert(condition, msg = 'no additional info') {
|
|
50
|
+
if (!condition) {
|
|
51
|
+
throw new Error(`AssertionError: ${msg}`);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
function sleep(ms = 0) {
|
|
55
|
+
return new Promise((res)=>setTimeout(res, ms));
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Ponyfill for
|
|
59
|
+
* [`AbortSignal.any`](https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal/any_static).
|
|
60
|
+
*/ function abortSignalsAnyPonyfill(signals) {
|
|
61
|
+
if (typeof AbortSignal.any === 'function') {
|
|
62
|
+
return AbortSignal.any(signals);
|
|
63
|
+
}
|
|
64
|
+
const ac = new AbortController();
|
|
65
|
+
for (const signal of signals){
|
|
66
|
+
if (signal.aborted) {
|
|
67
|
+
trigger();
|
|
68
|
+
} else if (!ac.signal.aborted) {
|
|
69
|
+
signal.addEventListener('abort', trigger, {
|
|
70
|
+
once: true
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
return ac.signal;
|
|
75
|
+
function trigger() {
|
|
76
|
+
ac.abort();
|
|
77
|
+
for (const signal of signals){
|
|
78
|
+
signal.removeEventListener('abort', trigger);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
44
82
|
|
|
45
|
-
export { identity, isAsyncIterable, isFunction, isObject, mergeWithoutOverrides, noop, omitPrototype, run, unsetMarker };
|
|
83
|
+
export { abortSignalsAnyPonyfill, assert, identity, isAsyncIterable, isFunction, isObject, mergeWithoutOverrides, noop, omitPrototype, run, sleep, unsetMarker };
|
|
@@ -78,6 +78,8 @@ exports.defaultTransformer = transformer.defaultTransformer;
|
|
|
78
78
|
exports.getDataTransformer = transformer.getDataTransformer;
|
|
79
79
|
exports.transformResult = transformer.transformResult;
|
|
80
80
|
exports.transformTRPCResponse = transformer.transformTRPCResponse;
|
|
81
|
+
exports.abortSignalsAnyPonyfill = utils.abortSignalsAnyPonyfill;
|
|
82
|
+
exports.assert = utils.assert;
|
|
81
83
|
exports.identity = utils.identity;
|
|
82
84
|
exports.isAsyncIterable = utils.isAsyncIterable;
|
|
83
85
|
exports.isFunction = utils.isFunction;
|
|
@@ -86,4 +88,5 @@ exports.mergeWithoutOverrides = utils.mergeWithoutOverrides;
|
|
|
86
88
|
exports.noop = utils.noop;
|
|
87
89
|
exports.omitPrototype = utils.omitPrototype;
|
|
88
90
|
exports.run = utils.run;
|
|
91
|
+
exports.sleep = utils.sleep;
|
|
89
92
|
exports.unsetMarker = utils.unsetMarker;
|
|
@@ -24,4 +24,4 @@ export { sseHeaders, sseStreamConsumer, sseStreamProducer } from './unstable-cor
|
|
|
24
24
|
export { isTrackedEnvelope, sse, tracked } from './unstable-core-do-not-import/stream/tracked.mjs';
|
|
25
25
|
export { createDeferred } from './unstable-core-do-not-import/stream/utils/createDeferred.mjs';
|
|
26
26
|
export { defaultTransformer, getDataTransformer, transformResult, transformTRPCResponse } from './unstable-core-do-not-import/transformer.mjs';
|
|
27
|
-
export { identity, isAsyncIterable, isFunction, isObject, mergeWithoutOverrides, noop, omitPrototype, run, unsetMarker } from './unstable-core-do-not-import/utils.mjs';
|
|
27
|
+
export { abortSignalsAnyPonyfill, assert, identity, isAsyncIterable, isFunction, isObject, mergeWithoutOverrides, noop, omitPrototype, run, sleep, unsetMarker } from './unstable-core-do-not-import/utils.mjs';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@trpc/server",
|
|
3
|
-
"version": "11.0.0-rc.
|
|
3
|
+
"version": "11.0.0-rc.590+3621497e1",
|
|
4
4
|
"description": "The tRPC server library",
|
|
5
5
|
"author": "KATT",
|
|
6
6
|
"license": "MIT",
|
|
@@ -149,5 +149,5 @@
|
|
|
149
149
|
"funding": [
|
|
150
150
|
"https://trpc.io/sponsor"
|
|
151
151
|
],
|
|
152
|
-
"gitHead": "
|
|
152
|
+
"gitHead": "3621497e1a831537d3c864074656a09527cefba5"
|
|
153
153
|
}
|
|
@@ -15,7 +15,7 @@ import type { TRPCResponse } from '../rpc';
|
|
|
15
15
|
import { isPromise, jsonlStreamProducer } from '../stream/jsonl';
|
|
16
16
|
import { sseHeaders, sseStreamProducer } from '../stream/sse';
|
|
17
17
|
import { transformTRPCResponse } from '../transformer';
|
|
18
|
-
import { isAsyncIterable, isObject } from '../utils';
|
|
18
|
+
import { abortSignalsAnyPonyfill, assert, isAsyncIterable, isObject } from '../utils';
|
|
19
19
|
import { getRequestInfo } from './contentType';
|
|
20
20
|
import { getHTTPStatusCode } from './getHTTPStatusCode';
|
|
21
21
|
import type {
|
|
@@ -260,9 +260,13 @@ export async function resolveResponse<TRouter extends AnyRouter>(
|
|
|
260
260
|
});
|
|
261
261
|
}
|
|
262
262
|
|
|
263
|
+
interface RPCResultOk {
|
|
264
|
+
data: unknown;
|
|
265
|
+
abortCtrl?: AbortController;
|
|
266
|
+
}
|
|
263
267
|
type RPCResult =
|
|
264
268
|
| [result: null, error: TRPCError]
|
|
265
|
-
| [result:
|
|
269
|
+
| [result: RPCResultOk, error?: never];
|
|
266
270
|
const rpcCalls = info.calls.map(async (call): Promise<RPCResult> => {
|
|
267
271
|
const proc = call.procedure;
|
|
268
272
|
try {
|
|
@@ -279,21 +283,28 @@ export async function resolveResponse<TRouter extends AnyRouter>(
|
|
|
279
283
|
message: `Unsupported ${req.method}-request to ${proc._def.type} procedure at path "${call.path}"`,
|
|
280
284
|
});
|
|
281
285
|
}
|
|
282
|
-
|
|
283
|
-
if (proc._def.type === 'subscription'
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
286
|
+
let abortCtrl: AbortController | undefined;
|
|
287
|
+
if (proc._def.type === 'subscription') {
|
|
288
|
+
/* istanbul ignore if -- @preserve */
|
|
289
|
+
if (info!.isBatchCall) {
|
|
290
|
+
throw new TRPCError({
|
|
291
|
+
code: 'BAD_REQUEST',
|
|
292
|
+
message: `Cannot batch subscription calls`,
|
|
293
|
+
});
|
|
294
|
+
}
|
|
295
|
+
abortCtrl = new AbortController();
|
|
288
296
|
}
|
|
297
|
+
|
|
289
298
|
const data: unknown = await proc({
|
|
290
299
|
path: call.path,
|
|
291
300
|
getRawInput: call.getRawInput,
|
|
292
301
|
ctx,
|
|
293
302
|
type: proc._def.type,
|
|
294
|
-
signal:
|
|
303
|
+
signal: abortCtrl
|
|
304
|
+
? abortSignalsAnyPonyfill([opts.req.signal, abortCtrl.signal])
|
|
305
|
+
: opts.req.signal,
|
|
295
306
|
});
|
|
296
|
-
return [data];
|
|
307
|
+
return [{ data, abortCtrl }];
|
|
297
308
|
} catch (cause) {
|
|
298
309
|
const error = getTRPCErrorFromUnknown(cause);
|
|
299
310
|
const input = call.result();
|
|
@@ -314,7 +325,7 @@ export async function resolveResponse<TRouter extends AnyRouter>(
|
|
|
314
325
|
// ----------- response handlers -----------
|
|
315
326
|
if (!info.isBatchCall) {
|
|
316
327
|
const [call] = info.calls;
|
|
317
|
-
const [
|
|
328
|
+
const [result, error] = await rpcCalls[0]!;
|
|
318
329
|
|
|
319
330
|
switch (info.type) {
|
|
320
331
|
case 'unknown':
|
|
@@ -323,7 +334,7 @@ export async function resolveResponse<TRouter extends AnyRouter>(
|
|
|
323
334
|
// httpLink
|
|
324
335
|
headers.set('content-type', 'application/json');
|
|
325
336
|
|
|
326
|
-
if (isDataStream(data)) {
|
|
337
|
+
if (isDataStream(result?.data)) {
|
|
327
338
|
throw new TRPCError({
|
|
328
339
|
code: 'UNSUPPORTED_MEDIA_TYPE',
|
|
329
340
|
message:
|
|
@@ -341,7 +352,7 @@ export async function resolveResponse<TRouter extends AnyRouter>(
|
|
|
341
352
|
type: info.type,
|
|
342
353
|
}),
|
|
343
354
|
}
|
|
344
|
-
: { result: { data } };
|
|
355
|
+
: { result: { data: result.data } };
|
|
345
356
|
|
|
346
357
|
const headResponse = initResponse({
|
|
347
358
|
ctx,
|
|
@@ -372,6 +383,11 @@ export async function resolveResponse<TRouter extends AnyRouter>(
|
|
|
372
383
|
if (error) {
|
|
373
384
|
throw error;
|
|
374
385
|
}
|
|
386
|
+
const { data, abortCtrl } = result;
|
|
387
|
+
assert(
|
|
388
|
+
abortCtrl !== undefined,
|
|
389
|
+
'subscription type must have an AbortController',
|
|
390
|
+
);
|
|
375
391
|
|
|
376
392
|
if (!isObservable(data) && !isAsyncIterable(data)) {
|
|
377
393
|
throw new TRPCError({
|
|
@@ -388,6 +404,7 @@ export async function resolveResponse<TRouter extends AnyRouter>(
|
|
|
388
404
|
const stream = sseStreamProducer({
|
|
389
405
|
...config.experimental?.sseSubscriptions,
|
|
390
406
|
data: dataAsIterable,
|
|
407
|
+
abortCtrl,
|
|
391
408
|
serialize: (v) => config.transformer.output.serialize(v),
|
|
392
409
|
formatError(errorOpts) {
|
|
393
410
|
const error = getTRPCErrorFromUnknown(errorOpts.error);
|
|
@@ -481,17 +498,18 @@ export async function resolveResponse<TRouter extends AnyRouter>(
|
|
|
481
498
|
}),
|
|
482
499
|
};
|
|
483
500
|
}
|
|
501
|
+
const { data } = result;
|
|
484
502
|
|
|
485
503
|
/**
|
|
486
504
|
* Not very pretty, but we need to wrap nested data in promises
|
|
487
505
|
* Our stream producer will only resolve top-level async values or async values that are directly nested in another async value
|
|
488
506
|
*/
|
|
489
|
-
const
|
|
490
|
-
? observableToAsyncIterable(
|
|
491
|
-
: Promise.resolve(
|
|
507
|
+
const dataAsPromiseOrIterable = isObservable(data)
|
|
508
|
+
? observableToAsyncIterable(data)
|
|
509
|
+
: Promise.resolve(data);
|
|
492
510
|
return {
|
|
493
511
|
result: Promise.resolve({
|
|
494
|
-
data,
|
|
512
|
+
data: dataAsPromiseOrIterable,
|
|
495
513
|
}),
|
|
496
514
|
};
|
|
497
515
|
}),
|
|
@@ -539,12 +557,12 @@ export async function resolveResponse<TRouter extends AnyRouter>(
|
|
|
539
557
|
headers.set('content-type', 'application/json');
|
|
540
558
|
const results: RPCResult[] = (await Promise.all(rpcCalls)).map(
|
|
541
559
|
(res): RPCResult => {
|
|
542
|
-
const [
|
|
560
|
+
const [result, error] = res;
|
|
543
561
|
if (error) {
|
|
544
562
|
return res;
|
|
545
563
|
}
|
|
546
564
|
|
|
547
|
-
if (isDataStream(data)) {
|
|
565
|
+
if (isDataStream(result.data)) {
|
|
548
566
|
return [
|
|
549
567
|
null,
|
|
550
568
|
new TRPCError({
|
|
@@ -559,7 +577,7 @@ export async function resolveResponse<TRouter extends AnyRouter>(
|
|
|
559
577
|
);
|
|
560
578
|
const resultAsRPCResponse = results.map(
|
|
561
579
|
(
|
|
562
|
-
[
|
|
580
|
+
[result, error],
|
|
563
581
|
index,
|
|
564
582
|
): TRPCResponse<unknown, inferRouterError<TRouter>> => {
|
|
565
583
|
const call = info!.calls[index]!;
|
|
@@ -576,7 +594,7 @@ export async function resolveResponse<TRouter extends AnyRouter>(
|
|
|
576
594
|
};
|
|
577
595
|
}
|
|
578
596
|
return {
|
|
579
|
-
result: { data },
|
|
597
|
+
result: { data: result.data },
|
|
580
598
|
};
|
|
581
599
|
},
|
|
582
600
|
);
|
|
@@ -32,6 +32,7 @@ export interface PingOptions {
|
|
|
32
32
|
export interface SSEStreamProducerOptions<TValue = unknown> {
|
|
33
33
|
serialize?: Serialize;
|
|
34
34
|
data: AsyncIterable<TValue>;
|
|
35
|
+
abortCtrl: AbortController;
|
|
35
36
|
maxDepth?: number;
|
|
36
37
|
ping?: PingOptions;
|
|
37
38
|
/**
|
|
@@ -61,7 +62,9 @@ type SSEvent = Partial<{
|
|
|
61
62
|
*
|
|
62
63
|
* @see https://html.spec.whatwg.org/multipage/server-sent-events.html
|
|
63
64
|
*/
|
|
64
|
-
export function sseStreamProducer<TValue = unknown>(
|
|
65
|
+
export function sseStreamProducer<TValue = unknown>(
|
|
66
|
+
opts: SSEStreamProducerOptions<TValue>,
|
|
67
|
+
) {
|
|
65
68
|
const stream = createReadableStream<SSEvent>();
|
|
66
69
|
stream.controller.enqueue({ comment: 'connected' });
|
|
67
70
|
|
|
@@ -74,11 +77,15 @@ export function sseStreamProducer<TValue = unknown>(opts: SSEStreamProducerOptio
|
|
|
74
77
|
|
|
75
78
|
run(async () => {
|
|
76
79
|
let iterable: AsyncIterable<TValue | typeof PING_SYM> = opts.data;
|
|
77
|
-
|
|
80
|
+
|
|
78
81
|
iterable = withCancel(iterable, stream.cancelledPromise);
|
|
79
82
|
|
|
80
83
|
if (opts.emitAndEndImmediately) {
|
|
81
|
-
iterable = takeWithGrace(iterable, {
|
|
84
|
+
iterable = takeWithGrace(iterable, {
|
|
85
|
+
count: 1,
|
|
86
|
+
gracePeriodMs: 1,
|
|
87
|
+
onCancel: () => opts.abortCtrl.abort(),
|
|
88
|
+
});
|
|
82
89
|
}
|
|
83
90
|
|
|
84
91
|
let maxDurationTimer: PromiseTimer | null = null;
|
|
@@ -88,7 +95,10 @@ export function sseStreamProducer<TValue = unknown>(opts: SSEStreamProducerOptio
|
|
|
88
95
|
opts.maxDurationMs !== Infinity
|
|
89
96
|
) {
|
|
90
97
|
maxDurationTimer = createPromiseTimer(opts.maxDurationMs).start();
|
|
91
|
-
iterable = withCancel(
|
|
98
|
+
iterable = withCancel(
|
|
99
|
+
iterable,
|
|
100
|
+
maxDurationTimer.promise.then(() => opts.abortCtrl.abort()),
|
|
101
|
+
);
|
|
92
102
|
}
|
|
93
103
|
|
|
94
104
|
if (ping.enabled && ping.intervalMs !== Infinity && ping.intervalMs > 0) {
|