@trpc/server 11.0.0-rc.589 → 11.0.0-rc.591
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 +154 -147
- package/dist/unstable-core-do-not-import/http/getHTTPStatusCode.d.ts +6 -1
- package/dist/unstable-core-do-not-import/http/getHTTPStatusCode.d.ts.map +1 -1
- package/dist/unstable-core-do-not-import/http/getHTTPStatusCode.js +27 -0
- package/dist/unstable-core-do-not-import/http/getHTTPStatusCode.mjs +24 -1
- 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/rpc/codes.d.ts +2 -9
- package/dist/unstable-core-do-not-import/rpc/codes.d.ts.map +1 -1
- 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/types.d.ts +6 -0
- package/dist/unstable-core-do-not-import/types.d.ts.map +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 +7 -0
- package/dist/unstable-core-do-not-import.mjs +2 -2
- package/package.json +2 -2
- package/src/unstable-core-do-not-import/http/getHTTPStatusCode.ts +35 -3
- package/src/unstable-core-do-not-import/http/resolveResponse.ts +39 -21
- package/src/unstable-core-do-not-import/rpc/codes.ts +2 -10
- 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/types.ts +11 -0
- package/src/unstable-core-do-not-import/utils.ts +48 -0
|
@@ -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 };
|
|
@@ -41,8 +41,12 @@ exports.getBatchStreamFormatter = batchStreamFormatter.getBatchStreamFormatter;
|
|
|
41
41
|
exports.getRequestInfo = contentType.getRequestInfo;
|
|
42
42
|
exports.octetInputParser = contentTypeParsers.octetInputParser;
|
|
43
43
|
exports.formDataToObject = formDataToObject.formDataToObject;
|
|
44
|
+
exports.HTTP_CODE_TO_JSONRPC2 = getHTTPStatusCode.HTTP_CODE_TO_JSONRPC2;
|
|
45
|
+
exports.JSONRPC2_TO_HTTP_CODE = getHTTPStatusCode.JSONRPC2_TO_HTTP_CODE;
|
|
44
46
|
exports.getHTTPStatusCode = getHTTPStatusCode.getHTTPStatusCode;
|
|
45
47
|
exports.getHTTPStatusCodeFromError = getHTTPStatusCode.getHTTPStatusCodeFromError;
|
|
48
|
+
exports.getStatusCodeFromKey = getHTTPStatusCode.getStatusCodeFromKey;
|
|
49
|
+
exports.getStatusKeyFromCode = getHTTPStatusCode.getStatusKeyFromCode;
|
|
46
50
|
exports.parseConnectionParamsFromString = parseConnectionParams.parseConnectionParamsFromString;
|
|
47
51
|
exports.parseConnectionParamsFromUnknown = parseConnectionParams.parseConnectionParamsFromUnknown;
|
|
48
52
|
exports.resolveResponse = resolveResponse.resolveResponse;
|
|
@@ -78,6 +82,8 @@ exports.defaultTransformer = transformer.defaultTransformer;
|
|
|
78
82
|
exports.getDataTransformer = transformer.getDataTransformer;
|
|
79
83
|
exports.transformResult = transformer.transformResult;
|
|
80
84
|
exports.transformTRPCResponse = transformer.transformTRPCResponse;
|
|
85
|
+
exports.abortSignalsAnyPonyfill = utils.abortSignalsAnyPonyfill;
|
|
86
|
+
exports.assert = utils.assert;
|
|
81
87
|
exports.identity = utils.identity;
|
|
82
88
|
exports.isAsyncIterable = utils.isAsyncIterable;
|
|
83
89
|
exports.isFunction = utils.isFunction;
|
|
@@ -86,4 +92,5 @@ exports.mergeWithoutOverrides = utils.mergeWithoutOverrides;
|
|
|
86
92
|
exports.noop = utils.noop;
|
|
87
93
|
exports.omitPrototype = utils.omitPrototype;
|
|
88
94
|
exports.run = utils.run;
|
|
95
|
+
exports.sleep = utils.sleep;
|
|
89
96
|
exports.unsetMarker = utils.unsetMarker;
|
|
@@ -6,7 +6,7 @@ export { getBatchStreamFormatter } from './unstable-core-do-not-import/http/batc
|
|
|
6
6
|
export { getRequestInfo } from './unstable-core-do-not-import/http/contentType.mjs';
|
|
7
7
|
export { octetInputParser } from './unstable-core-do-not-import/http/contentTypeParsers.mjs';
|
|
8
8
|
export { formDataToObject } from './unstable-core-do-not-import/http/formDataToObject.mjs';
|
|
9
|
-
export { getHTTPStatusCode, getHTTPStatusCodeFromError } from './unstable-core-do-not-import/http/getHTTPStatusCode.mjs';
|
|
9
|
+
export { HTTP_CODE_TO_JSONRPC2, JSONRPC2_TO_HTTP_CODE, getHTTPStatusCode, getHTTPStatusCodeFromError, getStatusCodeFromKey, getStatusKeyFromCode } from './unstable-core-do-not-import/http/getHTTPStatusCode.mjs';
|
|
10
10
|
export { parseConnectionParamsFromString, parseConnectionParamsFromUnknown } from './unstable-core-do-not-import/http/parseConnectionParams.mjs';
|
|
11
11
|
export { resolveResponse } from './unstable-core-do-not-import/http/resolveResponse.mjs';
|
|
12
12
|
export { toURL } from './unstable-core-do-not-import/http/toURL.mjs';
|
|
@@ -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.591+956d65f99",
|
|
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": "956d65f99bd9dd4b52343a4f406111079af0ce7f"
|
|
153
153
|
}
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import type { TRPCError } from '../error/TRPCError';
|
|
2
2
|
import type { TRPC_ERROR_CODES_BY_KEY, TRPCResponse } from '../rpc';
|
|
3
3
|
import { TRPC_ERROR_CODES_BY_NUMBER } from '../rpc';
|
|
4
|
+
import type { InvertKeyValue, ValueOf } from '../types';
|
|
4
5
|
import { isObject } from '../utils';
|
|
5
6
|
|
|
6
|
-
const JSONRPC2_TO_HTTP_CODE: Record<
|
|
7
|
+
export const JSONRPC2_TO_HTTP_CODE: Record<
|
|
7
8
|
keyof typeof TRPC_ERROR_CODES_BY_KEY,
|
|
8
9
|
number
|
|
9
10
|
> = {
|
|
@@ -28,11 +29,42 @@ const JSONRPC2_TO_HTTP_CODE: Record<
|
|
|
28
29
|
GATEWAY_TIMEOUT: 504,
|
|
29
30
|
};
|
|
30
31
|
|
|
31
|
-
|
|
32
|
+
export const HTTP_CODE_TO_JSONRPC2: InvertKeyValue<
|
|
33
|
+
typeof JSONRPC2_TO_HTTP_CODE
|
|
34
|
+
> = {
|
|
35
|
+
400: 'BAD_REQUEST',
|
|
36
|
+
401: 'UNAUTHORIZED',
|
|
37
|
+
403: 'FORBIDDEN',
|
|
38
|
+
404: 'NOT_FOUND',
|
|
39
|
+
405: 'METHOD_NOT_SUPPORTED',
|
|
40
|
+
408: 'TIMEOUT',
|
|
41
|
+
409: 'CONFLICT',
|
|
42
|
+
412: 'PRECONDITION_FAILED',
|
|
43
|
+
413: 'PAYLOAD_TOO_LARGE',
|
|
44
|
+
415: 'UNSUPPORTED_MEDIA_TYPE',
|
|
45
|
+
422: 'UNPROCESSABLE_CONTENT',
|
|
46
|
+
429: 'TOO_MANY_REQUESTS',
|
|
47
|
+
499: 'CLIENT_CLOSED_REQUEST',
|
|
48
|
+
500: 'INTERNAL_SERVER_ERROR',
|
|
49
|
+
501: 'NOT_IMPLEMENTED',
|
|
50
|
+
502: 'BAD_GATEWAY',
|
|
51
|
+
503: 'SERVICE_UNAVAILABLE',
|
|
52
|
+
504: 'GATEWAY_TIMEOUT',
|
|
53
|
+
} as const;
|
|
54
|
+
|
|
55
|
+
export function getStatusCodeFromKey(
|
|
56
|
+
code: keyof typeof TRPC_ERROR_CODES_BY_KEY,
|
|
57
|
+
) {
|
|
32
58
|
return JSONRPC2_TO_HTTP_CODE[code] ?? 500;
|
|
33
59
|
}
|
|
34
60
|
|
|
35
|
-
export function
|
|
61
|
+
export function getStatusKeyFromCode(
|
|
62
|
+
code: keyof typeof HTTP_CODE_TO_JSONRPC2,
|
|
63
|
+
): ValueOf<typeof HTTP_CODE_TO_JSONRPC2> {
|
|
64
|
+
return HTTP_CODE_TO_JSONRPC2[code] ?? 'INTERNAL_SERVER_ERROR';
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export function getHTTPStatusCode(json: TRPCResponse | TRPCResponse[]) {
|
|
36
68
|
const arr = Array.isArray(json) ? json : [json];
|
|
37
69
|
const httpStatuses = new Set<number>(
|
|
38
70
|
arr.map((res) => {
|
|
@@ -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
|
);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ValueOf } from '../types';
|
|
1
|
+
import type { InvertKeyValue, ValueOf } from '../types';
|
|
2
2
|
|
|
3
3
|
// reference: https://www.jsonrpc.org/specification
|
|
4
4
|
|
|
@@ -41,16 +41,8 @@ export const TRPC_ERROR_CODES_BY_KEY = {
|
|
|
41
41
|
CLIENT_CLOSED_REQUEST: -32099, // 499
|
|
42
42
|
} as const;
|
|
43
43
|
|
|
44
|
-
type KeyFromValue<TValue, TType extends Record<PropertyKey, PropertyKey>> = {
|
|
45
|
-
[K in keyof TType]: TValue extends TType[K] ? K : never;
|
|
46
|
-
}[keyof TType];
|
|
47
|
-
|
|
48
|
-
type Invert<TType extends Record<PropertyKey, PropertyKey>> = {
|
|
49
|
-
[TValue in TType[keyof TType]]: KeyFromValue<TValue, TType>;
|
|
50
|
-
};
|
|
51
|
-
|
|
52
44
|
// pure
|
|
53
|
-
export const TRPC_ERROR_CODES_BY_NUMBER:
|
|
45
|
+
export const TRPC_ERROR_CODES_BY_NUMBER: InvertKeyValue<
|
|
54
46
|
typeof TRPC_ERROR_CODES_BY_KEY
|
|
55
47
|
> = {
|
|
56
48
|
[-32700]: 'PARSE_ERROR',
|
|
@@ -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) {
|
|
@@ -27,6 +27,7 @@ export async function* withCancel<T>(
|
|
|
27
27
|
interface TakeWithGraceOptions {
|
|
28
28
|
count: number;
|
|
29
29
|
gracePeriodMs: number;
|
|
30
|
+
onCancel?: () => void;
|
|
30
31
|
}
|
|
31
32
|
|
|
32
33
|
/**
|
|
@@ -36,7 +37,7 @@ interface TakeWithGraceOptions {
|
|
|
36
37
|
*/
|
|
37
38
|
export async function* takeWithGrace<T>(
|
|
38
39
|
iterable: AsyncIterable<T>,
|
|
39
|
-
{ count, gracePeriodMs }: TakeWithGraceOptions,
|
|
40
|
+
{ count, gracePeriodMs, onCancel }: TakeWithGraceOptions,
|
|
40
41
|
): AsyncGenerator<T> {
|
|
41
42
|
const iterator = iterable[Symbol.asyncIterator]();
|
|
42
43
|
const timer = createPromiseTimer(gracePeriodMs);
|
|
@@ -53,7 +54,7 @@ export async function* takeWithGrace<T>(
|
|
|
53
54
|
}
|
|
54
55
|
yield result.value;
|
|
55
56
|
if (--count === 0) {
|
|
56
|
-
timer.start();
|
|
57
|
+
timer.start().promise.then(onCancel, noop);
|
|
57
58
|
}
|
|
58
59
|
}
|
|
59
60
|
} finally {
|
|
@@ -17,7 +17,7 @@ export function createPromiseTimer(ms: number) {
|
|
|
17
17
|
|
|
18
18
|
function start(): PromiseTimer {
|
|
19
19
|
if (timeout != null) {
|
|
20
|
-
throw new Error(
|
|
20
|
+
throw new Error('PromiseTimer already started.');
|
|
21
21
|
}
|
|
22
22
|
timeout = setTimeout(deferred.resolve, ms);
|
|
23
23
|
return timer;
|
|
@@ -131,6 +131,17 @@ export type PickFirstDefined<TType, TPick> = undefined extends TType
|
|
|
131
131
|
: TPick
|
|
132
132
|
: TType;
|
|
133
133
|
|
|
134
|
+
export type KeyFromValue<
|
|
135
|
+
TValue,
|
|
136
|
+
TType extends Record<PropertyKey, PropertyKey>,
|
|
137
|
+
> = {
|
|
138
|
+
[K in keyof TType]: TValue extends TType[K] ? K : never;
|
|
139
|
+
}[keyof TType];
|
|
140
|
+
|
|
141
|
+
export type InvertKeyValue<TType extends Record<PropertyKey, PropertyKey>> = {
|
|
142
|
+
[TValue in TType[keyof TType]]: KeyFromValue<TValue, TType>;
|
|
143
|
+
};
|
|
144
|
+
|
|
134
145
|
/**
|
|
135
146
|
* ================================
|
|
136
147
|
* tRPC specific types
|
|
@@ -68,3 +68,51 @@ export function noop(): void {}
|
|
|
68
68
|
export function identity<T>(it: T): T {
|
|
69
69
|
return it;
|
|
70
70
|
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Generic runtime assertion function. Throws, if the condition is not `true`.
|
|
74
|
+
*
|
|
75
|
+
* Can be used as a slightly less dangerous variant of type assertions. Code
|
|
76
|
+
* mistakes would be revealed at runtime then (hopefully during testing).
|
|
77
|
+
*/
|
|
78
|
+
export function assert(
|
|
79
|
+
condition: boolean,
|
|
80
|
+
msg = 'no additional info',
|
|
81
|
+
): asserts condition {
|
|
82
|
+
if (!condition) {
|
|
83
|
+
throw new Error(`AssertionError: ${msg}`);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export function sleep(ms = 0): Promise<void> {
|
|
88
|
+
return new Promise<void>((res) => setTimeout(res, ms));
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Ponyfill for
|
|
93
|
+
* [`AbortSignal.any`](https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal/any_static).
|
|
94
|
+
*/
|
|
95
|
+
export function abortSignalsAnyPonyfill(signals: AbortSignal[]): AbortSignal {
|
|
96
|
+
if (typeof AbortSignal.any === 'function') {
|
|
97
|
+
return AbortSignal.any(signals);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const ac = new AbortController();
|
|
101
|
+
|
|
102
|
+
for (const signal of signals) {
|
|
103
|
+
if (signal.aborted) {
|
|
104
|
+
trigger();
|
|
105
|
+
} else if (!ac.signal.aborted) {
|
|
106
|
+
signal.addEventListener('abort', trigger, { once: true });
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
return ac.signal;
|
|
111
|
+
|
|
112
|
+
function trigger() {
|
|
113
|
+
ac.abort();
|
|
114
|
+
for (const signal of signals) {
|
|
115
|
+
signal.removeEventListener('abort', trigger);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|