@trpc/server 11.0.0-alpha-tmp-issues-6374.694 → 11.0.0-alpha-tmp-issues-6374.697
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/ws.js +1 -1
- package/dist/adapters/ws.mjs +1 -1
- package/dist/bundle-analysis.json +109 -93
- package/dist/unstable-core-do-not-import/stream/jsonl.d.ts.map +1 -1
- package/dist/unstable-core-do-not-import/stream/jsonl.js +154 -188
- package/dist/unstable-core-do-not-import/stream/jsonl.mjs +156 -190
- package/dist/unstable-core-do-not-import/stream/utils/mergeAsyncIterables.d.ts +17 -0
- package/dist/unstable-core-do-not-import/stream/utils/mergeAsyncIterables.d.ts.map +1 -0
- package/dist/unstable-core-do-not-import/stream/utils/mergeAsyncIterables.js +241 -0
- package/dist/unstable-core-do-not-import/stream/utils/mergeAsyncIterables.mjs +239 -0
- package/package.json +2 -2
- package/src/unstable-core-do-not-import/stream/jsonl.ts +14 -46
- package/src/unstable-core-do-not-import/stream/utils/mergeAsyncIterables.ts +193 -0
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
import { createDeferred } from './createDeferred.mjs';
|
|
2
|
+
import { makeAsyncResource } from './disposable.mjs';
|
|
3
|
+
|
|
4
|
+
function _ts_add_disposable_resource(env, value, async) {
|
|
5
|
+
if (value !== null && value !== void 0) {
|
|
6
|
+
if (typeof value !== "object" && typeof value !== "function") throw new TypeError("Object expected.");
|
|
7
|
+
var dispose, inner;
|
|
8
|
+
{
|
|
9
|
+
if (!Symbol.asyncDispose) throw new TypeError("Symbol.asyncDispose is not defined.");
|
|
10
|
+
dispose = value[Symbol.asyncDispose];
|
|
11
|
+
}
|
|
12
|
+
if (dispose === void 0) {
|
|
13
|
+
if (!Symbol.dispose) throw new TypeError("Symbol.dispose is not defined.");
|
|
14
|
+
dispose = value[Symbol.dispose];
|
|
15
|
+
inner = dispose;
|
|
16
|
+
}
|
|
17
|
+
if (typeof dispose !== "function") throw new TypeError("Object not disposable.");
|
|
18
|
+
if (inner) dispose = function() {
|
|
19
|
+
try {
|
|
20
|
+
inner.call(this);
|
|
21
|
+
} catch (e) {
|
|
22
|
+
return Promise.reject(e);
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
env.stack.push({
|
|
26
|
+
value: value,
|
|
27
|
+
dispose: dispose,
|
|
28
|
+
async: async
|
|
29
|
+
});
|
|
30
|
+
} else {
|
|
31
|
+
env.stack.push({
|
|
32
|
+
async: true
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
return value;
|
|
36
|
+
}
|
|
37
|
+
function _ts_dispose_resources(env) {
|
|
38
|
+
var _SuppressedError = typeof SuppressedError === "function" ? SuppressedError : function(error, suppressed, message) {
|
|
39
|
+
var e = new Error(message);
|
|
40
|
+
return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
|
|
41
|
+
};
|
|
42
|
+
return (_ts_dispose_resources = function _ts_dispose_resources(env) {
|
|
43
|
+
function fail(e) {
|
|
44
|
+
env.error = env.hasError ? new _SuppressedError(e, env.error, "An error was suppressed during disposal.") : e;
|
|
45
|
+
env.hasError = true;
|
|
46
|
+
}
|
|
47
|
+
var r, s = 0;
|
|
48
|
+
function next() {
|
|
49
|
+
while(r = env.stack.pop()){
|
|
50
|
+
try {
|
|
51
|
+
if (!r.async && s === 1) return s = 0, env.stack.push(r), Promise.resolve().then(next);
|
|
52
|
+
if (r.dispose) {
|
|
53
|
+
var result = r.dispose.call(r.value);
|
|
54
|
+
if (r.async) return s |= 2, Promise.resolve(result).then(next, function(e) {
|
|
55
|
+
fail(e);
|
|
56
|
+
return next();
|
|
57
|
+
});
|
|
58
|
+
} else s |= 1;
|
|
59
|
+
} catch (e) {
|
|
60
|
+
fail(e);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
if (s === 1) return env.hasError ? Promise.reject(env.error) : Promise.resolve();
|
|
64
|
+
if (env.hasError) throw env.error;
|
|
65
|
+
}
|
|
66
|
+
return next();
|
|
67
|
+
})(env);
|
|
68
|
+
}
|
|
69
|
+
function createManagedIterator(iterable, onResult) {
|
|
70
|
+
const iterator = iterable[Symbol.asyncIterator]();
|
|
71
|
+
let state = 'idle';
|
|
72
|
+
function cleanup() {
|
|
73
|
+
state = 'done';
|
|
74
|
+
onResult = ()=>{
|
|
75
|
+
// noop
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
function pull() {
|
|
79
|
+
if (state !== 'idle') {
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
state = 'pending';
|
|
83
|
+
const next = iterator.next();
|
|
84
|
+
next.then((result)=>{
|
|
85
|
+
if (result.done) {
|
|
86
|
+
state = 'done';
|
|
87
|
+
onResult({
|
|
88
|
+
status: 'return',
|
|
89
|
+
value: result.value
|
|
90
|
+
});
|
|
91
|
+
cleanup();
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
state = 'idle';
|
|
95
|
+
onResult({
|
|
96
|
+
status: 'yield',
|
|
97
|
+
value: result.value
|
|
98
|
+
});
|
|
99
|
+
}).catch((cause)=>{
|
|
100
|
+
onResult({
|
|
101
|
+
status: 'error',
|
|
102
|
+
error: cause
|
|
103
|
+
});
|
|
104
|
+
cleanup();
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
return {
|
|
108
|
+
pull,
|
|
109
|
+
destroy: async ()=>{
|
|
110
|
+
cleanup();
|
|
111
|
+
await iterator.return?.();
|
|
112
|
+
}
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Creates a new async iterable that merges multiple async iterables into a single stream.
|
|
117
|
+
* Values from the input iterables are yielded in the order they resolve, similar to Promise.race().
|
|
118
|
+
*
|
|
119
|
+
* New iterables can be added dynamically using the returned {@link MergedAsyncIterables.add} method, even after iteration has started.
|
|
120
|
+
*
|
|
121
|
+
* If any of the input iterables throws an error, that error will be propagated through the merged stream.
|
|
122
|
+
* Other iterables will not continue to be processed.
|
|
123
|
+
*
|
|
124
|
+
* @template TYield The type of values yielded by the input iterables
|
|
125
|
+
*/ function mergeAsyncIterables() {
|
|
126
|
+
let state = 'idle';
|
|
127
|
+
let flushSignal = createDeferred();
|
|
128
|
+
/**
|
|
129
|
+
* used while {@link state} is `idle`
|
|
130
|
+
*/ const iterables = [];
|
|
131
|
+
/**
|
|
132
|
+
* used while {@link state} is `pending`
|
|
133
|
+
*/ const iterators = new Set();
|
|
134
|
+
const buffer = [];
|
|
135
|
+
function initIterable(iterable) {
|
|
136
|
+
if (state !== 'pending') {
|
|
137
|
+
// shouldn't happen
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
const iterator = createManagedIterator(iterable, (result)=>{
|
|
141
|
+
if (state !== 'pending') {
|
|
142
|
+
// shouldn't happen
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
switch(result.status){
|
|
146
|
+
case 'yield':
|
|
147
|
+
buffer.push([
|
|
148
|
+
iterator,
|
|
149
|
+
result
|
|
150
|
+
]);
|
|
151
|
+
break;
|
|
152
|
+
case 'return':
|
|
153
|
+
iterators.delete(iterator);
|
|
154
|
+
break;
|
|
155
|
+
case 'error':
|
|
156
|
+
buffer.push([
|
|
157
|
+
iterator,
|
|
158
|
+
result
|
|
159
|
+
]);
|
|
160
|
+
iterators.delete(iterator);
|
|
161
|
+
break;
|
|
162
|
+
}
|
|
163
|
+
flushSignal.resolve();
|
|
164
|
+
});
|
|
165
|
+
iterators.add(iterator);
|
|
166
|
+
iterator.pull();
|
|
167
|
+
}
|
|
168
|
+
return {
|
|
169
|
+
add (iterable) {
|
|
170
|
+
switch(state){
|
|
171
|
+
case 'idle':
|
|
172
|
+
iterables.push(iterable);
|
|
173
|
+
break;
|
|
174
|
+
case 'pending':
|
|
175
|
+
initIterable(iterable);
|
|
176
|
+
break;
|
|
177
|
+
}
|
|
178
|
+
},
|
|
179
|
+
async *[Symbol.asyncIterator] () {
|
|
180
|
+
const env = {
|
|
181
|
+
stack: [],
|
|
182
|
+
error: void 0,
|
|
183
|
+
hasError: false
|
|
184
|
+
};
|
|
185
|
+
try {
|
|
186
|
+
if (state !== 'idle') {
|
|
187
|
+
throw new Error('Cannot iterate twice');
|
|
188
|
+
}
|
|
189
|
+
state = 'pending';
|
|
190
|
+
const _finally = _ts_add_disposable_resource(env, makeAsyncResource({}, async ()=>{
|
|
191
|
+
state = 'done';
|
|
192
|
+
const errors = [];
|
|
193
|
+
await Promise.all(Array.from(iterators.values()).map(async (it)=>{
|
|
194
|
+
try {
|
|
195
|
+
await it.destroy();
|
|
196
|
+
} catch (cause) {
|
|
197
|
+
errors.push(cause);
|
|
198
|
+
}
|
|
199
|
+
}));
|
|
200
|
+
buffer.length = 0;
|
|
201
|
+
iterators.clear();
|
|
202
|
+
flushSignal.resolve();
|
|
203
|
+
if (errors.length > 0) {
|
|
204
|
+
throw new AggregateError(errors);
|
|
205
|
+
}
|
|
206
|
+
}), true);
|
|
207
|
+
;
|
|
208
|
+
while(iterables.length > 0){
|
|
209
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
210
|
+
initIterable(iterables.shift());
|
|
211
|
+
}
|
|
212
|
+
while(iterators.size > 0){
|
|
213
|
+
await flushSignal.promise;
|
|
214
|
+
while(buffer.length > 0){
|
|
215
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
216
|
+
const [iterator, result] = buffer.shift();
|
|
217
|
+
switch(result.status){
|
|
218
|
+
case 'yield':
|
|
219
|
+
yield result.value;
|
|
220
|
+
iterator.pull();
|
|
221
|
+
break;
|
|
222
|
+
case 'error':
|
|
223
|
+
throw result.error;
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
flushSignal = createDeferred();
|
|
227
|
+
}
|
|
228
|
+
} catch (e) {
|
|
229
|
+
env.error = e;
|
|
230
|
+
env.hasError = true;
|
|
231
|
+
} finally{
|
|
232
|
+
const result = _ts_dispose_resources(env);
|
|
233
|
+
if (result) await result;
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
};
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
export { mergeAsyncIterables };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@trpc/server",
|
|
3
|
-
"version": "11.0.0-alpha-tmp-issues-6374.
|
|
3
|
+
"version": "11.0.0-alpha-tmp-issues-6374.697+41465ba29",
|
|
4
4
|
"description": "The tRPC server library",
|
|
5
5
|
"author": "KATT",
|
|
6
6
|
"license": "MIT",
|
|
@@ -152,5 +152,5 @@
|
|
|
152
152
|
"peerDependencies": {
|
|
153
153
|
"typescript": ">=5.7.2"
|
|
154
154
|
},
|
|
155
|
-
"gitHead": "
|
|
155
|
+
"gitHead": "41465ba29ad50fa0a28146f8d8fb85cde796c562"
|
|
156
156
|
}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { Unpromise } from '../../vendor/unpromise';
|
|
2
1
|
import { isAsyncIterable, isFunction, isObject, run } from '../utils';
|
|
3
2
|
import { iteratorResource } from './utils/asyncIterable';
|
|
4
3
|
import type { Deferred } from './utils/createDeferred';
|
|
5
4
|
import { createDeferred } from './utils/createDeferred';
|
|
6
|
-
import {
|
|
5
|
+
import { makeResource } from './utils/disposable';
|
|
6
|
+
import { mergeAsyncIterables } from './utils/mergeAsyncIterables';
|
|
7
7
|
import { readableStreamFrom } from './utils/readableStreamFrom';
|
|
8
8
|
|
|
9
9
|
/**
|
|
@@ -128,31 +128,14 @@ async function* createBatchStreamProducer(
|
|
|
128
128
|
let counter = 0 as ChunkIndex;
|
|
129
129
|
const placeholder = 0 as PlaceholderValue;
|
|
130
130
|
|
|
131
|
-
|
|
132
|
-
new Set<{
|
|
133
|
-
iterator: AsyncIterator<ChunkData, ChunkData>;
|
|
134
|
-
nextPromise: Promise<IteratorResult<ChunkData, ChunkData>>;
|
|
135
|
-
}>(),
|
|
136
|
-
async () => {
|
|
137
|
-
await Promise.all(Array.from(queue).map((it) => it.iterator.return?.()));
|
|
138
|
-
},
|
|
139
|
-
);
|
|
131
|
+
const mergedIterables = mergeAsyncIterables<ChunkData>();
|
|
140
132
|
function registerAsync(
|
|
141
|
-
callback: (idx: ChunkIndex) => AsyncIterable<ChunkData,
|
|
133
|
+
callback: (idx: ChunkIndex) => AsyncIterable<ChunkData, void>,
|
|
142
134
|
) {
|
|
143
135
|
const idx = counter++ as ChunkIndex;
|
|
144
136
|
|
|
145
|
-
const
|
|
146
|
-
|
|
147
|
-
const nextPromise = iterator.next();
|
|
148
|
-
|
|
149
|
-
nextPromise.catch(() => {
|
|
150
|
-
// prevent unhandled promise rejection
|
|
151
|
-
});
|
|
152
|
-
queue.add({
|
|
153
|
-
iterator,
|
|
154
|
-
nextPromise,
|
|
155
|
-
});
|
|
137
|
+
const iterable = callback(idx);
|
|
138
|
+
mergedIterables.add(iterable);
|
|
156
139
|
|
|
157
140
|
return idx;
|
|
158
141
|
}
|
|
@@ -170,10 +153,10 @@ async function* createBatchStreamProducer(
|
|
|
170
153
|
}
|
|
171
154
|
try {
|
|
172
155
|
const next = await promise;
|
|
173
|
-
|
|
156
|
+
yield [idx, PROMISE_STATUS_FULFILLED, encode(next, path)];
|
|
174
157
|
} catch (cause) {
|
|
175
158
|
opts.onError?.({ error: cause, path });
|
|
176
|
-
|
|
159
|
+
yield [
|
|
177
160
|
idx,
|
|
178
161
|
PROMISE_STATUS_REJECTED,
|
|
179
162
|
opts.formatError?.({ error: cause, path }),
|
|
@@ -196,17 +179,15 @@ async function* createBatchStreamProducer(
|
|
|
196
179
|
while (true) {
|
|
197
180
|
const next = await iterator.next();
|
|
198
181
|
if (next.done) {
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
ASYNC_ITERABLE_STATUS_RETURN,
|
|
202
|
-
encode(next.value, path),
|
|
203
|
-
];
|
|
182
|
+
yield [idx, ASYNC_ITERABLE_STATUS_RETURN, encode(next.value, path)];
|
|
183
|
+
break;
|
|
204
184
|
}
|
|
205
185
|
yield [idx, ASYNC_ITERABLE_STATUS_YIELD, encode(next.value, path)];
|
|
206
186
|
}
|
|
207
187
|
} catch (cause) {
|
|
208
188
|
opts.onError?.({ error: cause, path });
|
|
209
|
-
|
|
189
|
+
|
|
190
|
+
yield [
|
|
210
191
|
idx,
|
|
211
192
|
ASYNC_ITERABLE_STATUS_ERROR,
|
|
212
193
|
opts.formatError?.({ error: cause, path }),
|
|
@@ -270,21 +251,8 @@ async function* createBatchStreamProducer(
|
|
|
270
251
|
|
|
271
252
|
yield newHead;
|
|
272
253
|
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
// Race all iterators to get the next value from any of them
|
|
276
|
-
const [entry, res] = await Unpromise.race(
|
|
277
|
-
Array.from(queue).map(async (it) => [it, await it.nextPromise] as const),
|
|
278
|
-
);
|
|
279
|
-
|
|
280
|
-
yield res.value;
|
|
281
|
-
|
|
282
|
-
// Remove current iterator and re-add if not done
|
|
283
|
-
queue.delete(entry);
|
|
284
|
-
if (!res.done) {
|
|
285
|
-
entry.nextPromise = entry.iterator.next();
|
|
286
|
-
queue.add(entry);
|
|
287
|
-
}
|
|
254
|
+
for await (const value of mergedIterables) {
|
|
255
|
+
yield value;
|
|
288
256
|
}
|
|
289
257
|
}
|
|
290
258
|
/**
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
import { createDeferred } from './createDeferred';
|
|
2
|
+
import { makeAsyncResource } from './disposable';
|
|
3
|
+
|
|
4
|
+
type ManagedIteratorResult<TYield, TReturn> =
|
|
5
|
+
| { status: 'yield'; value: TYield }
|
|
6
|
+
| { status: 'return'; value: TReturn }
|
|
7
|
+
| { status: 'error'; error: unknown };
|
|
8
|
+
function createManagedIterator<TYield, TReturn>(
|
|
9
|
+
iterable: AsyncIterable<TYield, TReturn>,
|
|
10
|
+
onResult: (result: ManagedIteratorResult<TYield, TReturn>) => void,
|
|
11
|
+
) {
|
|
12
|
+
const iterator = iterable[Symbol.asyncIterator]();
|
|
13
|
+
let state: 'idle' | 'pending' | 'done' = 'idle';
|
|
14
|
+
|
|
15
|
+
function cleanup() {
|
|
16
|
+
state = 'done';
|
|
17
|
+
onResult = () => {
|
|
18
|
+
// noop
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function pull() {
|
|
23
|
+
if (state !== 'idle') {
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
state = 'pending';
|
|
27
|
+
|
|
28
|
+
const next = iterator.next();
|
|
29
|
+
next
|
|
30
|
+
.then((result) => {
|
|
31
|
+
if (result.done) {
|
|
32
|
+
state = 'done';
|
|
33
|
+
onResult({ status: 'return', value: result.value });
|
|
34
|
+
cleanup();
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
state = 'idle';
|
|
38
|
+
onResult({ status: 'yield', value: result.value });
|
|
39
|
+
})
|
|
40
|
+
.catch((cause) => {
|
|
41
|
+
onResult({ status: 'error', error: cause });
|
|
42
|
+
cleanup();
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return {
|
|
47
|
+
pull,
|
|
48
|
+
destroy: async () => {
|
|
49
|
+
cleanup();
|
|
50
|
+
await iterator.return?.();
|
|
51
|
+
},
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
type ManagedIterator<TYield, TReturn> = ReturnType<
|
|
55
|
+
typeof createManagedIterator<TYield, TReturn>
|
|
56
|
+
>;
|
|
57
|
+
|
|
58
|
+
interface MergedAsyncIterables<TYield>
|
|
59
|
+
extends AsyncIterable<TYield, void, unknown> {
|
|
60
|
+
add(iterable: AsyncIterable<TYield>): void;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Creates a new async iterable that merges multiple async iterables into a single stream.
|
|
65
|
+
* Values from the input iterables are yielded in the order they resolve, similar to Promise.race().
|
|
66
|
+
*
|
|
67
|
+
* New iterables can be added dynamically using the returned {@link MergedAsyncIterables.add} method, even after iteration has started.
|
|
68
|
+
*
|
|
69
|
+
* If any of the input iterables throws an error, that error will be propagated through the merged stream.
|
|
70
|
+
* Other iterables will not continue to be processed.
|
|
71
|
+
*
|
|
72
|
+
* @template TYield The type of values yielded by the input iterables
|
|
73
|
+
*/
|
|
74
|
+
export function mergeAsyncIterables<TYield>(): MergedAsyncIterables<TYield> {
|
|
75
|
+
let state: 'idle' | 'pending' | 'done' = 'idle';
|
|
76
|
+
let flushSignal = createDeferred<void>();
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* used while {@link state} is `idle`
|
|
80
|
+
*/
|
|
81
|
+
const iterables: AsyncIterable<TYield, void, unknown>[] = [];
|
|
82
|
+
/**
|
|
83
|
+
* used while {@link state} is `pending`
|
|
84
|
+
*/
|
|
85
|
+
const iterators = new Set<ManagedIterator<TYield, void>>();
|
|
86
|
+
|
|
87
|
+
const buffer: Array<
|
|
88
|
+
[
|
|
89
|
+
iterator: ManagedIterator<TYield, void>,
|
|
90
|
+
result: Exclude<
|
|
91
|
+
ManagedIteratorResult<TYield, void>,
|
|
92
|
+
{ status: 'return' }
|
|
93
|
+
>,
|
|
94
|
+
]
|
|
95
|
+
> = [];
|
|
96
|
+
|
|
97
|
+
function initIterable(iterable: AsyncIterable<TYield, void, unknown>) {
|
|
98
|
+
if (state !== 'pending') {
|
|
99
|
+
// shouldn't happen
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
const iterator = createManagedIterator(iterable, (result) => {
|
|
103
|
+
if (state !== 'pending') {
|
|
104
|
+
// shouldn't happen
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
switch (result.status) {
|
|
108
|
+
case 'yield':
|
|
109
|
+
buffer.push([iterator, result]);
|
|
110
|
+
break;
|
|
111
|
+
case 'return':
|
|
112
|
+
iterators.delete(iterator);
|
|
113
|
+
break;
|
|
114
|
+
case 'error':
|
|
115
|
+
buffer.push([iterator, result]);
|
|
116
|
+
iterators.delete(iterator);
|
|
117
|
+
break;
|
|
118
|
+
}
|
|
119
|
+
flushSignal.resolve();
|
|
120
|
+
});
|
|
121
|
+
iterators.add(iterator);
|
|
122
|
+
iterator.pull();
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
return {
|
|
126
|
+
add(iterable: AsyncIterable<TYield, void, unknown>) {
|
|
127
|
+
switch (state) {
|
|
128
|
+
case 'idle':
|
|
129
|
+
iterables.push(iterable);
|
|
130
|
+
break;
|
|
131
|
+
case 'pending':
|
|
132
|
+
initIterable(iterable);
|
|
133
|
+
break;
|
|
134
|
+
case 'done': {
|
|
135
|
+
// shouldn't happen
|
|
136
|
+
break;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
},
|
|
140
|
+
async *[Symbol.asyncIterator]() {
|
|
141
|
+
if (state !== 'idle') {
|
|
142
|
+
throw new Error('Cannot iterate twice');
|
|
143
|
+
}
|
|
144
|
+
state = 'pending';
|
|
145
|
+
|
|
146
|
+
await using _finally = makeAsyncResource({}, async () => {
|
|
147
|
+
state = 'done';
|
|
148
|
+
|
|
149
|
+
const errors: unknown[] = [];
|
|
150
|
+
await Promise.all(
|
|
151
|
+
Array.from(iterators.values()).map(async (it) => {
|
|
152
|
+
try {
|
|
153
|
+
await it.destroy();
|
|
154
|
+
} catch (cause) {
|
|
155
|
+
errors.push(cause);
|
|
156
|
+
}
|
|
157
|
+
}),
|
|
158
|
+
);
|
|
159
|
+
buffer.length = 0;
|
|
160
|
+
iterators.clear();
|
|
161
|
+
flushSignal.resolve();
|
|
162
|
+
|
|
163
|
+
if (errors.length > 0) {
|
|
164
|
+
throw new AggregateError(errors);
|
|
165
|
+
}
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
while (iterables.length > 0) {
|
|
169
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
170
|
+
initIterable(iterables.shift()!);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
while (iterators.size > 0) {
|
|
174
|
+
await flushSignal.promise;
|
|
175
|
+
|
|
176
|
+
while (buffer.length > 0) {
|
|
177
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
178
|
+
const [iterator, result] = buffer.shift()!;
|
|
179
|
+
|
|
180
|
+
switch (result.status) {
|
|
181
|
+
case 'yield':
|
|
182
|
+
yield result.value;
|
|
183
|
+
iterator.pull();
|
|
184
|
+
break;
|
|
185
|
+
case 'error':
|
|
186
|
+
throw result.error;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
flushSignal = createDeferred();
|
|
190
|
+
}
|
|
191
|
+
},
|
|
192
|
+
};
|
|
193
|
+
}
|