@dbx-tools/shared-core 0.1.18 → 0.1.20

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/iterable.ts DELETED
@@ -1,790 +0,0 @@
1
- /**
2
- * Dependency-free iterable utilities. {@link generator} flattens mixed arguments;
3
- * {@link sequence} wraps source(s) in a lazy, `Array`-compatible {@link Sequence}.
4
- *
5
- * Every transform/terminal is a standalone function operating on plain
6
- * {@link Container}s (see {@link map}, {@link filter}, {@link group}, ...); the
7
- * {@link Sequence} methods are thin forwarders over them so the same logic backs
8
- * both the free-function and the fluent/chained styles.
9
- */
10
-
11
- /** Lazy sequence over iterable source(s). See {@link sequence}. */
12
- export type Sequence<T> = SequenceImpl<T>;
13
-
14
-
15
- type SequenceSource<T> = Iterable<T> | ReadonlyMap<unknown, T> | OneOrMany<T> | null | undefined;
16
-
17
- /**
18
- * A non-scalar {@link Iterable} - one to treat as a collection of elements
19
- * rather than a scalar. {@link isContainer} narrows to this, excluding strings,
20
- * `String`/`RegExp` objects, and functions. {@link Collection} is the eagerly-
21
- * sized subset. The element defaults to `unknown` so any `Collection` is
22
- * assignable to a bare `Container`.
23
- */
24
- export type Container<T = unknown> = Iterable<T>;
25
-
26
- /**
27
- * A built-in, eagerly-sized {@link Container}: an {@link Array}, {@link Set}, or
28
- * {@link Map} (whose *values* are `T` - a Map iterates `[key, value]` entries,
29
- * so its element type differs, but its value type is `T`). All share a cheap
30
- * emptiness check ({@link isEmpty}).
31
- */
32
- export type Collection<T> = ReadonlyArray<T> | ReadonlySet<T> | ReadonlyMap<unknown, T>;
33
-
34
- export type OneOrMany<T> = [T, ...T[]];
35
-
36
- export function isOneOrMany<T = unknown>(value: readonly T[]): value is OneOrMany<T> {
37
- return value.length > 0;
38
- }
39
-
40
-
41
- /** A source accepted by a variadic op: a {@link Container} of `T`, or nothing. */
42
- type Source<T> = Container<T> | null | undefined;
43
-
44
- /**
45
- * Element type of a {@link group} bucket array: when the predicate `P` is a type
46
- * guard (`value is S`), the bucket is narrowed to `S & T`; otherwise it stays `T`.
47
- */
48
- type GroupValue<T, P> = P extends (value: any, ...rest: any[]) => value is infer S ? S & T : T;
49
-
50
- /** A map of group name -> predicate, as accepted by {@link group}. */
51
- type GroupPredicates<T> = Record<string, (value: T, index: number) => boolean>;
52
-
53
- /**
54
- * Type guard for a {@link Collection}: an {@link Array}, {@link Set}, or
55
- * {@link Map}. Narrows `value` so its element/value type is treated as `T`.
56
- *
57
- * @typeParam T - Element (or Map value) type asserted for the collection.
58
- * @param value - Value to test.
59
- * @returns `true` (narrowing `value` to {@link Collection}<`T`>) for a
60
- * built-in array/set/map.
61
- */
62
- export function isCollection<T = unknown>(value: unknown): value is Collection<T> {
63
- return Array.isArray(value) || value instanceof Set || value instanceof Map;
64
- }
65
-
66
- /**
67
- * `true` when a {@link Collection} has no elements. Uses `length` for arrays
68
- * and `size` for {@link Set}/{@link Map}.
69
- *
70
- * @param collection - The array, set, or map to test.
71
- */
72
- export function isEmpty(
73
- collection: Collection<unknown> | Record<string, unknown>,
74
- options?: { recursive?: boolean },
75
- ): boolean {
76
-
77
- function visit(value: unknown, seen?: Set<unknown>): boolean {
78
- if (value == null) return true;
79
- else if (typeof value === "object") {
80
- if (seen?.has(value)) return true;
81
- seen?.add(value);
82
- if (Array.isArray(value)) {
83
- return value.length === 0 || (seen ? value.every((item) => visit(item, seen)) : false);
84
- }
85
- if (value instanceof Set) {
86
- return (
87
- value.size === 0 || (seen ? [...value].every((item) => visit(item, seen)) : false)
88
- );
89
- }
90
- if (value instanceof Map) {
91
- return (
92
- value.size === 0 ||
93
- (seen ? [...value.values()].every((item) => visit(item, seen)) : false)
94
- );
95
- }
96
- const keys = Object.keys(value);
97
- if (keys.length === 0) return true;
98
- else if (seen) {
99
- return keys.every((key) => visit((value as Record<string, unknown>)[key], seen));
100
- } else {
101
- return false
102
- }
103
- } else {
104
- return false;
105
- }
106
- }
107
- return visit(collection, options?.recursive ? new Set() : undefined);
108
- }
109
-
110
-
111
- /**
112
- * Normalizes a source to an iterable of its `T` values, so Maps are treated
113
- * uniformly with arrays/sets: a {@link Map} yields its *values* (matching
114
- * {@link Collection}'s value-typed `T`), any other iterable yields itself. This
115
- * is what {@link sequence} consumes, so a `Map` contributes values everywhere
116
- * rather than `[key, value]` entries.
117
- */
118
- export function values<T>(source: Iterable<T> | ReadonlyMap<unknown, T>): Iterable<T> {
119
- return source instanceof Map ? source.values() : (source as Iterable<T>);
120
- }
121
-
122
- /**
123
- * Type guard for a {@link Container} - an iterable to be treated as a collection
124
- * rather than a scalar.
125
- *
126
- * Deliberately excludes values that are technically iterable but should be
127
- * treated as scalars here - strings, `String`/`RegExp` objects, and functions -
128
- * so a lone string is never spread character-by-character.
129
- *
130
- * @typeParam T - Element type asserted for the iterable.
131
- * @param value - Value to test.
132
- * @returns `true` (narrowing `value` to {@link Container}<`T`>) for a non-string
133
- * iterable.
134
- */
135
- export function isContainer<T = unknown>(value: unknown): value is Container<T> {
136
- return (
137
- value != null &&
138
- typeof value !== "string" &&
139
- !(value instanceof String) &&
140
- !(value instanceof RegExp) &&
141
- typeof value !== "function" &&
142
- typeof (value as { [Symbol.iterator]?: unknown })[Symbol.iterator] === "function"
143
- );
144
- }
145
-
146
-
147
- function sequenceSources<T>(...sources: SequenceSource<T>[]): Iterable<T>[] {
148
- const sourceIterables: Iterable<T>[] = [];
149
- for (const source of sources) {
150
- if (source == null || (isCollection(source) && isEmpty(source))) continue;
151
- sourceIterables.push(values(source));
152
- }
153
- return sourceIterables;
154
- }
155
-
156
- /**
157
- * Flattens nested arrays for {@link flat}. Non-array values are wrapped as a
158
- * single-element iterable; depth decrements per array level.
159
- */
160
- function flattenValue(value: unknown, depth: number): Iterable<unknown> {
161
- if (depth > 0 && Array.isArray(value)) {
162
- const nextDepth = Number.isFinite(depth) ? depth - 1 : depth;
163
- return {
164
- *[Symbol.iterator]() {
165
- for (const item of value) yield* flattenValue(item, nextDepth);
166
- },
167
- };
168
- }
169
- return [value];
170
- }
171
-
172
- /** Wrap a per-element transform (each element -> an iterable) as a lazy {@link Sequence}. */
173
- function derive<T, U>(
174
- source: Iterable<T>,
175
- fn: (value: T, index: number) => Iterable<U>,
176
- ): Sequence<U> {
177
- return sequence({
178
- *[Symbol.iterator]() {
179
- let index = 0;
180
- for (const value of source) yield* fn(value, index++);
181
- },
182
- });
183
- }
184
-
185
- /** Like {@link derive}, but yields one value per source element (no wrapper iterable). */
186
- function deriveOne<T, U>(source: Iterable<T>, fn: (value: T, index: number) => U): Sequence<U> {
187
- return sequence({
188
- *[Symbol.iterator]() {
189
- let index = 0;
190
- for (const value of source) yield fn(value, index++);
191
- },
192
- });
193
- }
194
-
195
- /**
196
- * Same semantics as `Array.prototype.map`, over a single {@link Container}.
197
- *
198
- * @param source - The container to map (nullish yields an empty sequence).
199
- * @param callback - Called per element with its index; its result is emitted.
200
- */
201
- export function map<T, U>(
202
- source: Source<T>,
203
- callback: (value: T, index: number) => U,
204
- ): Sequence<U> {
205
- return deriveOne(sequence(source), callback);
206
- }
207
-
208
- /**
209
- * Same semantics as `Array.prototype.filter`, over a single {@link Container}.
210
- * A type-guard predicate narrows the resulting element type.
211
- *
212
- * @param source - The container to filter (nullish yields an empty sequence).
213
- * @param predicate - Keeps elements for which it returns `true`.
214
- */
215
- export function filter<T, S extends T>(
216
- source: Source<T>,
217
- predicate: (value: T, index: number) => value is S,
218
- ): Sequence<S>;
219
- export function filter<T>(
220
- source: Source<T>,
221
- predicate: (value: T, index: number) => boolean,
222
- ): Sequence<T>;
223
- export function filter<T>(
224
- source: Source<T>,
225
- predicate: (value: T, index: number) => boolean,
226
- ): Sequence<T> {
227
- return derive(sequence(source), function* (value, index) {
228
- if (predicate(value, index)) yield value;
229
- });
230
- }
231
-
232
- /**
233
- * Concatenates the sources and yields only elements that are not `null` or
234
- * `undefined`, narrowing the element type to {@link NonNullable}<`T`>.
235
- *
236
- * @param sources - Containers to concatenate (nullish sources are skipped).
237
- */
238
- export function nonNull<T>(...sources: readonly Source<T>[]): Sequence<NonNullable<T>> {
239
- return filter(sequence(...sources), (value): value is NonNullable<T> => value != null);
240
- }
241
-
242
- /**
243
- * Same semantics as `Array.prototype.flatMap`, over a single {@link Container}.
244
- *
245
- * @param source - The container to map (nullish yields an empty sequence).
246
- * @param callback - Returns a value or array of values, flattened one level.
247
- */
248
- export function flatMap<T, U>(
249
- source: Source<T>,
250
- callback: (value: T, index: number) => U | ReadonlyArray<U>,
251
- ): Sequence<U> {
252
- return derive(sequence(source), function* (value, index) {
253
- const result = callback(value, index);
254
- if (Array.isArray(result)) yield* result;
255
- else yield result as U;
256
- });
257
- }
258
-
259
- /**
260
- * Same semantics as `Array.prototype.flat` (arrays only). `depth` leads so the
261
- * sources can stay variadic; use `depth < 1` for a no-op passthrough.
262
- *
263
- * @param depth - How many array levels to flatten.
264
- * @param sources - Containers to concatenate, then flatten (nullish skipped).
265
- */
266
- export function flat<T>(depth: number, ...sources: readonly Source<T>[]): Sequence<T> {
267
- const src = sequence(...sources);
268
- if (depth < 1) return src;
269
- return derive(src, (value) => flattenValue(value, depth)) as Sequence<T>;
270
- }
271
-
272
- /**
273
- * Concatenates the sources, then lazily yields values in encounter order,
274
- * skipping a value only when an equal one was already yielded (`Set` /
275
- * SameValueZero). Uniqueness is checked per element as it is consumed.
276
- *
277
- * @param sources - Containers to concatenate (nullish sources are skipped).
278
- */
279
- export function distinct<T>(...sources: readonly Source<T>[]): Sequence<T> {
280
- const src = sequence(...sources);
281
- return sequence({
282
- *[Symbol.iterator]() {
283
- const seen = new Set<T>();
284
- for (const value of src) {
285
- if (seen.has(value)) continue;
286
- seen.add(value);
287
- yield value;
288
- }
289
- },
290
- });
291
- }
292
-
293
- /**
294
- * Yields `source`, then each appended `item` in order (arrays spread one level),
295
- * mirroring `Array.prototype.concat`. `items` are scalar values/arrays, not
296
- * containers, so `source` stays a single leading argument.
297
- *
298
- * @param source - The leading container (nullish yields just the items).
299
- * @param items - Values (or arrays of values) appended after the source.
300
- */
301
- export function concat<T>(
302
- source: Source<T>,
303
- ...items: readonly (T | ReadonlyArray<T>)[]
304
- ): Sequence<T> {
305
- const src = sequence(source);
306
- if (items.length === 0) return src;
307
- return sequence({
308
- *[Symbol.iterator]() {
309
- yield* src;
310
- for (const item of items) {
311
- if (Array.isArray(item)) yield* item;
312
- else yield item as T;
313
- }
314
- },
315
- });
316
- }
317
-
318
- /**
319
- * Yields at most `count` elements from the front of the concatenated sources.
320
- * `count` leads so the sources can stay variadic.
321
- *
322
- * @param count - Maximum number of elements to yield (`<= 0` yields none).
323
- * @param sources - Containers to concatenate (nullish sources are skipped).
324
- */
325
- export function take<T>(count: number, ...sources: readonly Source<T>[]): Sequence<T> {
326
- if (count <= 0) return emptySequence as Sequence<T>;
327
- const src = sequence(...sources);
328
- return sequence({
329
- *[Symbol.iterator]() {
330
- let taken = 0;
331
- for (const value of src) {
332
- yield value;
333
- if (++taken >= count) return;
334
- }
335
- },
336
- });
337
- }
338
-
339
- /**
340
- * Splits the concatenated sources into one array per named predicate. Consumes
341
- * the input once, routing each element to the FIRST predicate it satisfies (so
342
- * groups are disjoint); elements matching no predicate are dropped. Type-guard
343
- * predicates narrow their group's element type (see {@link GroupValue}).
344
- * `predicates` leads so the sources can stay variadic.
345
- *
346
- * @typeParam G - The map of group name -> predicate.
347
- * @param predicates - Named predicates; evaluated in declaration order.
348
- * @param sources - Containers to concatenate (nullish sources are skipped).
349
- * @returns An object with the same keys, each an array of its group's elements.
350
- *
351
- * @example
352
- * const { strings, fns } = group({ strings: isString, fns: isFunction }, xs);
353
- */
354
- export function group<T, G extends GroupPredicates<T>>(
355
- predicates: G,
356
- ...sources: readonly Source<T>[]
357
- ): { [K in keyof G]: GroupValue<T, G[K]>[] } {
358
- const keys = Object.keys(predicates) as (keyof G)[];
359
- const buckets = new Map<keyof G, T[]>(keys.map((key) => [key, []]));
360
- let index = 0;
361
- for (const value of sequence(...sources)) {
362
- const i = index++;
363
- for (const key of keys) {
364
- if (predicates[key]!(value, i)) {
365
- buckets.get(key)!.push(value);
366
- break;
367
- }
368
- }
369
- }
370
- const result = {} as { [K in keyof G]: GroupValue<T, G[K]>[] };
371
- for (const key of keys) {
372
- result[key] = buckets.get(key)! as GroupValue<T, G[typeof key]>[];
373
- }
374
- return result;
375
- }
376
-
377
- /**
378
- * Same semantics as `Array.prototype.find`, over a single {@link Container}.
379
- * Consumes elements until a match. A type guard narrows the return type.
380
- */
381
- export function find<T, S extends T>(
382
- source: Source<T>,
383
- predicate: (value: T, index: number) => value is S,
384
- ): S | undefined;
385
- export function find<T>(
386
- source: Source<T>,
387
- predicate: (value: T, index: number) => boolean,
388
- ): T | undefined;
389
- export function find<T>(
390
- source: Source<T>,
391
- predicate: (value: T, index: number) => boolean,
392
- ): T | undefined {
393
- let index = 0;
394
- for (const value of sequence(source)) {
395
- if (predicate(value, index++)) return value;
396
- }
397
- return undefined;
398
- }
399
-
400
- /**
401
- * Same semantics as `Array.prototype.findLast`, over a single {@link Container}.
402
- * Consumes the full source. A type guard narrows the return type.
403
- */
404
- export function findLast<T, S extends T>(
405
- source: Source<T>,
406
- predicate: (value: T, index: number) => value is S,
407
- ): S | undefined;
408
- export function findLast<T>(
409
- source: Source<T>,
410
- predicate: (value: T, index: number) => boolean,
411
- ): T | undefined;
412
- export function findLast<T>(
413
- source: Source<T>,
414
- predicate: (value: T, index: number) => boolean,
415
- ): T | undefined {
416
- let index = 0;
417
- let match: T | undefined;
418
- for (const value of sequence(source)) {
419
- if (predicate(value, index++)) match = value;
420
- }
421
- return match;
422
- }
423
-
424
- /**
425
- * Same semantics as `Array.prototype.findIndex`, over a single {@link Container}.
426
- * Consumes elements until a match.
427
- */
428
- export function findIndex<T>(
429
- source: Source<T>,
430
- predicate: (value: T, index: number) => boolean,
431
- ): number {
432
- let index = 0;
433
- for (const value of sequence(source)) {
434
- if (predicate(value, index)) return index;
435
- index++;
436
- }
437
- return -1;
438
- }
439
-
440
- /**
441
- * Same semantics as `Array.prototype.findLastIndex`, over a single
442
- * {@link Container}. Consumes the full source.
443
- */
444
- export function findLastIndex<T>(
445
- source: Source<T>,
446
- predicate: (value: T, index: number) => boolean,
447
- ): number {
448
- let index = 0;
449
- let match = -1;
450
- for (const value of sequence(source)) {
451
- if (predicate(value, index)) match = index;
452
- index++;
453
- }
454
- return match;
455
- }
456
-
457
- /**
458
- * Same semantics as `Array.prototype.some`, over a single {@link Container}.
459
- * Short-circuits on the first match.
460
- */
461
- export function some<T>(
462
- source: Source<T>,
463
- predicate: (value: T, index: number) => boolean,
464
- ): boolean {
465
- let index = 0;
466
- for (const value of sequence(source)) if (predicate(value, index++)) return true;
467
- return false;
468
- }
469
-
470
- /**
471
- * Same semantics as `Array.prototype.every`, over a single {@link Container}.
472
- * Short-circuits on the first failure.
473
- */
474
- export function every<T, S extends T>(
475
- source: Source<T>,
476
- predicate: (value: T, index: number) => value is S,
477
- ): boolean;
478
- export function every<T>(
479
- source: Source<T>,
480
- predicate: (value: T, index: number) => boolean,
481
- ): boolean;
482
- export function every<T>(
483
- source: Source<T>,
484
- predicate: (value: T, index: number) => boolean,
485
- ): boolean {
486
- let index = 0;
487
- for (const value of sequence(source)) if (!predicate(value, index++)) return false;
488
- return true;
489
- }
490
-
491
- /**
492
- * Same semantics as `Array.prototype.forEach`, over a single {@link Container}.
493
- * Consumes the source.
494
- */
495
- export function forEach<T>(source: Source<T>, callback: (value: T, index: number) => void): void {
496
- let index = 0;
497
- for (const value of sequence(source)) callback(value, index++);
498
- }
499
-
500
- /**
501
- * Same semantics as `Array.prototype.at` over the concatenated sources.
502
- * Non-negative indices scan lazily; negative indices materialize first. `index`
503
- * leads so the sources can stay variadic.
504
- *
505
- * @param index - Zero-based position; negative counts from the end.
506
- * @param sources - Containers to concatenate (nullish sources are skipped).
507
- */
508
- export function at<T>(index: number, ...sources: readonly Source<T>[]): T | undefined {
509
- const src = sequence(...sources);
510
- if (index < 0) return toArray(src).at(index);
511
- let i = 0;
512
- for (const value of src) {
513
- if (i++ === index) return value;
514
- }
515
- return undefined;
516
- }
517
-
518
- /**
519
- * Materializes the concatenated sources into a new array. Consumes single-pass
520
- * sources.
521
- *
522
- * @param sources - Containers to concatenate (nullish sources are skipped).
523
- */
524
- export function toArray<T>(...sources: readonly Source<T>[]): readonly T[] {
525
- return [...sequence(...sources)];
526
- }
527
-
528
- /**
529
- * Lazy iterable sequence with `Array`-compatible transforms and terminal
530
- * methods. Single-pass by default; call {@link SequenceImpl.cache} to retain
531
- * pulled values for re-iteration. Built for generators and other sources where
532
- * a second pass is not guaranteed.
533
- *
534
- * The methods forward to the standalone functions of the same name - see each
535
- * method's `@see` - so the free-function and chained styles share one impl.
536
- */
537
- class SequenceImpl<T> {
538
- private iterator?: Iterator<T>;
539
- private exhausted: boolean;
540
-
541
- constructor(
542
- private readonly source: Iterable<T>,
543
- private readonly buffer: T[] | undefined,
544
- state: { readonly exhausted?: boolean } = {},
545
- ) {
546
- this.exhausted = state.exhausted ?? false;
547
- }
548
-
549
- private get caching(): boolean {
550
- return this.buffer !== undefined;
551
- }
552
-
553
- /** Advance the underlying source once, creating the iterator on first use. */
554
- private pull(): IteratorResult<T> {
555
- this.iterator ??= this.source[Symbol.iterator]();
556
- return this.iterator.next();
557
- }
558
-
559
- *[Symbol.iterator](): Iterator<T> {
560
- if (!this.caching) {
561
- if (this.exhausted) return;
562
- for (let next = this.pull(); !next.done; next = this.pull()) {
563
- yield next.value;
564
- }
565
- this.exhausted = true;
566
- } else {
567
- // Cached: iterate like a list. Always replay the buffer from the start,
568
- // extending it from the source on demand until exhausted. A broken loop
569
- // leaves the buffer intact, so the next iteration starts over from the
570
- // beginning.
571
- const buffer = this.buffer!;
572
- let index = 0;
573
- for (; ;) {
574
- if (index < buffer.length) {
575
- yield buffer[index++]!;
576
- continue;
577
- }
578
- if (this.exhausted) return;
579
- const next = this.pull();
580
- if (next.done) {
581
- this.exhausted = true;
582
- return;
583
- }
584
- buffer.push(next.value);
585
- yield next.value;
586
- index++;
587
- }
588
- }
589
- }
590
-
591
- /** @see {@link map} */
592
- map<U>(callback: (value: T, index: number) => U): Sequence<U> {
593
- return map(this, callback);
594
- }
595
-
596
- /** @see {@link filter} */
597
- filter<S extends T>(predicate: (value: T, index: number) => value is S): Sequence<S>;
598
- filter(predicate: (value: T, index: number) => boolean): Sequence<T>;
599
- filter(predicate: (value: T, index: number) => boolean): Sequence<T> {
600
- return filter(this, predicate);
601
- }
602
-
603
- /** @see {@link nonNull} */
604
- nonNull(): Sequence<NonNullable<T>> {
605
- return nonNull(this);
606
- }
607
-
608
- /** @see {@link flatMap} */
609
- flatMap<U>(callback: (value: T, index: number) => U | ReadonlyArray<U>): Sequence<U> {
610
- return flatMap(this, callback);
611
- }
612
-
613
- /** @see {@link flat} */
614
- flat(depth = 1): Sequence<T> {
615
- return flat(depth, this);
616
- }
617
-
618
- /** @see {@link distinct} */
619
- distinct(): Sequence<T> {
620
- return distinct(this);
621
- }
622
-
623
- /** @see {@link concat} */
624
- concat(...items: readonly (T | ReadonlyArray<T>)[]): Sequence<T> {
625
- return concat(this, ...items);
626
- }
627
-
628
- /**
629
- * Lazily yields this sequence followed by each iterable `source` in order.
630
- * Like {@link concat}, but for iterable sources (generators, other sequences,
631
- * `Set`, `Map`, etc.) rather than scalar values or arrays. A {@link Map}
632
- * source contributes its values.
633
- *
634
- * @see {@link sequence}
635
- */
636
- join(
637
- ...sources: readonly SequenceSource<T>[]
638
- ): Sequence<T> {
639
- const sourceIterables = sequenceSources(this, ...sources);
640
- return sequenceSources.length === 0 ? this : sequence(...sourceIterables);
641
- }
642
-
643
- /** @see {@link take} */
644
- take(count: number): Sequence<T> {
645
- return take(count, this);
646
- }
647
-
648
- /** @see {@link group} */
649
- group<G extends GroupPredicates<T>>(predicates: G): { [K in keyof G]: GroupValue<T, G[K]>[] } {
650
- return group(predicates, this);
651
- }
652
-
653
- /**
654
- * Returns a cached, re-iterable view of this sequence. An already-caching
655
- * sequence returns itself; otherwise this one-pass sequence is wrapped in a
656
- * new instance that retains pulled values. Iterate the returned instance, not
657
- * the original, to avoid competing for the same single-pass source.
658
- */
659
- cache(): Sequence<T> {
660
- return this.caching ? this : new SequenceImpl(this, []);
661
- }
662
-
663
- /** @see {@link find} */
664
- find<S extends T>(predicate: (value: T, index: number) => value is S): S | undefined;
665
- find(predicate: (value: T, index: number) => boolean): T | undefined;
666
- find(predicate: (value: T, index: number) => boolean): T | undefined {
667
- return find(this, predicate);
668
- }
669
-
670
- /** @see {@link findLast} */
671
- findLast<S extends T>(predicate: (value: T, index: number) => value is S): S | undefined;
672
- findLast(predicate: (value: T, index: number) => boolean): T | undefined;
673
- findLast(predicate: (value: T, index: number) => boolean): T | undefined {
674
- return findLast(this, predicate);
675
- }
676
-
677
- /** @see {@link findIndex} */
678
- findIndex(predicate: (value: T, index: number) => boolean): number {
679
- return findIndex(this, predicate);
680
- }
681
-
682
- /** @see {@link findLastIndex} */
683
- findLastIndex(predicate: (value: T, index: number) => boolean): number {
684
- return findLastIndex(this, predicate);
685
- }
686
-
687
- /** @see {@link some} */
688
- some(predicate: (value: T, index: number) => boolean): boolean {
689
- return some(this, predicate);
690
- }
691
-
692
- /** @see {@link every} (the `S extends T` overload narrows at compile time only). */
693
- every<S extends T>(predicate: (value: T, index: number) => value is S): this is Sequence<S>;
694
- every(predicate: (value: T, index: number) => boolean): boolean;
695
- every(predicate: (value: T, index: number) => boolean): boolean {
696
- return every(this, predicate);
697
- }
698
-
699
- /** @see {@link forEach} */
700
- forEach(callback: (value: T, index: number) => void): void {
701
- forEach(this, callback);
702
- }
703
-
704
- /** @see {@link at} */
705
- at(index: number): T | undefined {
706
- return at(index, this);
707
- }
708
-
709
- /**
710
- * Materialize the sequence into a new array. Consumes a single-pass source; a
711
- * cached, exhausted sequence copies its buffer directly.
712
- *
713
- * @see {@link toArray}
714
- */
715
- toArray(): readonly T[] {
716
- if (this.caching && this.exhausted) return [...this.buffer!];
717
- return toArray(this);
718
- }
719
- }
720
-
721
- /** Shared empty sequence singleton, reusable for any element type. */
722
- const emptySequence: Sequence<never> = new SequenceImpl([], undefined, {
723
- exhausted: true,
724
- });
725
-
726
- /**
727
- * Wrap one or more iterable `sources` in a single lazy {@link Sequence},
728
- * iterated in order. `null`/`undefined` sources are skipped; when nothing
729
- * remains (every source omitted, `null`, `undefined`, or an empty array /
730
- * `Set` / `Map`), {@link emptySequence} is returned. The result is single-pass
731
- * - call `.cache()` to make it re-iterable.
732
- *
733
- * A {@link Map} source contributes its values (see {@link values}), consistent
734
- * with {@link Collection}'s value-typed `T`.
735
- *
736
- * @typeParam T - Element type of the sequence.
737
- * @param sources - Iterables to concatenate, in order (`Map` sources use values).
738
- */
739
- export function sequence<T>(
740
- ...sources: readonly SequenceSource<T>[]
741
- ): Sequence<T> {
742
- // Skip nullish sources and known-empty collections; normalize the rest to
743
- // their values so a Map contributes values rather than [key, value] entries.
744
- const sourceIterables = sequenceSources(...sources);
745
- if (sourceIterables.length === 0) return emptySequence as Sequence<T>;
746
- // Reuse an existing sequence as-is rather than re-wrapping it.
747
- if (sourceIterables.length === 1) {
748
- const only = sourceIterables[0]!;
749
- return only instanceof SequenceImpl ? (only as Sequence<T>) : new SequenceImpl(only, undefined);
750
- }
751
- return new SequenceImpl(
752
- {
753
- *[Symbol.iterator]() {
754
- for (const source of sourceIterables) yield* source;
755
- },
756
- },
757
- undefined,
758
- );
759
- }
760
-
761
-
762
-
763
- /**
764
- * Flattens a mix of single items and iterables into one lazy {@link Generator}.
765
- *
766
- * Arguments are emitted in order: `null`/`undefined` are skipped, non-string
767
- * iterables (per {@link isContainer}) are yielded element-by-element, and
768
- * anything else (including strings) is yielded as a single item.
769
- *
770
- * @typeParam T - Element type produced by the generator.
771
- * @param items - Items and/or iterables to flatten, in order.
772
- * @returns A generator over the flattened elements.
773
- */
774
- export function* generator<T>(
775
- ...items: readonly (T | Iterable<T> | null | undefined)[]
776
- ): Generator<T> {
777
- for (const item of items) {
778
- if (item === null || item === undefined) {
779
- continue;
780
- } else if (isContainer(item)) {
781
- yield* item;
782
- } else {
783
- yield item;
784
- }
785
- }
786
- }
787
-
788
-
789
-
790
-