@trpc/server 11.0.0-rc.637 → 11.0.0-rc.642
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/adapters/aws-lambda/index.js +1 -1
- package/dist/adapters/aws-lambda/index.mjs +1 -1
- package/dist/adapters/express.js +1 -1
- package/dist/adapters/express.mjs +1 -1
- package/dist/adapters/fastify/fastifyRequestHandler.js +1 -1
- package/dist/adapters/fastify/fastifyRequestHandler.mjs +1 -1
- package/dist/adapters/fetch/fetchRequestHandler.js +1 -1
- package/dist/adapters/fetch/fetchRequestHandler.mjs +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/notFound.js +1 -1
- package/dist/adapters/next-app-dir/notFound.mjs +1 -1
- package/dist/adapters/next-app-dir/redirect.js +1 -1
- package/dist/adapters/next-app-dir/redirect.mjs +1 -1
- package/dist/adapters/next.js +1 -1
- package/dist/adapters/next.mjs +1 -1
- package/dist/adapters/node-http/incomingMessageToRequest.js +1 -1
- package/dist/adapters/node-http/incomingMessageToRequest.mjs +1 -1
- package/dist/adapters/node-http/nodeHTTPRequestHandler.js +1 -1
- package/dist/adapters/node-http/nodeHTTPRequestHandler.mjs +1 -1
- package/dist/adapters/node-http/writeResponse.js +1 -1
- package/dist/adapters/node-http/writeResponse.mjs +1 -1
- package/dist/adapters/standalone.js +1 -1
- package/dist/adapters/standalone.mjs +1 -1
- package/dist/adapters/ws.js +1 -1
- package/dist/adapters/ws.mjs +1 -1
- package/dist/bundle-analysis.json +107 -107
- package/dist/http.js +1 -1
- package/dist/http.mjs +1 -1
- package/dist/index.js +1 -1
- package/dist/index.mjs +1 -1
- package/dist/rpc.js +1 -1
- package/dist/rpc.mjs +1 -1
- package/dist/shared.js +1 -1
- package/dist/shared.mjs +1 -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 +0 -1
- package/dist/unstable-core-do-not-import/http/resolveResponse.mjs +0 -1
- package/dist/unstable-core-do-not-import/rootConfig.d.ts +1 -1
- package/dist/unstable-core-do-not-import/rootConfig.d.ts.map +1 -1
- package/dist/unstable-core-do-not-import/stream/jsonl.d.ts +1 -1
- package/dist/unstable-core-do-not-import/stream/jsonl.d.ts.map +1 -1
- package/dist/unstable-core-do-not-import/stream/jsonl.js +73 -47
- package/dist/unstable-core-do-not-import/stream/jsonl.mjs +73 -47
- package/dist/unstable-core-do-not-import/stream/sse.d.ts +20 -12
- package/dist/unstable-core-do-not-import/stream/sse.d.ts.map +1 -1
- package/dist/unstable-core-do-not-import/stream/sse.js +22 -11
- package/dist/unstable-core-do-not-import/stream/sse.mjs +22 -11
- package/dist/unstable-core-do-not-import/stream/utils/asyncIterable.d.ts +0 -2
- 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 +5 -8
- package/dist/unstable-core-do-not-import/stream/utils/asyncIterable.mjs +3 -6
- package/dist/unstable-core-do-not-import/stream/utils/{disposablePromiseTimer.d.ts → timerResource.d.ts} +2 -2
- package/dist/unstable-core-do-not-import/stream/utils/timerResource.d.ts.map +1 -0
- package/dist/unstable-core-do-not-import/stream/utils/{disposablePromiseTimer.js → timerResource.js} +2 -2
- package/dist/unstable-core-do-not-import/stream/utils/{disposablePromiseTimer.mjs → timerResource.mjs} +2 -2
- package/dist/unstable-core-do-not-import/stream/utils/withPing.d.ts.map +1 -1
- package/dist/unstable-core-do-not-import/stream/utils/withPing.js +3 -3
- package/dist/unstable-core-do-not-import/stream/utils/withPing.mjs +2 -2
- package/package.json +7 -3
- package/src/unstable-core-do-not-import/http/resolveResponse.ts +0 -1
- package/src/unstable-core-do-not-import/rootConfig.ts +1 -1
- package/src/unstable-core-do-not-import/stream/jsonl.ts +81 -51
- package/src/unstable-core-do-not-import/stream/sse.ts +56 -24
- package/src/unstable-core-do-not-import/stream/utils/asyncIterable.ts +5 -11
- package/src/unstable-core-do-not-import/stream/utils/{disposablePromiseTimer.ts → timerResource.ts} +1 -1
- package/src/unstable-core-do-not-import/stream/utils/withPing.ts +2 -5
- package/dist/unstable-core-do-not-import/stream/utils/createServer.d.ts +0 -7
- package/dist/unstable-core-do-not-import/stream/utils/createServer.d.ts.map +0 -1
- package/dist/unstable-core-do-not-import/stream/utils/disposablePromiseTimer.d.ts.map +0 -1
- package/src/unstable-core-do-not-import/stream/utils/createServer.ts +0 -44
|
@@ -3,7 +3,6 @@
|
|
|
3
3
|
*/
|
|
4
4
|
export declare function withMaxDuration<T>(iterable: AsyncIterable<T>, opts: {
|
|
5
5
|
maxDurationMs: number;
|
|
6
|
-
abortCtrl: AbortController;
|
|
7
6
|
}): AsyncGenerator<T>;
|
|
8
7
|
/**
|
|
9
8
|
* Derives a new {@link AsyncGenerator} based of {@link iterable}, that yields its first
|
|
@@ -13,6 +12,5 @@ export declare function withMaxDuration<T>(iterable: AsyncIterable<T>, opts: {
|
|
|
13
12
|
export declare function takeWithGrace<T>(iterable: AsyncIterable<T>, opts: {
|
|
14
13
|
count: number;
|
|
15
14
|
gracePeriodMs: number;
|
|
16
|
-
abortCtrl: AbortController;
|
|
17
15
|
}): AsyncGenerator<T>;
|
|
18
16
|
//# 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":"
|
|
1
|
+
{"version":3,"file":"asyncIterable.d.ts","sourceRoot":"","sources":["../../../../src/unstable-core-do-not-import/stream/utils/asyncIterable.ts"],"names":[],"mappings":"AAGA;;GAEG;AACH,wBAAuB,eAAe,CAAC,CAAC,EACtC,QAAQ,EAAE,aAAa,CAAC,CAAC,CAAC,EAC1B,IAAI,EAAE;IAAE,aAAa,EAAE,MAAM,CAAA;CAAE,GAC9B,cAAc,CAAC,CAAC,CAAC,CA8BnB;AAED;;;;GAIG;AACH,wBAAuB,aAAa,CAAC,CAAC,EACpC,QAAQ,EAAE,aAAa,CAAC,CAAC,CAAC,EAC1B,IAAI,EAAE;IACJ,KAAK,EAAE,MAAM,CAAC;IACd,aAAa,EAAE,MAAM,CAAC;CACvB,GACA,cAAc,CAAC,CAAC,CAAC,CAkCnB"}
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
var unpromise = require('../../../vendor/unpromise/unpromise.js');
|
|
4
|
-
var
|
|
4
|
+
var timerResource = require('./timerResource.js');
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* Derives a new {@link AsyncGenerator} based on {@link iterable}, that automatically stops after the specified duration.
|
|
8
8
|
*/ async function* withMaxDuration(iterable, opts) {
|
|
9
9
|
const iterator = iterable[Symbol.asyncIterator]();
|
|
10
|
-
const timer =
|
|
10
|
+
const timer = timerResource.timerResource(opts.maxDurationMs);
|
|
11
11
|
try {
|
|
12
12
|
const timerPromise = timer.start();
|
|
13
13
|
// declaration outside the loop for garbage collection reasons
|
|
@@ -17,9 +17,8 @@ var disposablePromiseTimer = require('./disposablePromiseTimer.js');
|
|
|
17
17
|
iterator.next(),
|
|
18
18
|
timerPromise
|
|
19
19
|
]);
|
|
20
|
-
if (result ===
|
|
20
|
+
if (result === timerResource.disposablePromiseTimerResult) {
|
|
21
21
|
// cancelled due to timeout
|
|
22
|
-
opts.abortCtrl.abort();
|
|
23
22
|
const res = await iterator.return?.();
|
|
24
23
|
return res?.value;
|
|
25
24
|
}
|
|
@@ -44,7 +43,7 @@ var disposablePromiseTimer = require('./disposablePromiseTimer.js');
|
|
|
44
43
|
const iterator = iterable[Symbol.asyncIterator]();
|
|
45
44
|
// declaration outside the loop for garbage collection reasons
|
|
46
45
|
let result;
|
|
47
|
-
const timer =
|
|
46
|
+
const timer = timerResource.timerResource(opts.gracePeriodMs);
|
|
48
47
|
try {
|
|
49
48
|
let count = opts.count;
|
|
50
49
|
let timerPromise = new Promise(()=>{
|
|
@@ -55,7 +54,7 @@ var disposablePromiseTimer = require('./disposablePromiseTimer.js');
|
|
|
55
54
|
iterator.next(),
|
|
56
55
|
timerPromise
|
|
57
56
|
]);
|
|
58
|
-
if (result ===
|
|
57
|
+
if (result === timerResource.disposablePromiseTimerResult) {
|
|
59
58
|
// cancelled
|
|
60
59
|
const res = await iterator.return?.();
|
|
61
60
|
return res?.value;
|
|
@@ -66,8 +65,6 @@ var disposablePromiseTimer = require('./disposablePromiseTimer.js');
|
|
|
66
65
|
yield result.value;
|
|
67
66
|
if (--count === 0) {
|
|
68
67
|
timerPromise = timer.start();
|
|
69
|
-
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
70
|
-
timerPromise.then(()=>opts.abortCtrl.abort());
|
|
71
68
|
}
|
|
72
69
|
// free up reference for garbage collection
|
|
73
70
|
result = null;
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { Unpromise } from '../../../vendor/unpromise/unpromise.mjs';
|
|
2
|
-
import { disposablePromiseTimerResult,
|
|
2
|
+
import { disposablePromiseTimerResult, timerResource } from './timerResource.mjs';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Derives a new {@link AsyncGenerator} based on {@link iterable}, that automatically stops after the specified duration.
|
|
6
6
|
*/ async function* withMaxDuration(iterable, opts) {
|
|
7
7
|
const iterator = iterable[Symbol.asyncIterator]();
|
|
8
|
-
const timer =
|
|
8
|
+
const timer = timerResource(opts.maxDurationMs);
|
|
9
9
|
try {
|
|
10
10
|
const timerPromise = timer.start();
|
|
11
11
|
// declaration outside the loop for garbage collection reasons
|
|
@@ -17,7 +17,6 @@ import { disposablePromiseTimerResult, disposablePromiseTimer } from './disposab
|
|
|
17
17
|
]);
|
|
18
18
|
if (result === disposablePromiseTimerResult) {
|
|
19
19
|
// cancelled due to timeout
|
|
20
|
-
opts.abortCtrl.abort();
|
|
21
20
|
const res = await iterator.return?.();
|
|
22
21
|
return res?.value;
|
|
23
22
|
}
|
|
@@ -42,7 +41,7 @@ import { disposablePromiseTimerResult, disposablePromiseTimer } from './disposab
|
|
|
42
41
|
const iterator = iterable[Symbol.asyncIterator]();
|
|
43
42
|
// declaration outside the loop for garbage collection reasons
|
|
44
43
|
let result;
|
|
45
|
-
const timer =
|
|
44
|
+
const timer = timerResource(opts.gracePeriodMs);
|
|
46
45
|
try {
|
|
47
46
|
let count = opts.count;
|
|
48
47
|
let timerPromise = new Promise(()=>{
|
|
@@ -64,8 +63,6 @@ import { disposablePromiseTimerResult, disposablePromiseTimer } from './disposab
|
|
|
64
63
|
yield result.value;
|
|
65
64
|
if (--count === 0) {
|
|
66
65
|
timerPromise = timer.start();
|
|
67
|
-
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
68
|
-
timerPromise.then(()=>opts.abortCtrl.abort());
|
|
69
66
|
}
|
|
70
67
|
// free up reference for garbage collection
|
|
71
68
|
result = null;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export declare const disposablePromiseTimerResult: unique symbol;
|
|
2
|
-
export declare function
|
|
2
|
+
export declare function timerResource(ms: number): {
|
|
3
3
|
start(): Promise<typeof disposablePromiseTimerResult>;
|
|
4
4
|
[Symbol.dispose]: () => void;
|
|
5
5
|
};
|
|
6
|
-
//# sourceMappingURL=
|
|
6
|
+
//# sourceMappingURL=timerResource.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"timerResource.d.ts","sourceRoot":"","sources":["../../../../src/unstable-core-do-not-import/stream/utils/timerResource.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,4BAA4B,eAAW,CAAC;AACrD,wBAAgB,aAAa,CAAC,EAAE,EAAE,MAAM;;;EAsBvC"}
|
package/dist/unstable-core-do-not-import/stream/utils/{disposablePromiseTimer.js → timerResource.js}
RENAMED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
var _Symbol;
|
|
5
5
|
(_Symbol = Symbol).dispose ?? (_Symbol.dispose = Symbol());
|
|
6
6
|
const disposablePromiseTimerResult = Symbol();
|
|
7
|
-
function
|
|
7
|
+
function timerResource(ms) {
|
|
8
8
|
let timer = null;
|
|
9
9
|
return {
|
|
10
10
|
start () {
|
|
@@ -24,5 +24,5 @@ function disposablePromiseTimer(ms) {
|
|
|
24
24
|
};
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
-
exports.disposablePromiseTimer = disposablePromiseTimer;
|
|
28
27
|
exports.disposablePromiseTimerResult = disposablePromiseTimerResult;
|
|
28
|
+
exports.timerResource = timerResource;
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
var _Symbol;
|
|
3
3
|
(_Symbol = Symbol).dispose ?? (_Symbol.dispose = Symbol());
|
|
4
4
|
const disposablePromiseTimerResult = Symbol();
|
|
5
|
-
function
|
|
5
|
+
function timerResource(ms) {
|
|
6
6
|
let timer = null;
|
|
7
7
|
return {
|
|
8
8
|
start () {
|
|
@@ -22,4 +22,4 @@ function disposablePromiseTimer(ms) {
|
|
|
22
22
|
};
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
-
export {
|
|
25
|
+
export { disposablePromiseTimerResult, timerResource };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"withPing.d.ts","sourceRoot":"","sources":["../../../../src/unstable-core-do-not-import/stream/utils/withPing.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"withPing.d.ts","sourceRoot":"","sources":["../../../../src/unstable-core-do-not-import/stream/utils/withPing.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,QAAQ,eAAiB,CAAC;AAEvC;;;GAGG;AACH,wBAAuB,QAAQ,CAAC,MAAM,EACpC,QAAQ,EAAE,aAAa,CAAC,MAAM,CAAC,EAC/B,cAAc,EAAE,MAAM,GACrB,cAAc,CAAC,MAAM,GAAG,OAAO,QAAQ,CAAC,CAmC1C"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
var unpromise = require('../../../vendor/unpromise/unpromise.js');
|
|
4
|
-
var
|
|
4
|
+
var timerResource = require('./timerResource.js');
|
|
5
5
|
|
|
6
6
|
const PING_SYM = Symbol('ping');
|
|
7
7
|
/**
|
|
@@ -13,13 +13,13 @@ const PING_SYM = Symbol('ping');
|
|
|
13
13
|
let result;
|
|
14
14
|
let nextPromise = iterator.next();
|
|
15
15
|
while(true){
|
|
16
|
-
const pingPromise =
|
|
16
|
+
const pingPromise = timerResource.timerResource(pingIntervalMs);
|
|
17
17
|
try {
|
|
18
18
|
result = await unpromise.Unpromise.race([
|
|
19
19
|
nextPromise,
|
|
20
20
|
pingPromise.start()
|
|
21
21
|
]);
|
|
22
|
-
if (result ===
|
|
22
|
+
if (result === timerResource.disposablePromiseTimerResult) {
|
|
23
23
|
// cancelled
|
|
24
24
|
yield PING_SYM;
|
|
25
25
|
continue;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Unpromise } from '../../../vendor/unpromise/unpromise.mjs';
|
|
2
|
-
import { disposablePromiseTimerResult,
|
|
2
|
+
import { disposablePromiseTimerResult, timerResource } from './timerResource.mjs';
|
|
3
3
|
|
|
4
4
|
const PING_SYM = Symbol('ping');
|
|
5
5
|
/**
|
|
@@ -11,7 +11,7 @@ const PING_SYM = Symbol('ping');
|
|
|
11
11
|
let result;
|
|
12
12
|
let nextPromise = iterator.next();
|
|
13
13
|
while(true){
|
|
14
|
-
const pingPromise =
|
|
14
|
+
const pingPromise = timerResource(pingIntervalMs);
|
|
15
15
|
try {
|
|
16
16
|
result = await Unpromise.race([
|
|
17
17
|
nextPromise,
|
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.642+5c904587e",
|
|
4
4
|
"description": "The tRPC server library",
|
|
5
5
|
"author": "KATT",
|
|
6
6
|
"license": "MIT",
|
|
@@ -110,7 +110,8 @@
|
|
|
110
110
|
"rpc",
|
|
111
111
|
"shared",
|
|
112
112
|
"unstable-core-do-not-import",
|
|
113
|
-
"!**/*.test.*"
|
|
113
|
+
"!**/*.test.*",
|
|
114
|
+
"!**/__tests__"
|
|
114
115
|
],
|
|
115
116
|
"publishConfig": {
|
|
116
117
|
"access": "public"
|
|
@@ -156,5 +157,8 @@
|
|
|
156
157
|
"next": "*",
|
|
157
158
|
"ws": "*"
|
|
158
159
|
},
|
|
159
|
-
"
|
|
160
|
+
"peerDependencies": {
|
|
161
|
+
"typescript": ">=5.6.2"
|
|
162
|
+
},
|
|
163
|
+
"gitHead": "5c904587e6f3e3eaadb1bbd80d6aa38d20c00800"
|
|
160
164
|
}
|
|
@@ -466,7 +466,6 @@ export async function resolveResponse<TRouter extends AnyRouter>(
|
|
|
466
466
|
const stream = sseStreamProducer({
|
|
467
467
|
...config.sse,
|
|
468
468
|
data: iterable,
|
|
469
|
-
abortCtrl: result?.abortCtrl ?? new AbortController(),
|
|
470
469
|
serialize: (v) => config.transformer.output.serialize(v),
|
|
471
470
|
formatError(errorOpts) {
|
|
472
471
|
const error = getTRPCErrorFromUnknown(errorOpts.error);
|
|
@@ -82,7 +82,7 @@ export interface RootConfig<TTypes extends RootTypes> {
|
|
|
82
82
|
enabled?: boolean;
|
|
83
83
|
} & Pick<
|
|
84
84
|
SSEStreamProducerOptions,
|
|
85
|
-
'ping' | 'emitAndEndImmediately' | 'maxDurationMs'
|
|
85
|
+
'ping' | 'emitAndEndImmediately' | 'maxDurationMs' | 'client'
|
|
86
86
|
>;
|
|
87
87
|
experimental?: {};
|
|
88
88
|
}
|
|
@@ -425,13 +425,20 @@ export async function jsonlStreamConsumer<THead>(opts: {
|
|
|
425
425
|
|
|
426
426
|
type ControllerChunk = ChunkData | StreamInterruptedError;
|
|
427
427
|
type ChunkController = ReadableStreamDefaultController<ControllerChunk>;
|
|
428
|
+
/**
|
|
429
|
+
* This is needed as new values can come in before the controller has read the chunk
|
|
430
|
+
* Not pretty, could likely be refactored and omitted somehow
|
|
431
|
+
*/
|
|
432
|
+
const chunkDeferred = new Map<ChunkIndex, Deferred<ChunkController>>();
|
|
428
433
|
|
|
429
|
-
const controllers =
|
|
430
|
-
|
|
431
|
-
|
|
434
|
+
const controllers = new Map<ChunkIndex, ChunkController>();
|
|
435
|
+
|
|
436
|
+
const maybeAbort = () => {
|
|
437
|
+
if (chunkDeferred.size === 0 && controllers.size === 0) {
|
|
438
|
+
// nothing is listening to the stream anymore
|
|
432
439
|
opts.abortController?.abort();
|
|
433
|
-
}
|
|
434
|
-
|
|
440
|
+
}
|
|
441
|
+
};
|
|
435
442
|
|
|
436
443
|
function decodeChunkDefinition(value: ChunkDefinition) {
|
|
437
444
|
const [_path, type, chunkId] = value;
|
|
@@ -440,6 +447,13 @@ export async function jsonlStreamConsumer<THead>(opts: {
|
|
|
440
447
|
|
|
441
448
|
controllers.set(chunkId, stream.controller);
|
|
442
449
|
|
|
450
|
+
// resolve chunk deferred if it exists
|
|
451
|
+
const deferred = chunkDeferred.get(chunkId);
|
|
452
|
+
if (deferred) {
|
|
453
|
+
deferred.resolve(stream.controller);
|
|
454
|
+
chunkDeferred.delete(chunkId);
|
|
455
|
+
}
|
|
456
|
+
|
|
443
457
|
switch (type) {
|
|
444
458
|
case CHUNK_VALUE_TYPE_PROMISE: {
|
|
445
459
|
return new Promise((resolve, reject) => {
|
|
@@ -473,60 +487,66 @@ export async function jsonlStreamConsumer<THead>(opts: {
|
|
|
473
487
|
.catch(reject)
|
|
474
488
|
.finally(() => {
|
|
475
489
|
controllers.delete(chunkId);
|
|
490
|
+
maybeAbort();
|
|
476
491
|
});
|
|
477
492
|
});
|
|
478
493
|
}
|
|
479
494
|
case CHUNK_VALUE_TYPE_ASYNC_ITERABLE: {
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
const
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
controllers.delete(chunkId);
|
|
507
|
-
return {
|
|
508
|
-
done: true,
|
|
509
|
-
value: decode(data),
|
|
510
|
-
};
|
|
511
|
-
case ASYNC_ITERABLE_STATUS_ERROR:
|
|
512
|
-
controllers.delete(chunkId);
|
|
513
|
-
throw (
|
|
514
|
-
opts.formatError?.({ error: data }) ??
|
|
515
|
-
new AsyncError(data)
|
|
516
|
-
);
|
|
517
|
-
}
|
|
518
|
-
},
|
|
519
|
-
return: async () => {
|
|
495
|
+
const reader = stream.readable.getReader();
|
|
496
|
+
const iterator: AsyncIterator<unknown> = {
|
|
497
|
+
next: async () => {
|
|
498
|
+
const { done, value } = await reader.read();
|
|
499
|
+
if (value instanceof StreamInterruptedError) {
|
|
500
|
+
throw value;
|
|
501
|
+
}
|
|
502
|
+
if (done) {
|
|
503
|
+
controllers.delete(chunkId);
|
|
504
|
+
maybeAbort();
|
|
505
|
+
|
|
506
|
+
return {
|
|
507
|
+
done: true,
|
|
508
|
+
value: undefined,
|
|
509
|
+
};
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
const [_chunkId, status, data] = value as IterableChunk;
|
|
513
|
+
|
|
514
|
+
switch (status) {
|
|
515
|
+
case ASYNC_ITERABLE_STATUS_VALUE:
|
|
516
|
+
return {
|
|
517
|
+
done: false,
|
|
518
|
+
value: decode(data),
|
|
519
|
+
};
|
|
520
|
+
case ASYNC_ITERABLE_STATUS_RETURN:
|
|
520
521
|
controllers.delete(chunkId);
|
|
522
|
+
maybeAbort();
|
|
523
|
+
|
|
521
524
|
return {
|
|
522
525
|
done: true,
|
|
523
|
-
value:
|
|
526
|
+
value: decode(data),
|
|
524
527
|
};
|
|
525
|
-
|
|
528
|
+
case ASYNC_ITERABLE_STATUS_ERROR:
|
|
529
|
+
controllers.delete(chunkId);
|
|
530
|
+
maybeAbort();
|
|
531
|
+
|
|
532
|
+
throw (
|
|
533
|
+
opts.formatError?.({ error: data }) ?? new AsyncError(data)
|
|
534
|
+
);
|
|
535
|
+
}
|
|
536
|
+
},
|
|
537
|
+
return: async () => {
|
|
538
|
+
controllers.delete(chunkId);
|
|
539
|
+
maybeAbort();
|
|
540
|
+
|
|
541
|
+
return {
|
|
542
|
+
done: true,
|
|
543
|
+
value: undefined,
|
|
526
544
|
};
|
|
527
|
-
return iterator;
|
|
528
545
|
},
|
|
529
546
|
};
|
|
547
|
+
return {
|
|
548
|
+
[Symbol.asyncIterator]: () => iterator,
|
|
549
|
+
};
|
|
530
550
|
}
|
|
531
551
|
}
|
|
532
552
|
}
|
|
@@ -571,19 +591,29 @@ export async function jsonlStreamConsumer<THead>(opts: {
|
|
|
571
591
|
headDeferred.resolve(head as THead);
|
|
572
592
|
headDeferred = null;
|
|
573
593
|
|
|
574
|
-
controllers.activate();
|
|
575
594
|
return;
|
|
576
595
|
}
|
|
577
596
|
const chunk = chunkOrHead as ChunkData;
|
|
578
597
|
const [idx] = chunk;
|
|
579
598
|
|
|
580
599
|
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
581
|
-
|
|
582
|
-
|
|
600
|
+
let readController = controllers.get(idx)!;
|
|
601
|
+
if (!readController) {
|
|
602
|
+
let deferred = chunkDeferred.get(idx);
|
|
603
|
+
if (!deferred) {
|
|
604
|
+
deferred = createDeferred();
|
|
605
|
+
chunkDeferred.set(idx, deferred);
|
|
606
|
+
}
|
|
607
|
+
readController = await deferred.promise;
|
|
608
|
+
}
|
|
609
|
+
readController.enqueue(chunk);
|
|
583
610
|
},
|
|
584
611
|
close: closeOrAbort,
|
|
585
612
|
abort: closeOrAbort,
|
|
586
613
|
}),
|
|
614
|
+
{
|
|
615
|
+
signal: opts.abortController.signal,
|
|
616
|
+
},
|
|
587
617
|
)
|
|
588
618
|
.catch((error) => {
|
|
589
619
|
opts.onError?.({ error });
|
|
@@ -16,7 +16,7 @@ type Deserialize = (value: any) => any;
|
|
|
16
16
|
/**
|
|
17
17
|
* @internal
|
|
18
18
|
*/
|
|
19
|
-
export interface
|
|
19
|
+
export interface SSEPingOptions {
|
|
20
20
|
/**
|
|
21
21
|
* Enable ping comments sent from the server
|
|
22
22
|
* @default false
|
|
@@ -29,15 +29,22 @@ export interface PingOptions {
|
|
|
29
29
|
intervalMs?: number;
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
+
export interface SSEClientOptions {
|
|
33
|
+
/**
|
|
34
|
+
* Timeout and reconnect after inactivity in milliseconds
|
|
35
|
+
* @default undefined
|
|
36
|
+
*/
|
|
37
|
+
reconnectAfterInactivityMs?: number;
|
|
38
|
+
}
|
|
39
|
+
|
|
32
40
|
export interface SSEStreamProducerOptions<TValue = unknown> {
|
|
33
41
|
serialize?: Serialize;
|
|
34
42
|
data: AsyncIterable<TValue>;
|
|
35
|
-
|
|
43
|
+
|
|
36
44
|
maxDepth?: number;
|
|
37
|
-
ping?:
|
|
45
|
+
ping?: SSEPingOptions;
|
|
38
46
|
/**
|
|
39
47
|
* Maximum duration in milliseconds for the request before ending the stream
|
|
40
|
-
* Only useful for serverless runtimes
|
|
41
48
|
* @default undefined
|
|
42
49
|
*/
|
|
43
50
|
maxDurationMs?: number;
|
|
@@ -48,10 +55,16 @@ export interface SSEStreamProducerOptions<TValue = unknown> {
|
|
|
48
55
|
*/
|
|
49
56
|
emitAndEndImmediately?: boolean;
|
|
50
57
|
formatError?: (opts: { error: unknown }) => unknown;
|
|
58
|
+
/**
|
|
59
|
+
* Client-specific options - these will be sent to the client as part of the first message
|
|
60
|
+
* @default {}
|
|
61
|
+
*/
|
|
62
|
+
client?: SSEClientOptions;
|
|
51
63
|
}
|
|
52
64
|
|
|
53
65
|
const PING_EVENT = 'ping';
|
|
54
66
|
const SERIALIZED_ERROR_EVENT = 'serialized-error';
|
|
67
|
+
const CONNECTED_EVENT = 'connected';
|
|
55
68
|
|
|
56
69
|
type SSEvent = Partial<{
|
|
57
70
|
id: string;
|
|
@@ -67,14 +80,28 @@ export function sseStreamProducer<TValue = unknown>(
|
|
|
67
80
|
opts: SSEStreamProducerOptions<TValue>,
|
|
68
81
|
) {
|
|
69
82
|
const stream = createReadableStream<SSEvent>();
|
|
70
|
-
stream.controller.enqueue({ comment: 'connected' });
|
|
71
83
|
|
|
72
84
|
const { serialize = identity } = opts;
|
|
73
85
|
|
|
74
|
-
const ping: Required<
|
|
86
|
+
const ping: Required<SSEPingOptions> = {
|
|
75
87
|
enabled: opts.ping?.enabled ?? false,
|
|
76
88
|
intervalMs: opts.ping?.intervalMs ?? 1000,
|
|
77
89
|
};
|
|
90
|
+
const client: SSEClientOptions = opts.client ?? {};
|
|
91
|
+
|
|
92
|
+
stream.controller.enqueue({
|
|
93
|
+
event: CONNECTED_EVENT,
|
|
94
|
+
data: JSON.stringify(client),
|
|
95
|
+
});
|
|
96
|
+
if (
|
|
97
|
+
ping.enabled &&
|
|
98
|
+
client.reconnectAfterInactivityMs &&
|
|
99
|
+
ping.intervalMs > client.reconnectAfterInactivityMs
|
|
100
|
+
) {
|
|
101
|
+
throw new Error(
|
|
102
|
+
`Ping interval must be less than client reconnect interval to prevent unnecessary reconnection - ping.intervalMs: ${ping.intervalMs} client.reconnectAfterInactivityMs: ${client.reconnectAfterInactivityMs}`,
|
|
103
|
+
);
|
|
104
|
+
}
|
|
78
105
|
|
|
79
106
|
run(async () => {
|
|
80
107
|
type TIteratorValue = Awaited<TValue> | typeof PING_SYM;
|
|
@@ -85,7 +112,6 @@ export function sseStreamProducer<TValue = unknown>(
|
|
|
85
112
|
iterable = takeWithGrace(iterable, {
|
|
86
113
|
count: 1,
|
|
87
114
|
gracePeriodMs: 1,
|
|
88
|
-
abortCtrl: opts.abortCtrl,
|
|
89
115
|
});
|
|
90
116
|
}
|
|
91
117
|
|
|
@@ -96,7 +122,6 @@ export function sseStreamProducer<TValue = unknown>(
|
|
|
96
122
|
) {
|
|
97
123
|
iterable = withMaxDuration(iterable, {
|
|
98
124
|
maxDurationMs: opts.maxDurationMs,
|
|
99
|
-
abortCtrl: opts.abortCtrl,
|
|
100
125
|
});
|
|
101
126
|
}
|
|
102
127
|
|
|
@@ -191,11 +216,6 @@ interface ConsumerStreamResultError<TConfig extends ConsumerConfig>
|
|
|
191
216
|
error: TConfig['error'];
|
|
192
217
|
}
|
|
193
218
|
|
|
194
|
-
interface ConsumerStreamResultOpened<TConfig extends ConsumerConfig>
|
|
195
|
-
extends ConsumerStreamResultBase<TConfig> {
|
|
196
|
-
type: 'opened';
|
|
197
|
-
}
|
|
198
|
-
|
|
199
219
|
interface ConsumerStreamResultConnecting<TConfig extends ConsumerConfig>
|
|
200
220
|
extends ConsumerStreamResultBase<TConfig> {
|
|
201
221
|
type: 'connecting';
|
|
@@ -204,20 +224,26 @@ interface ConsumerStreamResultConnecting<TConfig extends ConsumerConfig>
|
|
|
204
224
|
interface ConsumerStreamResultTimeout<TConfig extends ConsumerConfig>
|
|
205
225
|
extends ConsumerStreamResultBase<TConfig> {
|
|
206
226
|
type: 'timeout';
|
|
227
|
+
ms: number;
|
|
207
228
|
}
|
|
208
|
-
|
|
209
229
|
interface ConsumerStreamResultPing<TConfig extends ConsumerConfig>
|
|
210
230
|
extends ConsumerStreamResultBase<TConfig> {
|
|
211
231
|
type: 'ping';
|
|
212
232
|
}
|
|
213
233
|
|
|
234
|
+
interface ConsumerStreamResultConnected<TConfig extends ConsumerConfig>
|
|
235
|
+
extends ConsumerStreamResultBase<TConfig> {
|
|
236
|
+
type: 'connected';
|
|
237
|
+
options: SSEClientOptions;
|
|
238
|
+
}
|
|
239
|
+
|
|
214
240
|
type ConsumerStreamResult<TConfig extends ConsumerConfig> =
|
|
215
241
|
| ConsumerStreamResultData<TConfig>
|
|
216
242
|
| ConsumerStreamResultError<TConfig>
|
|
217
|
-
| ConsumerStreamResultOpened<TConfig>
|
|
218
243
|
| ConsumerStreamResultConnecting<TConfig>
|
|
219
244
|
| ConsumerStreamResultTimeout<TConfig>
|
|
220
|
-
| ConsumerStreamResultPing<TConfig
|
|
245
|
+
| ConsumerStreamResultPing<TConfig>
|
|
246
|
+
| ConsumerStreamResultConnected<TConfig>;
|
|
221
247
|
|
|
222
248
|
export interface SSEStreamConsumerOptions<TConfig extends ConsumerConfig> {
|
|
223
249
|
url: () => MaybePromise<string>;
|
|
@@ -227,10 +253,6 @@ export interface SSEStreamConsumerOptions<TConfig extends ConsumerConfig> {
|
|
|
227
253
|
signal: AbortSignal;
|
|
228
254
|
deserialize?: Deserialize;
|
|
229
255
|
EventSource: TConfig['EventSource'];
|
|
230
|
-
/**
|
|
231
|
-
* Reconnect after inactivity in milliseconds
|
|
232
|
-
*/
|
|
233
|
-
reconnectAfterInactivityMs?: number;
|
|
234
256
|
}
|
|
235
257
|
|
|
236
258
|
interface ConsumerConfig {
|
|
@@ -272,6 +294,8 @@ export function sseStreamConsumer<TConfig extends ConsumerConfig>(
|
|
|
272
294
|
): AsyncIterable<ConsumerStreamResult<TConfig>> {
|
|
273
295
|
const { deserialize = (v) => v } = opts;
|
|
274
296
|
|
|
297
|
+
let clientOptions: SSEClientOptions = {};
|
|
298
|
+
|
|
275
299
|
const signal = opts.signal;
|
|
276
300
|
|
|
277
301
|
let _es: InstanceType<TConfig['EventSource']> | null = null;
|
|
@@ -290,9 +314,15 @@ export function sseStreamConsumer<TConfig extends ConsumerConfig>(
|
|
|
290
314
|
eventSource: _es,
|
|
291
315
|
event: null,
|
|
292
316
|
});
|
|
293
|
-
|
|
317
|
+
|
|
318
|
+
eventSource.addEventListener(CONNECTED_EVENT, (_msg) => {
|
|
319
|
+
const msg = _msg as EventSourceLike.MessageEvent;
|
|
320
|
+
const options: SSEClientOptions = JSON.parse(msg.data);
|
|
321
|
+
|
|
322
|
+
clientOptions = options;
|
|
294
323
|
controller.enqueue({
|
|
295
|
-
type: '
|
|
324
|
+
type: 'connected',
|
|
325
|
+
options,
|
|
296
326
|
eventSource,
|
|
297
327
|
});
|
|
298
328
|
});
|
|
@@ -376,10 +406,11 @@ export function sseStreamConsumer<TConfig extends ConsumerConfig>(
|
|
|
376
406
|
async next() {
|
|
377
407
|
let promise = stream.reader.read();
|
|
378
408
|
|
|
379
|
-
|
|
409
|
+
const timeoutMs = clientOptions.reconnectAfterInactivityMs;
|
|
410
|
+
if (timeoutMs) {
|
|
380
411
|
promise = withTimeout({
|
|
381
412
|
promise,
|
|
382
|
-
timeoutMs
|
|
413
|
+
timeoutMs,
|
|
383
414
|
onTimeout: async () => {
|
|
384
415
|
// Close and release old reader
|
|
385
416
|
await stream.cancel();
|
|
@@ -390,6 +421,7 @@ export function sseStreamConsumer<TConfig extends ConsumerConfig>(
|
|
|
390
421
|
return {
|
|
391
422
|
value: {
|
|
392
423
|
type: 'timeout',
|
|
424
|
+
ms: timeoutMs,
|
|
393
425
|
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
394
426
|
eventSource: _es!,
|
|
395
427
|
},
|