tyneq 1.0.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.
Files changed (45) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +319 -0
  3. package/dist/Lazy.cjs +762 -0
  4. package/dist/Lazy.cjs.map +1 -0
  5. package/dist/Lazy.js +691 -0
  6. package/dist/Lazy.js.map +1 -0
  7. package/dist/TyneqCachedTerminalOperator.cjs +4950 -0
  8. package/dist/TyneqCachedTerminalOperator.cjs.map +1 -0
  9. package/dist/TyneqCachedTerminalOperator.d.cts +724 -0
  10. package/dist/TyneqCachedTerminalOperator.d.cts.map +1 -0
  11. package/dist/TyneqCachedTerminalOperator.d.ts +724 -0
  12. package/dist/TyneqCachedTerminalOperator.d.ts.map +1 -0
  13. package/dist/TyneqCachedTerminalOperator.js +4741 -0
  14. package/dist/TyneqCachedTerminalOperator.js.map +1 -0
  15. package/dist/ValidationBuilder.cjs +80 -0
  16. package/dist/ValidationBuilder.cjs.map +1 -0
  17. package/dist/ValidationBuilder.d.cts +319 -0
  18. package/dist/ValidationBuilder.d.cts.map +1 -0
  19. package/dist/ValidationBuilder.d.ts +319 -0
  20. package/dist/ValidationBuilder.d.ts.map +1 -0
  21. package/dist/ValidationBuilder.js +69 -0
  22. package/dist/ValidationBuilder.js.map +1 -0
  23. package/dist/core.d.cts +1393 -0
  24. package/dist/core.d.cts.map +1 -0
  25. package/dist/core.d.ts +1393 -0
  26. package/dist/core.d.ts.map +1 -0
  27. package/dist/index.cjs +863 -0
  28. package/dist/index.cjs.map +1 -0
  29. package/dist/index.d.cts +1038 -0
  30. package/dist/index.d.cts.map +1 -0
  31. package/dist/index.d.ts +1038 -0
  32. package/dist/index.d.ts.map +1 -0
  33. package/dist/index.js +809 -0
  34. package/dist/index.js.map +1 -0
  35. package/dist/plugin/index.cjs +24 -0
  36. package/dist/plugin/index.d.cts +89 -0
  37. package/dist/plugin/index.d.cts.map +1 -0
  38. package/dist/plugin/index.d.ts +89 -0
  39. package/dist/plugin/index.d.ts.map +1 -0
  40. package/dist/plugin/index.js +2 -0
  41. package/dist/utility/index.cjs +9 -0
  42. package/dist/utility/index.d.cts +2 -0
  43. package/dist/utility/index.d.ts +2 -0
  44. package/dist/utility/index.js +3 -0
  45. package/package.json +96 -0
