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.
- package/LICENSE +21 -0
- package/README.md +319 -0
- package/dist/Lazy.cjs +762 -0
- package/dist/Lazy.cjs.map +1 -0
- package/dist/Lazy.js +691 -0
- package/dist/Lazy.js.map +1 -0
- package/dist/TyneqCachedTerminalOperator.cjs +4950 -0
- package/dist/TyneqCachedTerminalOperator.cjs.map +1 -0
- package/dist/TyneqCachedTerminalOperator.d.cts +724 -0
- package/dist/TyneqCachedTerminalOperator.d.cts.map +1 -0
- package/dist/TyneqCachedTerminalOperator.d.ts +724 -0
- package/dist/TyneqCachedTerminalOperator.d.ts.map +1 -0
- package/dist/TyneqCachedTerminalOperator.js +4741 -0
- package/dist/TyneqCachedTerminalOperator.js.map +1 -0
- package/dist/ValidationBuilder.cjs +80 -0
- package/dist/ValidationBuilder.cjs.map +1 -0
- package/dist/ValidationBuilder.d.cts +319 -0
- package/dist/ValidationBuilder.d.cts.map +1 -0
- package/dist/ValidationBuilder.d.ts +319 -0
- package/dist/ValidationBuilder.d.ts.map +1 -0
- package/dist/ValidationBuilder.js +69 -0
- package/dist/ValidationBuilder.js.map +1 -0
- package/dist/core.d.cts +1393 -0
- package/dist/core.d.cts.map +1 -0
- package/dist/core.d.ts +1393 -0
- package/dist/core.d.ts.map +1 -0
- package/dist/index.cjs +863 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +1038 -0
- package/dist/index.d.cts.map +1 -0
- package/dist/index.d.ts +1038 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +809 -0
- package/dist/index.js.map +1 -0
- package/dist/plugin/index.cjs +24 -0
- package/dist/plugin/index.d.cts +89 -0
- package/dist/plugin/index.d.cts.map +1 -0
- package/dist/plugin/index.d.ts +89 -0
- package/dist/plugin/index.d.ts.map +1 -0
- package/dist/plugin/index.js +2 -0
- package/dist/utility/index.cjs +9 -0
- package/dist/utility/index.d.cts +2 -0
- package/dist/utility/index.d.ts +2 -0
- package/dist/utility/index.js +3 -0
- 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
|