evnty 5.1.1 → 5.2.15

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/src/signal.ts ADDED
@@ -0,0 +1,135 @@
1
+ import { Async } from './async.js';
2
+
3
+ /**
4
+ * Promise-based async coordination primitive.
5
+ * `emit()` resolves the pending `receive()` promise (shared across callers).
6
+ * Reusable - after each emission a new round of `receive()` calls can be made.
7
+ * Disposable via `[Symbol.dispose]()` or an optional AbortSignal.
8
+ *
9
+ * @template T The type of value that this signal carries.
10
+ *
11
+ * @example
12
+ * ```typescript
13
+ * const signal = new Signal<string>();
14
+ *
15
+ * const promise = signal.receive();
16
+ * signal.emit('hello');
17
+ * await promise; // 'hello'
18
+ * ```
19
+ */
20
+ export class Signal<T> extends Async<T, boolean> {
21
+ #rx?: PromiseWithResolvers<T>;
22
+
23
+ readonly [Symbol.toStringTag] = 'Signal';
24
+
25
+ /**
26
+ * Merges multiple source signals into a target signal.
27
+ * Values from any source signal are emitted to the target signal.
28
+ * The merge continues until the target signal is disposed.
29
+ *
30
+ * Note: When the target is disposed, iteration stops after the next value
31
+ * from each source. For immediate cleanup, dispose source signals directly.
32
+ *
33
+ * @param target The signal that will receive values from all sources
34
+ * @param signals The source signals to merge from
35
+ *
36
+ * @example
37
+ * ```typescript
38
+ * // Create a target signal and source signals
39
+ * const target = new Signal<string>();
40
+ * const source1 = new Signal<string>();
41
+ * const source2 = new Signal<string>();
42
+ *
43
+ * // Merge sources into target
44
+ * Signal.merge(target, source1, source2);
45
+ *
46
+ * // Values from any source appear in target
47
+ * const promise = target.receive();
48
+ * source1.emit('Hello');
49
+ * const value = await promise; // 'Hello'
50
+ * ```
51
+ */
52
+ static merge<T>(target: Signal<T>, ...signals: Signal<T>[]): void {
53
+ if (target.disposed) {
54
+ return;
55
+ }
56
+ for (const source of signals) {
57
+ void (async () => {
58
+ try {
59
+ const sink = target.sink;
60
+ for await (const value of source) {
61
+ if (!sink(value) && target.disposed) {
62
+ return;
63
+ }
64
+ }
65
+ } catch {
66
+ // ignore disposed signal
67
+ }
68
+ })();
69
+ }
70
+ }
71
+
72
+ /**
73
+ * Creates a new Signal instance.
74
+ *
75
+ * @param abortSignal An optional AbortSignal that can be used to cancel the signal operation.
76
+ *
77
+ * @example
78
+ * ```typescript
79
+ * // Create a signal with abort capability
80
+ * const controller = new AbortController();
81
+ * const signal = new Signal<number>(controller.signal);
82
+ *
83
+ * // Signal can be cancelled
84
+ * controller.abort('Operation cancelled');
85
+ * ```
86
+ */
87
+ constructor(abortSignal?: AbortSignal) {
88
+ super(abortSignal);
89
+ }
90
+
91
+ /**
92
+ * Sends a value to the waiting receiver, if any.
93
+ *
94
+ * @param value - The value to send.
95
+ * @returns `true` if the value was emitted.
96
+ */
97
+ emit(value: T): boolean {
98
+ if (!this.#rx) return false;
99
+ this.#rx.resolve(value);
100
+ this.#rx = undefined;
101
+ return true;
102
+ }
103
+
104
+ /**
105
+ * Waits for the next value to be sent to this signal. If the signal has been aborted
106
+ * or disposed, this method rejects with Error('Disposed').
107
+ *
108
+ * @returns A promise that resolves with the next value sent to the signal.
109
+ *
110
+ * @example
111
+ * ```typescript
112
+ * const signal = new Signal<string>();
113
+ *
114
+ * // Wait for a value
115
+ * const valuePromise = signal.receive();
116
+ *
117
+ * // Send a value from elsewhere
118
+ * signal.emit('Hello');
119
+ *
120
+ * const value = await valuePromise; // 'Hello'
121
+ * ```
122
+ */
123
+ receive(): Promise<T> {
124
+ if (this.disposed) {
125
+ return Promise.reject(new Error('Disposed'));
126
+ }
127
+ this.#rx ??= Promise.withResolvers<T>();
128
+ return this.#rx.promise;
129
+ }
130
+
131
+ dispose(): void {
132
+ this.#rx?.reject(new Error('Disposed'));
133
+ this.#rx = undefined;
134
+ }
135
+ }
package/src/types.ts ADDED
@@ -0,0 +1,137 @@
1
+ export interface Action {
2
+ (): void;
3
+ }
4
+
5
+ /**
6
+ * Generic function type with typed arguments and return value.
7
+ * @template A - Tuple of argument types
8
+ * @template R - Return type
9
+ */
10
+ export interface Fn<A extends unknown[], R> {
11
+ (...args: A): R;
12
+ }
13
+
14
+ /**
15
+ * A value that may be a PromiseLike or a plain value.
16
+ * @template T - The underlying value type
17
+ */
18
+ export type MaybePromise<T> = T | PromiseLike<T>;
19
+
20
+ /**
21
+ * Union of sync and async iterable types.
22
+ * @template T - The yielded value type
23
+ * @template TReturn - The return value type
24
+ * @template TNext - The type passed to next()
25
+ */
26
+ export type AnyIterable<T, TReturn, TNext> = Iterable<T, TReturn, TNext> | AsyncIterable<T, TReturn, TNext>;
27
+
28
+ /**
29
+ * Union of sync and async iterator types.
30
+ * @template T - The yielded value type
31
+ * @template TReturn - The return value type
32
+ * @template TNext - The type passed to next()
33
+ */
34
+ export type AnyIterator<T, TReturn, TNext> = Iterator<T, TReturn, TNext> | AsyncIterator<T, TReturn, TNext>;
35
+
36
+ /**
37
+ * A zero-argument callback that may return a value or promise.
38
+ * @template R - The return type (defaults to void)
39
+ */
40
+ export interface Callback<R = void> extends Fn<[], MaybePromise<R>> {}
41
+
42
+ /**
43
+ * An event listener function that receives a value and optionally returns a result.
44
+ * @template T - The event payload type
45
+ * @template R - The return type (defaults to unknown)
46
+ */
47
+ export interface Listener<T, R = unknown> extends Fn<[T], R | void> {}
48
+
49
+ export interface Emitter<T, R> {
50
+ readonly sink: Fn<[T], R>;
51
+
52
+ emit(value: T): R;
53
+ }
54
+
55
+ /**
56
+ * A filter function that receives both value and index.
57
+ * @template T - The value type to filter
58
+ */
59
+ export interface FilterIndexFunction<T> {
60
+ (value: T, index: number): boolean;
61
+ }
62
+
63
+ /**
64
+ * A simple filter function that tests a value.
65
+ * @template T - The value type to filter
66
+ */
67
+ export interface FilterFunction<T> {
68
+ (value: T): boolean;
69
+ }
70
+
71
+ /**
72
+ * An async filter function that may return a promise.
73
+ * @template T - The value type to filter
74
+ */
75
+ export interface AsyncFilterFunction<T> {
76
+ (value: T): MaybePromise<boolean>;
77
+ }
78
+
79
+ /**
80
+ * A type guard predicate for narrowing types.
81
+ * @template T - The input type
82
+ * @template P - The narrowed output type
83
+ */
84
+ export interface Predicate<T, P extends T> {
85
+ (value: T): value is P;
86
+ }
87
+
88
+ /**
89
+ * Union of filter function types including predicates.
90
+ * @template T - The value type to filter
91
+ * @template P - The narrowed type for predicates
92
+ */
93
+ export type Filter<T, P extends T> = Predicate<T, P> | FilterFunction<T> | AsyncFilterFunction<T>;
94
+
95
+ /**
96
+ * A function that transforms a value to another type.
97
+ * @template T - The input type
98
+ * @template R - The output type
99
+ */
100
+ export interface Mapper<T, R> {
101
+ (value: T): MaybePromise<R>;
102
+ }
103
+
104
+ /**
105
+ * A function that produces an async generator from a value.
106
+ * @template T - The input type
107
+ * @template R - The yielded value type
108
+ */
109
+ export interface AsyncGenerable<T, R> {
110
+ (value: T): AsyncGenerator<R, void, unknown>;
111
+ }
112
+
113
+ /**
114
+ * A reducer function for accumulating values.
115
+ * @template T - The value type being reduced
116
+ * @template R - The accumulator type
117
+ */
118
+ export interface Reducer<T, R> {
119
+ (result: R, value: T): MaybePromise<R>;
120
+ }
121
+
122
+ /**
123
+ * A function that expands a value into another form.
124
+ * @template T - The input type
125
+ * @template R - The output type
126
+ */
127
+ export interface Expander<T, R> {
128
+ (value: T): MaybePromise<R>;
129
+ }
130
+
131
+ /**
132
+ * An object that provides a receive() method returning a promise.
133
+ * @template T - The resolved value type
134
+ */
135
+ export interface Promiseable<T> {
136
+ receive(): Promise<T>;
137
+ }
package/src/utils.ts ADDED
@@ -0,0 +1,426 @@
1
+ import { Sequence } from './sequence.js';
2
+ import { AnyIterator, AnyIterable, MaybePromise } from './types.js';
3
+
4
+ /**
5
+ * @internal
6
+ */
7
+ export function isThenable(value: unknown): value is PromiseLike<unknown> {
8
+ return value !== null && typeof value === 'object' && typeof (value as PromiseLike<unknown>).then === 'function';
9
+ }
10
+
11
+ /**
12
+ * @internal
13
+ * A no-operation function. Useful as a default callback or placeholder.
14
+ */
15
+ export const noop = () => {};
16
+
17
+ /**
18
+ * @internal
19
+ * Returns the minimum value from an iterable, or a fallback if empty.
20
+ */
21
+ export function min(values: Iterable<number>, fallback: number): number {
22
+ let result = Infinity;
23
+ for (const value of values) {
24
+ if (value < result) result = value;
25
+ }
26
+ return result === Infinity ? fallback : result;
27
+ }
28
+
29
+ /**
30
+ * @internal
31
+ * Indicates which iterator method triggered a mapping operation.
32
+ */
33
+ export enum MapIteratorType {
34
+ /** The next() method was called */
35
+ NEXT,
36
+ /** The return() method was called */
37
+ RETURN,
38
+ /** The throw() method was called */
39
+ THROW,
40
+ }
41
+
42
+ /**
43
+ * @internal
44
+ * A mapping function for transforming iterator results.
45
+ * @template T - The input value type
46
+ * @template U - The output value type
47
+ * @template TReturn - The iterator return type
48
+ */
49
+ export interface MapNext<T, U, TReturn> {
50
+ (result: MaybePromise<IteratorResult<T, TReturn>>, type: MapIteratorType): MaybePromise<IteratorResult<U, TReturn>>;
51
+ }
52
+
53
+ /**
54
+ * @internal
55
+ * Wraps an iterator with a mapping function applied to each result.
56
+ * @template U - The output value type
57
+ * @template T - The input value type
58
+ * @template TReturn - The iterator return type
59
+ * @template TNext - The type passed to next()
60
+ * @param iterator - The source iterator to wrap
61
+ * @param map - The mapping function to apply to each result
62
+ * @returns An async iterator with mapped results
63
+ */
64
+ export const mapIterator = <U, T, TReturn, TNext>(iterator: AnyIterator<T, TReturn, TNext>, map: MapNext<T, U, TReturn>): AsyncIterator<U, TReturn, TNext> => {
65
+ const subIterator: AsyncIterator<U, TReturn, TNext> = {
66
+ next: async (...args: [] | [TNext]) => {
67
+ const result = await iterator.next(...args);
68
+ return map(result, MapIteratorType.NEXT);
69
+ },
70
+ };
71
+ if (iterator.return) {
72
+ subIterator.return = async (...args: [] | [TReturn]) => {
73
+ const result = await iterator.return!(...args);
74
+ return map(result, MapIteratorType.RETURN);
75
+ };
76
+ } else {
77
+ subIterator.return = async (value: TReturn) => {
78
+ return map({ done: true, value }, MapIteratorType.RETURN);
79
+ };
80
+ }
81
+ if (iterator.throw) {
82
+ subIterator.throw = async (...args: [] | [unknown]) => {
83
+ const result = await iterator.throw!(...args);
84
+ return map(result, MapIteratorType.THROW);
85
+ };
86
+ }
87
+
88
+ return subIterator;
89
+ };
90
+
91
+ /**
92
+ * Wraps an async iterable with abort signal support.
93
+ * Each iteration creates a fresh iterator with scoped abort handling.
94
+ * Listener is added at iteration start and removed on completion/abort/return.
95
+ *
96
+ * @template T - The yielded value type
97
+ * @template TReturn - The return value type
98
+ * @template TNext - The type passed to next()
99
+ * @param iterable - The source async iterable to wrap
100
+ * @param signal - AbortSignal to cancel iteration
101
+ * @returns An async iterable with abort support
102
+ *
103
+ * @example
104
+ * ```typescript
105
+ * const controller = new AbortController();
106
+ * const source = async function*() { yield 1; yield 2; yield 3; };
107
+ *
108
+ * for await (const value of abortableIterable(source(), controller.signal)) {
109
+ * console.log(value);
110
+ * if (value === 2) controller.abort();
111
+ * }
112
+ * ```
113
+ */
114
+ export function abortableIterable<T, TReturn, TNext>(iterable: AsyncIterable<T, TReturn, TNext>, signal: AbortSignal): AsyncIterable<T, TReturn, TNext> {
115
+ return {
116
+ [Symbol.asyncIterator](): AsyncIterator<T, TReturn, TNext> {
117
+ const iterator = iterable[Symbol.asyncIterator]();
118
+ const { promise, resolve } = Promise.withResolvers<void>();
119
+ const onAbort = () => resolve();
120
+ let closed = false;
121
+
122
+ const finish = (value?: TReturn): Promise<IteratorResult<T, TReturn>> => {
123
+ if (closed) {
124
+ return Promise.resolve({ done: true, value: value as TReturn });
125
+ }
126
+ closed = true;
127
+ signal.removeEventListener('abort', onAbort);
128
+ return iterator.return?.(value) ?? Promise.resolve({ done: true, value: value as TReturn });
129
+ };
130
+
131
+ if (signal.aborted) {
132
+ onAbort();
133
+ } else {
134
+ signal.addEventListener('abort', onAbort);
135
+ }
136
+
137
+ const race = [promise, undefined] as unknown as [Promise<void>, Promise<IteratorResult<T, TReturn>>];
138
+
139
+ return {
140
+ async next(...args: [] | [TNext]): Promise<IteratorResult<T, TReturn>> {
141
+ race[1] = iterator.next(...args);
142
+ const result = await Promise.race(race);
143
+ if (result === undefined) {
144
+ void finish();
145
+ return { done: true, value: undefined as TReturn };
146
+ }
147
+ if (result.done) {
148
+ closed = true;
149
+ signal.removeEventListener('abort', onAbort);
150
+ }
151
+ return result;
152
+ },
153
+ async return(value?: TReturn): Promise<IteratorResult<T, TReturn>> {
154
+ return finish(value);
155
+ },
156
+ };
157
+ },
158
+ };
159
+ }
160
+
161
+ /**
162
+ * Interface for creating iterable number sequences with various parameter combinations.
163
+ * Supports infinite sequences, counted sequences, and sequences with custom start and step values.
164
+ */
165
+ export interface Iterate {
166
+ (): Iterable<number, void, unknown>;
167
+ (count: number): Iterable<number, void, unknown>;
168
+ (start: number, count: number): Iterable<number, void, unknown>;
169
+ (start: number, count: number, step: number): Iterable<number, void, unknown>;
170
+ }
171
+
172
+ /**
173
+ * Creates an iterable sequence of numbers with flexible parameters.
174
+ * Can generate infinite sequences, finite sequences, or sequences with custom start and step values.
175
+ *
176
+ * @param args Variable arguments to configure the sequence:
177
+ * - No args: Infinite sequence starting at 0 with step 1
178
+ * - 1 arg (count): Sequence from 0 to count-1
179
+ * - 2 args (start, count): Sequence starting at 'start' for 'count' iterations
180
+ * - 3 args (start, count, step): Custom start, count, and step value
181
+ * @returns An iterable that generates numbers according to the parameters
182
+ *
183
+ * @example
184
+ * ```typescript
185
+ * // Infinite sequence: 0, 1, 2, 3, ...
186
+ * for (const n of iterate()) { }
187
+ *
188
+ * // Count only: 0, 1, 2, 3, 4
189
+ * for (const n of iterate(5)) { }
190
+ *
191
+ * // Start and count: 10, 11, 12, 13, 14
192
+ * for (const n of iterate(10, 5)) { }
193
+ *
194
+ * // Start, count, and step: 0, 2, 4, 6, 8
195
+ * for (const n of iterate(0, 5, 2)) { }
196
+ * ```
197
+ */
198
+ export const iterate: Iterate = (startOrCount?: number, countWhenTwoArgs?: number, step: number = 1): Iterable<number, void, unknown> => {
199
+ const hasStartArg = countWhenTwoArgs !== undefined;
200
+ const start = hasStartArg ? startOrCount! : 0;
201
+ const count = startOrCount === undefined ? Infinity : hasStartArg ? countWhenTwoArgs : startOrCount;
202
+
203
+ return {
204
+ [Symbol.iterator]() {
205
+ let idx = 0;
206
+ let current = start;
207
+ return {
208
+ next() {
209
+ if (idx < count) {
210
+ const value = current;
211
+ current += step;
212
+ idx++;
213
+ return { value, done: false };
214
+ }
215
+ return { value: undefined, done: true };
216
+ },
217
+ return(value) {
218
+ idx = count;
219
+ return { value, done: true };
220
+ },
221
+ throw(error?: unknown) {
222
+ idx = count;
223
+ throw error;
224
+ },
225
+ } satisfies Iterator<number, void, unknown>;
226
+ },
227
+ };
228
+ };
229
+
230
+ /**
231
+ * @internal
232
+ * Creates a promise that resolves after a specified timeout. If an `AbortSignal` is provided and triggered,
233
+ * the timeout is cleared, and the promise resolves to `false`.
234
+ *
235
+ * @param {number} timeout - The time in milliseconds to wait before resolving the promise.
236
+ * @param {AbortSignal} [signal] - An optional `AbortSignal` that can abort the timeout.
237
+ * @returns {Promise<boolean>} A promise that resolves to `true` if the timeout completed, or `false` if it was aborted.
238
+ *
239
+ * @example
240
+ * ```typescript
241
+ * const controller = new AbortController();
242
+ * setTimeout(() => controller.abort(), 500);
243
+ * const result = await setTimeoutAsync(1000, controller.signal);
244
+ * console.log(result); // false
245
+ * ```
246
+ */
247
+ export const setTimeoutAsync = async (timeout: number, signal?: AbortSignal): Promise<boolean> => {
248
+ if (signal?.aborted) {
249
+ return false;
250
+ }
251
+ const { promise, resolve } = Promise.withResolvers<boolean>();
252
+ const timerId = setTimeout(resolve, timeout, true);
253
+ const onAbort = () => {
254
+ clearTimeout(timerId);
255
+ resolve(false);
256
+ };
257
+ signal?.addEventListener('abort', onAbort);
258
+
259
+ return promise.finally(() => signal?.removeEventListener('abort', onAbort));
260
+ };
261
+
262
+ /**
263
+ * Converts a synchronous iterable to an asynchronous iterable.
264
+ * Wraps the sync iterator methods to return promises, enabling uniform async handling.
265
+ *
266
+ * @template T The type of values yielded by the iterator
267
+ * @template TReturn The return type of the iterator
268
+ * @template TNext The type of value that can be passed to next()
269
+ * @param iterable A synchronous iterable to convert
270
+ * @returns An async iterable that yields the same values as the input
271
+ *
272
+ * @example
273
+ * ```typescript
274
+ * const syncArray = [1, 2, 3, 4, 5];
275
+ * const asyncIterable = toAsyncIterable(syncArray);
276
+ *
277
+ * for await (const value of asyncIterable) {
278
+ * console.log(value); // 1, 2, 3, 4, 5
279
+ * }
280
+ * ```
281
+ */
282
+ export const toAsyncIterable = <T, TReturn, TNext>(iterable: Iterable<T, TReturn, TNext>): AsyncIterable<T, TReturn, TNext> => {
283
+ return {
284
+ [Symbol.asyncIterator]() {
285
+ const iterator = iterable[Symbol.iterator]();
286
+ return {
287
+ async next(...args: [TNext] | []) {
288
+ return iterator.next(...args);
289
+ },
290
+ async return(maybeValue) {
291
+ const value = await maybeValue;
292
+ return iterator.return?.(value) ?? ({ value, done: true } as IteratorResult<T, TReturn>);
293
+ },
294
+ async throw(error) {
295
+ if (iterator.throw) {
296
+ return iterator.throw(error);
297
+ }
298
+ throw error;
299
+ },
300
+ } satisfies AsyncIterator<T, TReturn, TNext>;
301
+ },
302
+ };
303
+ };
304
+
305
+ /**
306
+ * @internal
307
+ * Pipes values from an async iterable through a generator transformation.
308
+ * Applies a generator function to each value, yielding all resulting values.
309
+ * Supports cancellation via AbortSignal for early termination.
310
+ *
311
+ * @template T The input value type
312
+ * @template U The output value type
313
+ * @param iterable The source async iterable
314
+ * @param generatorFactory A factory that returns a generator function for transforming values
315
+ * @param signal Optional AbortSignal to cancel the operation
316
+ * @returns An async generator yielding transformed values
317
+ *
318
+ * @example
319
+ * ```typescript
320
+ * async function* source() {
321
+ * yield 1; yield 2; yield 3;
322
+ * }
323
+ *
324
+ * const doubled = pipe(source(), () => async function*(n) {
325
+ * yield n * 2;
326
+ * });
327
+ *
328
+ * for await (const value of doubled) {
329
+ * console.log(value); // 2, 4, 6
330
+ * }
331
+ * ```
332
+ */
333
+ const isAsyncIterable = <T, TReturn, TNext>(value: AnyIterable<T, TReturn, TNext>): value is AsyncIterable<T, TReturn, TNext> => {
334
+ return typeof (value as AsyncIterable<T, TReturn, TNext>)[Symbol.asyncIterator] === 'function';
335
+ };
336
+
337
+ /**
338
+ * @internal
339
+ */
340
+ export async function* pipe<T, U>(
341
+ iterable: AsyncIterable<T>,
342
+ generatorFactory: () => (value: T) => AnyIterable<U, void, unknown>,
343
+ signal?: AbortSignal,
344
+ ): AsyncGenerator<Awaited<U>, void, unknown> {
345
+ const source = signal ? abortableIterable(iterable, signal) : iterable;
346
+ const generator = generatorFactory();
347
+
348
+ for await (const value of source) {
349
+ const produced = generator(value);
350
+ const subIterable = isAsyncIterable(produced) ? produced : toAsyncIterable(produced);
351
+ const abortableSub = signal ? abortableIterable(subIterable, signal) : subIterable;
352
+
353
+ for await (const subValue of abortableSub) {
354
+ yield subValue;
355
+ }
356
+ }
357
+ }
358
+
359
+ /**
360
+ * @internal
361
+ * Merges multiple async iterables into a single stream.
362
+ * Values are yielded as they become available from any source.
363
+ * Completes when all sources complete; aborts all on error.
364
+ * @template T - The value type yielded by all iterables
365
+ * @param iterables - The async iterables to merge
366
+ * @returns A merged async iterable
367
+ */
368
+ export const mergeIterables = <T>(...iterables: AsyncIterable<T, void, unknown>[]): AsyncIterable<T, void, unknown> => {
369
+ return {
370
+ [Symbol.asyncIterator]() {
371
+ if (iterables.length === 0) {
372
+ return {
373
+ next: async () => ({ value: undefined, done: true }),
374
+ };
375
+ }
376
+
377
+ const exit = Symbol('mergeIterables.exit');
378
+ const ctrl = new AbortController();
379
+ const sequence = new Sequence<T>(ctrl.signal);
380
+ let remaining = iterables.length;
381
+
382
+ const pump = async (iterable: AsyncIterable<T, void, unknown>) => {
383
+ try {
384
+ for await (const value of abortableIterable(iterable, ctrl.signal)) {
385
+ if (!sequence.emit(value)) {
386
+ break;
387
+ }
388
+ }
389
+ } catch (error) {
390
+ ctrl.abort(error);
391
+ } finally {
392
+ remaining -= 1;
393
+ if (remaining === 0 && !ctrl.signal.aborted) {
394
+ ctrl.abort(exit);
395
+ }
396
+ }
397
+ };
398
+
399
+ for (const iterable of iterables) {
400
+ void pump(iterable);
401
+ }
402
+
403
+ return {
404
+ next: async () => {
405
+ try {
406
+ const value = await sequence.receive();
407
+ return { value, done: false };
408
+ } catch {
409
+ if (ctrl.signal.aborted && ctrl.signal.reason === exit) {
410
+ return { value: undefined, done: true };
411
+ }
412
+ throw ctrl.signal.reason;
413
+ }
414
+ },
415
+ return: async () => {
416
+ ctrl.abort(exit);
417
+ return { value: undefined, done: true };
418
+ },
419
+ throw: async (error?: unknown) => {
420
+ ctrl.abort(error);
421
+ return { value: undefined, done: true };
422
+ },
423
+ };
424
+ },
425
+ };
426
+ };