@@ -0,0 +1,4741 @@
1
+ import { a as ArgumentUtility, c as ArgumentOutOfRangeError, d as TyneqError, f as nameof, r as reflect, t as Lazy } from "./Lazy.js";
2
+ //#region src/core/enumerators/TyneqBaseEnumerator.ts
3
+ /**
4
+ * Abstract base class implementing the pull-iterator lifecycle for all Tyneq enumerators.
5
+ *
6
+ * @remarks
7
+ * State machine:
8
+ * - `next()` calls `initialize()` on the first invocation, then delegates to `handleNext()`.
9
+ * - When `handleNext()` returns `{ done: true }`, `dispose()` is called then the enumerator marks itself completed.
10
+ * - `doneWithYield(value)` emits one final element, calls `dispose()`, then marks completed.
11
+ * - `earlyComplete()` calls `dispose()` and marks completed without yielding.
12
+ * - `return()` triggers early termination: calls `dispose()` then marks completed. Idempotent.
13
+ * - Once completed, all `next()` calls return `{ done: true }` without re-invoking `handleNext()`.
14
+ *
15
+ * Subclasses must implement `handleNext()`. Override `initialize()`, `disposeSource()`, and
16
+ * `disposeAdditional()` as needed.
17
+ *
18
+ * @typeParam TInput - Source element type.
19
+ * @typeParam TOutput - Output element type (defaults to `TInput`).
20
+ * @group Plugin
21
+ */
22
+ var TyneqBaseEnumerator = class {
23
+ _initialized = false;
24
+ _completed = false;
25
+ constructor() {}
26
+ /** Advances the iterator, calling `initialize()` on first call. Idempotent after completion. */
27
+ next() {
28
+ if (this._completed) return this.done();
29
+ if (!this._initialized) {
30
+ this.initialize();
31
+ this._initialized = true;
32
+ }
33
+ const result = this.handleNext();
34
+ if (result.done) {
35
+ if (!this._completed) {
36
+ this.dispose();
37
+ this._completed = true;
38
+ }
39
+ return this.done();
40
+ }
41
+ return result;
42
+ }
43
+ /** Terminates iteration early, disposes resources, and marks completed. Idempotent. */
44
+ return(value) {
45
+ if (!this._completed) {
46
+ this.dispose(value);
47
+ this._completed = true;
48
+ }
49
+ return this.done();
50
+ }
51
+ /** Called once before the first `handleNext()` invocation. Override to set up state. */
52
+ initialize() {}
53
+ /** Wraps `value` in a non-done `IteratorResult`. */
54
+ yield(value) {
55
+ return {
56
+ done: false,
57
+ value
58
+ };
59
+ }
60
+ /** Returns a done `IteratorResult`. */
61
+ done() {
62
+ return {
63
+ done: true,
64
+ value: void 0
65
+ };
66
+ }
67
+ /**
68
+ * Marks the enumerator completed and yields `value` as the final element.
69
+ *
70
+ * @remarks
71
+ * Use when the last element must be emitted together with completion in one step.
72
+ */
73
+ doneWithYield(value) {
74
+ this.dispose();
75
+ this._completed = true;
76
+ return this.yield(value);
77
+ }
78
+ /**
79
+ * Disposes resources, marks completed, and returns a done result.
80
+ *
81
+ * @remarks
82
+ * Use inside `handleNext()` to terminate iteration before the source is exhausted.
83
+ */
84
+ earlyComplete(reason) {
85
+ this.dispose(reason);
86
+ this._completed = true;
87
+ return this.done();
88
+ }
89
+ /** Calls `disposeSource()` then `disposeAdditional()`. */
90
+ dispose(value) {
91
+ this.disposeSource();
92
+ this.disposeAdditional(value);
93
+ }
94
+ /** Override to dispose the upstream source enumerator. */
95
+ disposeSource() {}
96
+ /**
97
+ * Override to release any additional resources.
98
+ *
99
+ * @remarks
100
+ * Called after `disposeSource()`. `value` is the return value passed to `return()`.
101
+ * Must be idempotent and must not throw.
102
+ */
103
+ disposeAdditional(_value) {}
104
+ };
105
+ //#endregion
106
+ //#region src/core/TyneqComparer.ts
107
+ /**
108
+ * Built-in comparers and equality comparers used by ordering and equality operators.
109
+ *
110
+ * @remarks
111
+ * All members are static. This class cannot be instantiated.
112
+ *
113
+ * @group Utilities
114
+ */
115
+ var TyneqComparer = class {
116
+ constructor() {}
117
+ /**
118
+ * Natural-order comparer using `<` and `>`.
119
+ *
120
+ * @remarks
121
+ * Works correctly for numbers, strings, dates, and any type that supports the relational operators.
122
+ *
123
+ * @returns Negative if `a < b`, positive if `a > b`, `0` if equal.
124
+ */
125
+ static defaultComparer(a, b) {
126
+ return a > b ? 1 : a < b ? -1 : 0;
127
+ }
128
+ /** Strict equality comparer using `===`. */
129
+ static defaultEqualityComparer(a, b) {
130
+ return a === b;
131
+ }
132
+ /**
133
+ * Returns a comparer that reverses the order of `comparer`.
134
+ *
135
+ * @remarks
136
+ * Use this to invert any custom comparer - for example, to sort by a locale-aware comparer
137
+ * in descending order without rewriting it.
138
+ *
139
+ * @example
140
+ * ```ts
141
+ * const desc = TyneqComparer.reverse(TyneqComparer.createLocaleComparer("en"));
142
+ * seq.orderBy((s) => s, desc);
143
+ * ```
144
+ */
145
+ static reverse(comparer) {
146
+ return (a, b) => comparer(b, a);
147
+ }
148
+ /**
149
+ * Returns a locale-aware string comparer backed by `Intl.Collator`.
150
+ *
151
+ * @remarks
152
+ * Pass a `locale` and optional `options` for deterministic cross-environment ordering.
153
+ * Without arguments the comparer uses the runtime locale, which may vary across environments.
154
+ *
155
+ * @example
156
+ * ```ts
157
+ * seq.orderBy((s) => s, TyneqComparer.createLocaleComparer("en"));
158
+ * ```
159
+ *
160
+ * @param locale - BCP 47 language tag(s) passed to `Intl.Collator`.
161
+ * @param options - `Intl.CollatorOptions` passed to `Intl.Collator`.
162
+ *
163
+ * @see {@link caseInsensitiveComparer} for a locale-independent case-insensitive ordering comparer.
164
+ */
165
+ static createLocaleComparer(locale, options) {
166
+ const collator = new Intl.Collator(locale, options);
167
+ return (a, b) => collator.compare(a, b);
168
+ }
169
+ /**
170
+ * Case-insensitive string equality comparer.
171
+ *
172
+ * @remarks
173
+ * Converts both values to lower-case with `toLowerCase()` before comparing with `===`.
174
+ * Locale-independent: results are consistent across environments.
175
+ *
176
+ * Use {@link createLocaleComparer} with `{ sensitivity: "base" }` for locale-aware
177
+ * case-insensitive equality.
178
+ *
179
+ * @see {@link caseInsensitiveComparer} for the ordering (negative/zero/positive) counterpart.
180
+ */
181
+ static caseInsensitiveEqualityComparer(a, b) {
182
+ return a.toLowerCase() === b.toLowerCase();
183
+ }
184
+ /**
185
+ * Case-insensitive ordering comparer.
186
+ *
187
+ * @remarks
188
+ * Converts both values to lower-case with `toLowerCase()` and compares with `<` / `>`.
189
+ * Locale-independent: results are consistent across environments and match
190
+ * {@link caseInsensitiveEqualityComparer} - strings that compare equal here return `true`
191
+ * there, and vice versa.
192
+ *
193
+ * Use {@link createLocaleComparer} when you need locale-aware case-insensitive ordering.
194
+ *
195
+ * @example
196
+ * ```ts
197
+ * seq.orderBy((s) => s, TyneqComparer.caseInsensitiveComparer)
198
+ * ```
199
+ *
200
+ * @see {@link caseInsensitiveEqualityComparer} for the boolean equality counterpart.
201
+ * @see {@link createLocaleComparer} for locale-aware ordering.
202
+ */
203
+ static caseInsensitiveComparer(a, b) {
204
+ const la = a.toLowerCase();
205
+ const lb = b.toLowerCase();
206
+ return la > lb ? 1 : la < lb ? -1 : 0;
207
+ }
208
+ };
209
+ //#endregion
210
+ //#region src/types/queryplan.ts
211
+ /**
212
+ * Symbol key used to access the {@link QueryPlanNode} on a `TyneqSequence`.
213
+ *
214
+ * @example
215
+ * ```ts
216
+ * import { tyneqQueryNode } from "tyneq";
217
+ * const node = seq[tyneqQueryNode]; // QueryPlanNode | null
218
+ * ```
219
+ *
220
+ * @group QueryPlan
221
+ */
222
+ const tyneqQueryNode = Symbol("tyneq.queryNode");
223
+ /**
224
+ * Narrows a `QueryPlanNode` to one that is guaranteed to have a `sourceKind`.
225
+ *
226
+ * @remarks
227
+ * `sourceKind` is only present on source nodes (`category === "source"`). Reading it on any
228
+ * other node returns `undefined`. Use this guard before accessing `node.sourceKind` to get
229
+ * proper type narrowing and avoid ambiguous `undefined`.
230
+ *
231
+ * @example
232
+ * ```ts
233
+ * if (isSourceNode(node)) {
234
+ * console.log(node.sourceKind); // "array" | "set" | "map" | "string" | "other"
235
+ * }
236
+ * ```
237
+ *
238
+ * @group QueryPlan
239
+ */
240
+ function isSourceNode(node) {
241
+ return node.category === "source";
242
+ }
243
+ //#endregion
244
+ //#region src/queryplan/QueryNode.ts
245
+ /**
246
+ * Concrete `QueryPlanNode` implementation.
247
+ * One node is created per operator call and linked to its upstream source node,
248
+ * forming a singly-linked list that represents the full query plan.
249
+ *
250
+ * @group QueryPlan
251
+ * @internal
252
+ */
253
+ var QueryNode = class {
254
+ constructor(operatorName, args, source, category, sourceKind) {
255
+ this.operatorName = operatorName;
256
+ this.args = args;
257
+ this.source = source;
258
+ this.category = category;
259
+ this.sourceKind = sourceKind;
260
+ }
261
+ accept(visitor) {
262
+ return visitor.visit(this);
263
+ }
264
+ };
265
+ //#endregion
266
+ //#region src/plugin/decorators/builtin.ts
267
+ const builtinMeta = Symbol("tyneq.builtinMetadata");
268
+ /**
269
+ * Method decorator - declares this method as a built-in operator.
270
+ *
271
+ * Stores `BuiltinOptions` on the function object so `@sequence` can find it.
272
+ * Does NOT register anything by itself - registration happens in `@sequence`.
273
+ *
274
+ * @remarks
275
+ * This is one half of a two-decorator pattern. Apply `@builtin` to each method on a class
276
+ * decorated with `@sequence`. When the module is evaluated, `@sequence` will scan all
277
+ * `@builtin`-tagged methods and call `OperatorRegistry.registerBuiltin` for each.
278
+ *
279
+ * Only use on methods of `TyneqEnumerableCore` or `TyneqEnumerableBase`. Third-party
280
+ * operators should use `@operator`, `@terminal`, or the `createOperator` factory instead.
281
+ *
282
+ * @internal
283
+ */
284
+ function builtin(options) {
285
+ return function(value, context) {
286
+ value[builtinMeta] = {
287
+ name: context.name,
288
+ kind: options.kind
289
+ };
290
+ return value;
291
+ };
292
+ }
293
+ //#endregion
294
+ //#region src/core/errors/PluginError.ts
295
+ /**
296
+ * Thrown when a plugin, decorator (`@operator`, `@terminal`), or factory
297
+ * (`createOperator`, `createTerminalOperator`) is used incorrectly.
298
+ *
299
+ * @example
300
+ * ```ts
301
+ * // @operator('myOp') applied to a class that doesn't extend TyneqEnumerator
302
+ * // throws PluginError with decoratorName = "operator" and targetName = "MyOp"
303
+ * ```
304
+ *
305
+ * @see {@link TyneqError}
306
+ * @group Errors
307
+ */
308
+ var PluginError = class extends TyneqError {
309
+ /**
310
+ * The name of the decorator or factory that produced the error.
311
+ * e.g. `"operator"`, `"terminal"`, `"createOperator"`.
312
+ */
313
+ decoratorName;
314
+ /**
315
+ * The name of the class or function the decorator was applied to, if available.
316
+ */
317
+ targetName;
318
+ constructor(message, decoratorName, targetName, inner) {
319
+ super(message, { inner });
320
+ this.decoratorName = decoratorName;
321
+ this.targetName = targetName;
322
+ }
323
+ };
324
+ //#endregion
325
+ //#region src/core/OperatorMetadata.ts
326
+ /**
327
+ * Metadata describing a registered operator.
328
+ *
329
+ * @group Classes
330
+ */
331
+ var OperatorMetadata = class OperatorMetadata {
332
+ name;
333
+ kind;
334
+ source;
335
+ targetClass;
336
+ extensions;
337
+ constructor(name, kind, source = "external", targetClass, extensions = {}) {
338
+ this.name = name;
339
+ this.kind = kind;
340
+ this.source = source;
341
+ this.targetClass = arguments.length < 4 ? TyneqEnumerableBase : targetClass;
342
+ this.extensions = extensions;
343
+ }
344
+ /**
345
+ * Creates metadata for a source operator.
346
+ *
347
+ * @remarks
348
+ * `targetClass` is always `undefined` for source operators - they are static
349
+ * factories with no prototype and are never patched onto a class instance.
350
+ */
351
+ static source(name, src = "external") {
352
+ return new OperatorMetadata(name, "source", src, void 0);
353
+ }
354
+ /** Creates metadata for a streaming operator. Defaults targetClass to TyneqEnumerableBase. */
355
+ static streaming(name, targetClass = TyneqEnumerableBase, source, extensions) {
356
+ return new OperatorMetadata(name, "streaming", source ?? "external", targetClass, extensions);
357
+ }
358
+ /** Creates metadata for a buffering operator. Defaults targetClass to TyneqEnumerableBase. */
359
+ static buffer(name, targetClass = TyneqEnumerableBase, source, extensions) {
360
+ return new OperatorMetadata(name, "buffer", source ?? "external", targetClass, extensions);
361
+ }
362
+ /** Creates metadata for a terminal operator. Defaults targetClass to TyneqEnumerableBase. */
363
+ static terminal(name, targetClass = TyneqEnumerableBase, source, extensions) {
364
+ return new OperatorMetadata(name, "terminal", source ?? "external", targetClass, extensions);
365
+ }
366
+ /**
367
+ * Creates metadata for a streaming or buffer operator determined at runtime.
368
+ *
369
+ * @remarks
370
+ * Use when the category is a variable rather than a compile-time literal --
371
+ * for example in `@operator` and `@orderedOperator` whose `category` parameter
372
+ * is provided by the caller. For compile-time-known categories prefer the
373
+ * dedicated {@link streaming} / {@link buffer} / {@link terminal} statics.
374
+ *
375
+ * Only `"streaming"` and `"buffer"` are valid; passing `"terminal"` or `"source"` throws.
376
+ */
377
+ static forCategory(category, name, targetClass = TyneqEnumerableBase, source, extensions) {
378
+ if (category === "streaming") return OperatorMetadata.streaming(name, targetClass, source, extensions);
379
+ if (category === "buffer") return OperatorMetadata.buffer(name, targetClass, source, extensions);
380
+ throw new PluginError(`OperatorMetadata.forCategory: unsupported category "${category}". Use .terminal() or .source() directly.`, "forCategory", name);
381
+ }
382
+ };
383
+ //#endregion
384
+ //#region src/core/errors/RegistryError.ts
385
+ /**
386
+ * Thrown when the operator registry encounters a conflict or invalid state
387
+ * during operator registration or invocation.
388
+ *
389
+ * @example
390
+ * ```ts
391
+ * try { OperatorRegistry.register(entry); }
392
+ * catch (e) {
393
+ * if (e instanceof RegistryError) {
394
+ * console.log(e.operatorName, e.conflictingKind, e.message);
395
+ * }
396
+ * }
397
+ * ```
398
+ *
399
+ * @see {@link TyneqError}
400
+ * @group Errors
401
+ */
402
+ var RegistryError = class extends TyneqError {
403
+ /** The name of the operator involved in the error. */
404
+ operatorName;
405
+ /**
406
+ * The kind of the operator that was being registered or invoked.
407
+ * `undefined` if the kind is not applicable to this error.
408
+ */
409
+ kind;
410
+ /**
411
+ * The kind already present in the registry for this name, when there is a conflict.
412
+ * `undefined` when the error is not a duplicate-registration conflict.
413
+ */
414
+ conflictingKind;
415
+ /**
416
+ * The source that originally registered the conflicting operator.
417
+ * `undefined` when the error is not a duplicate-registration conflict.
418
+ */
419
+ conflictingSource;
420
+ constructor(message, operatorName, kind, conflict, inner) {
421
+ super(message, { inner });
422
+ this.operatorName = operatorName;
423
+ this.kind = kind;
424
+ this.conflictingKind = conflict?.kind;
425
+ this.conflictingSource = conflict?.source;
426
+ }
427
+ };
428
+ //#endregion
429
+ //#region src/core/registry/TyneqOperatorRegistry.ts
430
+ /**
431
+ * Central registry for all Tyneq operators.
432
+ *
433
+ * Every registration path - `@operator`, `@terminal`, `createOperator`,
434
+ * `createGeneratorOperator`, `createTerminalOperator` - flows through this class.
435
+ * It is the single source of truth for which operators exist, their kind, and their
436
+ * prototype-level implementation.
437
+ *
438
+ * Internally the registry maintains two separate namespaces:
439
+ * - Source factories (`Tyneq.from`, `Tyneq.range`, etc.) - keyed by name alone.
440
+ * - Instance operators (`.where`, `.select`, etc.) - keyed by (name, targetClass).
441
+ *
442
+ * This means a source factory and an instance method can share a name without
443
+ * conflict (e.g. `Tyneq.concat` and `seq.concat`), and two instance operators
444
+ * with the same name on different prototype chains (e.g. `ordered.foo` and
445
+ * `cached.foo`) also coexist without conflict.
446
+ *
447
+ * @example
448
+ * ```ts
449
+ * import { OperatorRegistry } from "tyneq/plugin";
450
+ *
451
+ * OperatorRegistry.has("where"); // -> true
452
+ * OperatorRegistry.list(); // -> OperatorMetadata[]
453
+ * ```
454
+ *
455
+ * @group Classes
456
+ */
457
+ var OperatorRegistry = class {
458
+ static _sources = /* @__PURE__ */ new Map();
459
+ static _operators = /* @__PURE__ */ new Map();
460
+ static _registrationHooks = [];
461
+ static _registrationGuards = [];
462
+ /**
463
+ * Registers an operator entry and patches the method onto `entry.metadata.targetClass.prototype`.
464
+ *
465
+ * @throws {RegistryError} When an operator with the same name is already registered on the same targetClass.
466
+ */
467
+ static register(input) {
468
+ const { name } = input.metadata;
469
+ if (input.metadata.targetClass === void 0) throw new RegistryError(`Cannot register "${name}": targetClass is required for prototype-patching operators. Use registerSource() for source operators.`, name, input.metadata.kind);
470
+ const targetMap = this._operators.get(name);
471
+ if (targetMap?.has(input.metadata.targetClass)) {
472
+ const existing = targetMap.get(input.metadata.targetClass).metadata;
473
+ throw new RegistryError(`Cannot register "${name}" (${input.metadata.kind}) on ${input.metadata.targetClass.name}: already registered as "${existing.kind}" from source "${existing.source}".`, name, input.metadata.kind, {
474
+ kind: existing.kind,
475
+ source: existing.source
476
+ });
477
+ }
478
+ for (const guard of this._registrationGuards) guard(input);
479
+ if (!this._operators.has(name)) this._operators.set(name, /* @__PURE__ */ new Map());
480
+ this._operators.get(name).set(input.metadata.targetClass, input);
481
+ input.metadata.targetClass.prototype[name] = input.impl;
482
+ for (const hook of this._registrationHooks) hook(input);
483
+ }
484
+ /**
485
+ * Removes an operator registration and deletes the prototype method for external operators.
486
+ *
487
+ * Checks the instance operator namespace first, then the source namespace.
488
+ *
489
+ * @returns `true` if the operator was found and removed; `false` if no operator with that name existed.
490
+ * @remarks
491
+ * Internal operators (source `"internal"`) are not removed from the prototype - only
492
+ * their registry entry is deleted.
493
+ *
494
+ * For targeted removal use {@link unregisterOperator} or {@link unregisterSource}.
495
+ */
496
+ static unregister(name) {
497
+ if (this._operators.has(name)) {
498
+ const targetMap = this._operators.get(name);
499
+ for (const [targetClass, entry] of targetMap) if (entry.metadata.source !== "internal" && targetClass !== void 0) delete targetClass.prototype[name];
500
+ this._operators.delete(name);
501
+ return true;
502
+ }
503
+ if (this._sources.has(name)) {
504
+ this._sources.delete(name);
505
+ return true;
506
+ }
507
+ return false;
508
+ }
509
+ /**
510
+ * Removes a specific instance operator registration for a given (name, targetClass) pair
511
+ * and deletes the prototype method for external operators.
512
+ *
513
+ * @returns `true` if the entry was found and removed; `false` otherwise.
514
+ */
515
+ static unregisterOperator(name, targetClass) {
516
+ const targetMap = this._operators.get(name);
517
+ if (!targetMap?.has(targetClass)) return false;
518
+ if (targetMap.get(targetClass).metadata.source !== "internal") delete targetClass.prototype[name];
519
+ targetMap.delete(targetClass);
520
+ if (targetMap.size === 0) this._operators.delete(name);
521
+ return true;
522
+ }
523
+ /**
524
+ * Removes a source factory registration.
525
+ *
526
+ * @returns `true` if the source was found and removed; `false` otherwise.
527
+ */
528
+ static unregisterSource(name) {
529
+ return this._sources.delete(name);
530
+ }
531
+ /**
532
+ * Registers a hook called after every successful operator registration (both namespaces).
533
+ *
534
+ * @returns A function that removes the hook when called.
535
+ */
536
+ static onRegister(hook) {
537
+ this._registrationHooks.push(hook);
538
+ return () => {
539
+ const i = this._registrationHooks.indexOf(hook);
540
+ if (i !== -1) this._registrationHooks.splice(i, 1);
541
+ };
542
+ }
543
+ /**
544
+ * Registers a guard called before every registration (both namespaces).
545
+ * Throw from the guard to reject the registration.
546
+ *
547
+ * @returns A function that removes the guard when called.
548
+ */
549
+ static addGuard(guard) {
550
+ this._registrationGuards.push(guard);
551
+ return () => {
552
+ const i = this._registrationGuards.indexOf(guard);
553
+ if (i !== -1) this._registrationGuards.splice(i, 1);
554
+ };
555
+ }
556
+ /**
557
+ * Returns `true` if a name exists in either the source or instance operator namespace.
558
+ * Use {@link hasSource} or {@link hasOperator} for namespace-specific checks.
559
+ */
560
+ static has(name) {
561
+ return this._sources.has(name) || this._operators.has(name);
562
+ }
563
+ /**
564
+ * Returns `true` if a source factory with `name` is registered.
565
+ */
566
+ static hasSource(name) {
567
+ return this._sources.has(name);
568
+ }
569
+ /**
570
+ * Returns `true` if an instance operator with `name` is registered.
571
+ * When `targetClass` is provided, checks only that specific (name, targetClass) pair.
572
+ * When omitted, returns `true` if any target has an operator with that name.
573
+ */
574
+ static hasOperator(name, targetClass) {
575
+ if (targetClass !== void 0) return this._operators.get(name)?.has(targetClass) ?? false;
576
+ return this._operators.has(name);
577
+ }
578
+ /**
579
+ * Returns the operator entry for `name` from either namespace, or `undefined` if not found.
580
+ * Checks the instance operator namespace first, then the source namespace.
581
+ * For namespace-specific retrieval use {@link getSource} or {@link getOperator}.
582
+ */
583
+ static get(name) {
584
+ const targetMap = this._operators.get(name);
585
+ if (targetMap !== void 0) return targetMap.values().next().value;
586
+ return this._sources.get(name);
587
+ }
588
+ /**
589
+ * Returns the source factory entry for `name`, or `undefined` if not registered.
590
+ */
591
+ static getSource(name) {
592
+ return this._sources.get(name);
593
+ }
594
+ /**
595
+ * Returns the instance operator entry for `name` on `targetClass`, or `undefined`.
596
+ * When `targetClass` is omitted, returns the first entry found across all targets.
597
+ */
598
+ static getOperator(name, targetClass) {
599
+ const targetMap = this._operators.get(name);
600
+ if (targetMap === void 0) return;
601
+ if (targetClass !== void 0) return targetMap.get(targetClass);
602
+ return targetMap.values().next().value;
603
+ }
604
+ /**
605
+ * Returns the metadata for `name` from either namespace, or `undefined` if not found.
606
+ * Checks instance operators first, then sources.
607
+ */
608
+ static getMetadata(name) {
609
+ return this.get(name)?.metadata;
610
+ }
611
+ /**
612
+ * Returns metadata for all registered operators and source factories.
613
+ * Use {@link listOperators} or {@link listSources} for namespace-specific lists.
614
+ */
615
+ static list() {
616
+ return [...this.listSources(), ...this.listOperators()];
617
+ }
618
+ /**
619
+ * Returns metadata for all registered source factories.
620
+ */
621
+ static listSources() {
622
+ return [...this._sources.values()].map((e) => e.metadata);
623
+ }
624
+ /**
625
+ * Returns metadata for all registered instance operators.
626
+ * When `targetClass` is provided, returns only entries for that specific target class.
627
+ */
628
+ static listOperators(targetClass) {
629
+ const all = [];
630
+ for (const targetMap of this._operators.values()) if (targetClass !== void 0) {
631
+ const entry = targetMap.get(targetClass);
632
+ if (entry !== void 0) all.push(entry.metadata);
633
+ } else for (const entry of targetMap.values()) all.push(entry.metadata);
634
+ return all;
635
+ }
636
+ /** Returns metadata for all operators of `kind` across both namespaces. */
637
+ static listByKind(kind) {
638
+ return this.list().filter((m) => m.kind === kind);
639
+ }
640
+ /** Returns metadata for all operators from `source` across both namespaces. */
641
+ static listBySource(source) {
642
+ return this.list().filter((m) => m.source === source);
643
+ }
644
+ /** Returns the total number of registered operators across both namespaces. */
645
+ static count() {
646
+ let operatorCount = 0;
647
+ for (const targetMap of this._operators.values()) operatorCount += targetMap.size;
648
+ return this._sources.size + operatorCount;
649
+ }
650
+ /**
651
+ * Registers a source operator (a static factory, not a prototype method).
652
+ *
653
+ * @remarks
654
+ * Source operators differ from prototype operators in two ways:
655
+ * - They are called with `null` as `this` - they have no instance.
656
+ * - They are looked up by the compiler via {@link getSource} rather than
657
+ * being patched onto a prototype.
658
+ *
659
+ * The entry is stored in the source namespace and is never patched onto any prototype.
660
+ * Registration guards run for `"external"` sources (same policy as {@link register}).
661
+ * Guards are skipped for `"internal"` sources (same policy used for internal builtins).
662
+ *
663
+ * Third-party source operators registered here are automatically compiled by
664
+ * `QueryPlanCompiler` without any changes to the compiler.
665
+ *
666
+ * @param name - The operator name, matching the `operatorName` on the `QueryPlanNode`.
667
+ * @param factory - The factory function; receives the node args in order, `this` is `null`.
668
+ * @param source - Whether this is a built-in or external source operator. Defaults to `"external"`.
669
+ *
670
+ * @example
671
+ * ```ts
672
+ * import { OperatorRegistry } from "tyneq/plugin";
673
+ * import { Tyneq } from "tyneq";
674
+ *
675
+ * OperatorRegistry.registerSource("fibonacci", (count) => {
676
+ * // return a Tyneq sequence of fibonacci numbers
677
+ * });
678
+ * ```
679
+ *
680
+ * @group Classes
681
+ */
682
+ static registerSource(name, factory, source = "external") {
683
+ if (this._sources.has(name)) {
684
+ const existing = this._sources.get(name).metadata;
685
+ throw new RegistryError(`Cannot register source "${name}": already registered as "${existing.kind}" from source "${existing.source}".`, name, "source", {
686
+ kind: existing.kind,
687
+ source: existing.source
688
+ });
689
+ }
690
+ const entry = {
691
+ metadata: OperatorMetadata.source(name, source),
692
+ impl: function(...args) {
693
+ return factory(...args);
694
+ }
695
+ };
696
+ if (source !== "internal") for (const guard of this._registrationGuards) guard(entry);
697
+ this._sources.set(name, entry);
698
+ for (const hook of this._registrationHooks) hook(entry);
699
+ }
700
+ /**
701
+ * Records a built-in operator in the instance operator namespace without patching the prototype.
702
+ * Built-in operators already live as direct methods on their target class.
703
+ *
704
+ * @remarks
705
+ * Registration guards are intentionally skipped - builtins are internal and
706
+ * trusted; guards exist to validate external plugin registrations only.
707
+ *
708
+ * @internal
709
+ */
710
+ static registerBuiltin(name, kind, targetClass) {
711
+ const targetMap = this._operators.get(name);
712
+ if (targetMap?.has(targetClass)) {
713
+ const existing = targetMap.get(targetClass).metadata;
714
+ throw new RegistryError(`Cannot register builtin "${name}" (${kind}) on ${targetClass.name}: already registered as "${existing.kind}" from source "${existing.source}".`, name, kind, {
715
+ kind: existing.kind,
716
+ source: existing.source
717
+ });
718
+ }
719
+ const lazyMethod = new Lazy(() => reflect(targetClass.prototype).tryGetMethod(name)?.value);
720
+ const entry = {
721
+ metadata: new OperatorMetadata(name, kind, "internal", targetClass),
722
+ impl: function(...args) {
723
+ const method = lazyMethod.value;
724
+ if (!method) throw new RegistryError(`Cannot invoke builtin "${name}" (${kind}): method not found on prototype. Ensure the method exists on the target class before registering.`, name, kind);
725
+ return method.apply(this, args);
726
+ }
727
+ };
728
+ if (!this._operators.has(name)) this._operators.set(name, /* @__PURE__ */ new Map());
729
+ this._operators.get(name).set(targetClass, entry);
730
+ for (const hook of this._registrationHooks) hook(entry);
731
+ }
732
+ };
733
+ //#endregion
734
+ //#region src/plugin/decorators/sequence.ts
735
+ /**
736
+ * Class decorator - declares this class as a sequence whose `@builtin` methods are operators.
737
+ *
738
+ * Scans the prototype for `@builtin`-tagged methods and registers each one via
739
+ * `OperatorRegistry.registerBuiltin`, passing this class as `targetClass`.
740
+ *
741
+ * @remarks
742
+ * Works in tandem with `@builtin`. The two-decorator pattern:
743
+ * 1. Decorate each built-in method with `@builtin({ kind: "streaming" | "buffer" })`.
744
+ * `@builtin` stamps a `builtinMeta` symbol onto the function object - it does NOT register.
745
+ * 2. Decorate the containing class with `@sequence`.
746
+ * `@sequence` scans the prototype, finds all `@builtin`-tagged methods, and calls
747
+ * `OperatorRegistry.registerBuiltin` for each one.
748
+ *
749
+ * Apply only to `TyneqEnumerableCore` and `TyneqEnumerableBase` - the two base classes
750
+ * whose methods form the built-in operator surface. Never use on third-party or test classes.
751
+ *
752
+ * @internal
753
+ */
754
+ function sequence(target, _context) {
755
+ for (const key of Object.getOwnPropertyNames(target.prototype)) {
756
+ const method = reflect(target.prototype).tryGetMethod(key)?.value;
757
+ if (method && builtinMeta in method) {
758
+ const metadata = method[builtinMeta];
759
+ OperatorRegistry.registerBuiltin(metadata.name, metadata.kind, target);
760
+ }
761
+ }
762
+ }
763
+ //#endregion
764
+ //#region src/core/TyneqEnumerableCore.ts
765
+ var TyneqEnumerableCore = @sequence class {
766
+ [Symbol.iterator]() {
767
+ return this.getEnumerator();
768
+ }
769
+ @builtin({ kind: "buffer" }) orderBy(keySelector, comparer) {
770
+ ArgumentUtility.checkNotOptional({ keySelector });
771
+ const orderByArgs = comparer !== void 0 ? [keySelector, comparer] : [keySelector];
772
+ return this.createOrderedEnumerable(keySelector, comparer ?? TyneqComparer.defaultComparer, false, this.createNode("orderBy", "buffer", orderByArgs));
773
+ }
774
+ @builtin({ kind: "buffer" }) orderByDescending(keySelector, comparer) {
775
+ ArgumentUtility.checkNotOptional({ keySelector });
776
+ const orderByDescArgs = comparer !== void 0 ? [keySelector, comparer] : [keySelector];
777
+ return this.createOrderedEnumerable(keySelector, comparer ?? TyneqComparer.defaultComparer, true, this.createNode("orderByDescending", "buffer", orderByDescArgs));
778
+ }
779
+ @builtin({ kind: "cache" }) memoize() {
780
+ return this.createCachedEnumerable(this, this.createNode("memoize", "buffer"));
781
+ }
782
+ @builtin({ kind: "extension" }) pipe(factory) {
783
+ ArgumentUtility.checkNotOptional({ factory });
784
+ const self = this;
785
+ return this.createSequence(() => factory(self), this.createNode("pipe", "streaming", [factory]));
786
+ }
787
+ /**
788
+ * Helper that creates a new sequence with the provided enumerator factory and query node.
789
+ * @param factory The factory function that produces an enumerator for the new sequence.
790
+ * @param node The query plan node associated with the new sequence.
791
+ * @returns A new TyneqSequence instance.
792
+ */
793
+ createSequence(factory, node) {
794
+ return this.createEnumerable({ getEnumerator: factory }, node);
795
+ }
796
+ /**
797
+ * Helper for creating a query node with the current sequence's node as its parent.
798
+ * @param name The name of the query node.
799
+ * @param category The category of the operator.
800
+ * @param args The arguments for the query node.
801
+ * @returns A new QueryNode instance.
802
+ */
803
+ createNode(name, category, args = []) {
804
+ return new QueryNode(name, args, this[tyneqQueryNode], category);
805
+ }
806
+ };
807
+ //#endregion
808
+ //#region src/core/terminal/TyneqTerminalOperator.ts
809
+ /**
810
+ * Abstract base for all terminal operators.
811
+ *
812
+ * @remarks
813
+ * Terminal operators consume a sequence and return a concrete value.
814
+ * Subclasses implement `process()` which enumerates `this.source` and returns the result.
815
+ * Register with `@terminal` or `createTerminalOperator`.
816
+ *
817
+ * @typeParam TSource - Element type of the source sequence.
818
+ * @typeParam TResult - The return type of `process()`.
819
+ * @group Plugin
820
+ */
821
+ var TyneqTerminalOperator = class {
822
+ source;
823
+ constructor(source) {
824
+ ArgumentUtility.checkNotOptional({ source });
825
+ this.source = source;
826
+ }
827
+ };
828
+ //#endregion
829
+ //#region src/operators/aggregate.ts
830
+ /**
831
+ * Applies an accumulator over the sequence and transforms the final result through a selector.
832
+ *
833
+ * @remarks
834
+ * Immediate. Source is fully enumerated when this method is called.
835
+ *
836
+ * @see {@link TyneqSequence.aggregate}
837
+ * @group Operators
838
+ * @category Terminal
839
+ * @internal
840
+ */
841
+ var AggregateOperator = class extends TyneqTerminalOperator {
842
+ seed;
843
+ func;
844
+ resultSelector;
845
+ constructor(source, seed, func, resultSelector) {
846
+ super(source);
847
+ ArgumentUtility.checkNotOptional({ func });
848
+ ArgumentUtility.checkNotOptional({ resultSelector });
849
+ this.seed = seed;
850
+ this.func = func;
851
+ this.resultSelector = resultSelector;
852
+ }
853
+ process() {
854
+ let accumulate = this.seed;
855
+ for (const item of this.source) accumulate = this.func(accumulate, item);
856
+ return this.resultSelector(accumulate);
857
+ }
858
+ };
859
+ //#endregion
860
+ //#region src/operators/all.ts
861
+ /**
862
+ * Returns true if every element satisfies a predicate.
863
+ *
864
+ * @remarks
865
+ * Immediate. Source is fully enumerated when this method is called.
866
+ *
867
+ * @see {@link TyneqSequence.all}
868
+ * @group Operators
869
+ * @category Terminal
870
+ * @internal
871
+ */
872
+ var AllOperator = class extends TyneqTerminalOperator {
873
+ predicate;
874
+ constructor(source, predicate) {
875
+ super(source);
876
+ ArgumentUtility.checkNotOptional({ predicate });
877
+ this.predicate = predicate;
878
+ }
879
+ process() {
880
+ let index = 0;
881
+ for (const item of this.source) if (!this.predicate(item, index++)) return false;
882
+ return true;
883
+ }
884
+ };
885
+ //#endregion
886
+ //#region src/operators/any.ts
887
+ /**
888
+ * Returns true if any element satisfies a predicate.
889
+ *
890
+ * @remarks
891
+ * Immediate. Source is fully enumerated when this method is called.
892
+ *
893
+ * @see {@link TyneqSequence.any}
894
+ * @group Operators
895
+ * @category Terminal
896
+ * @internal
897
+ */
898
+ var AnyOperator = class extends TyneqTerminalOperator {
899
+ predicate;
900
+ constructor(source, predicate) {
901
+ super(source);
902
+ ArgumentUtility.checkNotOptional({ predicate });
903
+ this.predicate = predicate;
904
+ }
905
+ process() {
906
+ let index = 0;
907
+ for (const item of this.source) if (this.predicate(item, index++)) return true;
908
+ return false;
909
+ }
910
+ };
911
+ //#endregion
912
+ //#region src/operators/average.ts
913
+ /**
914
+ * Returns the average of all elements projected through a selector.
915
+ *
916
+ * @remarks
917
+ * Immediate. Source is fully enumerated when this method is called.
918
+ *
919
+ * @see {@link TyneqSequence.average}
920
+ * @group Operators
921
+ * @category Terminal
922
+ * @internal
923
+ */
924
+ var AverageOperator = class extends TyneqTerminalOperator {
925
+ selector;
926
+ constructor(source, selector) {
927
+ super(source);
928
+ ArgumentUtility.checkNotOptional({ selector });
929
+ this.selector = selector;
930
+ }
931
+ process() {
932
+ let count = 0;
933
+ let sum = 0;
934
+ for (const item of this.source) {
935
+ sum += this.selector(item);
936
+ count++;
937
+ }
938
+ return count === 0 ? 0 : sum / count;
939
+ }
940
+ };
941
+ //#endregion
942
+ //#region src/operators/consume.ts
943
+ /**
944
+ * Iterates the source sequence and discards all elements.
945
+ *
946
+ * @remarks
947
+ * Immediate. Source is fully enumerated when this method is called.
948
+ *
949
+ * @see {@link TyneqSequence.consume}
950
+ * @group Operators
951
+ * @category Terminal
952
+ * @internal
953
+ */
954
+ var ConsumeOperator = class extends TyneqTerminalOperator {
955
+ constructor(source) {
956
+ super(source);
957
+ }
958
+ process() {
959
+ for (const _ of this.source);
960
+ }
961
+ };
962
+ //#endregion
963
+ //#region src/operators/contains.ts
964
+ /**
965
+ * Returns `true` if the source sequence contains `value`.
966
+ *
967
+ * @remarks
968
+ * Immediate. Enumerates the source until a match is found or the sequence is exhausted.
969
+ * Uses `equalityComparer` for element comparison, or `===` when omitted.
970
+ * Returns `false` for an empty sequence.
971
+ *
972
+ * @see {@link TyneqSequence.contains}
973
+ * @group Operators
974
+ * @category Terminal
975
+ * @internal
976
+ */
977
+ var ContainsOperator = class extends TyneqTerminalOperator {
978
+ value;
979
+ equalityComparer;
980
+ constructor(source, value, equalityComparer) {
981
+ super(source);
982
+ ArgumentUtility.checkNotNull({ equalityComparer });
983
+ this.value = value;
984
+ this.equalityComparer = equalityComparer ?? TyneqComparer.defaultEqualityComparer;
985
+ }
986
+ process() {
987
+ for (const item of this.source) if (this.equalityComparer(item, this.value)) return true;
988
+ return false;
989
+ }
990
+ };
991
+ //#endregion
992
+ //#region src/operators/count.ts
993
+ /**
994
+ * Returns the total number of elements in the sequence.
995
+ *
996
+ * @remarks
997
+ * Immediate. Source is fully enumerated when this method is called.
998
+ *
999
+ * @see {@link TyneqSequence.count}
1000
+ * @group Operators
1001
+ * @category Terminal
1002
+ * @internal
1003
+ */
1004
+ var CountOperator = class extends TyneqTerminalOperator {
1005
+ constructor(source) {
1006
+ super(source);
1007
+ }
1008
+ process() {
1009
+ if (Array.isArray(this.source)) return this.source.length;
1010
+ let count = 0;
1011
+ for (const _ of this.source) count++;
1012
+ return count;
1013
+ }
1014
+ };
1015
+ //#endregion
1016
+ //#region src/operators/countBy.ts
1017
+ /**
1018
+ * Returns the number of elements that satisfy a predicate.
1019
+ *
1020
+ * @remarks
1021
+ * Immediate. Source is fully enumerated when this method is called.
1022
+ *
1023
+ * @see {@link TyneqSequence.countBy}
1024
+ * @group Operators
1025
+ * @category Terminal
1026
+ * @internal
1027
+ */
1028
+ var CountByOperator = class extends TyneqTerminalOperator {
1029
+ predicate;
1030
+ constructor(source, predicate) {
1031
+ super(source);
1032
+ ArgumentUtility.checkNotOptional({ predicate });
1033
+ this.predicate = predicate;
1034
+ }
1035
+ process() {
1036
+ let count = 0;
1037
+ let index = 0;
1038
+ for (const item of this.source) if (this.predicate(item, index++)) count++;
1039
+ return count;
1040
+ }
1041
+ };
1042
+ //#endregion
1043
+ //#region src/operators/elementAt.ts
1044
+ /**
1045
+ * Returns the element at a specified index, or throws if the index is out of range.
1046
+ *
1047
+ * @remarks
1048
+ * Immediate. Source is fully enumerated when this method is called.
1049
+ *
1050
+ * @see {@link TyneqSequence.elementAt}
1051
+ * @group Operators
1052
+ * @category Terminal
1053
+ * @internal
1054
+ */
1055
+ var ElementAtOperator = class extends TyneqTerminalOperator {
1056
+ index;
1057
+ constructor(source, index) {
1058
+ super(source);
1059
+ this.index = index;
1060
+ }
1061
+ process() {
1062
+ const index = this.index;
1063
+ let currentIndex = 0;
1064
+ for (const element of this.source) {
1065
+ if (currentIndex === index) return element;
1066
+ currentIndex++;
1067
+ }
1068
+ throw new ArgumentOutOfRangeError(nameof({ index })[0]);
1069
+ }
1070
+ };
1071
+ //#endregion
1072
+ //#region src/operators/elementAtOrDefault.ts
1073
+ /**
1074
+ * Returns the element at a specified index, or a default value if the index is out of range.
1075
+ *
1076
+ * @remarks
1077
+ * Immediate. Source is fully enumerated when this method is called.
1078
+ *
1079
+ * @see {@link TyneqSequence.elementAtOrDefault}
1080
+ * @group Operators
1081
+ * @category Terminal
1082
+ * @internal
1083
+ */
1084
+ var ElementAtOrDefaultOperator = class extends TyneqTerminalOperator {
1085
+ index;
1086
+ defaultValue;
1087
+ constructor(source, index, defaultValue) {
1088
+ super(source);
1089
+ this.index = index;
1090
+ this.defaultValue = defaultValue;
1091
+ }
1092
+ process() {
1093
+ const index = this.index;
1094
+ let currentIndex = 0;
1095
+ for (const element of this.source) {
1096
+ if (currentIndex === index) return element;
1097
+ currentIndex++;
1098
+ }
1099
+ return this.defaultValue;
1100
+ }
1101
+ };
1102
+ //#endregion
1103
+ //#region src/core/errors/InvalidOperationError.ts
1104
+ /**
1105
+ * Thrown when a method call is invalid for the current state of the object.
1106
+ *
1107
+ * @example
1108
+ * ```ts
1109
+ * try { Tyneq.from([1, 2]).single(); }
1110
+ * catch (e) { if (e instanceof InvalidOperationError) { ... } }
1111
+ * ```
1112
+ *
1113
+ * @see {@link TyneqError}
1114
+ * @group Errors
1115
+ */
1116
+ var InvalidOperationError = class extends TyneqError {
1117
+ constructor(message = "The operation is invalid in the current state.", inner) {
1118
+ super(message, { inner });
1119
+ }
1120
+ };
1121
+ //#endregion
1122
+ //#region src/operators/first.ts
1123
+ /**
1124
+ * Returns the first element matching a predicate, or throws if no match is found.
1125
+ *
1126
+ * @remarks
1127
+ * Immediate. Source is fully enumerated when this method is called.
1128
+ *
1129
+ * @see {@link TyneqSequence.first}
1130
+ * @group Operators
1131
+ * @category Terminal
1132
+ * @internal
1133
+ */
1134
+ var FirstOperator = class extends TyneqTerminalOperator {
1135
+ predicate;
1136
+ constructor(source, predicate) {
1137
+ super(source);
1138
+ ArgumentUtility.checkNotOptional({ predicate });
1139
+ this.predicate = predicate;
1140
+ }
1141
+ process() {
1142
+ let index = 0;
1143
+ for (const element of this.source) if (this.predicate(element, index++)) return element;
1144
+ throw new InvalidOperationError("Sequence contains no matching element");
1145
+ }
1146
+ };
1147
+ //#endregion
1148
+ //#region src/operators/firstOrDefault.ts
1149
+ /**
1150
+ * Returns the first element matching a predicate, or a default value if no match is found.
1151
+ *
1152
+ * @remarks
1153
+ * Immediate. Source is fully enumerated when this method is called.
1154
+ *
1155
+ * @see {@link TyneqSequence.firstOrDefault}
1156
+ * @group Operators
1157
+ * @category Terminal
1158
+ * @internal
1159
+ */
1160
+ var FirstOrDefaultOperator = class extends TyneqTerminalOperator {
1161
+ predicate;
1162
+ defaultValue;
1163
+ constructor(source, predicate, defaultValue) {
1164
+ super(source);
1165
+ ArgumentUtility.checkNotOptional({ predicate });
1166
+ this.predicate = predicate;
1167
+ this.defaultValue = defaultValue;
1168
+ }
1169
+ process() {
1170
+ let index = 0;
1171
+ for (const element of this.source) if (this.predicate(element, index++)) return element;
1172
+ return this.defaultValue;
1173
+ }
1174
+ };
1175
+ //#endregion
1176
+ //#region src/operators/indexOf.ts
1177
+ /**
1178
+ * Returns the zero-based index of the first element matching a predicate, or -1 if not found.
1179
+ *
1180
+ * @remarks
1181
+ * Immediate. Source is fully enumerated when this method is called.
1182
+ *
1183
+ * @see {@link TyneqSequence.indexOf}
1184
+ * @group Operators
1185
+ * @category Terminal
1186
+ * @internal
1187
+ */
1188
+ var IndexOfOperator = class extends TyneqTerminalOperator {
1189
+ predicate;
1190
+ startIndex;
1191
+ constructor(source, predicate, startIndex = 0) {
1192
+ super(source);
1193
+ ArgumentUtility.checkNotOptional({ predicate });
1194
+ ArgumentUtility.checkNonNegative({ startIndex });
1195
+ this.predicate = predicate;
1196
+ this.startIndex = startIndex;
1197
+ }
1198
+ process() {
1199
+ let index = -1;
1200
+ for (const item of this.source) {
1201
+ index++;
1202
+ if (index < this.startIndex) continue;
1203
+ if (this.predicate(item, index)) return index;
1204
+ }
1205
+ return -1;
1206
+ }
1207
+ };
1208
+ //#endregion
1209
+ //#region src/utility/EnumeratorUtility.ts
1210
+ /**
1211
+ * Low-level helpers for working with `Enumerator<T>` objects inside custom operator enumerators.
1212
+ *
1213
+ * Import from `"tyneq/plugin"` when writing class-based operators that need to convert
1214
+ * between `Iterable<T>` and `Enumerator<T>`, or that need safe cleanup via `tryDispose`.
1215
+ *
1216
+ * @group Plugin Utilities
1217
+ */
1218
+ var EnumeratorUtility = class {
1219
+ constructor() {}
1220
+ /**
1221
+ * Calls `enumerator.return()` if it exists, swallowing any error.
1222
+ * Safe to call on `null` or `undefined`.
1223
+ */
1224
+ static tryDispose(enumerator) {
1225
+ try {
1226
+ enumerator?.return?.();
1227
+ } catch {}
1228
+ }
1229
+ /** Wraps an `Enumerator<T>` in a minimal `Iterable<T>` adapter (no buffering). */
1230
+ static toIterable(enumerator) {
1231
+ return { [Symbol.iterator]: () => enumerator };
1232
+ }
1233
+ /** Gets an `Enumerator<T>` from any `Iterable<T>`. */
1234
+ static fromIterable(iterable) {
1235
+ return iterable[Symbol.iterator]();
1236
+ }
1237
+ /**
1238
+ * Advances the enumerator by one position and returns `true` if that position was done.
1239
+ *
1240
+ * **Warning:** this calls `next()` and irrevocably consumes one element.
1241
+ * If the enumerator is not exhausted, the yielded element is discarded.
1242
+ * Only call this when advancing past the current position is intentional.
1243
+ */
1244
+ static checkAndConsume(enumerator) {
1245
+ return enumerator.next().done === true;
1246
+ }
1247
+ };
1248
+ //#endregion
1249
+ //#region src/operators/isNullOrEmpty.ts
1250
+ /**
1251
+ * Returns true if the sequence has no elements, or if its first element is null or undefined.
1252
+ *
1253
+ * @remarks
1254
+ * Immediate. Reads at most one element from the source (O(1) enumeration) and then disposes the iterator.
1255
+ *
1256
+ * @see {@link TyneqSequence.isNullOrEmpty}
1257
+ * @group Operators
1258
+ * @category Terminal
1259
+ * @internal
1260
+ */
1261
+ var IsNullOrEmptyOperator = class extends TyneqTerminalOperator {
1262
+ constructor(source) {
1263
+ super(source);
1264
+ }
1265
+ process() {
1266
+ const iterator = this.source[Symbol.iterator]();
1267
+ const first = iterator.next();
1268
+ EnumeratorUtility.tryDispose(iterator);
1269
+ return first.done === true || first.value === null || first.value === void 0;
1270
+ }
1271
+ };
1272
+ //#endregion
1273
+ //#region src/operators/last.ts
1274
+ /**
1275
+ * Returns the last element matching a predicate, or throws if no match is found.
1276
+ *
1277
+ * @remarks
1278
+ * Immediate. Source is fully enumerated when this method is called.
1279
+ *
1280
+ * @see {@link TyneqSequence.last}
1281
+ * @group Operators
1282
+ * @category Terminal
1283
+ * @internal
1284
+ */
1285
+ var LastOperator = class extends TyneqTerminalOperator {
1286
+ predicate;
1287
+ constructor(source, predicate) {
1288
+ super(source);
1289
+ ArgumentUtility.checkNotOptional({ predicate });
1290
+ this.predicate = predicate;
1291
+ }
1292
+ process() {
1293
+ let lastMatchingElement = null;
1294
+ let found = false;
1295
+ let index = 0;
1296
+ for (const element of this.source) if (this.predicate(element, index++)) {
1297
+ lastMatchingElement = element;
1298
+ found = true;
1299
+ }
1300
+ if (!found) throw new InvalidOperationError("Sequence contains no matching element");
1301
+ return lastMatchingElement;
1302
+ }
1303
+ };
1304
+ //#endregion
1305
+ //#region src/operators/lastOrDefault.ts
1306
+ /**
1307
+ * Returns the last element matching a predicate, or a default value if no match is found.
1308
+ *
1309
+ * @remarks
1310
+ * Immediate. Source is fully enumerated when this method is called.
1311
+ *
1312
+ * @see {@link TyneqSequence.lastOrDefault}
1313
+ * @group Operators
1314
+ * @category Terminal
1315
+ * @internal
1316
+ */
1317
+ var LastOrDefaultOperator = class extends TyneqTerminalOperator {
1318
+ predicate;
1319
+ defaultValue;
1320
+ constructor(source, predicate, defaultValue) {
1321
+ super(source);
1322
+ ArgumentUtility.checkNotOptional({ predicate });
1323
+ this.predicate = predicate;
1324
+ this.defaultValue = defaultValue;
1325
+ }
1326
+ process() {
1327
+ let lastMatchingElement = null;
1328
+ let found = false;
1329
+ let index = 0;
1330
+ for (const element of this.source) if (this.predicate(element, index++)) {
1331
+ lastMatchingElement = element;
1332
+ found = true;
1333
+ }
1334
+ if (!found) return this.defaultValue;
1335
+ return lastMatchingElement;
1336
+ }
1337
+ };
1338
+ //#endregion
1339
+ //#region src/core/errors/SequenceContainsNoElementsError.ts
1340
+ /**
1341
+ * Thrown when an element is required from a sequence that contains no elements.
1342
+ *
1343
+ * @example
1344
+ * ```ts
1345
+ * try { Tyneq.from([]).first(); }
1346
+ * catch (e) { if (e instanceof SequenceContainsNoElementsError) { ... } }
1347
+ * ```
1348
+ *
1349
+ * @see {@link InvalidOperationError}
1350
+ * @group Errors
1351
+ */
1352
+ var SequenceContainsNoElementsError = class extends InvalidOperationError {
1353
+ operatorName;
1354
+ constructor(operatorName, inner) {
1355
+ const message = operatorName ? `Sequence contains no elements (in "${operatorName}").` : "Sequence contains no elements.";
1356
+ super(message, inner);
1357
+ this.operatorName = operatorName;
1358
+ }
1359
+ };
1360
+ //#endregion
1361
+ //#region src/operators/extremum.ts
1362
+ /**
1363
+ * Returns the minimum or maximum element in the sequence using a comparer.
1364
+ *
1365
+ * @remarks
1366
+ * Immediate. Source is fully enumerated when this method is called.
1367
+ * Parameterised by direction to avoid duplicating the iteration logic
1368
+ * across separate min and max operators.
1369
+ *
1370
+ * @see {@link TyneqSequence.min}
1371
+ * @see {@link TyneqSequence.max}
1372
+ * @group Operators
1373
+ * @category Terminal
1374
+ * @internal
1375
+ */
1376
+ var ExtremumOperator = class extends TyneqTerminalOperator {
1377
+ comparer;
1378
+ direction;
1379
+ operatorName;
1380
+ constructor(source, direction, operatorName, comparer) {
1381
+ super(source);
1382
+ this.comparer = comparer ?? TyneqComparer.defaultComparer;
1383
+ this.direction = direction;
1384
+ this.operatorName = operatorName;
1385
+ }
1386
+ process() {
1387
+ let best = null;
1388
+ let hasElement = false;
1389
+ for (const element of this.source) if (!hasElement || this.comparer(element, best) * this.direction > 0) {
1390
+ best = element;
1391
+ hasElement = true;
1392
+ }
1393
+ if (!hasElement) throw new SequenceContainsNoElementsError(this.operatorName);
1394
+ return best;
1395
+ }
1396
+ };
1397
+ /**
1398
+ * Returns the element with the minimum or maximum key as determined by a key selector.
1399
+ *
1400
+ * @remarks
1401
+ * Immediate. Source is fully enumerated when this method is called.
1402
+ * Parameterised by direction to avoid duplicating the iteration logic
1403
+ * across separate minBy and maxBy operators.
1404
+ *
1405
+ * @see {@link TyneqSequence.minBy}
1406
+ * @see {@link TyneqSequence.maxBy}
1407
+ * @group Operators
1408
+ * @category Terminal
1409
+ * @internal
1410
+ */
1411
+ var ExtremumByOperator = class extends TyneqTerminalOperator {
1412
+ comparer;
1413
+ keySelector;
1414
+ direction;
1415
+ operatorName;
1416
+ constructor(source, keySelector, direction, operatorName, comparer) {
1417
+ super(source);
1418
+ ArgumentUtility.checkNotOptional({ keySelector });
1419
+ this.comparer = comparer ?? TyneqComparer.defaultComparer;
1420
+ this.keySelector = keySelector;
1421
+ this.direction = direction;
1422
+ this.operatorName = operatorName;
1423
+ }
1424
+ process() {
1425
+ let bestElement = null;
1426
+ let bestKey = null;
1427
+ let hasElement = false;
1428
+ for (const element of this.source) {
1429
+ const key = this.keySelector(element);
1430
+ if (!hasElement || this.comparer(key, bestKey) * this.direction > 0) {
1431
+ bestElement = element;
1432
+ bestKey = key;
1433
+ hasElement = true;
1434
+ }
1435
+ }
1436
+ if (!hasElement) throw new SequenceContainsNoElementsError(this.operatorName);
1437
+ return bestElement;
1438
+ }
1439
+ };
1440
+ //#endregion
1441
+ //#region src/operators/minMax.ts
1442
+ /**
1443
+ * Returns both the minimum and maximum elements of the sequence in a single pass.
1444
+ *
1445
+ * @remarks
1446
+ * Immediate. Source is fully enumerated when this method is called.
1447
+ *
1448
+ * @see {@link TyneqSequence.minMax}
1449
+ * @group Operators
1450
+ * @category Terminal
1451
+ * @internal
1452
+ */
1453
+ var MinMaxOperator = class extends TyneqTerminalOperator {
1454
+ comparer;
1455
+ constructor(source, comparer) {
1456
+ super(source);
1457
+ this.comparer = comparer ?? TyneqComparer.defaultComparer;
1458
+ }
1459
+ process() {
1460
+ let min;
1461
+ let max;
1462
+ let hasElements = false;
1463
+ for (const item of this.source) if (!hasElements) {
1464
+ min = item;
1465
+ max = item;
1466
+ hasElements = true;
1467
+ } else {
1468
+ if (this.comparer(item, min) < 0) min = item;
1469
+ if (this.comparer(item, max) > 0) max = item;
1470
+ }
1471
+ if (!hasElements) throw new SequenceContainsNoElementsError("minMax");
1472
+ return {
1473
+ min,
1474
+ max
1475
+ };
1476
+ }
1477
+ };
1478
+ //#endregion
1479
+ //#region src/operators/sequenceEqual.ts
1480
+ /**
1481
+ * Returns true if the source and a second sequence contain equal elements in the same order.
1482
+ *
1483
+ * @remarks
1484
+ * Immediate. Source is fully enumerated when this method is called.
1485
+ *
1486
+ * @see {@link TyneqSequence.sequenceEqual}
1487
+ * @group Operators
1488
+ * @category Terminal
1489
+ * @internal
1490
+ */
1491
+ var SequenceEqualOperator = class extends TyneqTerminalOperator {
1492
+ other;
1493
+ equalityComparer;
1494
+ constructor(source, other, equalityComparer) {
1495
+ super(source);
1496
+ ArgumentUtility.checkNotOptional({ other });
1497
+ ArgumentUtility.checkNotNull({ equalityComparer });
1498
+ this.equalityComparer = equalityComparer ?? TyneqComparer.defaultEqualityComparer;
1499
+ this.other = other;
1500
+ }
1501
+ process() {
1502
+ const sourceIterator = this.source[Symbol.iterator]();
1503
+ const otherIterator = this.other[Symbol.iterator]();
1504
+ while (true) {
1505
+ const sourceNext = sourceIterator.next();
1506
+ const otherNext = otherIterator.next();
1507
+ if (sourceNext.done && otherNext.done) break;
1508
+ if (sourceNext.done !== otherNext.done) return false;
1509
+ if (!this.equalityComparer(sourceNext.value, otherNext.value)) return false;
1510
+ }
1511
+ return true;
1512
+ }
1513
+ };
1514
+ //#endregion
1515
+ //#region src/operators/single.ts
1516
+ /**
1517
+ * Returns the single element matching a predicate, or throws if there is not exactly one match.
1518
+ *
1519
+ * @remarks
1520
+ * Immediate. Source is fully enumerated when this method is called.
1521
+ *
1522
+ * @see {@link TyneqSequence.single}
1523
+ * @group Operators
1524
+ * @category Terminal
1525
+ * @internal
1526
+ */
1527
+ var SingleOperator = class extends TyneqTerminalOperator {
1528
+ predicate;
1529
+ constructor(source, predicate) {
1530
+ super(source);
1531
+ ArgumentUtility.checkNotOptional({ predicate });
1532
+ this.predicate = predicate;
1533
+ }
1534
+ process() {
1535
+ let found = false;
1536
+ let single = null;
1537
+ let index = 0;
1538
+ for (const element of this.source) if (this.predicate(element, index++)) {
1539
+ if (found) throw new InvalidOperationError("Sequence contains more than one matching element");
1540
+ found = true;
1541
+ single = element;
1542
+ }
1543
+ if (!found) throw new InvalidOperationError("Sequence contains no matching element");
1544
+ return single;
1545
+ }
1546
+ };
1547
+ //#endregion
1548
+ //#region src/operators/singleOrDefault.ts
1549
+ /**
1550
+ * Returns the single element matching a predicate, or a default value if no match is found.
1551
+ *
1552
+ * @remarks
1553
+ * Immediate. Source is fully enumerated when this method is called.
1554
+ *
1555
+ * @see {@link TyneqSequence.singleOrDefault}
1556
+ * @group Operators
1557
+ * @category Terminal
1558
+ * @internal
1559
+ */
1560
+ var SingleOrDefaultOperator = class extends TyneqTerminalOperator {
1561
+ predicate;
1562
+ defaultValue;
1563
+ constructor(source, predicate, defaultValue) {
1564
+ super(source);
1565
+ ArgumentUtility.checkNotOptional({ predicate });
1566
+ this.predicate = predicate;
1567
+ this.defaultValue = defaultValue;
1568
+ }
1569
+ process() {
1570
+ let found = false;
1571
+ let single = null;
1572
+ let index = 0;
1573
+ for (const element of this.source) if (this.predicate(element, index++)) {
1574
+ if (found) throw new InvalidOperationError("Sequence contains more than one matching element");
1575
+ found = true;
1576
+ single = element;
1577
+ }
1578
+ if (!found) return this.defaultValue;
1579
+ return single;
1580
+ }
1581
+ };
1582
+ //#endregion
1583
+ //#region src/operators/endsWith.ts
1584
+ var EndsWithOperator = class extends TyneqTerminalOperator {
1585
+ sequence;
1586
+ equalityComparer;
1587
+ constructor(source, sequence, equalityComparer) {
1588
+ super(source);
1589
+ ArgumentUtility.checkNotOptional({ sequence });
1590
+ ArgumentUtility.checkIterable({ sequence });
1591
+ ArgumentUtility.checkNotNull({ equalityComparer });
1592
+ this.sequence = sequence;
1593
+ this.equalityComparer = equalityComparer ?? TyneqComparer.defaultEqualityComparer;
1594
+ }
1595
+ process() {
1596
+ const suffixElements = Array.from(this.sequence);
1597
+ if (suffixElements.length === 0) return true;
1598
+ const bufferResult = this.fillCircularBuffer(this.source, suffixElements.length);
1599
+ if (bufferResult === null) return false;
1600
+ const { buffer, readIndex } = bufferResult;
1601
+ for (let i = 0; i < suffixElements.length; i++) if (!this.equalityComparer(buffer[(readIndex + i) % buffer.length], suffixElements[i])) return false;
1602
+ return true;
1603
+ }
1604
+ fillCircularBuffer(elements, windowSize) {
1605
+ const buffer = new Array(windowSize);
1606
+ let writeIndex = 0;
1607
+ for (const item of elements) {
1608
+ buffer[writeIndex % windowSize] = item;
1609
+ writeIndex++;
1610
+ }
1611
+ if (writeIndex < windowSize) return null;
1612
+ return {
1613
+ buffer,
1614
+ readIndex: writeIndex % windowSize
1615
+ };
1616
+ }
1617
+ };
1618
+ //#endregion
1619
+ //#region src/operators/startsWith.ts
1620
+ /**
1621
+ * Returns `true` if the source sequence begins with all elements of `sequence` in order.
1622
+ *
1623
+ * @remarks
1624
+ * Immediate. Enumerates the source only until the result is determined.
1625
+ * Uses `equalityComparer` for element comparison, or `===` when omitted.
1626
+ * Returns `true` when `sequence` is empty (vacuous truth).
1627
+ * Returns `false` when `sequence` is longer than the source.
1628
+ *
1629
+ * @see {@link TyneqSequence.startsWith}
1630
+ * @group Operators
1631
+ * @category Terminal
1632
+ * @internal
1633
+ */
1634
+ var StartsWithOperator = class extends TyneqTerminalOperator {
1635
+ sequence;
1636
+ equalityComparer;
1637
+ constructor(source, sequence, equalityComparer) {
1638
+ super(source);
1639
+ ArgumentUtility.checkNotOptional({ sequence });
1640
+ ArgumentUtility.checkIterable({ sequence });
1641
+ ArgumentUtility.checkNotNull({ equalityComparer });
1642
+ this.sequence = sequence;
1643
+ this.equalityComparer = equalityComparer ?? TyneqComparer.defaultEqualityComparer;
1644
+ }
1645
+ process() {
1646
+ const sourceEnumerator = this.source[Symbol.iterator]();
1647
+ const sequenceEnumerator = this.sequence[Symbol.iterator]();
1648
+ while (true) {
1649
+ const { value: sourceValue, done: sourceDone } = sourceEnumerator.next();
1650
+ const { value: sequenceValue, done: sequenceDone } = sequenceEnumerator.next();
1651
+ if (sequenceDone) return true;
1652
+ if (sourceDone || !this.equalityComparer(sourceValue, sequenceValue)) return false;
1653
+ }
1654
+ }
1655
+ };
1656
+ //#endregion
1657
+ //#region src/operators/sum.ts
1658
+ /**
1659
+ * Returns the sum of all elements projected through a selector.
1660
+ *
1661
+ * @remarks
1662
+ * Immediate. Source is fully enumerated when this method is called.
1663
+ *
1664
+ * @see {@link TyneqSequence.sum}
1665
+ * @group Operators
1666
+ * @category Terminal
1667
+ * @internal
1668
+ */
1669
+ var SumOperator = class extends TyneqTerminalOperator {
1670
+ selector;
1671
+ constructor(source, selector) {
1672
+ super(source);
1673
+ ArgumentUtility.checkNotOptional({ selector });
1674
+ this.selector = selector;
1675
+ }
1676
+ process() {
1677
+ let sum = 0;
1678
+ for (const item of this.source) sum += this.selector(item);
1679
+ return sum;
1680
+ }
1681
+ };
1682
+ //#endregion
1683
+ //#region src/operators/toArray.ts
1684
+ /**
1685
+ * Collects all elements into an array.
1686
+ *
1687
+ * @remarks
1688
+ * Immediate. Source is fully enumerated when this method is called.
1689
+ *
1690
+ * @see {@link TyneqSequence.toArray}
1691
+ * @group Operators
1692
+ * @category Terminal
1693
+ * @internal
1694
+ */
1695
+ var ToArrayOperator = class extends TyneqTerminalOperator {
1696
+ constructor(source) {
1697
+ super(source);
1698
+ }
1699
+ process() {
1700
+ return Array.from(this.source);
1701
+ }
1702
+ };
1703
+ //#endregion
1704
+ //#region src/operators/toAsync.ts
1705
+ /**
1706
+ * Wraps the source sequence as an async iterable.
1707
+ *
1708
+ * @remarks
1709
+ * Deferred. The source is enumerated lazily as the returned async iterable is iterated.
1710
+ *
1711
+ * @see {@link TyneqSequence.toAsync}
1712
+ * @group Operators
1713
+ * @category Terminal
1714
+ * @internal
1715
+ */
1716
+ var ToAsyncOperator = class extends TyneqTerminalOperator {
1717
+ constructor(source) {
1718
+ super(source);
1719
+ }
1720
+ process() {
1721
+ const source = this.source;
1722
+ return { async *[Symbol.asyncIterator]() {
1723
+ for (const item of source) yield item;
1724
+ } };
1725
+ }
1726
+ };
1727
+ //#endregion
1728
+ //#region src/operators/toMap.ts
1729
+ /**
1730
+ * Collects all elements into a Map using a key-value selector.
1731
+ *
1732
+ * @remarks
1733
+ * Immediate. Source is fully enumerated when this method is called.
1734
+ *
1735
+ * @see {@link TyneqSequence.toMap}
1736
+ * @group Operators
1737
+ * @category Terminal
1738
+ * @internal
1739
+ */
1740
+ var ToMapOperator = class extends TyneqTerminalOperator {
1741
+ selector;
1742
+ constructor(source, selector) {
1743
+ super(source);
1744
+ ArgumentUtility.checkNotOptional({ selector });
1745
+ this.selector = selector;
1746
+ }
1747
+ process() {
1748
+ return new Map(Array.from(this.source, (item) => {
1749
+ const pair = this.selector(item);
1750
+ return this.transformPairToTuple(pair);
1751
+ }));
1752
+ }
1753
+ transformPairToTuple(pair) {
1754
+ return [pair.key, pair.value];
1755
+ }
1756
+ };
1757
+ //#endregion
1758
+ //#region src/operators/toRecord.ts
1759
+ /**
1760
+ * Collects all elements into a plain object record using a key-value selector.
1761
+ *
1762
+ * @remarks
1763
+ * Immediate. Source is fully enumerated when this method is called.
1764
+ *
1765
+ * @see {@link TyneqSequence.toRecord}
1766
+ * @group Operators
1767
+ * @category Terminal
1768
+ * @internal
1769
+ */
1770
+ var ToRecordOperator = class extends TyneqTerminalOperator {
1771
+ selector;
1772
+ constructor(source, selector) {
1773
+ super(source);
1774
+ ArgumentUtility.checkNotOptional({ selector });
1775
+ this.selector = selector;
1776
+ }
1777
+ process() {
1778
+ const result = {};
1779
+ for (const item of this.source) {
1780
+ const pair = this.selector(item);
1781
+ result[pair.key] = pair.value;
1782
+ }
1783
+ return result;
1784
+ }
1785
+ };
1786
+ //#endregion
1787
+ //#region src/operators/toSet.ts
1788
+ /**
1789
+ * Collects all elements into a Set.
1790
+ *
1791
+ * @remarks
1792
+ * Immediate. Source is fully enumerated when this method is called.
1793
+ *
1794
+ * @see {@link TyneqSequence.toSet}
1795
+ * @group Operators
1796
+ * @category Terminal
1797
+ * @internal
1798
+ */
1799
+ var ToSetOperator = class extends TyneqTerminalOperator {
1800
+ constructor(source) {
1801
+ super(source);
1802
+ }
1803
+ process() {
1804
+ return new Set(this.source);
1805
+ }
1806
+ };
1807
+ //#endregion
1808
+ //#region src/core/enumerators/TyneqEnumerator.ts
1809
+ /**
1810
+ * Base class for all pipeline operator enumerators (streaming and buffering).
1811
+ *
1812
+ * @remarks
1813
+ * Extends {@link TyneqBaseEnumerator} with a typed source enumerator.
1814
+ * Override `initialize()` to buffer or prepare state before the first `handleNext()`.
1815
+ * Override `disposeAdditional()` to release resources beyond the source enumerator.
1816
+ *
1817
+ * @typeParam TInput - Source element type.
1818
+ * @typeParam TOutput - Output element type (defaults to `TInput`).
1819
+ * @group Plugin
1820
+ */
1821
+ var TyneqEnumerator = class extends TyneqBaseEnumerator {
1822
+ sourceEnumerator;
1823
+ sourceDisposed = false;
1824
+ constructor(sourceEnumerator) {
1825
+ super();
1826
+ ArgumentUtility.checkNotOptional({ sourceEnumerator });
1827
+ this.sourceEnumerator = sourceEnumerator;
1828
+ }
1829
+ disposeSource() {
1830
+ if (this.sourceDisposed) return;
1831
+ this.sourceDisposed = true;
1832
+ EnumeratorUtility.tryDispose(this.sourceEnumerator);
1833
+ }
1834
+ };
1835
+ //#endregion
1836
+ //#region src/enumerators/streaming/append.ts
1837
+ /**
1838
+ * Appends a single element to the end of the source sequence.
1839
+ *
1840
+ * @remarks
1841
+ * Deferred. Source is not enumerated until the returned sequence is iterated.
1842
+ *
1843
+ * @see {@link TyneqSequence.append}
1844
+ * @group Operators
1845
+ * @category Streaming
1846
+ * @internal
1847
+ */
1848
+ var AppendEnumerator = class extends TyneqEnumerator {
1849
+ isSourceDone = false;
1850
+ appended = false;
1851
+ item;
1852
+ constructor(sourceEnumerator, item) {
1853
+ super(sourceEnumerator);
1854
+ this.item = item;
1855
+ }
1856
+ handleNext() {
1857
+ if (!this.isSourceDone) {
1858
+ const sourceNext = this.sourceEnumerator.next();
1859
+ if (!sourceNext.done) return this.yield(sourceNext.value);
1860
+ this.isSourceDone = true;
1861
+ }
1862
+ if (!this.appended) {
1863
+ this.appended = true;
1864
+ return this.yield(this.item);
1865
+ }
1866
+ return this.done();
1867
+ }
1868
+ };
1869
+ //#endregion
1870
+ //#region src/enumerators/streaming/chunk.ts
1871
+ /**
1872
+ * Splits the source sequence into non-overlapping chunks of a fixed size.
1873
+ *
1874
+ * @remarks
1875
+ * Deferred. Source is not enumerated until the returned sequence is iterated.
1876
+ *
1877
+ * @see {@link TyneqSequence.chunk}
1878
+ * @group Operators
1879
+ * @category Streaming
1880
+ * @internal
1881
+ */
1882
+ var ChunkEnumerator = class extends TyneqEnumerator {
1883
+ size;
1884
+ currentChunk = [];
1885
+ constructor(sourceEnumerator, size) {
1886
+ super(sourceEnumerator);
1887
+ this.size = size;
1888
+ }
1889
+ handleNext() {
1890
+ while (this.currentChunk.length < this.size) {
1891
+ const next = this.sourceEnumerator.next();
1892
+ if (next.done) break;
1893
+ this.currentChunk.push(next.value);
1894
+ }
1895
+ if (this.currentChunk.length === 0) return this.done();
1896
+ const chunk = this.currentChunk;
1897
+ this.currentChunk = [];
1898
+ return this.yield(chunk);
1899
+ }
1900
+ };
1901
+ //#endregion
1902
+ //#region src/enumerators/streaming/concat.ts
1903
+ /**
1904
+ * Concatenates a second sequence after the source sequence.
1905
+ *
1906
+ * @remarks
1907
+ * Deferred. Source is not enumerated until the returned sequence is iterated.
1908
+ *
1909
+ * @see {@link TyneqSequence.concat}
1910
+ * @group Operators
1911
+ * @category Streaming
1912
+ * @internal
1913
+ */
1914
+ var ConcatEnumerator = class extends TyneqEnumerator {
1915
+ otherEnumerator;
1916
+ isSourceDone = false;
1917
+ constructor(sourceEnumerator, other) {
1918
+ super(sourceEnumerator);
1919
+ this.otherEnumerator = other[Symbol.iterator]();
1920
+ }
1921
+ disposeAdditional() {
1922
+ EnumeratorUtility.tryDispose(this.otherEnumerator);
1923
+ }
1924
+ handleNext() {
1925
+ if (!this.isSourceDone) {
1926
+ const next = this.sourceEnumerator.next();
1927
+ if (!next.done) return this.yield(next.value);
1928
+ this.isSourceDone = true;
1929
+ }
1930
+ const next = this.otherEnumerator.next();
1931
+ if (!next.done) return this.yield(next.value);
1932
+ return this.done();
1933
+ }
1934
+ };
1935
+ //#endregion
1936
+ //#region src/enumerators/streaming/flatten.ts
1937
+ /**
1938
+ * Flattens one level of nesting from a sequence of iterables.
1939
+ *
1940
+ * @remarks
1941
+ * Deferred. Source is not enumerated until the returned sequence is iterated.
1942
+ * Each inner iterable is consumed lazily as the outer sequence advances.
1943
+ *
1944
+ * @see {@link TyneqSequence.flatten}
1945
+ * @group Operators
1946
+ * @category Streaming
1947
+ * @internal
1948
+ */
1949
+ var FlattenEnumerator = class extends TyneqEnumerator {
1950
+ innerEnumerator = null;
1951
+ constructor(sourceEnumerator) {
1952
+ super(sourceEnumerator);
1953
+ }
1954
+ handleNext() {
1955
+ while (true) {
1956
+ if (this.innerEnumerator !== null) {
1957
+ const innerNext = this.innerEnumerator.next();
1958
+ if (!innerNext.done) return this.yield(innerNext.value);
1959
+ this.innerEnumerator = null;
1960
+ }
1961
+ const sourceNext = this.sourceEnumerator.next();
1962
+ if (sourceNext.done) return this.done();
1963
+ this.innerEnumerator = sourceNext.value[Symbol.iterator]();
1964
+ }
1965
+ }
1966
+ };
1967
+ //#endregion
1968
+ //#region src/enumerators/streaming/defaultIfEmpty.ts
1969
+ /**
1970
+ * Returns the source sequence, or a single default element if the source is empty.
1971
+ *
1972
+ * @remarks
1973
+ * Deferred. Source is not enumerated until the returned sequence is iterated.
1974
+ *
1975
+ * @see {@link TyneqSequence.defaultIfEmpty}
1976
+ * @group Operators
1977
+ * @category Streaming
1978
+ * @internal
1979
+ */
1980
+ var DefaultIfEmptyEnumerator = class extends TyneqEnumerator {
1981
+ defaultValue;
1982
+ sourceDone = false;
1983
+ hasYieldedAny = false;
1984
+ defaultYielded = false;
1985
+ constructor(sourceEnumerator, defaultValue) {
1986
+ super(sourceEnumerator);
1987
+ this.defaultValue = defaultValue;
1988
+ }
1989
+ handleNext() {
1990
+ if (!this.sourceDone) {
1991
+ const next = this.sourceEnumerator.next();
1992
+ if (!next.done) {
1993
+ this.hasYieldedAny = true;
1994
+ return this.yield(next.value);
1995
+ }
1996
+ this.sourceDone = true;
1997
+ }
1998
+ if (!this.hasYieldedAny && !this.defaultYielded) {
1999
+ this.defaultYielded = true;
2000
+ return this.doneWithYield(this.defaultValue);
2001
+ }
2002
+ return this.done();
2003
+ }
2004
+ };
2005
+ //#endregion
2006
+ //#region src/enumerators/streaming/ofType.ts
2007
+ /**
2008
+ * Filters elements to only those matching a type guard.
2009
+ *
2010
+ * @remarks
2011
+ * Deferred. Source is not enumerated until the returned sequence is iterated.
2012
+ *
2013
+ * @see {@link TyneqSequence.ofType}
2014
+ * @group Operators
2015
+ * @category Streaming
2016
+ * @internal
2017
+ */
2018
+ var OfTypeEnumerator = class extends TyneqEnumerator {
2019
+ guard;
2020
+ constructor(sourceEnumerator, guard) {
2021
+ super(sourceEnumerator);
2022
+ this.guard = guard;
2023
+ }
2024
+ handleNext() {
2025
+ while (true) {
2026
+ const { value, done } = this.sourceEnumerator.next();
2027
+ if (done) return this.done();
2028
+ if (this.guard(value)) return this.yield(value);
2029
+ }
2030
+ }
2031
+ };
2032
+ //#endregion
2033
+ //#region src/enumerators/streaming/pairwise.ts
2034
+ /**
2035
+ * Projects each consecutive pair of elements as a two-element tuple.
2036
+ *
2037
+ * @remarks
2038
+ * Deferred. Source is not enumerated until the returned sequence is iterated.
2039
+ *
2040
+ * @see {@link TyneqSequence.pairwise}
2041
+ * @group Operators
2042
+ * @category Streaming
2043
+ * @internal
2044
+ */
2045
+ var PairwiseEnumerator = class extends TyneqEnumerator {
2046
+ hasPrevious = false;
2047
+ previous;
2048
+ constructor(sourceEnumerator) {
2049
+ super(sourceEnumerator);
2050
+ }
2051
+ handleNext() {
2052
+ while (true) {
2053
+ const next = this.sourceEnumerator.next();
2054
+ if (next.done) return this.done();
2055
+ if (!this.hasPrevious) {
2056
+ this.previous = next.value;
2057
+ this.hasPrevious = true;
2058
+ continue;
2059
+ }
2060
+ const pair = [this.previous, next.value];
2061
+ this.previous = next.value;
2062
+ return this.yield(pair);
2063
+ }
2064
+ }
2065
+ };
2066
+ //#endregion
2067
+ //#region src/enumerators/streaming/populate.ts
2068
+ /**
2069
+ * Replaces every element in the source with a fixed value.
2070
+ *
2071
+ * @remarks
2072
+ * Deferred. Source is not enumerated until the returned sequence is iterated.
2073
+ *
2074
+ * @see {@link TyneqSequence.populate}
2075
+ * @group Operators
2076
+ * @category Streaming
2077
+ * @internal
2078
+ */
2079
+ var PopulateEnumerator = class extends TyneqEnumerator {
2080
+ value;
2081
+ constructor(sourceEnumerator, value) {
2082
+ super(sourceEnumerator);
2083
+ this.value = value;
2084
+ }
2085
+ handleNext() {
2086
+ while (true) {
2087
+ const { done } = this.sourceEnumerator.next();
2088
+ if (done) return this.done();
2089
+ return this.yield(this.value);
2090
+ }
2091
+ }
2092
+ };
2093
+ //#endregion
2094
+ //#region src/enumerators/streaming/prepend.ts
2095
+ /**
2096
+ * Prepends a single element to the beginning of the source sequence.
2097
+ *
2098
+ * @remarks
2099
+ * Deferred. Source is not enumerated until the returned sequence is iterated.
2100
+ *
2101
+ * @see {@link TyneqSequence.prepend}
2102
+ * @group Operators
2103
+ * @category Streaming
2104
+ * @internal
2105
+ */
2106
+ var PrependEnumerator = class extends TyneqEnumerator {
2107
+ prepended = false;
2108
+ item;
2109
+ constructor(sourceEnumerator, item) {
2110
+ super(sourceEnumerator);
2111
+ this.item = item;
2112
+ }
2113
+ handleNext() {
2114
+ if (!this.prepended) {
2115
+ this.prepended = true;
2116
+ return this.yield(this.item);
2117
+ }
2118
+ const nextItem = this.sourceEnumerator.next();
2119
+ if (!nextItem.done) return this.yield(nextItem.value);
2120
+ return this.done();
2121
+ }
2122
+ };
2123
+ //#endregion
2124
+ //#region src/enumerators/streaming/scan.ts
2125
+ /**
2126
+ * Applies an accumulator function and yields the running result after each element.
2127
+ *
2128
+ * @remarks
2129
+ * Deferred. Source is not enumerated until the returned sequence is iterated.
2130
+ *
2131
+ * @see {@link TyneqSequence.scan}
2132
+ * @group Operators
2133
+ * @category Streaming
2134
+ * @internal
2135
+ */
2136
+ var ScanEnumerator = class extends TyneqEnumerator {
2137
+ accumulator;
2138
+ current;
2139
+ constructor(sourceEnumerator, seed, accumulator) {
2140
+ super(sourceEnumerator);
2141
+ this.current = seed;
2142
+ this.accumulator = accumulator;
2143
+ }
2144
+ handleNext() {
2145
+ const { value, done } = this.sourceEnumerator.next();
2146
+ if (done) return this.done();
2147
+ this.current = this.accumulator(this.current, value);
2148
+ return this.yield(this.current);
2149
+ }
2150
+ };
2151
+ //#endregion
2152
+ //#region src/enumerators/streaming/select.ts
2153
+ /**
2154
+ * Projects each element through a selector.
2155
+ *
2156
+ * @remarks
2157
+ * Deferred. Source is not enumerated until the returned sequence is iterated.
2158
+ *
2159
+ * @see {@link TyneqSequence.select}
2160
+ * @group Operators
2161
+ * @category Streaming
2162
+ * @internal
2163
+ */
2164
+ var SelectEnumerator = class extends TyneqEnumerator {
2165
+ selector;
2166
+ index = 0;
2167
+ constructor(sourceEnumerator, selector) {
2168
+ super(sourceEnumerator);
2169
+ this.selector = selector;
2170
+ }
2171
+ handleNext() {
2172
+ const next = this.sourceEnumerator.next();
2173
+ if (next.done) return this.done();
2174
+ return this.yield(this.selector(next.value, this.index++));
2175
+ }
2176
+ };
2177
+ //#endregion
2178
+ //#region src/enumerators/streaming/selectMany.ts
2179
+ /**
2180
+ * Flattens each element into a sub-sequence and yields each element of those sub-sequences.
2181
+ *
2182
+ * @remarks
2183
+ * Deferred. Source is not enumerated until the returned sequence is iterated.
2184
+ *
2185
+ * @see {@link TyneqSequence.selectMany}
2186
+ * @group Operators
2187
+ * @category Streaming
2188
+ * @internal
2189
+ */
2190
+ var SelectManyEnumerator = class extends TyneqEnumerator {
2191
+ selector;
2192
+ innerEnumerator = null;
2193
+ constructor(sourceEnumerator, selector) {
2194
+ super(sourceEnumerator);
2195
+ this.selector = selector;
2196
+ }
2197
+ handleNext() {
2198
+ while (true) {
2199
+ if (this.innerEnumerator !== null) {
2200
+ const innerNext = this.innerEnumerator.next();
2201
+ if (!innerNext.done) return this.yield(innerNext.value);
2202
+ this.innerEnumerator = null;
2203
+ }
2204
+ const sourceNext = this.sourceEnumerator.next();
2205
+ if (sourceNext.done) return this.done();
2206
+ this.innerEnumerator = this.selector(sourceNext.value)[Symbol.iterator]();
2207
+ }
2208
+ }
2209
+ };
2210
+ //#endregion
2211
+ //#region src/enumerators/streaming/repeatSequence.ts
2212
+ /**
2213
+ * Repeats the source sequence a specified number of times.
2214
+ *
2215
+ * @remarks
2216
+ * Deferred. Source is not enumerated until the returned sequence is iterated.
2217
+ * Re-enumerates the source from the beginning for each repetition.
2218
+ * Returns an empty sequence when `count` is 0.
2219
+ *
2220
+ * @see {@link TyneqSequence.repeat}
2221
+ * @group Operators
2222
+ * @category Streaming
2223
+ * @internal
2224
+ */
2225
+ var RepeatSequenceEnumerator = class extends TyneqEnumerator {
2226
+ source;
2227
+ count;
2228
+ repetition = 0;
2229
+ currentEnumerator;
2230
+ constructor(sourceEnumerator, source, count) {
2231
+ super(sourceEnumerator);
2232
+ this.source = source;
2233
+ this.count = count;
2234
+ this.currentEnumerator = sourceEnumerator;
2235
+ }
2236
+ disposeAdditional() {
2237
+ if (this.currentEnumerator !== this.sourceEnumerator) EnumeratorUtility.tryDispose(this.currentEnumerator);
2238
+ }
2239
+ handleNext() {
2240
+ while (this.repetition < this.count) {
2241
+ const next = this.currentEnumerator.next();
2242
+ if (!next.done) return this.yield(next.value);
2243
+ this.repetition++;
2244
+ if (this.repetition < this.count) {
2245
+ const fresh = this.source[Symbol.iterator]();
2246
+ const probe = fresh.next();
2247
+ if (probe.done) {
2248
+ this.repetition = this.count;
2249
+ return this.done();
2250
+ }
2251
+ this.currentEnumerator = fresh;
2252
+ return this.yield(probe.value);
2253
+ }
2254
+ }
2255
+ return this.done();
2256
+ }
2257
+ };
2258
+ //#endregion
2259
+ //#region src/enumerators/streaming/skip.ts
2260
+ /**
2261
+ * Skips a specified number of elements from the beginning of the source sequence.
2262
+ *
2263
+ * @remarks
2264
+ * Deferred. Source is not enumerated until the returned sequence is iterated.
2265
+ *
2266
+ * @see {@link TyneqSequence.skip}
2267
+ * @group Operators
2268
+ * @category Streaming
2269
+ * @internal
2270
+ */
2271
+ var SkipEnumerator = class extends TyneqEnumerator {
2272
+ count;
2273
+ skipped = false;
2274
+ constructor(sourceEnumerator, count) {
2275
+ super(sourceEnumerator);
2276
+ this.count = count;
2277
+ }
2278
+ handleNext() {
2279
+ if (!this.skipped) {
2280
+ let skippedCount = 0;
2281
+ while (skippedCount < this.count) {
2282
+ if (this.sourceEnumerator.next().done) return this.done();
2283
+ skippedCount++;
2284
+ }
2285
+ this.skipped = true;
2286
+ }
2287
+ const sourceNext = this.sourceEnumerator.next();
2288
+ if (sourceNext.done) return this.done();
2289
+ return this.yield(sourceNext.value);
2290
+ }
2291
+ };
2292
+ //#endregion
2293
+ //#region src/enumerators/streaming/skipLast.ts
2294
+ /**
2295
+ * Skips a specified number of elements from the end of the source sequence.
2296
+ *
2297
+ * @remarks
2298
+ * Deferred. Source is not enumerated until the returned sequence is iterated.
2299
+ *
2300
+ * @see {@link TyneqSequence.skipLast}
2301
+ * @group Operators
2302
+ * @category Streaming
2303
+ * @internal
2304
+ */
2305
+ var SkipLastEnumerator = class extends TyneqEnumerator {
2306
+ count;
2307
+ buffer;
2308
+ writeIndex = 0;
2309
+ filledCount = 0;
2310
+ constructor(sourceEnumerator, count) {
2311
+ super(sourceEnumerator);
2312
+ this.count = count;
2313
+ this.buffer = new Array(this.count);
2314
+ }
2315
+ handleNext() {
2316
+ if (this.count === 0) {
2317
+ const current = this.sourceEnumerator.next();
2318
+ if (current.done) return this.done();
2319
+ else return this.yield(current.value);
2320
+ }
2321
+ while (true) {
2322
+ const current = this.sourceEnumerator.next();
2323
+ if (current.done) return this.done();
2324
+ if (this.filledCount < this.count) {
2325
+ this.buffer[this.writeIndex] = current.value;
2326
+ this.writeIndex = (this.writeIndex + 1) % this.count;
2327
+ this.filledCount++;
2328
+ continue;
2329
+ }
2330
+ const oldest = this.buffer[this.writeIndex];
2331
+ this.buffer[this.writeIndex] = current.value;
2332
+ this.writeIndex = (this.writeIndex + 1) % this.count;
2333
+ return this.yield(oldest);
2334
+ }
2335
+ }
2336
+ };
2337
+ //#endregion
2338
+ //#region src/enumerators/streaming/skipUntil.ts
2339
+ /**
2340
+ * Skips elements from the beginning of the source sequence until a predicate returns `true`,
2341
+ * then yields all remaining elements including the one that triggered the predicate.
2342
+ *
2343
+ * @remarks
2344
+ * Deferred. Source is not enumerated until the returned sequence is iterated.
2345
+ *
2346
+ * @see {@link TyneqSequence.skipUntil}
2347
+ * @group Operators
2348
+ * @category Streaming
2349
+ * @internal
2350
+ */
2351
+ var SkipUntilEnumerator = class extends TyneqEnumerator {
2352
+ predicate;
2353
+ index = 0;
2354
+ triggered = false;
2355
+ constructor(sourceEnumerator, predicate) {
2356
+ super(sourceEnumerator);
2357
+ this.predicate = predicate;
2358
+ }
2359
+ handleNext() {
2360
+ while (true) {
2361
+ const next = this.sourceEnumerator.next();
2362
+ if (next.done) return this.done();
2363
+ if (!this.triggered) this.triggered = this.predicate(next.value, this.index++);
2364
+ if (this.triggered) return this.yield(next.value);
2365
+ }
2366
+ }
2367
+ };
2368
+ //#endregion
2369
+ //#region src/enumerators/streaming/skipWhile.ts
2370
+ /**
2371
+ * Skips elements from the beginning of the source sequence as long as a predicate is true.
2372
+ *
2373
+ * @remarks
2374
+ * Deferred. Source is not enumerated until the returned sequence is iterated.
2375
+ *
2376
+ * @see {@link TyneqSequence.skipWhile}
2377
+ * @group Operators
2378
+ * @category Streaming
2379
+ * @internal
2380
+ */
2381
+ var SkipWhileEnumerator = class extends TyneqEnumerator {
2382
+ predicate;
2383
+ index = 0;
2384
+ isSkipping = true;
2385
+ constructor(sourceEnumerator, predicate) {
2386
+ super(sourceEnumerator);
2387
+ this.predicate = predicate;
2388
+ }
2389
+ handleNext() {
2390
+ while (true) {
2391
+ const next = this.sourceEnumerator.next();
2392
+ if (next.done) return this.done();
2393
+ this.isSkipping = this.isSkipping && this.predicate(next.value, this.index++);
2394
+ if (!this.isSkipping) return this.yield(next.value);
2395
+ }
2396
+ }
2397
+ };
2398
+ //#endregion
2399
+ //#region src/enumerators/streaming/slice.ts
2400
+ /**
2401
+ * Yields elements between a start index (inclusive) and an end index (exclusive).
2402
+ *
2403
+ * @remarks
2404
+ * Deferred. Source is not enumerated until the returned sequence is iterated.
2405
+ *
2406
+ * @see {@link TyneqSequence.slice}
2407
+ * @group Operators
2408
+ * @category Streaming
2409
+ * @internal
2410
+ */
2411
+ var SliceEnumerator = class extends TyneqEnumerator {
2412
+ start;
2413
+ end;
2414
+ index = 0;
2415
+ started = false;
2416
+ constructor(sourceEnumerator, start, end) {
2417
+ super(sourceEnumerator);
2418
+ this.start = start;
2419
+ this.end = end;
2420
+ }
2421
+ handleNext() {
2422
+ if (!this.started) {
2423
+ this.started = true;
2424
+ while (this.index < this.start) {
2425
+ if (this.sourceEnumerator.next().done) return this.done();
2426
+ this.index++;
2427
+ }
2428
+ }
2429
+ if (this.index >= this.end) return this.earlyComplete();
2430
+ const next = this.sourceEnumerator.next();
2431
+ if (next.done) return this.done();
2432
+ this.index++;
2433
+ return this.yield(next.value);
2434
+ }
2435
+ };
2436
+ //#endregion
2437
+ //#region src/enumerators/streaming/split.ts
2438
+ /**
2439
+ * Splits the source sequence into sub-arrays at each element matching a predicate.
2440
+ *
2441
+ * @remarks
2442
+ * Deferred. Source is not enumerated until the returned sequence is iterated.
2443
+ *
2444
+ * @see {@link TyneqSequence.split}
2445
+ * @group Operators
2446
+ * @category Streaming
2447
+ * @internal
2448
+ */
2449
+ var SplitEnumerator = class extends TyneqEnumerator {
2450
+ splitOn;
2451
+ constructor(sourceEnumerator, splitOn) {
2452
+ super(sourceEnumerator);
2453
+ this.splitOn = splitOn;
2454
+ }
2455
+ handleNext() {
2456
+ const currentSplit = [];
2457
+ while (true) {
2458
+ const { value, done } = this.sourceEnumerator.next();
2459
+ if (done) {
2460
+ if (currentSplit.length > 0) return this.doneWithYield(currentSplit);
2461
+ return this.done();
2462
+ }
2463
+ if (this.splitOn(value)) {
2464
+ if (currentSplit.length > 0) return this.yield(currentSplit);
2465
+ } else currentSplit.push(value);
2466
+ }
2467
+ }
2468
+ };
2469
+ //#endregion
2470
+ //#region src/enumerators/streaming/take.ts
2471
+ /**
2472
+ * Returns a specified number of elements from the beginning of the source sequence.
2473
+ *
2474
+ * @remarks
2475
+ * Deferred. Source is not enumerated until the returned sequence is iterated.
2476
+ *
2477
+ * @see {@link TyneqSequence.take}
2478
+ * @group Operators
2479
+ * @category Streaming
2480
+ * @internal
2481
+ */
2482
+ var TakeEnumerator = class extends TyneqEnumerator {
2483
+ count;
2484
+ takenCount = 0;
2485
+ constructor(sourceEnumerator, count) {
2486
+ super(sourceEnumerator);
2487
+ this.count = count;
2488
+ }
2489
+ handleNext() {
2490
+ if (this.takenCount >= this.count) return this.earlyComplete();
2491
+ const result = this.sourceEnumerator.next();
2492
+ if (result.done) return this.done();
2493
+ this.takenCount++;
2494
+ return this.yield(result.value);
2495
+ }
2496
+ };
2497
+ //#endregion
2498
+ //#region src/enumerators/streaming/takeUntil.ts
2499
+ /**
2500
+ * Yields elements from the beginning of the source sequence until a predicate returns `true`,
2501
+ * then stops. The element that triggered the predicate is not included.
2502
+ *
2503
+ * @remarks
2504
+ * Deferred. Source is not enumerated until the returned sequence is iterated.
2505
+ *
2506
+ * @see {@link TyneqSequence.takeUntil}
2507
+ * @group Operators
2508
+ * @category Streaming
2509
+ * @internal
2510
+ */
2511
+ var TakeUntilEnumerator = class extends TyneqEnumerator {
2512
+ predicate;
2513
+ index = 0;
2514
+ constructor(sourceEnumerator, predicate) {
2515
+ super(sourceEnumerator);
2516
+ this.predicate = predicate;
2517
+ }
2518
+ handleNext() {
2519
+ const next = this.sourceEnumerator.next();
2520
+ if (next.done) return this.done();
2521
+ if (this.predicate(next.value, this.index++)) return this.earlyComplete();
2522
+ return this.yield(next.value);
2523
+ }
2524
+ };
2525
+ //#endregion
2526
+ //#region src/enumerators/streaming/takeWhile.ts
2527
+ /**
2528
+ * Yields elements from the beginning of the source sequence as long as a predicate is true.
2529
+ *
2530
+ * @remarks
2531
+ * Deferred. Source is not enumerated until the returned sequence is iterated.
2532
+ *
2533
+ * @see {@link TyneqSequence.takeWhile}
2534
+ * @group Operators
2535
+ * @category Streaming
2536
+ * @internal
2537
+ */
2538
+ var TakeWhileEnumerator = class extends TyneqEnumerator {
2539
+ predicate;
2540
+ index = 0;
2541
+ constructor(sourceEnumerator, predicate) {
2542
+ super(sourceEnumerator);
2543
+ this.predicate = predicate;
2544
+ }
2545
+ handleNext() {
2546
+ const result = this.sourceEnumerator.next();
2547
+ if (result.done) return this.done();
2548
+ if (this.predicate(result.value, this.index++)) return this.yield(result.value);
2549
+ return this.earlyComplete();
2550
+ }
2551
+ };
2552
+ //#endregion
2553
+ //#region src/enumerators/streaming/tap.ts
2554
+ /**
2555
+ * Invokes a side-effect action for each element without modifying the sequence.
2556
+ *
2557
+ * @remarks
2558
+ * Deferred. Source is not enumerated until the returned sequence is iterated.
2559
+ *
2560
+ * @see {@link TyneqSequence.tap}
2561
+ * @group Operators
2562
+ * @category Streaming
2563
+ * @internal
2564
+ */
2565
+ var TapEnumerator = class extends TyneqEnumerator {
2566
+ action;
2567
+ index = 0;
2568
+ constructor(sourceEnumerator, action) {
2569
+ super(sourceEnumerator);
2570
+ this.action = action;
2571
+ }
2572
+ handleNext() {
2573
+ const next = this.sourceEnumerator.next();
2574
+ if (next.done) return this.done();
2575
+ this.action(next.value, this.index++);
2576
+ return this.yield(next.value);
2577
+ }
2578
+ };
2579
+ //#endregion
2580
+ //#region src/enumerators/streaming/tapIf.ts
2581
+ /**
2582
+ * Conditionally invokes a side-effect action for each element based on a predicate.
2583
+ *
2584
+ * @remarks
2585
+ * Deferred. Source is not enumerated until the returned sequence is iterated.
2586
+ *
2587
+ * @see {@link TyneqSequence.tapIf}
2588
+ * @group Operators
2589
+ * @category Streaming
2590
+ * @internal
2591
+ */
2592
+ var TapIfEnumerator = class extends TyneqEnumerator {
2593
+ action;
2594
+ predicate;
2595
+ index = 0;
2596
+ constructor(sourceEnumerator, action, predicate) {
2597
+ super(sourceEnumerator);
2598
+ this.action = action;
2599
+ this.predicate = predicate;
2600
+ }
2601
+ handleNext() {
2602
+ const next = this.sourceEnumerator.next();
2603
+ if (next.done) return this.done();
2604
+ if (this.predicate()) this.action(next.value, this.index);
2605
+ this.index++;
2606
+ return this.yield(next.value);
2607
+ }
2608
+ };
2609
+ //#endregion
2610
+ //#region src/enumerators/streaming/throttle.ts
2611
+ /**
2612
+ * Yields every nth element from the source sequence.
2613
+ *
2614
+ * @remarks
2615
+ * Deferred. Source is not enumerated until the returned sequence is iterated.
2616
+ *
2617
+ * @see {@link TyneqSequence.throttle}
2618
+ * @group Operators
2619
+ * @category Streaming
2620
+ * @internal
2621
+ */
2622
+ var ThrottleEnumerator = class extends TyneqEnumerator {
2623
+ count;
2624
+ index = -1;
2625
+ constructor(sourceEnumerator, count) {
2626
+ super(sourceEnumerator);
2627
+ this.count = count;
2628
+ }
2629
+ handleNext() {
2630
+ while (true) {
2631
+ const { value, done } = this.sourceEnumerator.next();
2632
+ if (done) return this.done();
2633
+ this.index++;
2634
+ if (this.index % this.count === 0) return this.yield(value);
2635
+ }
2636
+ }
2637
+ };
2638
+ //#endregion
2639
+ //#region src/enumerators/streaming/where.ts
2640
+ /**
2641
+ * Filters elements using a predicate.
2642
+ *
2643
+ * @remarks
2644
+ * Deferred. Source is not enumerated until the returned sequence is iterated.
2645
+ *
2646
+ * @see {@link TyneqSequence.where}
2647
+ * @group Operators
2648
+ * @category Streaming
2649
+ * @internal
2650
+ */
2651
+ var WhereEnumerator = class extends TyneqEnumerator {
2652
+ index = 0;
2653
+ predicate;
2654
+ constructor(sourceEnumerator, predicate) {
2655
+ super(sourceEnumerator);
2656
+ this.predicate = predicate;
2657
+ }
2658
+ handleNext() {
2659
+ while (true) {
2660
+ const { value, done } = this.sourceEnumerator.next();
2661
+ if (done) return this.done();
2662
+ if (this.predicate(value, this.index++)) return this.yield(value);
2663
+ }
2664
+ }
2665
+ };
2666
+ //#endregion
2667
+ //#region src/enumerators/streaming/window.ts
2668
+ /**
2669
+ * Yields overlapping or tumbling windows (fixed-size sub-arrays) over the source sequence.
2670
+ *
2671
+ * @remarks
2672
+ * Deferred. Source is not enumerated until the returned sequence is iterated.
2673
+ * Windows with fewer elements than `size` are not yielded.
2674
+ * Each yielded array is a snapshot - mutating it does not affect subsequent windows.
2675
+ *
2676
+ * @see {@link TyneqSequence.window}
2677
+ * @group Operators
2678
+ * @category Streaming
2679
+ * @internal
2680
+ */
2681
+ var WindowEnumerator = class extends TyneqEnumerator {
2682
+ size;
2683
+ step;
2684
+ buffer = [];
2685
+ started = false;
2686
+ constructor(sourceEnumerator, size, step) {
2687
+ super(sourceEnumerator);
2688
+ this.size = size;
2689
+ this.step = step;
2690
+ }
2691
+ handleNext() {
2692
+ if (!this.started) {
2693
+ this.started = true;
2694
+ while (this.buffer.length < this.size) {
2695
+ const next = this.sourceEnumerator.next();
2696
+ if (next.done) return this.done();
2697
+ this.buffer.push(next.value);
2698
+ }
2699
+ return this.yield(this.buffer.slice());
2700
+ }
2701
+ if (this.step <= this.size) this.buffer = this.buffer.slice(this.step);
2702
+ else {
2703
+ this.buffer = [];
2704
+ const toSkip = this.step - this.size;
2705
+ for (let i = 0; i < toSkip; i++) if (this.sourceEnumerator.next().done) return this.done();
2706
+ }
2707
+ while (this.buffer.length < this.size) {
2708
+ const next = this.sourceEnumerator.next();
2709
+ if (next.done) return this.done();
2710
+ this.buffer.push(next.value);
2711
+ }
2712
+ return this.yield(this.buffer.slice());
2713
+ }
2714
+ };
2715
+ //#endregion
2716
+ //#region src/enumerators/streaming/zip.ts
2717
+ /**
2718
+ * Merges two sequences element-by-element using a selector.
2719
+ *
2720
+ * @remarks
2721
+ * Deferred. Source is not enumerated until the returned sequence is iterated.
2722
+ *
2723
+ * @see {@link TyneqSequence.zip}
2724
+ * @group Operators
2725
+ * @category Streaming
2726
+ * @internal
2727
+ */
2728
+ var ZipEnumerator = class extends TyneqEnumerator {
2729
+ otherEnumerator;
2730
+ selector;
2731
+ constructor(sourceEnumerator, other, selector) {
2732
+ super(sourceEnumerator);
2733
+ this.otherEnumerator = other[Symbol.iterator]();
2734
+ this.selector = selector;
2735
+ }
2736
+ handleNext() {
2737
+ const first = this.sourceEnumerator.next();
2738
+ if (first.done) {
2739
+ this.disposeAdditional();
2740
+ return this.done();
2741
+ }
2742
+ const second = this.otherEnumerator.next();
2743
+ if (second.done) return this.earlyComplete();
2744
+ return this.yield(this.selector(first.value, second.value));
2745
+ }
2746
+ disposeAdditional() {
2747
+ EnumeratorUtility.tryDispose(this.otherEnumerator);
2748
+ }
2749
+ };
2750
+ //#endregion
2751
+ //#region src/enumerators/buffer/backsert.ts
2752
+ /**
2753
+ * Inserts a second sequence at a specified offset from the end of the source sequence.
2754
+ *
2755
+ * @remarks
2756
+ * Deferred. Source is fully buffered on the first iteration of the returned sequence.
2757
+ *
2758
+ * @see {@link TyneqSequence.backsert}
2759
+ * @group Operators
2760
+ * @category Buffering
2761
+ * @internal
2762
+ */
2763
+ var BacksertEnumerator = class extends TyneqEnumerator {
2764
+ other;
2765
+ backIndex;
2766
+ buffer = [];
2767
+ current = 0;
2768
+ constructor(sourceEnumerator, backIndex, other) {
2769
+ super(sourceEnumerator);
2770
+ this.backIndex = backIndex;
2771
+ this.other = other;
2772
+ }
2773
+ initialize() {
2774
+ const source = Array.from(EnumeratorUtility.toIterable(this.sourceEnumerator));
2775
+ const other = Array.from(this.other);
2776
+ const insertionIndex = Math.max(0, source.length - this.backIndex);
2777
+ this.buffer = [
2778
+ ...source.slice(0, insertionIndex),
2779
+ ...other,
2780
+ ...source.slice(insertionIndex)
2781
+ ];
2782
+ this.current = 0;
2783
+ }
2784
+ handleNext() {
2785
+ if (this.current >= this.buffer.length) return this.done();
2786
+ return this.yield(this.buffer[this.current++]);
2787
+ }
2788
+ };
2789
+ //#endregion
2790
+ //#region src/enumerators/buffer/distinct.ts
2791
+ /**
2792
+ * Returns distinct elements by eliminating duplicates.
2793
+ *
2794
+ * @remarks
2795
+ * Deferred. Source is fully buffered on the first iteration of the returned sequence.
2796
+ *
2797
+ * @see {@link TyneqSequence.distinct}
2798
+ * @group Operators
2799
+ * @category Buffering
2800
+ * @internal
2801
+ */
2802
+ var DistinctEnumerator = class extends TyneqEnumerator {
2803
+ seenValues = /* @__PURE__ */ new Set();
2804
+ constructor(sourceEnumerator) {
2805
+ super(sourceEnumerator);
2806
+ }
2807
+ handleNext() {
2808
+ while (true) {
2809
+ const { done, value } = this.sourceEnumerator.next();
2810
+ if (done) return this.done();
2811
+ if (!this.seenValues.has(value)) {
2812
+ this.seenValues.add(value);
2813
+ return this.yield(value);
2814
+ }
2815
+ }
2816
+ }
2817
+ };
2818
+ //#endregion
2819
+ //#region src/enumerators/buffer/distinctBy.ts
2820
+ /**
2821
+ * Returns distinct elements by eliminating duplicates based on a key selector.
2822
+ *
2823
+ * @remarks
2824
+ * Deferred. Source is fully buffered on the first iteration of the returned sequence.
2825
+ *
2826
+ * @see {@link TyneqSequence.distinctBy}
2827
+ * @group Operators
2828
+ * @category Buffering
2829
+ * @internal
2830
+ */
2831
+ var DistinctByEnumerator = class extends TyneqEnumerator {
2832
+ seenValues = /* @__PURE__ */ new Set();
2833
+ keySelector;
2834
+ constructor(sourceEnumerator, keySelector) {
2835
+ super(sourceEnumerator);
2836
+ this.keySelector = keySelector;
2837
+ }
2838
+ handleNext() {
2839
+ while (true) {
2840
+ const { done, value } = this.sourceEnumerator.next();
2841
+ if (done) return this.done();
2842
+ const key = this.keySelector(value);
2843
+ if (!this.seenValues.has(key)) {
2844
+ this.seenValues.add(key);
2845
+ return this.yield(value);
2846
+ }
2847
+ }
2848
+ }
2849
+ };
2850
+ //#endregion
2851
+ //#region src/enumerators/buffer/except.ts
2852
+ /**
2853
+ * Returns elements from the source sequence that are not present in a second sequence.
2854
+ *
2855
+ * @remarks
2856
+ * Deferred. Source is fully buffered on the first iteration of the returned sequence.
2857
+ *
2858
+ * @see {@link TyneqSequence.except}
2859
+ * @group Operators
2860
+ * @category Buffering
2861
+ * @internal
2862
+ */
2863
+ var ExceptEnumerator = class extends TyneqEnumerator {
2864
+ excludedValues;
2865
+ excludeSet = /* @__PURE__ */ new Set();
2866
+ constructor(sourceEnumerator, excludedValues) {
2867
+ super(sourceEnumerator);
2868
+ this.excludedValues = excludedValues;
2869
+ }
2870
+ initialize() {
2871
+ this.excludeSet = new Set(this.excludedValues);
2872
+ }
2873
+ handleNext() {
2874
+ while (true) {
2875
+ const { done, value } = this.sourceEnumerator.next();
2876
+ if (done) return this.done();
2877
+ if (!this.excludeSet.has(value)) {
2878
+ this.excludeSet.add(value);
2879
+ return this.yield(value);
2880
+ }
2881
+ }
2882
+ }
2883
+ };
2884
+ //#endregion
2885
+ //#region src/enumerators/buffer/exceptBy.ts
2886
+ /**
2887
+ * Returns elements from the source sequence whose keys are not present in a second key sequence.
2888
+ *
2889
+ * @remarks
2890
+ * Deferred. Source is fully buffered on the first iteration of the returned sequence.
2891
+ *
2892
+ * @see {@link TyneqSequence.exceptBy}
2893
+ * @group Operators
2894
+ * @category Buffering
2895
+ * @internal
2896
+ */
2897
+ var ExceptByEnumerator = class extends TyneqEnumerator {
2898
+ excludedKeys;
2899
+ excludeSet = /* @__PURE__ */ new Set();
2900
+ keySelector;
2901
+ constructor(sourceEnumerator, excludedKeys, keySelector) {
2902
+ super(sourceEnumerator);
2903
+ this.excludedKeys = excludedKeys;
2904
+ this.keySelector = keySelector;
2905
+ }
2906
+ initialize() {
2907
+ this.excludeSet = new Set(this.excludedKeys);
2908
+ }
2909
+ handleNext() {
2910
+ while (true) {
2911
+ const { done, value } = this.sourceEnumerator.next();
2912
+ if (done) return this.done();
2913
+ const key = this.keySelector(value);
2914
+ if (!this.excludeSet.has(key)) {
2915
+ this.excludeSet.add(key);
2916
+ return this.yield(value);
2917
+ }
2918
+ }
2919
+ }
2920
+ };
2921
+ //#endregion
2922
+ //#region src/utility/DefaultingMap.ts
2923
+ /**
2924
+ * `Map` subclass with convenience helpers for initialise-on-first-access patterns.
2925
+ *
2926
+ * @internal
2927
+ */
2928
+ var DefaultingMap = class extends Map {
2929
+ /** Returns the value for `key`, or initialises and stores it via `initValue()` if absent. */
2930
+ getOrInit(key, initValue) {
2931
+ if (!this.has(key)) this.set(key, initValue());
2932
+ return this.get(key);
2933
+ }
2934
+ /** Sets `key` to `initValue` if absent; otherwise replaces the current value with `updateValue(current)`. */
2935
+ setOrUpdate(key, updateValue, initValue) {
2936
+ if (!this.has(key)) {
2937
+ this.set(key, initValue);
2938
+ return;
2939
+ }
2940
+ this.set(key, updateValue(this.get(key)));
2941
+ }
2942
+ };
2943
+ //#endregion
2944
+ //#region src/enumerators/buffer/groupBy.ts
2945
+ /**
2946
+ * Groups elements by a key selector and projects each group through a result selector.
2947
+ *
2948
+ * @remarks
2949
+ * Deferred. Source is fully buffered on the first iteration of the returned sequence.
2950
+ *
2951
+ * @see {@link TyneqSequence.groupBy}
2952
+ * @group Operators
2953
+ * @category Buffering
2954
+ * @internal
2955
+ */
2956
+ var GroupByEnumerator = class extends TyneqEnumerator {
2957
+ keySelector;
2958
+ valueSelector;
2959
+ resultSelector;
2960
+ groupFactory;
2961
+ lookupEnumerator;
2962
+ lookup = new DefaultingMap();
2963
+ constructor(sourceEnumerator, keySelector, valueSelector, resultSelector, groupFactory) {
2964
+ super(sourceEnumerator);
2965
+ this.keySelector = keySelector;
2966
+ this.valueSelector = valueSelector;
2967
+ this.resultSelector = resultSelector;
2968
+ this.groupFactory = groupFactory;
2969
+ }
2970
+ initialize() {
2971
+ while (true) {
2972
+ const { done, value } = this.sourceEnumerator.next();
2973
+ if (done) break;
2974
+ const key = this.keySelector(value);
2975
+ const mappedValue = this.valueSelector(value);
2976
+ this.lookup.getOrInit(key, () => []).push(mappedValue);
2977
+ }
2978
+ this.lookupEnumerator = this.lookup.entries();
2979
+ }
2980
+ handleNext() {
2981
+ if (this.lookupEnumerator === void 0) return this.done();
2982
+ const { done, value } = this.lookupEnumerator.next();
2983
+ if (done) return this.done();
2984
+ const [key, values] = value;
2985
+ const result = this.resultSelector(key, this.groupFactory(values));
2986
+ return this.yield(result);
2987
+ }
2988
+ };
2989
+ //#endregion
2990
+ //#region src/enumerators/buffer/groupJoin.ts
2991
+ /**
2992
+ * Correlates outer elements with groups of matching inner elements.
2993
+ *
2994
+ * @remarks
2995
+ * Deferred. Source is fully buffered on the first iteration of the returned sequence.
2996
+ *
2997
+ * @see {@link TyneqSequence.groupJoin}
2998
+ * @group Operators
2999
+ * @category Buffering
3000
+ * @internal
3001
+ */
3002
+ var GroupJoinEnumerator = class extends TyneqEnumerator {
3003
+ innerSource;
3004
+ outerKeySelector;
3005
+ innerKeySelector;
3006
+ resultSelector;
3007
+ groupFactory;
3008
+ innerLookup = new DefaultingMap();
3009
+ constructor(sourceEnumerator, innerSource, outerKeySelector, innerKeySelector, resultSelector, groupFactory) {
3010
+ super(sourceEnumerator);
3011
+ this.innerSource = innerSource;
3012
+ this.outerKeySelector = outerKeySelector;
3013
+ this.innerKeySelector = innerKeySelector;
3014
+ this.resultSelector = resultSelector;
3015
+ this.groupFactory = groupFactory;
3016
+ }
3017
+ initialize() {
3018
+ for (const innerItem of this.innerSource) {
3019
+ const key = this.innerKeySelector(innerItem);
3020
+ this.innerLookup.getOrInit(key, () => []).push(innerItem);
3021
+ }
3022
+ }
3023
+ handleNext() {
3024
+ const { done, value: outerItem } = this.sourceEnumerator.next();
3025
+ if (done) return this.done();
3026
+ const outerKey = this.outerKeySelector(outerItem);
3027
+ const innerItems = this.innerLookup.get(outerKey) ?? [];
3028
+ const resultItem = this.resultSelector(outerItem, this.groupFactory(innerItems));
3029
+ return this.yield(resultItem);
3030
+ }
3031
+ };
3032
+ //#endregion
3033
+ //#region src/enumerators/buffer/intersect.ts
3034
+ /**
3035
+ * Returns elements that are present in both the source and a second sequence.
3036
+ *
3037
+ * @remarks
3038
+ * Deferred. Source is fully buffered on the first iteration of the returned sequence.
3039
+ *
3040
+ * @see {@link TyneqSequence.intersect}
3041
+ * @group Operators
3042
+ * @category Buffering
3043
+ * @internal
3044
+ */
3045
+ var IntersectEnumerator = class extends TyneqEnumerator {
3046
+ otherValues;
3047
+ intersectionValues = /* @__PURE__ */ new Set();
3048
+ bufferedValues = /* @__PURE__ */ new Set();
3049
+ constructor(sourceEnumerator, otherValues) {
3050
+ super(sourceEnumerator);
3051
+ this.otherValues = otherValues;
3052
+ }
3053
+ initialize() {
3054
+ this.intersectionValues = new Set(this.otherValues);
3055
+ }
3056
+ handleNext() {
3057
+ while (true) {
3058
+ const { done, value } = this.sourceEnumerator.next();
3059
+ if (done) return this.done();
3060
+ if (this.intersectionValues.has(value) && !this.bufferedValues.has(value)) {
3061
+ this.bufferedValues.add(value);
3062
+ return this.yield(value);
3063
+ }
3064
+ }
3065
+ }
3066
+ };
3067
+ //#endregion
3068
+ //#region src/enumerators/buffer/intersectBy.ts
3069
+ /**
3070
+ * Returns elements whose keys appear in both the source and a second key sequence.
3071
+ *
3072
+ * @remarks
3073
+ * Deferred. Source is fully buffered on the first iteration of the returned sequence.
3074
+ *
3075
+ * @see {@link TyneqSequence.intersectBy}
3076
+ * @group Operators
3077
+ * @category Buffering
3078
+ * @internal
3079
+ */
3080
+ var IntersectByEnumerator = class extends TyneqEnumerator {
3081
+ otherValues;
3082
+ keySelector;
3083
+ intersectionKeys = /* @__PURE__ */ new Set();
3084
+ bufferedKeys = /* @__PURE__ */ new Set();
3085
+ constructor(sourceEnumerator, otherValues, keySelector) {
3086
+ super(sourceEnumerator);
3087
+ this.otherValues = otherValues;
3088
+ this.keySelector = keySelector;
3089
+ }
3090
+ initialize() {
3091
+ this.intersectionKeys = new Set(this.otherValues);
3092
+ }
3093
+ handleNext() {
3094
+ while (true) {
3095
+ const { done, value } = this.sourceEnumerator.next();
3096
+ if (done) return this.done();
3097
+ const key = this.keySelector(value);
3098
+ if (this.intersectionKeys.has(key) && !this.bufferedKeys.has(key)) {
3099
+ this.bufferedKeys.add(key);
3100
+ return this.yield(value);
3101
+ }
3102
+ }
3103
+ }
3104
+ };
3105
+ //#endregion
3106
+ //#region src/enumerators/buffer/join.ts
3107
+ /**
3108
+ * Correlates outer elements with matching inner elements using a key equality comparison.
3109
+ *
3110
+ * @remarks
3111
+ * Deferred. Source is fully buffered on the first iteration of the returned sequence.
3112
+ *
3113
+ * @see {@link TyneqSequence.join}
3114
+ * @group Operators
3115
+ * @category Buffering
3116
+ * @internal
3117
+ */
3118
+ var JoinEnumerator = class extends TyneqEnumerator {
3119
+ innerSource;
3120
+ outerKeySelector;
3121
+ innerKeySelector;
3122
+ resultSelector;
3123
+ innerLookup = new DefaultingMap();
3124
+ pendingOuter;
3125
+ pendingMatches = null;
3126
+ pendingIndex = 0;
3127
+ constructor(sourceEnumerator, innerSource, outerKeySelector, innerKeySelector, resultSelector) {
3128
+ super(sourceEnumerator);
3129
+ this.innerSource = innerSource;
3130
+ this.outerKeySelector = outerKeySelector;
3131
+ this.innerKeySelector = innerKeySelector;
3132
+ this.resultSelector = resultSelector;
3133
+ }
3134
+ initialize() {
3135
+ for (const innerItem of this.innerSource) {
3136
+ const key = this.innerKeySelector(innerItem);
3137
+ this.innerLookup.getOrInit(key, () => []).push(innerItem);
3138
+ }
3139
+ }
3140
+ handleNext() {
3141
+ while (true) {
3142
+ if (this.pendingMatches !== null) {
3143
+ if (this.pendingIndex < this.pendingMatches.length) return this.yield(this.resultSelector(this.pendingOuter, this.pendingMatches[this.pendingIndex++]));
3144
+ this.pendingMatches = null;
3145
+ }
3146
+ const nextOuter = this.sourceEnumerator.next();
3147
+ if (nextOuter.done) return this.done();
3148
+ const outerItem = nextOuter.value;
3149
+ const outerKey = this.outerKeySelector(outerItem);
3150
+ const innerItems = this.innerLookup.get(outerKey);
3151
+ if (innerItems === void 0 || innerItems.length === 0) continue;
3152
+ this.pendingOuter = outerItem;
3153
+ this.pendingMatches = innerItems;
3154
+ this.pendingIndex = 0;
3155
+ }
3156
+ }
3157
+ };
3158
+ //#endregion
3159
+ //#region src/enumerators/buffer/reverse.ts
3160
+ /**
3161
+ * Reverses the order of elements in the source sequence.
3162
+ *
3163
+ * @remarks
3164
+ * Deferred. Source is fully buffered on the first iteration of the returned sequence.
3165
+ *
3166
+ * @see {@link TyneqSequence.reverse}
3167
+ * @group Operators
3168
+ * @category Buffering
3169
+ * @internal
3170
+ */
3171
+ var ReverseEnumerator = class extends TyneqEnumerator {
3172
+ buffer = [];
3173
+ index = -1;
3174
+ constructor(sourceEnumerator) {
3175
+ super(sourceEnumerator);
3176
+ }
3177
+ initialize() {
3178
+ while (true) {
3179
+ const { done, value } = this.sourceEnumerator.next();
3180
+ if (done) {
3181
+ this.index = this.buffer.length - 1;
3182
+ break;
3183
+ }
3184
+ this.buffer.push(value);
3185
+ }
3186
+ }
3187
+ handleNext() {
3188
+ if (this.index < 0) return this.done();
3189
+ return this.yield(this.buffer[this.index--]);
3190
+ }
3191
+ };
3192
+ //#endregion
3193
+ //#region src/enumerators/buffer/shuffle.ts
3194
+ /**
3195
+ * Returns the elements of the source sequence in a random order.
3196
+ *
3197
+ * @remarks
3198
+ * Deferred. Source is fully buffered on the first iteration of the returned sequence.
3199
+ *
3200
+ * @see {@link TyneqSequence.shuffle}
3201
+ * @group Operators
3202
+ * @category Buffering
3203
+ * @internal
3204
+ */
3205
+ var ShuffleEnumerator = class extends TyneqEnumerator {
3206
+ buffer = [];
3207
+ currentIndex = 0;
3208
+ constructor(sourceEnumerator) {
3209
+ super(sourceEnumerator);
3210
+ }
3211
+ initialize() {
3212
+ const buffer = Array.from(this.toIterable(this.sourceEnumerator));
3213
+ this.shuffle(buffer);
3214
+ this.buffer = buffer;
3215
+ }
3216
+ handleNext() {
3217
+ if (this.buffer.length <= this.currentIndex) return this.done();
3218
+ const result = this.buffer[this.currentIndex];
3219
+ this.currentIndex++;
3220
+ return this.yield(result);
3221
+ }
3222
+ shuffle(array) {
3223
+ for (let i = array.length - 1; i > 0; i--) {
3224
+ const j = Math.floor(Math.random() * (i + 1));
3225
+ [array[i], array[j]] = [array[j], array[i]];
3226
+ }
3227
+ return array;
3228
+ }
3229
+ toIterable(sourceEnumerator) {
3230
+ return { [Symbol.iterator]: () => sourceEnumerator };
3231
+ }
3232
+ };
3233
+ //#endregion
3234
+ //#region src/enumerators/buffer/union.ts
3235
+ /**
3236
+ * Returns the set union of the source and a second sequence, eliminating duplicates.
3237
+ *
3238
+ * @remarks
3239
+ * Deferred. Source is fully buffered on the first iteration of the returned sequence.
3240
+ *
3241
+ * @see {@link TyneqSequence.union}
3242
+ * @group Operators
3243
+ * @category Buffering
3244
+ * @internal
3245
+ */
3246
+ var UnionEnumerator = class extends TyneqEnumerator {
3247
+ otherValues;
3248
+ bufferedValues = /* @__PURE__ */ new Set();
3249
+ currentEnumerator;
3250
+ isSourceDone = false;
3251
+ constructor(sourceEnumerator, otherValues) {
3252
+ super(sourceEnumerator);
3253
+ this.otherValues = otherValues;
3254
+ this.currentEnumerator = this.sourceEnumerator;
3255
+ }
3256
+ handleNext() {
3257
+ while (true) {
3258
+ const { done, value } = this.currentEnumerator.next();
3259
+ if (done) {
3260
+ if (this.isSourceDone) return this.done();
3261
+ this.isSourceDone = true;
3262
+ this.currentEnumerator = this.otherValues[Symbol.iterator]();
3263
+ continue;
3264
+ }
3265
+ if (!this.bufferedValues.has(value)) {
3266
+ this.bufferedValues.add(value);
3267
+ return this.yield(value);
3268
+ }
3269
+ }
3270
+ }
3271
+ };
3272
+ //#endregion
3273
+ //#region src/enumerators/buffer/unionBy.ts
3274
+ /**
3275
+ * Returns the set union of the source and a second sequence, eliminating duplicates by key.
3276
+ *
3277
+ * @remarks
3278
+ * Deferred. Source is fully buffered on the first iteration of the returned sequence.
3279
+ *
3280
+ * @see {@link TyneqSequence.unionBy}
3281
+ * @group Operators
3282
+ * @category Buffering
3283
+ * @internal
3284
+ */
3285
+ var UnionByEnumerator = class extends TyneqEnumerator {
3286
+ otherValues;
3287
+ bufferedKeys = /* @__PURE__ */ new Set();
3288
+ keySelector;
3289
+ currentEnumerator;
3290
+ isSourceDone = false;
3291
+ constructor(sourceEnumerator, otherValues, keySelector) {
3292
+ super(sourceEnumerator);
3293
+ this.otherValues = otherValues;
3294
+ this.currentEnumerator = this.sourceEnumerator;
3295
+ this.keySelector = keySelector;
3296
+ }
3297
+ handleNext() {
3298
+ while (true) {
3299
+ const { done, value } = this.currentEnumerator.next();
3300
+ if (done) {
3301
+ if (this.isSourceDone) return this.done();
3302
+ this.isSourceDone = true;
3303
+ this.currentEnumerator = this.otherValues[Symbol.iterator]();
3304
+ continue;
3305
+ }
3306
+ const key = this.keySelector(value);
3307
+ if (!this.bufferedKeys.has(key)) {
3308
+ this.bufferedKeys.add(key);
3309
+ return this.yield(value);
3310
+ }
3311
+ }
3312
+ }
3313
+ };
3314
+ //#endregion
3315
+ //#region src/enumerators/buffer/permutations.ts
3316
+ /**
3317
+ * Yields all permutations of the elements in the source sequence.
3318
+ *
3319
+ * @remarks
3320
+ * Deferred. Source is fully buffered on the first iteration of the returned sequence.
3321
+ *
3322
+ * @see {@link TyneqSequence.permutations}
3323
+ * @group Operators
3324
+ * @category Buffering
3325
+ * @internal
3326
+ */
3327
+ var PermutationsEnumerator = class extends TyneqEnumerator {
3328
+ buffer = [];
3329
+ c = [];
3330
+ n = 0;
3331
+ i = -1;
3332
+ initialize() {
3333
+ this.buffer = Array.from(EnumeratorUtility.toIterable(this.sourceEnumerator));
3334
+ this.n = this.buffer.length;
3335
+ this.c = new Array(this.n).fill(0);
3336
+ }
3337
+ handleNext() {
3338
+ if (this.i === -1) {
3339
+ this.i = 1;
3340
+ return this.yield([...this.buffer]);
3341
+ }
3342
+ while (this.i < this.n) if (this.c[this.i] < this.i) {
3343
+ const swapIndex = this.i % 2 === 0 ? 0 : this.c[this.i];
3344
+ [this.buffer[swapIndex], this.buffer[this.i]] = [this.buffer[this.i], this.buffer[swapIndex]];
3345
+ this.c[this.i]++;
3346
+ this.i = 1;
3347
+ return this.yield([...this.buffer]);
3348
+ } else {
3349
+ this.c[this.i] = 0;
3350
+ this.i++;
3351
+ }
3352
+ return this.done();
3353
+ }
3354
+ };
3355
+ //#endregion
3356
+ //#region src/core/TyneqEnumerableBase.ts
3357
+ var TyneqEnumerableBase = @sequence class extends TyneqEnumerableCore {
3358
+ @builtin({ kind: "terminal" }) aggregate(seed, func, resultSelector) {
3359
+ return new AggregateOperator(this, seed, func, resultSelector).process();
3360
+ }
3361
+ @builtin({ kind: "terminal" }) all(predicate) {
3362
+ return new AllOperator(this, predicate).process();
3363
+ }
3364
+ @builtin({ kind: "terminal" }) any(predicate) {
3365
+ return new AnyOperator(this, predicate).process();
3366
+ }
3367
+ @builtin({ kind: "terminal" }) average(selector) {
3368
+ return new AverageOperator(this, selector).process();
3369
+ }
3370
+ @builtin({ kind: "terminal" }) consume() {
3371
+ new ConsumeOperator(this).process();
3372
+ }
3373
+ @builtin({ kind: "terminal" }) contains(value, equalityComparer) {
3374
+ return new ContainsOperator(this, value, equalityComparer).process();
3375
+ }
3376
+ @builtin({ kind: "terminal" }) count() {
3377
+ return new CountOperator(this).process();
3378
+ }
3379
+ @builtin({ kind: "terminal" }) countBy(predicate) {
3380
+ return new CountByOperator(this, predicate).process();
3381
+ }
3382
+ @builtin({ kind: "terminal" }) elementAt(index) {
3383
+ ArgumentUtility.checkSafeInteger({ index });
3384
+ ArgumentUtility.checkNonNegative({ index });
3385
+ return new ElementAtOperator(this, index).process();
3386
+ }
3387
+ @builtin({ kind: "terminal" }) elementAtOrDefault(index, defaultValue) {
3388
+ ArgumentUtility.checkSafeInteger({ index });
3389
+ ArgumentUtility.checkNonNegative({ index });
3390
+ return new ElementAtOrDefaultOperator(this, index, defaultValue).process();
3391
+ }
3392
+ @builtin({ kind: "terminal" }) first(predicate) {
3393
+ return new FirstOperator(this, predicate).process();
3394
+ }
3395
+ @builtin({ kind: "terminal" }) firstOrDefault(predicate, defaultValue) {
3396
+ return new FirstOrDefaultOperator(this, predicate, defaultValue).process();
3397
+ }
3398
+ @builtin({ kind: "terminal" }) indexOf(predicate, startIndex = 0) {
3399
+ return new IndexOfOperator(this, predicate, startIndex).process();
3400
+ }
3401
+ @builtin({ kind: "terminal" }) isNullOrEmpty() {
3402
+ return new IsNullOrEmptyOperator(this).process();
3403
+ }
3404
+ @builtin({ kind: "terminal" }) last(predicate) {
3405
+ return new LastOperator(this, predicate).process();
3406
+ }
3407
+ @builtin({ kind: "terminal" }) lastOrDefault(predicate, defaultValue) {
3408
+ return new LastOrDefaultOperator(this, predicate, defaultValue).process();
3409
+ }
3410
+ @builtin({ kind: "terminal" }) max(comparer) {
3411
+ return new ExtremumOperator(this, 1, "max", comparer).process();
3412
+ }
3413
+ @builtin({ kind: "terminal" }) maxBy(keySelector, comparer) {
3414
+ return new ExtremumByOperator(this, keySelector, 1, "maxBy", comparer).process();
3415
+ }
3416
+ @builtin({ kind: "terminal" }) min(comparer) {
3417
+ return new ExtremumOperator(this, -1, "min", comparer).process();
3418
+ }
3419
+ @builtin({ kind: "terminal" }) minBy(keySelector, comparer) {
3420
+ return new ExtremumByOperator(this, keySelector, -1, "minBy", comparer).process();
3421
+ }
3422
+ @builtin({ kind: "terminal" }) minMax(comparer) {
3423
+ return new MinMaxOperator(this, comparer).process();
3424
+ }
3425
+ @builtin({ kind: "terminal" }) sequenceEqual(other, equalityComparer) {
3426
+ return new SequenceEqualOperator(this, other, equalityComparer).process();
3427
+ }
3428
+ @builtin({ kind: "terminal" }) single(predicate) {
3429
+ return new SingleOperator(this, predicate).process();
3430
+ }
3431
+ @builtin({ kind: "terminal" }) singleOrDefault(predicate, defaultValue) {
3432
+ return new SingleOrDefaultOperator(this, predicate, defaultValue).process();
3433
+ }
3434
+ @builtin({ kind: "terminal" }) endsWith(sequence, equalityComparer) {
3435
+ return new EndsWithOperator(this, sequence, equalityComparer).process();
3436
+ }
3437
+ @builtin({ kind: "terminal" }) startsWith(sequence, equalityComparer) {
3438
+ return new StartsWithOperator(this, sequence, equalityComparer).process();
3439
+ }
3440
+ @builtin({ kind: "terminal" }) sum(selector) {
3441
+ return new SumOperator(this, selector).process();
3442
+ }
3443
+ @builtin({ kind: "terminal" }) toArray() {
3444
+ return new ToArrayOperator(this).process();
3445
+ }
3446
+ @builtin({ kind: "terminal" }) toAsync() {
3447
+ return new ToAsyncOperator(this).process();
3448
+ }
3449
+ @builtin({ kind: "terminal" }) toMap(selector) {
3450
+ return new ToMapOperator(this, selector).process();
3451
+ }
3452
+ @builtin({ kind: "terminal" }) toRecord(selector) {
3453
+ return new ToRecordOperator(this, selector).process();
3454
+ }
3455
+ @builtin({ kind: "terminal" }) toSet() {
3456
+ return new ToSetOperator(this).process();
3457
+ }
3458
+ @builtin({ kind: "streaming" }) ofType(guard) {
3459
+ ArgumentUtility.checkNotOptional({ guard });
3460
+ return this.createSequence(() => new OfTypeEnumerator(this.getEnumerator(), guard), this.createNode("ofType", "streaming", [guard]));
3461
+ }
3462
+ @builtin({ kind: "streaming" }) append(item) {
3463
+ return this.createSequence(() => new AppendEnumerator(this.getEnumerator(), item), this.createNode("append", "streaming", [item]));
3464
+ }
3465
+ @builtin({ kind: "streaming" }) chunk(size) {
3466
+ ArgumentUtility.checkSafeInteger({ size });
3467
+ ArgumentUtility.checkPositive({ size });
3468
+ return this.createSequence(() => new ChunkEnumerator(this.getEnumerator(), size), this.createNode("chunk", "streaming", [size]));
3469
+ }
3470
+ @builtin({ kind: "streaming" }) concat(other) {
3471
+ ArgumentUtility.checkNotOptional({ other });
3472
+ ArgumentUtility.checkIterable({ other });
3473
+ return this.createSequence(() => new ConcatEnumerator(this.getEnumerator(), other), this.createNode("concat", "streaming", [other]));
3474
+ }
3475
+ @builtin({ kind: "streaming" }) defaultIfEmpty(defaultValue) {
3476
+ return this.createSequence(() => new DefaultIfEmptyEnumerator(this.getEnumerator(), defaultValue), this.createNode("defaultIfEmpty", "streaming", [defaultValue]));
3477
+ }
3478
+ @builtin({ kind: "streaming" }) flatten() {
3479
+ const self = this;
3480
+ return self.createSequence(() => new FlattenEnumerator(self.getEnumerator()), self.createNode("flatten", "streaming"));
3481
+ }
3482
+ @builtin({ kind: "streaming" }) pairwise() {
3483
+ return this.createSequence(() => new PairwiseEnumerator(this.getEnumerator()), this.createNode("pairwise", "streaming"));
3484
+ }
3485
+ @builtin({ kind: "streaming" }) populate(value) {
3486
+ return this.createSequence(() => new PopulateEnumerator(this.getEnumerator(), value), this.createNode("populate", "streaming", [value]));
3487
+ }
3488
+ @builtin({ kind: "streaming" }) prepend(item) {
3489
+ return this.createSequence(() => new PrependEnumerator(this.getEnumerator(), item), this.createNode("prepend", "streaming", [item]));
3490
+ }
3491
+ @builtin({ kind: "streaming" }) scan(seed, accumulator) {
3492
+ ArgumentUtility.checkNotOptional({ accumulator });
3493
+ ArgumentUtility.checkFunction({ accumulator });
3494
+ return this.createSequence(() => new ScanEnumerator(this.getEnumerator(), seed, accumulator), this.createNode("scan", "streaming", [seed, accumulator]));
3495
+ }
3496
+ @builtin({ kind: "streaming" }) select(selector) {
3497
+ ArgumentUtility.checkNotOptional({ selector });
3498
+ return this.createSequence(() => new SelectEnumerator(this.getEnumerator(), selector), this.createNode("select", "streaming", [selector]));
3499
+ }
3500
+ @builtin({ kind: "streaming" }) selectMany(selector) {
3501
+ ArgumentUtility.checkNotOptional({ selector });
3502
+ return this.createSequence(() => new SelectManyEnumerator(this.getEnumerator(), selector), this.createNode("selectMany", "streaming", [selector]));
3503
+ }
3504
+ @builtin({ kind: "streaming" }) window(size, step = 1) {
3505
+ ArgumentUtility.checkSafeInteger({ size });
3506
+ ArgumentUtility.checkPositive({ size });
3507
+ ArgumentUtility.checkSafeInteger({ step });
3508
+ ArgumentUtility.checkPositive({ step });
3509
+ return this.createSequence(() => new WindowEnumerator(this.getEnumerator(), size, step), this.createNode("window", "streaming", [size, step]));
3510
+ }
3511
+ @builtin({ kind: "streaming" }) repeat(count) {
3512
+ ArgumentUtility.checkNonNegative({ count });
3513
+ ArgumentUtility.checkInteger({ count });
3514
+ return this.createSequence(() => new RepeatSequenceEnumerator(this.getEnumerator(), this, count), this.createNode("repeat", "streaming", [count]));
3515
+ }
3516
+ @builtin({ kind: "streaming" }) skip(count) {
3517
+ ArgumentUtility.checkSafeInteger({ count });
3518
+ ArgumentUtility.checkNonNegative({ count });
3519
+ return this.createSequence(() => new SkipEnumerator(this.getEnumerator(), count), this.createNode("skip", "streaming", [count]));
3520
+ }
3521
+ @builtin({ kind: "streaming" }) skipLast(count) {
3522
+ ArgumentUtility.checkSafeInteger({ count });
3523
+ ArgumentUtility.checkNonNegative({ count });
3524
+ return this.createSequence(() => new SkipLastEnumerator(this.getEnumerator(), count), this.createNode("skipLast", "streaming", [count]));
3525
+ }
3526
+ @builtin({ kind: "streaming" }) skipUntil(predicate) {
3527
+ ArgumentUtility.checkNotOptional({ predicate });
3528
+ return this.createSequence(() => new SkipUntilEnumerator(this.getEnumerator(), predicate), this.createNode("skipUntil", "streaming", [predicate]));
3529
+ }
3530
+ @builtin({ kind: "streaming" }) skipWhile(predicate) {
3531
+ ArgumentUtility.checkNotOptional({ predicate });
3532
+ return this.createSequence(() => new SkipWhileEnumerator(this.getEnumerator(), predicate), this.createNode("skipWhile", "streaming", [predicate]));
3533
+ }
3534
+ @builtin({ kind: "streaming" }) slice(start, end) {
3535
+ ArgumentUtility.checkNonNegative({ start });
3536
+ ArgumentUtility.checkSafeInteger({ start });
3537
+ if (end !== void 0) {
3538
+ ArgumentUtility.checkNonNegative({ end });
3539
+ ArgumentUtility.checkSafeInteger({ end });
3540
+ if (end < start) throw new ArgumentOutOfRangeError("end", "`end` must be greater than or equal to `start`.");
3541
+ }
3542
+ const resolvedEnd = end ?? Number.MAX_SAFE_INTEGER;
3543
+ return this.createSequence(() => new SliceEnumerator(this.getEnumerator(), start, resolvedEnd), this.createNode("slice", "streaming", [start, end]));
3544
+ }
3545
+ @builtin({ kind: "streaming" }) split(splitOn) {
3546
+ ArgumentUtility.checkNotOptional({ splitOn });
3547
+ return this.createSequence(() => new SplitEnumerator(this.getEnumerator(), splitOn), this.createNode("split", "streaming", [splitOn]));
3548
+ }
3549
+ @builtin({ kind: "streaming" }) take(count) {
3550
+ ArgumentUtility.checkSafeInteger({ count });
3551
+ ArgumentUtility.checkNonNegative({ count });
3552
+ return this.createSequence(() => new TakeEnumerator(this.getEnumerator(), count), this.createNode("take", "streaming", [count]));
3553
+ }
3554
+ @builtin({ kind: "streaming" }) takeUntil(predicate) {
3555
+ ArgumentUtility.checkNotOptional({ predicate });
3556
+ return this.createSequence(() => new TakeUntilEnumerator(this.getEnumerator(), predicate), this.createNode("takeUntil", "streaming", [predicate]));
3557
+ }
3558
+ @builtin({ kind: "streaming" }) takeWhile(predicate) {
3559
+ ArgumentUtility.checkNotOptional({ predicate });
3560
+ return this.createSequence(() => new TakeWhileEnumerator(this.getEnumerator(), predicate), this.createNode("takeWhile", "streaming", [predicate]));
3561
+ }
3562
+ @builtin({ kind: "streaming" }) tap(action) {
3563
+ ArgumentUtility.checkNotOptional({ action });
3564
+ return this.createSequence(() => new TapEnumerator(this.getEnumerator(), action), this.createNode("tap", "streaming", [action]));
3565
+ }
3566
+ @builtin({ kind: "streaming" }) tapIf(action, predicate) {
3567
+ ArgumentUtility.checkNotOptional({ action });
3568
+ ArgumentUtility.checkNotOptional({ predicate });
3569
+ return this.createSequence(() => new TapIfEnumerator(this.getEnumerator(), action, predicate), this.createNode("tapIf", "streaming", [action, predicate]));
3570
+ }
3571
+ @builtin({ kind: "streaming" }) throttle(count) {
3572
+ ArgumentUtility.checkSafeInteger({ count });
3573
+ ArgumentUtility.checkPositive({ count });
3574
+ return this.createSequence(() => new ThrottleEnumerator(this.getEnumerator(), count), this.createNode("throttle", "streaming", [count]));
3575
+ }
3576
+ @builtin({ kind: "streaming" }) where(predicate) {
3577
+ ArgumentUtility.checkNotOptional({ predicate });
3578
+ return this.createSequence(() => new WhereEnumerator(this.getEnumerator(), predicate), this.createNode("where", "streaming", [predicate]));
3579
+ }
3580
+ @builtin({ kind: "streaming" }) zip(other, selector) {
3581
+ ArgumentUtility.checkNotOptional({ other });
3582
+ ArgumentUtility.checkIterable({ other });
3583
+ ArgumentUtility.checkNotOptional({ selector });
3584
+ return this.createSequence(() => new ZipEnumerator(this.getEnumerator(), other, selector), this.createNode("zip", "streaming", [other, selector]));
3585
+ }
3586
+ @builtin({ kind: "buffer" }) backsert(index, other) {
3587
+ ArgumentUtility.checkNotOptional({ other });
3588
+ ArgumentUtility.checkSafeInteger({ index });
3589
+ ArgumentUtility.checkNonNegative({ index });
3590
+ ArgumentUtility.checkIterable({ other });
3591
+ return this.createSequence(() => new BacksertEnumerator(this.getEnumerator(), index, other), this.createNode("backsert", "buffer", [index, other]));
3592
+ }
3593
+ @builtin({ kind: "buffer" }) distinct() {
3594
+ return this.createSequence(() => new DistinctEnumerator(this.getEnumerator()), this.createNode("distinct", "buffer"));
3595
+ }
3596
+ @builtin({ kind: "buffer" }) distinctBy(keySelector) {
3597
+ ArgumentUtility.checkNotOptional({ keySelector });
3598
+ return this.createSequence(() => new DistinctByEnumerator(this.getEnumerator(), keySelector), this.createNode("distinctBy", "buffer", [keySelector]));
3599
+ }
3600
+ @builtin({ kind: "buffer" }) except(excludedValues) {
3601
+ ArgumentUtility.checkNotOptional({ excludedValues });
3602
+ ArgumentUtility.checkIterable({ excludedValues });
3603
+ return this.createSequence(() => new ExceptEnumerator(this.getEnumerator(), excludedValues), this.createNode("except", "buffer", [excludedValues]));
3604
+ }
3605
+ @builtin({ kind: "buffer" }) exceptBy(excludedKeys, keySelector) {
3606
+ ArgumentUtility.checkNotOptional({ excludedKeys });
3607
+ ArgumentUtility.checkIterable({ excludedKeys });
3608
+ ArgumentUtility.checkNotOptional({ keySelector });
3609
+ return this.createSequence(() => new ExceptByEnumerator(this.getEnumerator(), excludedKeys, keySelector), this.createNode("exceptBy", "buffer", [excludedKeys, keySelector]));
3610
+ }
3611
+ @builtin({ kind: "buffer" }) groupBy(keySelector, valueSelector, resultSelector) {
3612
+ ArgumentUtility.checkNotOptional({ keySelector });
3613
+ ArgumentUtility.checkNotOptional({ valueSelector });
3614
+ ArgumentUtility.checkNotOptional({ resultSelector });
3615
+ const groupFactory = (values) => this.createEnumerable({ getEnumerator: () => values[Symbol.iterator]() }, null);
3616
+ return this.createSequence(() => new GroupByEnumerator(this.getEnumerator(), keySelector, valueSelector, resultSelector, groupFactory), this.createNode("groupBy", "buffer", [
3617
+ keySelector,
3618
+ valueSelector,
3619
+ resultSelector
3620
+ ]));
3621
+ }
3622
+ @builtin({ kind: "buffer" }) groupJoin(inner, outerKeySelector, innerKeySelector, resultSelector) {
3623
+ ArgumentUtility.checkNotOptional({ inner });
3624
+ ArgumentUtility.checkIterable({ inner });
3625
+ ArgumentUtility.checkNotOptional({ outerKeySelector });
3626
+ ArgumentUtility.checkNotOptional({ innerKeySelector });
3627
+ ArgumentUtility.checkNotOptional({ resultSelector });
3628
+ const groupFactory = (values) => this.createEnumerable({ getEnumerator: () => values[Symbol.iterator]() }, null);
3629
+ return this.createSequence(() => new GroupJoinEnumerator(this.getEnumerator(), inner, outerKeySelector, innerKeySelector, resultSelector, groupFactory), this.createNode("groupJoin", "buffer", [
3630
+ inner,
3631
+ outerKeySelector,
3632
+ innerKeySelector,
3633
+ resultSelector
3634
+ ]));
3635
+ }
3636
+ @builtin({ kind: "buffer" }) intersect(intersectedValues) {
3637
+ ArgumentUtility.checkNotOptional({ intersectedValues });
3638
+ ArgumentUtility.checkIterable({ intersectedValues });
3639
+ return this.createSequence(() => new IntersectEnumerator(this.getEnumerator(), intersectedValues), this.createNode("intersect", "buffer", [intersectedValues]));
3640
+ }
3641
+ @builtin({ kind: "buffer" }) intersectBy(intersectedKeys, keySelector) {
3642
+ ArgumentUtility.checkNotOptional({ intersectedKeys });
3643
+ ArgumentUtility.checkIterable({ intersectedKeys });
3644
+ ArgumentUtility.checkNotOptional({ keySelector });
3645
+ return this.createSequence(() => new IntersectByEnumerator(this.getEnumerator(), intersectedKeys, keySelector), this.createNode("intersectBy", "buffer", [intersectedKeys, keySelector]));
3646
+ }
3647
+ @builtin({ kind: "buffer" }) join(inner, outerKeySelector, innerKeySelector, resultSelector) {
3648
+ ArgumentUtility.checkNotOptional({ inner });
3649
+ ArgumentUtility.checkIterable({ inner });
3650
+ ArgumentUtility.checkNotOptional({ outerKeySelector });
3651
+ ArgumentUtility.checkNotOptional({ innerKeySelector });
3652
+ ArgumentUtility.checkNotOptional({ resultSelector });
3653
+ return this.createSequence(() => new JoinEnumerator(this.getEnumerator(), inner, outerKeySelector, innerKeySelector, resultSelector), this.createNode("join", "buffer", [
3654
+ inner,
3655
+ outerKeySelector,
3656
+ innerKeySelector,
3657
+ resultSelector
3658
+ ]));
3659
+ }
3660
+ @builtin({ kind: "buffer" }) permutations() {
3661
+ return this.createSequence(() => new PermutationsEnumerator(this.getEnumerator()), this.createNode("permutations", "buffer"));
3662
+ }
3663
+ @builtin({ kind: "buffer" }) reverse() {
3664
+ return this.createSequence(() => new ReverseEnumerator(this.getEnumerator()), this.createNode("reverse", "buffer"));
3665
+ }
3666
+ @builtin({ kind: "buffer" }) shuffle() {
3667
+ return this.createSequence(() => new ShuffleEnumerator(this.getEnumerator()), this.createNode("shuffle", "buffer"));
3668
+ }
3669
+ @builtin({ kind: "buffer" }) union(otherValues) {
3670
+ ArgumentUtility.checkNotOptional({ otherValues });
3671
+ ArgumentUtility.checkIterable({ otherValues });
3672
+ return this.createSequence(() => new UnionEnumerator(this.getEnumerator(), otherValues), this.createNode("union", "buffer", [otherValues]));
3673
+ }
3674
+ @builtin({ kind: "buffer" }) unionBy(otherValues, keySelector) {
3675
+ ArgumentUtility.checkNotOptional({ otherValues });
3676
+ ArgumentUtility.checkIterable({ otherValues });
3677
+ ArgumentUtility.checkNotOptional({ keySelector });
3678
+ return this.createSequence(() => new UnionByEnumerator(this.getEnumerator(), otherValues, keySelector), this.createNode("unionBy", "buffer", [otherValues, keySelector]));
3679
+ }
3680
+ };
3681
+ //#endregion
3682
+ //#region src/enumerators/buffer/memoize.ts
3683
+ /**
3684
+ * Iterates a cached enumerable, replaying previously computed elements on subsequent iterations.
3685
+ *
3686
+ * @remarks
3687
+ * Deferred. Source is fully buffered on the first iteration of the returned sequence.
3688
+ *
3689
+ * @see {@link TyneqSequence.memoize}
3690
+ * @group Operators
3691
+ * @category Buffering
3692
+ * @internal
3693
+ */
3694
+ var MemoizeEnumerator = class extends TyneqBaseEnumerator {
3695
+ cachedEnumerable;
3696
+ index = 0;
3697
+ constructor(cachedEnumerable) {
3698
+ super();
3699
+ this.cachedEnumerable = cachedEnumerable;
3700
+ }
3701
+ disposeSource() {}
3702
+ handleNext() {
3703
+ const result = this.cachedEnumerable.tryGetAtFromCache(this.index);
3704
+ if (!result.has) return this.done();
3705
+ this.index++;
3706
+ return this.yield(result.value);
3707
+ }
3708
+ };
3709
+ //#endregion
3710
+ //#region src/core/ordering/BaseEnumerableSorter.ts
3711
+ /**
3712
+ * Abstract base for multi-key stable sorters used by the ordering infrastructure.
3713
+ *
3714
+ * @remarks
3715
+ * `computeKeys()` pre-computes sort keys for each element; `compareKeys()` compares by index.
3716
+ * Sorters chain together via the `next` field on {@link TyneqEnumerableSorter} to implement
3717
+ * multi-key (thenBy) sorting.
3718
+ *
3719
+ * @internal
3720
+ */
3721
+ var BaseEnumerableSorter = class {
3722
+ /**
3723
+ * Returns a stable sorted index map for `source[0..count-1]`.
3724
+ *
3725
+ * @returns An array of indices sorted by the key order defined by this sorter chain.
3726
+ */
3727
+ sort(source, count) {
3728
+ this.computeKeys([...source], count);
3729
+ const indexMap = Array.from({ length: count }, (_, i) => i);
3730
+ indexMap.sort((a, b) => this.compareKeys(a, b));
3731
+ return indexMap;
3732
+ }
3733
+ };
3734
+ //#endregion
3735
+ //#region src/core/ordering/TyneqEnumerableSorter.ts
3736
+ /**
3737
+ * Concrete sorter that extracts keys via a selector and compares them with a comparer.
3738
+ *
3739
+ * @remarks
3740
+ * Chains to a `next` sorter for secondary sort keys (thenBy). Stability is preserved by
3741
+ * falling back to index comparison when keys are equal at the innermost sorter level.
3742
+ *
3743
+ * @internal
3744
+ */
3745
+ var TyneqEnumerableSorter = class extends BaseEnumerableSorter {
3746
+ keys = [];
3747
+ keySelector;
3748
+ comparer;
3749
+ descending;
3750
+ next = null;
3751
+ constructor(keySelector, comparer, descending, next) {
3752
+ super();
3753
+ ArgumentUtility.checkNotOptional({ keySelector });
3754
+ ArgumentUtility.checkNotOptional({ comparer });
3755
+ this.keySelector = keySelector;
3756
+ this.comparer = comparer;
3757
+ this.descending = descending ? -1 : 1;
3758
+ this.next = next ?? null;
3759
+ }
3760
+ computeKeys(source, count) {
3761
+ this.keys = new Array(count);
3762
+ for (let i = 0; i < count; i++) this.keys[i] = this.keySelector(source[i]);
3763
+ this.next?.computeKeys(source, count);
3764
+ }
3765
+ compareKeys(i, j) {
3766
+ const result = this.comparer(this.keys[i], this.keys[j]) * this.descending;
3767
+ if (result !== 0) return result;
3768
+ if (this.next === null) return this.stabilityCompare(i, j);
3769
+ return this.next.compareKeys(i, j);
3770
+ }
3771
+ stabilityCompare(i, j) {
3772
+ return i - j;
3773
+ }
3774
+ };
3775
+ //#endregion
3776
+ //#region src/enumerators/buffer/orderBy.ts
3777
+ /**
3778
+ * Yields the elements of an ordered sequence in sorted order.
3779
+ *
3780
+ * @remarks
3781
+ * Deferred. Source is fully buffered on the first iteration of the returned sequence.
3782
+ *
3783
+ * @see {@link TyneqSequence.orderBy}
3784
+ * @group Operators
3785
+ * @category Buffering
3786
+ * @internal
3787
+ */
3788
+ var OrderByEnumerator = class extends TyneqBaseEnumerator {
3789
+ buffer = [];
3790
+ indexMap = [];
3791
+ currentIndex = 0;
3792
+ orderedEnumerable;
3793
+ constructor(orderedEnumerable) {
3794
+ super();
3795
+ this.orderedEnumerable = orderedEnumerable;
3796
+ }
3797
+ initialize() {
3798
+ this.buffer = Array.from(this.orderedEnumerable.source);
3799
+ const sorter = this.getSorter(this.orderedEnumerable);
3800
+ this.indexMap = sorter.sort(this.buffer, this.buffer.length);
3801
+ }
3802
+ handleNext() {
3803
+ if (this.currentIndex >= this.indexMap.length) return this.done();
3804
+ const index = this.indexMap[this.currentIndex++];
3805
+ return this.yield(this.buffer[index]);
3806
+ }
3807
+ getSorter(base) {
3808
+ let sorter = null;
3809
+ for (let enumerable = base; enumerable !== null; enumerable = enumerable.parent) sorter = enumerable.getSorter(sorter);
3810
+ return sorter;
3811
+ }
3812
+ };
3813
+ //#endregion
3814
+ //#region src/core/ordering/TyneqOrderedEnumerable.ts
3815
+ const ASC_TO_DESC = {
3816
+ "orderBy": "orderByDescending",
3817
+ "thenBy": "thenByDescending"
3818
+ };
3819
+ const DESC_TO_ASC = {
3820
+ "orderByDescending": "orderBy",
3821
+ "thenByDescending": "thenBy"
3822
+ };
3823
+ var TyneqOrderedEnumerable = @sequence class TyneqOrderedEnumerable extends TyneqEnumerableBase {
3824
+ keySelector;
3825
+ comparer;
3826
+ descending;
3827
+ source;
3828
+ parent;
3829
+ [tyneqQueryNode];
3830
+ constructor(source, keySelector, comparer, descending, parent, node) {
3831
+ super();
3832
+ ArgumentUtility.checkNotOptional({ source });
3833
+ ArgumentUtility.checkNotOptional({ keySelector });
3834
+ ArgumentUtility.checkNotOptional({ comparer });
3835
+ this.source = source;
3836
+ this.keySelector = keySelector;
3837
+ this.comparer = comparer;
3838
+ this.descending = descending;
3839
+ this.parent = parent ?? null;
3840
+ this[tyneqQueryNode] = node ?? null;
3841
+ }
3842
+ asc() {
3843
+ if (!this.descending) return this;
3844
+ const currentNode = this[tyneqQueryNode];
3845
+ const node = currentNode ? new QueryNode(DESC_TO_ASC[currentNode.operatorName] ?? currentNode.operatorName, currentNode.args, currentNode.source, currentNode.category, currentNode.sourceKind) : null;
3846
+ return new TyneqOrderedEnumerable(this.source, this.keySelector, this.comparer, false, this.parent ?? void 0, node);
3847
+ }
3848
+ desc() {
3849
+ if (this.descending) return this;
3850
+ const currentNode = this[tyneqQueryNode];
3851
+ const node = currentNode ? new QueryNode(ASC_TO_DESC[currentNode.operatorName] ?? currentNode.operatorName, currentNode.args, currentNode.source, currentNode.category, currentNode.sourceKind) : null;
3852
+ return new TyneqOrderedEnumerable(this.source, this.keySelector, this.comparer, true, this.parent ?? void 0, node);
3853
+ }
3854
+ getEnumerator() {
3855
+ return new OrderByEnumerator(this);
3856
+ }
3857
+ getSorter(next) {
3858
+ return new TyneqEnumerableSorter(this.keySelector, this.comparer, this.descending, next ?? void 0);
3859
+ }
3860
+ @builtin({ kind: "buffer" }) thenBy(keySelector, comparer) {
3861
+ const node = new QueryNode("thenBy", comparer !== void 0 ? [keySelector, comparer] : [keySelector], this[tyneqQueryNode], "buffer");
3862
+ return new TyneqOrderedEnumerable(this.source, keySelector, comparer ?? TyneqComparer.defaultComparer, false, this, node);
3863
+ }
3864
+ @builtin({ kind: "buffer" }) thenByDescending(keySelector, comparer) {
3865
+ const node = new QueryNode("thenByDescending", comparer !== void 0 ? [keySelector, comparer] : [keySelector], this[tyneqQueryNode], "buffer");
3866
+ return new TyneqOrderedEnumerable(this.source, keySelector, comparer ?? TyneqComparer.defaultComparer, true, this, node);
3867
+ }
3868
+ createEnumerable(factory, node) {
3869
+ return new TyneqEnumerable(factory, node);
3870
+ }
3871
+ createOrderedEnumerable(keySelector, comparer, descending, node) {
3872
+ return new TyneqOrderedEnumerable(this.source, keySelector, comparer, descending, this, node);
3873
+ }
3874
+ createCachedEnumerable(source, node) {
3875
+ return new TyneqCachedEnumerable(source, node);
3876
+ }
3877
+ };
3878
+ //#endregion
3879
+ //#region src/core/TyneqCachedEnumerable.ts
3880
+ var TyneqCachedEnumerable = @sequence class TyneqCachedEnumerable extends TyneqEnumerableBase {
3881
+ source;
3882
+ cache = [];
3883
+ done = false;
3884
+ sourceEnumerator = null;
3885
+ [tyneqQueryNode];
3886
+ constructor(source, node) {
3887
+ super();
3888
+ this.source = source;
3889
+ this[tyneqQueryNode] = node ?? null;
3890
+ }
3891
+ getEnumerator() {
3892
+ return new MemoizeEnumerator(this);
3893
+ }
3894
+ @builtin({ kind: "cache" }) refresh() {
3895
+ this.cache = [];
3896
+ this.done = false;
3897
+ this.sourceEnumerator?.return?.();
3898
+ this.sourceEnumerator = null;
3899
+ return this;
3900
+ }
3901
+ tryGetAtFromCache(index) {
3902
+ if (index < this.cache.length) return {
3903
+ has: true,
3904
+ value: this.cache[index]
3905
+ };
3906
+ if (this.done) return { has: false };
3907
+ if (this.sourceEnumerator === null) this.sourceEnumerator = this.source.getEnumerator();
3908
+ const next = this.sourceEnumerator.next();
3909
+ if (!next.done) {
3910
+ const value = next.value;
3911
+ this.cache.push(value);
3912
+ return {
3913
+ has: true,
3914
+ value
3915
+ };
3916
+ }
3917
+ this.done = true;
3918
+ this.sourceEnumerator.return?.();
3919
+ this.sourceEnumerator = null;
3920
+ return { has: false };
3921
+ }
3922
+ createEnumerable(factory, node) {
3923
+ return new TyneqEnumerable(factory, node);
3924
+ }
3925
+ createOrderedEnumerable(keySelector, comparer, descending, node) {
3926
+ return new TyneqOrderedEnumerable(this, keySelector, comparer, descending, void 0, node);
3927
+ }
3928
+ createCachedEnumerable(source, node) {
3929
+ return new TyneqCachedEnumerable(source, node);
3930
+ }
3931
+ };
3932
+ //#endregion
3933
+ //#region src/core/TyneqEnumerable.ts
3934
+ /**
3935
+ * The standard concrete implementation of {@link TyneqSequence}.
3936
+ *
3937
+ * @remarks
3938
+ * Created by operator methods in {@link TyneqEnumerableBase} and by the `Tyneq` factory.
3939
+ * Delegates element production to the `EnumeratorFactory` passed at construction.
3940
+ *
3941
+ * @internal
3942
+ */
3943
+ var TyneqEnumerable = class TyneqEnumerable extends TyneqEnumerableBase {
3944
+ enumeratorFactory;
3945
+ [tyneqQueryNode];
3946
+ constructor(enumeratorFactory, node) {
3947
+ super();
3948
+ ArgumentUtility.checkNotOptional({ enumeratorFactory });
3949
+ this.enumeratorFactory = enumeratorFactory;
3950
+ this[tyneqQueryNode] = node ?? null;
3951
+ }
3952
+ getEnumerator() {
3953
+ return this.enumeratorFactory.getEnumerator();
3954
+ }
3955
+ createEnumerable(factory, node) {
3956
+ return new TyneqEnumerable(factory, node);
3957
+ }
3958
+ createOrderedEnumerable(keySelector, comparer, descending, node) {
3959
+ return new TyneqOrderedEnumerable(this, keySelector, comparer, descending, void 0, node);
3960
+ }
3961
+ createCachedEnumerable(source, node) {
3962
+ return new TyneqCachedEnumerable(source, node);
3963
+ }
3964
+ };
3965
+ //#endregion
3966
+ //#region src/plugin/decorators/source.ts
3967
+ /**
3968
+ * Static method decorator that registers the decorated method as a source operator.
3969
+ *
3970
+ * The method itself becomes the factory: when the compiler resolves a source node
3971
+ * whose `operatorName` matches the registered name, it calls the method with the
3972
+ * node's `args`. No duplication of implementation - the registration points directly
3973
+ * at the method.
3974
+ *
3975
+ * The operator name defaults to the method name, so `@source()` is sufficient when
3976
+ * they match.
3977
+ *
3978
+ * @param options - Optional config. Omit entirely to use all defaults.
3979
+ *
3980
+ * @example
3981
+ * ```ts
3982
+ * import { source } from "tyneq/plugin";
3983
+ * import { Tyneq } from "tyneq";
3984
+ *
3985
+ * class MySources {
3986
+ * @source()
3987
+ * public static fibonacci(count: number): ReturnType<typeof Tyneq.range> {
3988
+ * // registered as "fibonacci"
3989
+ * }
3990
+ *
3991
+ * @source({ name: "fib2", source: "external" })
3992
+ * public static fibonacci2(count: number): ReturnType<typeof Tyneq.range> {
3993
+ * // registered as "fib2"
3994
+ * }
3995
+ * }
3996
+ * ```
3997
+ *
3998
+ * @group Decorators
3999
+ */
4000
+ function source(options = {}) {
4001
+ return function(method, context) {
4002
+ const operatorName = options.name ?? context.name;
4003
+ const operatorSource = options.source ?? "external";
4004
+ OperatorRegistry.registerSource(operatorName, method, operatorSource);
4005
+ };
4006
+ }
4007
+ //#endregion
4008
+ //#region src/plugin/RegistrationUtility.ts
4009
+ /**
4010
+ * Low-level helpers used by every decorator and registration function in `src/plugin`.
4011
+ *
4012
+ * @remarks
4013
+ * These methods centralise two patterns that would otherwise be copy-pasted into every
4014
+ * operator `impl` closure:
4015
+ *
4016
+ * - {@link buildEnumerable} - validate, create a query node, then call
4017
+ * `createEnumerable`. Used by every standard operator decorator and factory.
4018
+ * - {@link buildQueryNode} - create a query node only. Used by `createOrderedOperator`
4019
+ * and `createCachedOperator`, which construct the result sequence themselves.
4020
+ *
4021
+ * `asSequenceFactory` is kept private to this class; it exists only to resolve the
4022
+ * `protected` access modifier on `createEnumerable` via a structural double-cast.
4023
+ *
4024
+ * @internal
4025
+ */
4026
+ var RegistrationUtility = class RegistrationUtility {
4027
+ constructor() {}
4028
+ /**
4029
+ * Builds a new sequence from an enumerator factory and registers a query plan node.
4030
+ *
4031
+ * @remarks
4032
+ * Every standard (non-terminal, non-ordered, non-cached) operator `impl` follows
4033
+ * the same pattern: get the `SequenceFactory` view of `this`, create a `QueryNode`,
4034
+ * then call `factory.createEnumerable(enumeratorFactory, node)`. This method
4035
+ * centralises that pattern so decorator and factory `impl` bodies stay minimal.
4036
+ *
4037
+ * @param sequence - The current sequence (`this` inside an `impl` function).
4038
+ * @param name - Operator name, used as the query node label.
4039
+ * @param userArgs - Arguments passed by the caller, captured in the query node.
4040
+ * @param category - Operator category for the query node.
4041
+ * @param enumeratorFactory - The factory that produces the new enumerator.
4042
+ */
4043
+ static buildEnumerable(sequence, name, userArgs, category, enumeratorFactory) {
4044
+ const factory = RegistrationUtility.asSequenceFactory(sequence);
4045
+ const node = new QueryNode(name, userArgs, factory[tyneqQueryNode], category);
4046
+ return factory.createEnumerable(enumeratorFactory, node);
4047
+ }
4048
+ /**
4049
+ * Creates a `QueryPlanNode` for an operator, linked to the upstream node on `sequence`.
4050
+ *
4051
+ * @remarks
4052
+ * Used by `createOrderedOperator` and `createCachedOperator`, which hand the node
4053
+ * directly to their own factory function (because those factories construct the result
4054
+ * sequence themselves rather than delegating to `createEnumerable`).
4055
+ *
4056
+ * @param sequence - The current sequence (`this` inside an `impl` function).
4057
+ * @param name - Operator name, used as the query node label.
4058
+ * @param userArgs - Arguments passed by the caller, captured in the query node.
4059
+ * @param category - Operator category for the query node.
4060
+ */
4061
+ static buildQueryNode(sequence, name, userArgs, category) {
4062
+ return new QueryNode(name, userArgs, RegistrationUtility.asSequenceFactory(sequence)[tyneqQueryNode], category);
4063
+ }
4064
+ /**
4065
+ * Narrows a `TyneqEnumerableBase` to its `SequenceFactory` view.
4066
+ *
4067
+ * @remarks
4068
+ * The double-cast (`as unknown as SequenceFactory`) is required because
4069
+ * `createEnumerable` and `createCachedEnumerable` are `protected` on the
4070
+ * class hierarchy. `SequenceFactory` is a structural interface that describes
4071
+ * the same shape, allowing registration-time closures to call factory methods
4072
+ * without changing their access modifier. This is a known architectural trade-off
4073
+ * documented in `tasks/lessons.md` under "Architecture Decisions".
4074
+ */
4075
+ static asSequenceFactory(sequence) {
4076
+ return sequence;
4077
+ }
4078
+ };
4079
+ //#endregion
4080
+ //#region src/plugin/decorators/operator.ts
4081
+ /**
4082
+ * Class decorator that registers a `TyneqEnumerator` subclass as an operator on every sequence.
4083
+ *
4084
+ * Validation runs eagerly at the call site, before the lazy enumerator is created.
4085
+ *
4086
+ * @param name - Method name to expose on every sequence.
4087
+ * @param category - Operator kind (`"streaming"` | `"buffer"`).
4088
+ * @param validate - Optional eager validation function for user-supplied arguments.
4089
+ *
4090
+ * @example
4091
+ * ```ts
4092
+ * import { operator, TyneqEnumerator } from "tyneq/plugin";
4093
+ * import type { Enumerator } from "tyneq";
4094
+ *
4095
+ * @operator<[predicate: (item: unknown) => boolean]>("myFilter", "streaming", (predicate) => {
4096
+ * if (typeof predicate !== "function") throw new Error("predicate must be a function");
4097
+ * })
4098
+ * class MyFilterEnumerator<T> extends TyneqEnumerator<T> {
4099
+ * public constructor(source: Enumerator<T>, private readonly predicate: (item: T) => boolean) {
4100
+ * super(source);
4101
+ * }
4102
+ * protected handleNext(): IteratorResult<T> {
4103
+ * while (true) {
4104
+ * const next = this.sourceEnumerator.next();
4105
+ * if (next.done || this.predicate(next.value)) return next;
4106
+ * }
4107
+ * }
4108
+ * }
4109
+ * ```
4110
+ *
4111
+ * @group Decorators
4112
+ */
4113
+ function operator(name, category, validate) {
4114
+ return function(target, _context) {
4115
+ if (!reflect(target.prototype).hasMethod("handleNext")) throw new PluginError(`@operator("${name}"): class "${target.name}" must define a protected handleNext(): IteratorResult<T> method. Ensure the class extends TyneqEnumerator<TInput, TOutput>.`, "operator", target.name);
4116
+ OperatorRegistry.register({
4117
+ metadata: OperatorMetadata.forCategory(category, name, TyneqEnumerableBase),
4118
+ impl: function(...userArgs) {
4119
+ validate?.(...userArgs);
4120
+ const base = this;
4121
+ return RegistrationUtility.buildEnumerable(this, name, userArgs, category, { getEnumerator() {
4122
+ return new target(base.getEnumerator(), ...userArgs);
4123
+ } });
4124
+ }
4125
+ });
4126
+ return target;
4127
+ };
4128
+ }
4129
+ //#endregion
4130
+ //#region src/plugin/decorators/orderedOperator.ts
4131
+ /**
4132
+ * Class decorator that registers a `TyneqOrderedEnumerator` subclass as an operator
4133
+ * available only on ordered sequences.
4134
+ *
4135
+ * The enumerator constructor receives the full `TyneqOrderedEnumerable` as its first
4136
+ * argument (not just an `Enumerator<T>`).
4137
+ *
4138
+ * @param name - Method name to expose on ordered sequences.
4139
+ * @param category - Operator kind (`"streaming"` | `"buffer"`).
4140
+ * @param validate - Optional eager validation function for user-supplied arguments.
4141
+ *
4142
+ * @example
4143
+ * ```ts
4144
+ * import { orderedOperator, TyneqOrderedEnumerator } from "tyneq/plugin";
4145
+ *
4146
+ * @orderedOperator("myThenBy", "buffer", (keySelector) => {
4147
+ * if (typeof keySelector !== "function") throw new Error("keySelector must be a function");
4148
+ * })
4149
+ * class MyThenByEnumerator<T> extends TyneqOrderedEnumerator<T> {
4150
+ * private readonly iter: Enumerator<T>;
4151
+ * public constructor(source: OrderedEnumerable<T>, private readonly keySelector: (item: T) => unknown) {
4152
+ * super(source);
4153
+ * this.iter = this.orderedSource.getEnumerator();
4154
+ * }
4155
+ * protected handleNext(): IteratorResult<T> {
4156
+ * // this.orderedSource gives access to the full OrderedEnumerable
4157
+ * return this.iter.next();
4158
+ * }
4159
+ * }
4160
+ * ```
4161
+ *
4162
+ * @group Decorators
4163
+ */
4164
+ function orderedOperator(name, category, validate) {
4165
+ return function(target, _context) {
4166
+ if (!reflect(target.prototype).hasMethod("handleNext")) throw new PluginError(`@orderedOperator("${name}"): class "${target.name}" must define a protected handleNext(): IteratorResult<T> method. Ensure the class extends TyneqOrderedEnumerator<T>.`, "orderedOperator", target.name);
4167
+ OperatorRegistry.register({
4168
+ metadata: OperatorMetadata.forCategory(category, name, TyneqOrderedEnumerable),
4169
+ impl: function(...userArgs) {
4170
+ validate?.(...userArgs);
4171
+ const base = this;
4172
+ return RegistrationUtility.buildEnumerable(this, name, userArgs, category, { getEnumerator: () => new target(base, ...userArgs) });
4173
+ }
4174
+ });
4175
+ return target;
4176
+ };
4177
+ }
4178
+ //#endregion
4179
+ //#region src/plugin/decorators/cachedOperator.ts
4180
+ /**
4181
+ * Class decorator that registers a `TyneqCachedEnumerator` subclass as an operator
4182
+ * available only on cached sequences.
4183
+ *
4184
+ * The enumerator constructor receives the full `TyneqCachedEnumerable` as its first
4185
+ * argument (not just an `Enumerator<T>`).
4186
+ *
4187
+ * @param name - Method name to expose on cached sequences.
4188
+ * @param category - Operator kind (`"streaming"` | `"buffer"`).
4189
+ * @param validate - Optional eager validation function for user-supplied arguments.
4190
+ *
4191
+ * @example
4192
+ * ```ts
4193
+ * import { cachedOperator, TyneqCachedEnumerator } from "tyneq/plugin";
4194
+ *
4195
+ * @cachedOperator("myRefresh", "buffer")
4196
+ * class MyRefreshEnumerator<T> extends TyneqCachedEnumerator<T> {
4197
+ * private readonly iter: Enumerator<T>;
4198
+ * public constructor(source: CachedEnumerable<T>) {
4199
+ * super(source);
4200
+ * this.iter = this.cachedSource.getEnumerator();
4201
+ * }
4202
+ * protected handleNext(): IteratorResult<T> {
4203
+ * // this.cachedSource gives access to the full CachedEnumerable
4204
+ * return this.iter.next();
4205
+ * }
4206
+ * }
4207
+ * ```
4208
+ *
4209
+ * @group Decorators
4210
+ */
4211
+ function cachedOperator(name, category, validate) {
4212
+ return function(target, _context) {
4213
+ if (!reflect(target.prototype).hasMethod("handleNext")) throw new PluginError(`@cachedOperator("${name}"): class "${target.name}" must define a protected handleNext(): IteratorResult<T> method. Ensure the class extends TyneqCachedEnumerator<T>.`, "cachedOperator", target.name);
4214
+ OperatorRegistry.register({
4215
+ metadata: OperatorMetadata.forCategory(category, name, TyneqCachedEnumerable),
4216
+ impl: function(...userArgs) {
4217
+ validate?.(...userArgs);
4218
+ const base = this;
4219
+ return RegistrationUtility.buildEnumerable(this, name, userArgs, category, { getEnumerator: () => new target(base, ...userArgs) });
4220
+ }
4221
+ });
4222
+ return target;
4223
+ };
4224
+ }
4225
+ //#endregion
4226
+ //#region src/plugin/decorators/terminal.ts
4227
+ /**
4228
+ * Class decorator that registers a class as a terminal operator.
4229
+ *
4230
+ * The class must have a `process()` method returning the result.
4231
+ * Validation runs eagerly at the call site before the class is instantiated.
4232
+ *
4233
+ * @param name - Method name to expose on every sequence.
4234
+ * @param validate - Optional eager validation function for user-supplied arguments.
4235
+ *
4236
+ * @example
4237
+ * ```ts
4238
+ * import { terminal } from "tyneq/plugin";
4239
+ * import { TyneqTerminalOperator } from "tyneq/plugin";
4240
+ * import type { Enumerable } from "tyneq";
4241
+ *
4242
+ * @terminal("product")
4243
+ * class ProductOperator extends TyneqTerminalOperator<number, number> {
4244
+ * public process(): number {
4245
+ * let result = 1;
4246
+ * for (const item of this.source) result *= item;
4247
+ * return result;
4248
+ * }
4249
+ * }
4250
+ * ```
4251
+ *
4252
+ * @group Decorators
4253
+ */
4254
+ function terminal(name, validate) {
4255
+ return function(target, _context) {
4256
+ if (!reflect(target.prototype).hasMethod("process")) throw new PluginError(`@terminal("${name}"): class "${target.name}" must define a public process(): TResult method. Ensure the class extends TyneqTerminalOperator<TSource, TResult>.`, "terminal", target.name);
4257
+ OperatorRegistry.register({
4258
+ metadata: OperatorMetadata.terminal(name, TyneqEnumerableBase),
4259
+ impl: function(...userArgs) {
4260
+ validate?.(...userArgs);
4261
+ return new target(this, ...userArgs).process();
4262
+ }
4263
+ });
4264
+ return target;
4265
+ };
4266
+ }
4267
+ //#endregion
4268
+ //#region src/plugin/decorators/orderedTerminal.ts
4269
+ /**
4270
+ * Class decorator that registers a `TyneqOrderedTerminalOperator` subclass as a terminal
4271
+ * operator available only on ordered sequences.
4272
+ *
4273
+ * The class constructor receives the full `OrderedEnumerable` as its first argument,
4274
+ * giving access to ordered-sequence members (comparers, parent chain, etc.).
4275
+ *
4276
+ * @param name - Method name to expose on ordered sequences.
4277
+ * @param validate - Optional eager validation function for user-supplied arguments.
4278
+ *
4279
+ * @example
4280
+ * ```ts
4281
+ * @orderedTerminal("isSorted")
4282
+ * class IsSortedOperator<T> extends TyneqOrderedTerminalOperator<T, boolean> {
4283
+ * process(): boolean {
4284
+ * const items = [...this.source];
4285
+ * for (let i = 1; i < items.length; i++) {
4286
+ * if (this.source.comparer(items[i - 1], items[i]) > 0) return false;
4287
+ * }
4288
+ * return true;
4289
+ * }
4290
+ * }
4291
+ * ```
4292
+ *
4293
+ * @group Decorators
4294
+ */
4295
+ function orderedTerminal(name, validate) {
4296
+ return function(target, _context) {
4297
+ OperatorRegistry.register({
4298
+ metadata: OperatorMetadata.terminal(name, TyneqOrderedEnumerable),
4299
+ impl: function(...userArgs) {
4300
+ validate?.(...userArgs);
4301
+ return new target(this, ...userArgs).process();
4302
+ }
4303
+ });
4304
+ return target;
4305
+ };
4306
+ }
4307
+ //#endregion
4308
+ //#region src/plugin/decorators/cachedTerminal.ts
4309
+ /**
4310
+ * Class decorator that registers a `TyneqCachedTerminalOperator` subclass as a terminal
4311
+ * operator available only on cached sequences.
4312
+ *
4313
+ * The class constructor receives the full `CachedEnumerable` as its first argument,
4314
+ * giving access to cached-sequence members (`refresh()`, internal cache, etc.).
4315
+ *
4316
+ * @param name - Method name to expose on cached sequences.
4317
+ * @param validate - Optional eager validation function for user-supplied arguments.
4318
+ *
4319
+ * @example
4320
+ * ```ts
4321
+ * @cachedTerminal("cacheSize")
4322
+ * class CacheSizeOperator<T> extends TyneqCachedTerminalOperator<T, number> {
4323
+ * process(): number {
4324
+ * return [...this.source].length;
4325
+ * }
4326
+ * }
4327
+ * ```
4328
+ *
4329
+ * @group Decorators
4330
+ */
4331
+ function cachedTerminal(name, validate) {
4332
+ return function(target, _context) {
4333
+ OperatorRegistry.register({
4334
+ metadata: OperatorMetadata.terminal(name, TyneqCachedEnumerable),
4335
+ impl: function(...userArgs) {
4336
+ validate?.(...userArgs);
4337
+ return new target(this, ...userArgs).process();
4338
+ }
4339
+ });
4340
+ return target;
4341
+ };
4342
+ }
4343
+ //#endregion
4344
+ //#region src/plugin/registration/createOperator.ts
4345
+ /**
4346
+ * Registers a streaming or buffering operator using a factory function.
4347
+ *
4348
+ * Use this when the operator requires a class-level enumerator with custom state
4349
+ * but you want to avoid writing the decorator boilerplate. For simpler generator-based
4350
+ * operators, prefer {@link createGeneratorOperator}.
4351
+ *
4352
+ * @param config.name - Method name to expose on every sequence.
4353
+ * @param config.category - `"streaming"` or `"buffer"`.
4354
+ * @param config.factory - Returns an `EnumeratorFactory` given the source and arguments.
4355
+ * @param config.validate - Optional eager validation for user-supplied arguments.
4356
+ *
4357
+ * @example
4358
+ * ```ts
4359
+ * import { createOperator } from "tyneq/plugin";
4360
+ * import type { Enumerator } from "tyneq";
4361
+ *
4362
+ * createOperator({
4363
+ * name: "everyOther",
4364
+ * category: "streaming",
4365
+ * factory: <T>(source: Enumerable<T>) => ({
4366
+ * getEnumerator(): Enumerator<T> {
4367
+ * let skip = false;
4368
+ * const iter = source[Symbol.iterator]();
4369
+ * return {
4370
+ * next(): IteratorResult<T> {
4371
+ * while (true) {
4372
+ * const r = iter.next();
4373
+ * if (r.done) return r;
4374
+ * if (!skip) { skip = true; return r; }
4375
+ * skip = false;
4376
+ * }
4377
+ * }
4378
+ * };
4379
+ * }
4380
+ * })
4381
+ * });
4382
+ * ```
4383
+ *
4384
+ * @group Factory Functions
4385
+ */
4386
+ function createOperator(config) {
4387
+ OperatorRegistry.register({
4388
+ metadata: OperatorMetadata.forCategory(config.category, config.name, TyneqEnumerableBase, config.source),
4389
+ impl: function(...args) {
4390
+ config.validate?.(...args);
4391
+ return RegistrationUtility.buildEnumerable(this, config.name, args, config.category, config.factory(this, ...args));
4392
+ }
4393
+ });
4394
+ }
4395
+ //#endregion
4396
+ //#region src/plugin/registration/createGeneratorOperator.ts
4397
+ /**
4398
+ * Registers an operator using a generator function.
4399
+ *
4400
+ * The generator receives the source as an `Iterable<T>` and any user arguments.
4401
+ * This is the simplest functional registration path and works for both streaming
4402
+ * and buffering use cases:
4403
+ *
4404
+ * - **Streaming**: yield elements one at a time as they arrive. The downstream
4405
+ * sequence only pulls the next element when needed.
4406
+ * - **Buffering**: collect all input first, then yield the transformed output.
4407
+ * Pass `category: "buffer"` to tell the query plan that this operator
4408
+ * materialises the entire upstream before yielding.
4409
+ *
4410
+ * For class-based enumerators with custom stateful logic, prefer {@link createOperator}.
4411
+ *
4412
+ * @param config.name - Method name to expose on every sequence.
4413
+ * @param config.category - `"streaming"` (default) or `"buffer"`.
4414
+ * @param config.generator - Generator that yields transformed elements from `source`.
4415
+ * @param config.validate - Optional eager validation for user-supplied arguments.
4416
+ *
4417
+ * @example Streaming - yield elements lazily one at a time:
4418
+ * ```ts
4419
+ * import { createGeneratorOperator } from "tyneq/plugin";
4420
+ *
4421
+ * createGeneratorOperator({
4422
+ * name: "everyOther",
4423
+ * *generator(source) {
4424
+ * let skip = false;
4425
+ * for (const item of source) {
4426
+ * if (!skip) yield item;
4427
+ * skip = !skip;
4428
+ * }
4429
+ * }
4430
+ * });
4431
+ * ```
4432
+ *
4433
+ * @example Buffering - materialise the entire input first, then yield results:
4434
+ * ```ts
4435
+ * import { createGeneratorOperator } from "tyneq/plugin";
4436
+ *
4437
+ * createGeneratorOperator({
4438
+ * name: "sortedBy",
4439
+ * category: "buffer",
4440
+ * *generator(source, keyFn: (x: number) => number) {
4441
+ * const items = [...source].sort((a, b) => keyFn(a) - keyFn(b));
4442
+ * for (const item of items) yield item;
4443
+ * }
4444
+ * });
4445
+ * ```
4446
+ *
4447
+ * @group Factory Functions
4448
+ */
4449
+ function createGeneratorOperator(config) {
4450
+ const category = config.category ?? "streaming";
4451
+ OperatorRegistry.register({
4452
+ metadata: OperatorMetadata.forCategory(category, config.name, TyneqEnumerableBase, config.source),
4453
+ impl: function(...args) {
4454
+ config.validate?.(...args);
4455
+ const source = this;
4456
+ return RegistrationUtility.buildEnumerable(this, config.name, args, category, { getEnumerator: () => config.generator(source, ...args) });
4457
+ }
4458
+ });
4459
+ }
4460
+ //#endregion
4461
+ //#region src/plugin/registration/createOrderedOperator.ts
4462
+ /**
4463
+ * Registers a factory function as an operator available only on ordered sequences,
4464
+ * where the factory fully controls the return type.
4465
+ *
4466
+ * Use this when the operator must return a `TyneqOrderedSequence` (e.g. a `thenBy` variant).
4467
+ * The factory receives the ordered source and a query plan node. It is responsible for
4468
+ * constructing and returning the result sequence - typically by instantiating
4469
+ * `TyneqOrderedEnumerable` directly.
4470
+ *
4471
+ * For operators that return a plain sequence from an enumerator class, use `@orderedOperator`.
4472
+ *
4473
+ * @param config.name - Method name to expose on ordered sequences.
4474
+ * @param config.category - Operator kind (`"streaming"` | `"buffer"`).
4475
+ * @param config.factory - Constructs the result sequence from `(source, node, ...userArgs)`.
4476
+ * @param config.validate - Optional eager validation function for user-supplied arguments.
4477
+ *
4478
+ * @example
4479
+ * ```ts
4480
+ * createOrderedOperator({
4481
+ * name: "thenByLocale",
4482
+ * category: "buffer",
4483
+ * factory: (source, node, locale: string, keySelector: (item: unknown) => string) =>
4484
+ * new TyneqOrderedEnumerable(
4485
+ * source.source,
4486
+ * keySelector,
4487
+ * (a, b) => a.localeCompare(b, locale),
4488
+ * false,
4489
+ * source,
4490
+ * node
4491
+ * ),
4492
+ * validate: (locale, keySelector) => {
4493
+ * if (typeof locale !== "string") throw new Error("locale must be a string");
4494
+ * if (typeof keySelector !== "function") throw new Error("keySelector must be a function");
4495
+ * }
4496
+ * });
4497
+ * ```
4498
+ *
4499
+ * @group Factory Functions
4500
+ */
4501
+ function createOrderedOperator(config) {
4502
+ OperatorRegistry.register({
4503
+ metadata: OperatorMetadata.forCategory(config.category, config.name, TyneqOrderedEnumerable, config.source),
4504
+ impl: function(...userArgs) {
4505
+ config.validate?.(...userArgs);
4506
+ const source = this;
4507
+ const node = RegistrationUtility.buildQueryNode(this, config.name, userArgs, config.category);
4508
+ return config.factory(source, node, ...userArgs);
4509
+ }
4510
+ });
4511
+ }
4512
+ //#endregion
4513
+ //#region src/plugin/registration/createCachedOperator.ts
4514
+ /**
4515
+ * Registers a factory function as an operator available only on cached sequences,
4516
+ * where the factory fully controls the return type.
4517
+ *
4518
+ * Use this when the operator must return a `TyneqCachedSequence`. The factory
4519
+ * receives the cached source and a query plan node. It is responsible for
4520
+ * constructing and returning the result sequence.
4521
+ *
4522
+ * For operators that return a plain sequence from an enumerator class, use `@cachedOperator`.
4523
+ *
4524
+ * @param config.name - Method name to expose on cached sequences.
4525
+ * @param config.category - Operator kind (`"streaming"` | `"buffer"`).
4526
+ * @param config.factory - Constructs the result sequence from `(source, node, ...userArgs)`.
4527
+ * @param config.validate - Optional eager validation function for user-supplied arguments.
4528
+ *
4529
+ * @example
4530
+ * ```ts
4531
+ * createCachedOperator({
4532
+ * name: "refreshWith",
4533
+ * category: "buffer",
4534
+ * factory: (source, node, newSource: Iterable<unknown>) => {
4535
+ * const seq = tyneqFrom(newSource);
4536
+ * return new TyneqCachedEnumerable(seq, node);
4537
+ * },
4538
+ * validate: (newSource) => {
4539
+ * if (newSource == null) throw new Error("newSource must not be null");
4540
+ * }
4541
+ * });
4542
+ * ```
4543
+ *
4544
+ * @group Factory Functions
4545
+ */
4546
+ function createCachedOperator(config) {
4547
+ OperatorRegistry.register({
4548
+ metadata: OperatorMetadata.forCategory(config.category, config.name, TyneqCachedEnumerable, config.source),
4549
+ impl: function(...userArgs) {
4550
+ config.validate?.(...userArgs);
4551
+ const source = this;
4552
+ const node = RegistrationUtility.buildQueryNode(this, config.name, userArgs, config.category);
4553
+ return config.factory(source, node, ...userArgs);
4554
+ }
4555
+ });
4556
+ }
4557
+ //#endregion
4558
+ //#region src/plugin/registration/createTerminalOperator.ts
4559
+ /**
4560
+ * Registers a terminal operator using a plain function.
4561
+ *
4562
+ * The simplest registration API for terminal operators.
4563
+ * `execute` receives the full source sequence and any user arguments and returns a concrete value.
4564
+ *
4565
+ * @param config.name - Method name to expose on every sequence.
4566
+ * @param config.execute - Function that consumes the source and returns a result.
4567
+ * @param config.validate - Optional eager validation for user-supplied arguments.
4568
+ *
4569
+ * @example
4570
+ * ```ts
4571
+ * import { createTerminalOperator } from "tyneq/plugin";
4572
+ *
4573
+ * createTerminalOperator({
4574
+ * name: "product",
4575
+ * execute(source: Iterable<number>): number {
4576
+ * let result = 1;
4577
+ * for (const item of source) result *= item;
4578
+ * return result;
4579
+ * }
4580
+ * });
4581
+ * ```
4582
+ *
4583
+ * @group Factory Functions
4584
+ */
4585
+ function createTerminalOperator(config) {
4586
+ OperatorRegistry.register({
4587
+ metadata: OperatorMetadata.terminal(config.name, TyneqEnumerableBase, config.source),
4588
+ impl: function(...args) {
4589
+ config.validate?.(...args);
4590
+ return config.execute(this, ...args);
4591
+ }
4592
+ });
4593
+ }
4594
+ //#endregion
4595
+ //#region src/plugin/registration/createOrderedTerminalOperator.ts
4596
+ /**
4597
+ * Registers a terminal operator available only on ordered sequences.
4598
+ *
4599
+ * The `execute` function receives the full `OrderedEnumerable` as its first argument,
4600
+ * giving access to ordered-sequence members (comparers, parent chain, etc.).
4601
+ *
4602
+ * @param config.name - Method name to expose on ordered sequences.
4603
+ * @param config.execute - Function that consumes the ordered source and returns a result.
4604
+ * @param config.validate - Optional eager validation for user-supplied arguments.
4605
+ *
4606
+ * @example
4607
+ * ```ts
4608
+ * createOrderedTerminalOperator({
4609
+ * name: "isSorted",
4610
+ * execute(source: OrderedEnumerable<number>): boolean {
4611
+ * const items = [...source];
4612
+ * for (let i = 1; i < items.length; i++) {
4613
+ * if (items[i - 1] > items[i]) return false;
4614
+ * }
4615
+ * return true;
4616
+ * }
4617
+ * });
4618
+ * ```
4619
+ *
4620
+ * @group Factory Functions
4621
+ */
4622
+ function createOrderedTerminalOperator(config) {
4623
+ OperatorRegistry.register({
4624
+ metadata: OperatorMetadata.terminal(config.name, TyneqOrderedEnumerable, config.source),
4625
+ impl: function(...args) {
4626
+ config.validate?.(...args);
4627
+ return config.execute(this, ...args);
4628
+ }
4629
+ });
4630
+ }
4631
+ //#endregion
4632
+ //#region src/plugin/registration/createCachedTerminalOperator.ts
4633
+ /**
4634
+ * Registers a terminal operator available only on cached sequences.
4635
+ *
4636
+ * The `execute` function receives the full `CachedEnumerable` as its first argument,
4637
+ * giving access to cached-sequence members (`refresh()`, internal cache, etc.).
4638
+ *
4639
+ * @param config.name - Method name to expose on cached sequences.
4640
+ * @param config.execute - Function that consumes the cached source and returns a result.
4641
+ * @param config.validate - Optional eager validation for user-supplied arguments.
4642
+ *
4643
+ * @example
4644
+ * ```ts
4645
+ * createCachedTerminalOperator({
4646
+ * name: "cacheSize",
4647
+ * execute(source: CachedEnumerable<unknown>): number {
4648
+ * return [...source].length;
4649
+ * }
4650
+ * });
4651
+ * ```
4652
+ *
4653
+ * @group Factory Functions
4654
+ */
4655
+ function createCachedTerminalOperator(config) {
4656
+ OperatorRegistry.register({
4657
+ metadata: OperatorMetadata.terminal(config.name, TyneqCachedEnumerable, config.source),
4658
+ impl: function(...args) {
4659
+ config.validate?.(...args);
4660
+ return config.execute(this, ...args);
4661
+ }
4662
+ });
4663
+ }
4664
+ //#endregion
4665
+ //#region src/core/enumerators/TyneqOrderedEnumerator.ts
4666
+ /**
4667
+ * Base class for enumerators that need the full ordered sequence (not just an Enumerator<T>).
4668
+ * Lifecycle of the source is owned by the sequence, not the enumerator.
4669
+ *
4670
+ * @group Plugin
4671
+ * @internal
4672
+ */
4673
+ var TyneqOrderedEnumerator = class extends TyneqBaseEnumerator {
4674
+ constructor(orderedSource) {
4675
+ super();
4676
+ this.orderedSource = orderedSource;
4677
+ }
4678
+ disposeSource() {}
4679
+ };
4680
+ //#endregion
4681
+ //#region src/core/enumerators/TyneqCachedEnumerator.ts
4682
+ /**
4683
+ * Base class for enumerators that need the full cached sequence (not just an Enumerator<T>).
4684
+ * Lifecycle of the source is owned by the sequence, not the enumerator.
4685
+ *
4686
+ * @group Plugin
4687
+ * @internal
4688
+ */
4689
+ var TyneqCachedEnumerator = class extends TyneqBaseEnumerator {
4690
+ constructor(cachedSource) {
4691
+ super();
4692
+ this.cachedSource = cachedSource;
4693
+ }
4694
+ disposeSource() {}
4695
+ };
4696
+ //#endregion
4697
+ //#region src/core/terminal/TyneqOrderedTerminalOperator.ts
4698
+ /**
4699
+ * Abstract base for terminal operators that require a fully ordered sequence.
4700
+ *
4701
+ * @remarks
4702
+ * Use this when your terminal operator needs access to ordered-sequence members
4703
+ * (comparers, parent chain, etc.). Register with `@orderedTerminal` or
4704
+ * `createOrderedTerminalOperator`.
4705
+ *
4706
+ * @typeParam TSource - Element type of the source sequence.
4707
+ * @typeParam TResult - The return type of `process()`.
4708
+ * @group Plugin
4709
+ */
4710
+ var TyneqOrderedTerminalOperator = class {
4711
+ source;
4712
+ constructor(source) {
4713
+ ArgumentUtility.checkNotOptional({ source });
4714
+ this.source = source;
4715
+ }
4716
+ };
4717
+ //#endregion
4718
+ //#region src/core/terminal/TyneqCachedTerminalOperator.ts
4719
+ /**
4720
+ * Abstract base for terminal operators that require a fully cached sequence.
4721
+ *
4722
+ * @remarks
4723
+ * Use this when your terminal operator needs access to cached-sequence members
4724
+ * (the internal cache, `refresh()`, etc.). Register with `@cachedTerminal` or
4725
+ * `createCachedTerminalOperator`.
4726
+ *
4727
+ * @typeParam TSource - Element type of the source sequence.
4728
+ * @typeParam TResult - The return type of `process()`.
4729
+ * @group Plugin
4730
+ */
4731
+ var TyneqCachedTerminalOperator = class {
4732
+ source;
4733
+ constructor(source) {
4734
+ ArgumentUtility.checkNotOptional({ source });
4735
+ this.source = source;
4736
+ }
4737
+ };
4738
+ //#endregion
4739
+ export { PluginError as A, SequenceContainsNoElementsError as C, OperatorRegistry as D, TyneqTerminalOperator as E, TyneqBaseEnumerator as F, isSourceNode as M, tyneqQueryNode as N, RegistryError as O, TyneqComparer as P, TyneqEnumerator as S, InvalidOperationError as T, operator as _, createCachedTerminalOperator as a, TyneqCachedEnumerable as b, createCachedOperator as c, createOperator as d, cachedTerminal as f, orderedOperator as g, cachedOperator as h, TyneqOrderedEnumerator as i, QueryNode as j, OperatorMetadata as k, createOrderedOperator as l, terminal as m, TyneqOrderedTerminalOperator as n, createOrderedTerminalOperator as o, orderedTerminal as p, TyneqCachedEnumerator as r, createTerminalOperator as s, TyneqCachedTerminalOperator as t, createGeneratorOperator as u, source as v, EnumeratorUtility as w, TyneqOrderedEnumerable as x, TyneqEnumerable as y };
4740
+
4741
+ //# sourceMappingURL=TyneqCachedTerminalOperator.js.map