evnty 5.1.1 → 5.2.0

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.
@@ -0,0 +1,899 @@
1
+ import { mergeIterables, toAsyncIterable, pipe, isThenable } from './utils.js';
2
+ import { AnyIterable } from './types.js';
3
+
4
+ const enum OpKind {
5
+ MAP,
6
+ FILTER,
7
+ FILTER_MAP,
8
+ AWAITED,
9
+ INSPECT,
10
+ ENUMERATE,
11
+ TAKE,
12
+ DROP,
13
+ TAKE_WHILE,
14
+ DROP_WHILE,
15
+ REDUCE,
16
+ FLAT_MAP,
17
+ EXPAND,
18
+ }
19
+
20
+ /**
21
+ * @internal
22
+ * Represents a fusible operation that can be combined with other operations.
23
+ */
24
+ type FusedOp =
25
+ | { kind: OpKind.MAP; fn: (value: unknown, index: number) => unknown }
26
+ | { kind: OpKind.FILTER; fn: (value: unknown, index: number) => unknown }
27
+ | { kind: OpKind.FILTER_MAP; fn: (value: unknown, index: number) => unknown }
28
+ | { kind: OpKind.AWAITED }
29
+ | { kind: OpKind.INSPECT; fn: (value: unknown, index: number) => unknown }
30
+ | { kind: OpKind.ENUMERATE; start: number }
31
+ | { kind: OpKind.TAKE; limit: number }
32
+ | { kind: OpKind.DROP; count: number }
33
+ | { kind: OpKind.TAKE_WHILE; fn: (value: unknown, index: number) => unknown }
34
+ | { kind: OpKind.DROP_WHILE; fn: (value: unknown, index: number) => unknown }
35
+ | { kind: OpKind.REDUCE; fn: (acc: unknown, value: unknown, index: number) => unknown; init: unknown; hasInit: boolean }
36
+ | { kind: OpKind.FLAT_MAP; fn: (value: unknown, index: number) => AsyncIterable<unknown, void, unknown> }
37
+ | { kind: OpKind.EXPAND; fn: (value: unknown, index: number) => Iterable<unknown> | Promise<Iterable<unknown>> };
38
+
39
+ class OpState {
40
+ initialized = false;
41
+
42
+ constructor(
43
+ public index: number,
44
+ public remaining: number,
45
+ public dropping: boolean,
46
+ public value: unknown,
47
+ ) {}
48
+
49
+ static from(this: void, op: FusedOp): OpState {
50
+ switch (op.kind) {
51
+ case OpKind.ENUMERATE:
52
+ return new OpState(op.start, 0, false, undefined);
53
+ case OpKind.TAKE:
54
+ return new OpState(0, op.limit, false, undefined);
55
+ case OpKind.DROP:
56
+ return new OpState(0, op.count, false, undefined);
57
+ case OpKind.DROP_WHILE:
58
+ return new OpState(0, 0, true, undefined);
59
+ case OpKind.REDUCE:
60
+ return new OpState(0, 0, false, op.init);
61
+ default:
62
+ return new OpState(0, 0, false, undefined);
63
+ }
64
+ }
65
+ }
66
+
67
+ class ProcessResult {
68
+ constructor(
69
+ readonly value: unknown,
70
+ readonly shouldYield: boolean,
71
+ readonly done: boolean,
72
+ readonly expandIterator: Iterator<unknown> | null,
73
+ readonly expandOpIndex: number,
74
+ readonly flatMapIterator: AsyncIterator<unknown> | null,
75
+ readonly flatMapOpIndex: number,
76
+ ) {}
77
+
78
+ static continue(): ProcessResult {
79
+ return new ProcessResult(undefined, false, false, null, -1, null, -1);
80
+ }
81
+
82
+ static yield(value: unknown): ProcessResult {
83
+ return new ProcessResult(value, true, false, null, -1, null, -1);
84
+ }
85
+
86
+ static done(): ProcessResult {
87
+ return new ProcessResult(undefined, false, true, null, -1, null, -1);
88
+ }
89
+
90
+ static expand(iterator: Iterator<unknown>, opIndex: number): ProcessResult {
91
+ return new ProcessResult(undefined, false, false, iterator, opIndex, null, -1);
92
+ }
93
+
94
+ static flatMap(iterator: AsyncIterator<unknown>, opIndex: number): ProcessResult {
95
+ return new ProcessResult(undefined, false, false, null, -1, iterator, opIndex);
96
+ }
97
+ }
98
+
99
+ function findTakeStates(ops: FusedOp[], opStates: OpState[]): OpState[] {
100
+ const takeStates: OpState[] = [];
101
+ for (let i = 0; i < ops.length; i++) {
102
+ if (ops[i].kind === OpKind.TAKE) takeStates.push(opStates[i]);
103
+ }
104
+ return takeStates;
105
+ }
106
+
107
+ function hasExpandingOps(ops: FusedOp[]): boolean {
108
+ for (let i = 0; i < ops.length; i++) {
109
+ const kind = ops[i].kind;
110
+ if (kind === OpKind.FLAT_MAP || kind === OpKind.EXPAND) return true;
111
+ }
112
+ return false;
113
+ }
114
+
115
+ function checkTakeExhausted(takeStates: OpState[]): boolean {
116
+ for (let i = 0; i < takeStates.length; i++) {
117
+ if (takeStates[i].remaining <= 0) return true;
118
+ }
119
+ return false;
120
+ }
121
+
122
+ async function processOps(inputValue: unknown, ops: FusedOp[], opStates: OpState[], startIndex: number): Promise<ProcessResult> {
123
+ let value = inputValue;
124
+
125
+ for (let i = startIndex; i < ops.length; i++) {
126
+ const op = ops[i];
127
+ const state = opStates[i];
128
+
129
+ switch (op.kind) {
130
+ case OpKind.MAP:
131
+ value = op.fn(value, state.index++);
132
+ break;
133
+
134
+ case OpKind.FILTER: {
135
+ const result = op.fn(value, state.index++);
136
+ const passed = isThenable(result) ? await result : result;
137
+ if (!passed) return ProcessResult.continue();
138
+ break;
139
+ }
140
+
141
+ case OpKind.FILTER_MAP: {
142
+ const result = op.fn(value, state.index++);
143
+ const resolved = isThenable(result) ? await result : result;
144
+ if (resolved === undefined) return ProcessResult.continue();
145
+ value = resolved;
146
+ break;
147
+ }
148
+
149
+ case OpKind.AWAITED:
150
+ value = await value;
151
+ break;
152
+
153
+ case OpKind.INSPECT: {
154
+ const result = op.fn(value, state.index++);
155
+ if (isThenable(result)) await result;
156
+ break;
157
+ }
158
+
159
+ case OpKind.ENUMERATE:
160
+ value = [state.index++, value];
161
+ break;
162
+
163
+ case OpKind.TAKE:
164
+ if (state.remaining <= 0) return ProcessResult.done();
165
+ state.remaining--;
166
+ break;
167
+
168
+ case OpKind.TAKE_WHILE: {
169
+ const result = op.fn(value, state.index++);
170
+ const passed = isThenable(result) ? await result : result;
171
+ if (!passed) return ProcessResult.done();
172
+ break;
173
+ }
174
+
175
+ case OpKind.DROP:
176
+ if (state.remaining > 0) {
177
+ state.remaining--;
178
+ return ProcessResult.continue();
179
+ }
180
+ break;
181
+
182
+ case OpKind.DROP_WHILE:
183
+ if (state.dropping) {
184
+ const result = op.fn(value, state.index++);
185
+ const passed = isThenable(result) ? await result : result;
186
+ if (passed) return ProcessResult.continue();
187
+ state.dropping = false;
188
+ }
189
+ break;
190
+
191
+ case OpKind.REDUCE: {
192
+ if (!state.initialized) {
193
+ state.initialized = true;
194
+ if (op.hasInit) {
195
+ const result = op.fn(state.value, value, state.index++);
196
+ state.value = isThenable(result) ? await result : result;
197
+ value = state.value;
198
+ } else {
199
+ state.value = value;
200
+ return ProcessResult.continue();
201
+ }
202
+ } else {
203
+ const result = op.fn(state.value, value, state.index++);
204
+ state.value = isThenable(result) ? await result : result;
205
+ value = state.value;
206
+ }
207
+ break;
208
+ }
209
+
210
+ case OpKind.FLAT_MAP: {
211
+ const iterable = op.fn(value, state.index++);
212
+ return ProcessResult.flatMap(iterable[Symbol.asyncIterator](), i);
213
+ }
214
+
215
+ case OpKind.EXPAND: {
216
+ const result = op.fn(value, state.index++);
217
+ const expanded = isThenable(result) ? await result : result;
218
+ return ProcessResult.expand(expanded[Symbol.iterator](), i);
219
+ }
220
+ }
221
+ }
222
+
223
+ return ProcessResult.yield(value);
224
+ }
225
+
226
+ async function processOpsSimple(inputValue: unknown, ops: FusedOp[], opStates: OpState[]): Promise<{ value: unknown; shouldYield: boolean; done: boolean }> {
227
+ let value = inputValue;
228
+
229
+ for (let i = 0; i < ops.length; i++) {
230
+ const op = ops[i];
231
+ const state = opStates[i];
232
+
233
+ switch (op.kind) {
234
+ case OpKind.MAP:
235
+ value = op.fn(value, state.index++);
236
+ break;
237
+
238
+ case OpKind.FILTER: {
239
+ const result = op.fn(value, state.index++);
240
+ const passed = isThenable(result) ? await result : result;
241
+ if (!passed) return { value: undefined, shouldYield: false, done: false };
242
+ break;
243
+ }
244
+
245
+ case OpKind.FILTER_MAP: {
246
+ const result = op.fn(value, state.index++);
247
+ const resolved = isThenable(result) ? await result : result;
248
+ if (resolved === undefined) return { value: undefined, shouldYield: false, done: false };
249
+ value = resolved;
250
+ break;
251
+ }
252
+
253
+ case OpKind.AWAITED:
254
+ value = await value;
255
+ break;
256
+
257
+ case OpKind.INSPECT: {
258
+ const result = op.fn(value, state.index++);
259
+ if (isThenable(result)) await result;
260
+ break;
261
+ }
262
+
263
+ case OpKind.ENUMERATE:
264
+ value = [state.index++, value];
265
+ break;
266
+
267
+ case OpKind.TAKE:
268
+ state.remaining--;
269
+ break;
270
+
271
+ case OpKind.TAKE_WHILE: {
272
+ const result = op.fn(value, state.index++);
273
+ const passed = isThenable(result) ? await result : result;
274
+ if (!passed) return { value: undefined, shouldYield: false, done: true };
275
+ break;
276
+ }
277
+
278
+ case OpKind.DROP:
279
+ if (state.remaining > 0) {
280
+ state.remaining--;
281
+ return { value: undefined, shouldYield: false, done: false };
282
+ }
283
+ break;
284
+
285
+ case OpKind.DROP_WHILE:
286
+ if (state.dropping) {
287
+ const result = op.fn(value, state.index++);
288
+ const passed = isThenable(result) ? await result : result;
289
+ if (passed) return { value: undefined, shouldYield: false, done: false };
290
+ state.dropping = false;
291
+ }
292
+ break;
293
+
294
+ case OpKind.REDUCE: {
295
+ if (!state.initialized) {
296
+ state.initialized = true;
297
+ if ((op as { hasInit: boolean }).hasInit) {
298
+ const result = op.fn(state.value, value, state.index++);
299
+ state.value = isThenable(result) ? await result : result;
300
+ value = state.value;
301
+ } else {
302
+ state.value = value;
303
+ return { value: undefined, shouldYield: false, done: false };
304
+ }
305
+ } else {
306
+ const result = op.fn(state.value, value, state.index++);
307
+ state.value = isThenable(result) ? await result : result;
308
+ value = state.value;
309
+ }
310
+ break;
311
+ }
312
+ }
313
+ }
314
+
315
+ return { value, shouldYield: true, done: false };
316
+ }
317
+
318
+ function collectOps(iter: AsyncIteratorObject<unknown, unknown, unknown>): FusedOp[] {
319
+ const ops: FusedOp[] = [];
320
+ let current: AsyncIteratorObject<unknown, unknown, unknown> | null = iter;
321
+ while (current) {
322
+ const op = current.op;
323
+ if (op) ops.push(op);
324
+ current = current.parent;
325
+ }
326
+ ops.reverse();
327
+ return ops;
328
+ }
329
+
330
+ function getSource(iter: AsyncIteratorObject<unknown, unknown, unknown>): AsyncIterable<unknown, unknown, unknown> {
331
+ let current = iter;
332
+ while (current.parent) {
333
+ current = current.parent;
334
+ }
335
+ return current.iterable;
336
+ }
337
+
338
+ function createSimpleIterable(source: AsyncIterable<unknown, unknown, unknown>, ops: FusedOp[]): AsyncIterable<unknown, void, unknown> {
339
+ return {
340
+ [Symbol.asyncIterator]: () => {
341
+ const iterator = source[Symbol.asyncIterator]();
342
+ const opStates = ops.map(OpState.from);
343
+ const takeStates = findTakeStates(ops, opStates);
344
+ let done = false;
345
+
346
+ return {
347
+ async next(): Promise<IteratorResult<unknown, void>> {
348
+ while (!done) {
349
+ if (checkTakeExhausted(takeStates)) {
350
+ done = true;
351
+ await iterator.return?.();
352
+ return { value: undefined, done: true };
353
+ }
354
+
355
+ const sourceResult = await iterator.next();
356
+ if (sourceResult.done) {
357
+ done = true;
358
+ return { value: undefined, done: true };
359
+ }
360
+
361
+ const result = await processOpsSimple(sourceResult.value, ops, opStates);
362
+ if (result.done) {
363
+ done = true;
364
+ await iterator.return?.();
365
+ return { value: undefined, done: true };
366
+ }
367
+ if (result.shouldYield) return { value: result.value, done: false };
368
+ }
369
+ return { value: undefined, done: true };
370
+ },
371
+
372
+ async return(returnValue?: unknown): Promise<IteratorResult<unknown, void>> {
373
+ done = true;
374
+ await iterator.return?.(returnValue);
375
+ return { value: undefined, done: true };
376
+ },
377
+
378
+ async throw(error?: unknown): Promise<IteratorResult<unknown, void>> {
379
+ done = true;
380
+ if (iterator.throw) {
381
+ await iterator.throw(error);
382
+ }
383
+ throw error;
384
+ },
385
+ };
386
+ },
387
+ };
388
+ }
389
+
390
+ type InnerFrame = { type: 'expand'; iterator: Iterator<unknown>; opIndex: number } | { type: 'flatMap'; iterator: AsyncIterator<unknown>; opIndex: number };
391
+
392
+ function createExpandingIterable(source: AsyncIterable<unknown, unknown, unknown>, ops: FusedOp[]): AsyncIterable<unknown, void, unknown> {
393
+ return {
394
+ [Symbol.asyncIterator]: () => {
395
+ const iterator = source[Symbol.asyncIterator]();
396
+ const opStates = ops.map(OpState.from);
397
+ const takeStates = findTakeStates(ops, opStates);
398
+
399
+ let done = false;
400
+ const innerStack: InnerFrame[] = [];
401
+
402
+ const closeInnerIterators = async () => {
403
+ for (const frame of innerStack) {
404
+ if (frame.type === 'flatMap') {
405
+ await frame.iterator.return?.();
406
+ }
407
+ }
408
+ innerStack.length = 0;
409
+ };
410
+
411
+ const handleResult = async (result: ProcessResult): Promise<IteratorResult<unknown, void> | null> => {
412
+ if (result.done) {
413
+ done = true;
414
+ await closeInnerIterators();
415
+ await iterator.return?.();
416
+ return { value: undefined, done: true };
417
+ }
418
+ if (result.expandIterator) {
419
+ innerStack.push({ type: 'expand', iterator: result.expandIterator, opIndex: result.expandOpIndex });
420
+ return null;
421
+ }
422
+ if (result.flatMapIterator) {
423
+ innerStack.push({ type: 'flatMap', iterator: result.flatMapIterator, opIndex: result.flatMapOpIndex });
424
+ return null;
425
+ }
426
+ if (result.shouldYield) {
427
+ return { value: result.value, done: false };
428
+ }
429
+ return null;
430
+ };
431
+
432
+ return {
433
+ async next(): Promise<IteratorResult<unknown, void>> {
434
+ while (!done) {
435
+ if (innerStack.length > 0) {
436
+ const frame = innerStack[innerStack.length - 1];
437
+ if (frame.type === 'expand') {
438
+ const expandResult = frame.iterator.next();
439
+ if (!expandResult.done) {
440
+ const result = await processOps(expandResult.value, ops, opStates, frame.opIndex + 1);
441
+ const handled = await handleResult(result);
442
+ if (handled) return handled;
443
+ continue;
444
+ }
445
+ innerStack.pop();
446
+ continue;
447
+ } else {
448
+ const flatMapResult = await frame.iterator.next();
449
+ if (!flatMapResult.done) {
450
+ const result = await processOps(flatMapResult.value, ops, opStates, frame.opIndex + 1);
451
+ const handled = await handleResult(result);
452
+ if (handled) return handled;
453
+ continue;
454
+ }
455
+ innerStack.pop();
456
+ continue;
457
+ }
458
+ }
459
+
460
+ if (checkTakeExhausted(takeStates)) {
461
+ done = true;
462
+ await iterator.return?.();
463
+ return { value: undefined, done: true };
464
+ }
465
+
466
+ const sourceResult = await iterator.next();
467
+ if (sourceResult.done) {
468
+ done = true;
469
+ return { value: undefined, done: true };
470
+ }
471
+
472
+ const result = await processOps(sourceResult.value, ops, opStates, 0);
473
+ const handled = await handleResult(result);
474
+ if (handled) return handled;
475
+ }
476
+
477
+ return { value: undefined, done: true };
478
+ },
479
+
480
+ async return(returnValue?: unknown): Promise<IteratorResult<unknown, void>> {
481
+ done = true;
482
+ await closeInnerIterators();
483
+ await iterator.return?.(returnValue);
484
+ return { value: undefined, done: true };
485
+ },
486
+
487
+ async throw(error?: unknown): Promise<IteratorResult<unknown, void>> {
488
+ done = true;
489
+ await closeInnerIterators();
490
+ if (iterator.throw) {
491
+ await iterator.throw(error);
492
+ }
493
+ throw error;
494
+ },
495
+ };
496
+ },
497
+ };
498
+ }
499
+
500
+ function createFusedIterable(iter: AsyncIteratorObject<unknown, unknown, unknown>): AsyncIterable<unknown, void, unknown> {
501
+ const source = getSource(iter);
502
+ const ops = iter.cachedOps ?? (iter.cachedOps = collectOps(iter));
503
+
504
+ if (ops.length === 0) {
505
+ return source as AsyncIterable<unknown, void, unknown>;
506
+ }
507
+
508
+ return hasExpandingOps(ops) ? createExpandingIterable(source, ops) : createSimpleIterable(source, ops);
509
+ }
510
+
511
+ /**
512
+ * A wrapper class providing functional operations on async iterables.
513
+ * Enables lazy evaluation and chainable transformations on async data streams.
514
+ *
515
+ * Key characteristics:
516
+ * - Lazy evaluation - operations are not executed until iteration begins
517
+ * - Chainable - all transformation methods return new AsyncIteratorObject instances
518
+ * - Supports both sync and async transformation functions
519
+ * - Memory efficient - processes values one at a time
520
+ * - Operation fusion - chains execute in optimized passes
521
+ *
522
+ * @template T The type of values yielded by the iterator
523
+ * @template TReturn The return type of the iterator
524
+ * @template TNext The type of value that can be passed to next()
525
+ *
526
+ * @example
527
+ * ```typescript
528
+ * // Create from an async generator
529
+ * async function* numbers() {
530
+ * yield 1; yield 2; yield 3;
531
+ * }
532
+ *
533
+ * const iterator = new AsyncIteratorObject(numbers())
534
+ * .map(x => x * 2)
535
+ * .filter(x => x > 2);
536
+ *
537
+ * for await (const value of iterator) {
538
+ * console.log(value); // 4, 6
539
+ * }
540
+ * ```
541
+ */
542
+ export class AsyncIteratorObject<T, TReturn, TNext> {
543
+ /**
544
+ * Creates an AsyncIteratorObject from a synchronous iterable.
545
+ * Converts the sync iterable to async for uniform handling.
546
+ *
547
+ * @param iterable A synchronous iterable to convert
548
+ * @returns A new AsyncIteratorObject wrapping the converted iterable
549
+ *
550
+ * @example
551
+ * ```typescript
552
+ * const syncArray = [1, 2, 3, 4, 5];
553
+ * const asyncIterator = AsyncIteratorObject.from(syncArray);
554
+ *
555
+ * for await (const value of asyncIterator) {
556
+ * console.log(value); // 1, 2, 3, 4, 5
557
+ * }
558
+ * ```
559
+ */
560
+ static from<T, TReturn, TNext>(iterable: Iterable<T, TReturn, TNext>): AsyncIteratorObject<T, TReturn, TNext> {
561
+ const asyncIterable = toAsyncIterable(iterable);
562
+ return new AsyncIteratorObject<T, TReturn, TNext>(asyncIterable);
563
+ }
564
+
565
+ /**
566
+ * Merges multiple async iterables into a single stream.
567
+ * Values from all sources are interleaved as they become available.
568
+ * The merged iterator completes when all source iterators complete.
569
+ *
570
+ * @param iterables The async iterables to merge
571
+ * @returns A new AsyncIteratorObject yielding values from all sources
572
+ *
573
+ * @example
574
+ * ```typescript
575
+ * async function* source1() { yield 1; yield 3; }
576
+ * async function* source2() { yield 2; yield 4; }
577
+ *
578
+ * const merged = AsyncIteratorObject.merge(source1(), source2());
579
+ *
580
+ * for await (const value of merged) {
581
+ * console.log(value); // Order depends on timing: 1, 2, 3, 4 or similar
582
+ * }
583
+ * ```
584
+ */
585
+ static merge<T>(...iterables: AsyncIterable<T, void, unknown>[]): AsyncIteratorObject<T, void, unknown> {
586
+ return new AsyncIteratorObject<T, void, unknown>(mergeIterables(...iterables));
587
+ }
588
+
589
+ /** @internal */
590
+ iterable: AsyncIterable<unknown, unknown, unknown>;
591
+ /** @internal */
592
+ parent: AsyncIteratorObject<unknown, unknown, unknown> | null;
593
+ /** @internal */
594
+ op: FusedOp | null;
595
+ /** @internal */
596
+ cachedOps: FusedOp[] | null = null;
597
+
598
+ readonly [Symbol.toStringTag] = 'AsyncIteratorObject';
599
+
600
+ constructor(iterable: AsyncIterable<T, TReturn, TNext>, parent: AsyncIteratorObject<unknown, unknown, unknown> | null = null, op: FusedOp | null = null) {
601
+ this.iterable = iterable as AsyncIterable<unknown, unknown, unknown>;
602
+ this.parent = parent;
603
+ this.op = op;
604
+ }
605
+
606
+ /**
607
+ * Escape hatch for custom transformations not covered by the built-in operators.
608
+ * Materializes the fused operation chain, then applies a generator function to each value.
609
+ *
610
+ * @param generatorFactory A function that returns a generator function for transforming values
611
+ * @param signal Optional AbortSignal to cancel the operation
612
+ * @returns A new AsyncIteratorObject with transformed values
613
+ */
614
+ pipe<U>(generatorFactory: () => (value: T) => AnyIterable<U, void, unknown>, signal?: AbortSignal): AsyncIteratorObject<U, void, unknown> {
615
+ const materialized = createFusedIterable(this as AsyncIteratorObject<unknown, unknown, unknown>);
616
+ const generator = pipe(materialized as AsyncIterable<T>, generatorFactory, signal);
617
+ return new AsyncIteratorObject<U, void, unknown>(generator);
618
+ }
619
+
620
+ /**
621
+ * Resolves promise-like values from the source iterator.
622
+ * Useful for normalizing values before applying type-guard predicates.
623
+ *
624
+ * @returns A new AsyncIteratorObject yielding awaited values
625
+ */
626
+ awaited(): AsyncIteratorObject<Awaited<T>, void, unknown> {
627
+ return new AsyncIteratorObject<Awaited<T>, void, unknown>(
628
+ this.iterable as AsyncIterable<Awaited<T>, void, unknown>,
629
+ this as AsyncIteratorObject<unknown, unknown, unknown>,
630
+ { kind: OpKind.AWAITED },
631
+ );
632
+ }
633
+
634
+ /**
635
+ * Transforms each value using a mapping function.
636
+ * The callback can be synchronous or return a promise.
637
+ *
638
+ * @param callbackfn Function to transform each value
639
+ * @returns A new AsyncIteratorObject yielding transformed values
640
+ *
641
+ * @example
642
+ * ```typescript
643
+ * const numbers = AsyncIteratorObject.from([1, 2, 3]);
644
+ * const doubled = numbers.map(x => x * 2);
645
+ *
646
+ * for await (const value of doubled) {
647
+ * console.log(value); // 2, 4, 6
648
+ * }
649
+ * ```
650
+ */
651
+ map<U>(callbackfn: (value: T, index: number) => U): AsyncIteratorObject<U, void, unknown> {
652
+ return new AsyncIteratorObject<U, void, unknown>(this.iterable as AsyncIterable<U, void, unknown>, this as AsyncIteratorObject<unknown, unknown, unknown>, {
653
+ kind: OpKind.MAP,
654
+ fn: callbackfn as (value: unknown, index: number) => unknown,
655
+ });
656
+ }
657
+
658
+ /**
659
+ * Filters values based on a predicate function.
660
+ * Only values for which the predicate returns truthy are yielded.
661
+ * Supports type guard predicates for type narrowing.
662
+ *
663
+ * @param predicate Function to test each value
664
+ * @returns A new AsyncIteratorObject yielding only values that pass the test
665
+ *
666
+ * @example
667
+ * ```typescript
668
+ * const numbers = AsyncIteratorObject.from([1, 2, 3, 4, 5]);
669
+ * const evens = numbers.filter(x => x % 2 === 0);
670
+ *
671
+ * for await (const value of evens) {
672
+ * console.log(value); // 2, 4
673
+ * }
674
+ * ```
675
+ */
676
+ filter(predicate: (value: T, index: number) => unknown): AsyncIteratorObject<T, void, unknown>;
677
+ filter<S extends T>(predicate: (value: T, index: number) => value is S): AsyncIteratorObject<S, void, unknown>;
678
+ filter<S extends T>(predicate: (value: T, index: number) => value is S): AsyncIteratorObject<S, void, unknown> {
679
+ return new AsyncIteratorObject<S, void, unknown>(this.iterable as AsyncIterable<S, void, unknown>, this as AsyncIteratorObject<unknown, unknown, unknown>, {
680
+ kind: OpKind.FILTER,
681
+ fn: predicate as (value: unknown, index: number) => unknown,
682
+ });
683
+ }
684
+
685
+ /**
686
+ * Combined filter and map operation. Returns undefined to skip a value.
687
+ * The callback result is awaited to check for undefined.
688
+ *
689
+ * @param callbackfn Function that returns a transformed value or undefined to skip
690
+ * @returns A new AsyncIteratorObject yielding non-undefined transformed values
691
+ *
692
+ * @example
693
+ * ```typescript
694
+ * const numbers = AsyncIteratorObject.from([1, 2, 3, 4, 5]);
695
+ * const doubledEvens = numbers.filterMap(x => x % 2 === 0 ? x * 2 : undefined);
696
+ *
697
+ * for await (const value of doubledEvens) {
698
+ * console.log(value); // 4, 8
699
+ * }
700
+ * ```
701
+ */
702
+ filterMap<U>(callbackfn: (value: T, index: number) => U): AsyncIteratorObject<Exclude<Awaited<U>, undefined>, void, unknown> {
703
+ return new AsyncIteratorObject<Exclude<Awaited<U>, undefined>, void, unknown>(
704
+ this.iterable as AsyncIterable<Exclude<Awaited<U>, undefined>, void, unknown>,
705
+ this as AsyncIteratorObject<unknown, unknown, unknown>,
706
+ { kind: OpKind.FILTER_MAP, fn: callbackfn as (value: unknown, index: number) => unknown },
707
+ );
708
+ }
709
+
710
+ /**
711
+ * Executes a side-effect function for each value without modifying the stream.
712
+ * Useful for debugging or logging. The callback is awaited for proper sequencing.
713
+ *
714
+ * @param callbackfn Function to execute for each value
715
+ * @returns A new AsyncIteratorObject yielding the same values
716
+ *
717
+ * @example
718
+ * ```typescript
719
+ * const numbers = AsyncIteratorObject.from([1, 2, 3]);
720
+ * const logged = numbers.inspect(x => console.log('value:', x)).map(x => x * 2);
721
+ * ```
722
+ */
723
+ inspect(callbackfn: (value: T, index: number) => unknown): AsyncIteratorObject<T, TReturn, TNext> {
724
+ return new AsyncIteratorObject<T, TReturn, TNext>(
725
+ this.iterable as AsyncIterable<T, TReturn, TNext>,
726
+ this as AsyncIteratorObject<unknown, unknown, unknown>,
727
+ { kind: OpKind.INSPECT, fn: callbackfn as (value: unknown, index: number) => unknown },
728
+ );
729
+ }
730
+
731
+ /**
732
+ * Wraps each value with its index as a tuple.
733
+ * Useful after filtering when original indices are lost.
734
+ *
735
+ * @param start Starting index (default: 0)
736
+ * @returns A new AsyncIteratorObject yielding [index, value] tuples
737
+ *
738
+ * @example
739
+ * ```typescript
740
+ * const letters = AsyncIteratorObject.from(['a', 'b', 'c']);
741
+ * const enumerated = letters.enumerate();
742
+ *
743
+ * for await (const [i, v] of enumerated) {
744
+ * console.log(i, v); // 0 'a', 1 'b', 2 'c'
745
+ * }
746
+ * ```
747
+ */
748
+ enumerate(start: number = 0): AsyncIteratorObject<[number, T], void, unknown> {
749
+ return new AsyncIteratorObject<[number, T], void, unknown>(
750
+ this.iterable as AsyncIterable<[number, T], void, unknown>,
751
+ this as AsyncIteratorObject<unknown, unknown, unknown>,
752
+ { kind: OpKind.ENUMERATE, start },
753
+ );
754
+ }
755
+
756
+ /**
757
+ * Creates an iterator whose values are the values from this iterator, stopping once the provided limit is reached.
758
+ * @param limit The maximum number of values to yield.
759
+ */
760
+ take(limit: number): AsyncIteratorObject<T, void, unknown> {
761
+ return new AsyncIteratorObject<T, void, unknown>(this.iterable as AsyncIterable<T, void, unknown>, this as AsyncIteratorObject<unknown, unknown, unknown>, {
762
+ kind: OpKind.TAKE,
763
+ limit,
764
+ });
765
+ }
766
+
767
+ /**
768
+ * Takes values while the predicate returns truthy.
769
+ * Stops immediately when predicate returns falsy.
770
+ * Supports type guard predicates for type narrowing.
771
+ *
772
+ * @param predicate Function to test each value
773
+ * @returns A new AsyncIteratorObject yielding values until predicate fails
774
+ *
775
+ * @example
776
+ * ```typescript
777
+ * const numbers = AsyncIteratorObject.from([1, 2, 3, 4, 5]);
778
+ * const small = numbers.takeWhile(x => x < 4);
779
+ *
780
+ * for await (const value of small) {
781
+ * console.log(value); // 1, 2, 3
782
+ * }
783
+ * ```
784
+ */
785
+ takeWhile(predicate: (value: T, index: number) => unknown): AsyncIteratorObject<T, void, unknown>;
786
+ takeWhile<S extends T>(predicate: (value: T, index: number) => value is S): AsyncIteratorObject<S, void, unknown>;
787
+ takeWhile<S extends T>(predicate: (value: T, index: number) => value is S): AsyncIteratorObject<S, void, unknown> {
788
+ return new AsyncIteratorObject<S, void, unknown>(this.iterable as AsyncIterable<S, void, unknown>, this as AsyncIteratorObject<unknown, unknown, unknown>, {
789
+ kind: OpKind.TAKE_WHILE,
790
+ fn: predicate as (value: unknown, index: number) => unknown,
791
+ });
792
+ }
793
+
794
+ /**
795
+ * Creates an iterator whose values are the values from this iterator after skipping the provided count.
796
+ * @param count The number of values to drop.
797
+ */
798
+ drop(count: number): AsyncIteratorObject<T, void, unknown> {
799
+ return new AsyncIteratorObject<T, void, unknown>(this.iterable as AsyncIterable<T, void, unknown>, this as AsyncIteratorObject<unknown, unknown, unknown>, {
800
+ kind: OpKind.DROP,
801
+ count,
802
+ });
803
+ }
804
+
805
+ /**
806
+ * Skips values while the predicate returns truthy.
807
+ * Yields all remaining values once predicate returns falsy.
808
+ *
809
+ * @param predicate Function to test each value
810
+ * @returns A new AsyncIteratorObject skipping values until predicate fails
811
+ *
812
+ * @example
813
+ * ```typescript
814
+ * const numbers = AsyncIteratorObject.from([1, 2, 3, 4, 5]);
815
+ * const afterSmall = numbers.dropWhile(x => x < 3);
816
+ *
817
+ * for await (const value of afterSmall) {
818
+ * console.log(value); // 3, 4, 5
819
+ * }
820
+ * ```
821
+ */
822
+ dropWhile(predicate: (value: T, index: number) => unknown): AsyncIteratorObject<T, void, unknown> {
823
+ return new AsyncIteratorObject<T, void, unknown>(this.iterable as AsyncIterable<T, void, unknown>, this as AsyncIteratorObject<unknown, unknown, unknown>, {
824
+ kind: OpKind.DROP_WHILE,
825
+ fn: predicate as (value: unknown, index: number) => unknown,
826
+ });
827
+ }
828
+
829
+ /**
830
+ * Creates an iterator whose values are the result of applying the callback to the values from this iterator and then flattening the resulting iterators or iterables.
831
+ * @param callback A function that accepts up to two arguments to be used to transform values from the underlying iterator into new iterators or iterables to be flattened into the result.
832
+ */
833
+ flatMap<U>(callback: (value: T, index: number) => AsyncIterable<U, void, unknown>): AsyncIteratorObject<U, void, unknown> {
834
+ return new AsyncIteratorObject<U, void, unknown>(this.iterable as AsyncIterable<U, void, unknown>, this as AsyncIteratorObject<unknown, unknown, unknown>, {
835
+ kind: OpKind.FLAT_MAP,
836
+ fn: callback as (value: unknown, index: number) => AsyncIterable<unknown, void, unknown>,
837
+ });
838
+ }
839
+
840
+ /**
841
+ * Creates an iterator of accumulated values by applying a reducer function.
842
+ * Unlike Array.reduce, this returns an iterator that yields each intermediate accumulated value,
843
+ * not just the final result. This allows observing the accumulation process.
844
+ *
845
+ * @param callbackfn Reducer function to accumulate values
846
+ * @param initialValue Optional initial value for the accumulation
847
+ * @returns A new AsyncIteratorObject yielding accumulated values at each step
848
+ *
849
+ * @example
850
+ * ```typescript
851
+ * const numbers = AsyncIteratorObject.from([1, 2, 3, 4]);
852
+ * const sums = numbers.reduce((sum, x) => sum + x, 0);
853
+ *
854
+ * for await (const value of sums) {
855
+ * console.log(value); // 1, 3, 6, 10 (running totals)
856
+ * }
857
+ * ```
858
+ */
859
+ reduce(callbackfn: (previousValue: T, currentValue: T, currentIndex: number) => T): AsyncIteratorObject<T, void, unknown>;
860
+ reduce<R>(callbackfn: (previousValue: R, currentValue: T, currentIndex: number) => R, initialValue: R): AsyncIteratorObject<R, void, unknown>;
861
+ reduce<R>(callbackfn: (previousValue: R, currentValue: T, currentIndex: number) => R, ...args: unknown[]): AsyncIteratorObject<R, void, unknown> {
862
+ const hasInit = args.length > 0;
863
+ return new AsyncIteratorObject<R, void, unknown>(this.iterable as AsyncIterable<R, void, unknown>, this as AsyncIteratorObject<unknown, unknown, unknown>, {
864
+ kind: OpKind.REDUCE,
865
+ fn: callbackfn as (acc: unknown, value: unknown, index: number) => unknown,
866
+ init: args[0],
867
+ hasInit,
868
+ });
869
+ }
870
+
871
+ /**
872
+ * Transforms each value into multiple values using an expander function.
873
+ * Each input value is expanded into zero or more output values.
874
+ * Like `flatMap` but takes sync Iterables (or Promises of Iterables) instead of AsyncIterables.
875
+ *
876
+ * @param callbackfn Function that returns an iterable of values for each input
877
+ * @returns A new AsyncIteratorObject yielding all expanded values
878
+ *
879
+ * @example
880
+ * ```typescript
881
+ * const numbers = AsyncIteratorObject.from([1, 2, 3]);
882
+ * const expanded = numbers.expand(x => [x, x * 10]);
883
+ *
884
+ * for await (const value of expanded) {
885
+ * console.log(value); // 1, 10, 2, 20, 3, 30
886
+ * }
887
+ * ```
888
+ */
889
+ expand<U>(callbackfn: (value: T, index: number) => Promise<Iterable<U>> | Iterable<U>): AsyncIteratorObject<U, void, unknown> {
890
+ return new AsyncIteratorObject<U, void, unknown>(this.iterable as AsyncIterable<U, void, unknown>, this as AsyncIteratorObject<unknown, unknown, unknown>, {
891
+ kind: OpKind.EXPAND,
892
+ fn: callbackfn as (value: unknown, index: number) => Iterable<unknown> | Promise<Iterable<unknown>>,
893
+ });
894
+ }
895
+
896
+ [Symbol.asyncIterator](): AsyncIterator<T, TReturn, TNext> {
897
+ return createFusedIterable(this as AsyncIteratorObject<unknown, unknown, unknown>)[Symbol.asyncIterator]() as AsyncIterator<T, TReturn, TNext>;
898
+ }
899
+ }