@aidc-toolkit/utility 0.9.2 → 0.9.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,12 @@
1
+ <component name="ProjectRunConfigurationManager">
2
+ <configuration default="false" name="Test iterator proxy" type="JavaScriptTestRunnerVitest">
3
+ <node-interpreter value="project" />
4
+ <vitest-package value="$PROJECT_DIR$/node_modules/vitest" />
5
+ <working-dir value="$PROJECT_DIR$" />
6
+ <vitest-options value="--run" />
7
+ <envs />
8
+ <scope-kind value="TEST_FILE" />
9
+ <test-file value="$PROJECT_DIR$/test/iterator_proxy.test.ts" />
10
+ <method v="2" />
11
+ </configuration>
12
+ </component>
package/eslint.config.js CHANGED
@@ -1,13 +1,3 @@
1
- import tseslint from "typescript-eslint";
2
- import stylistic from "@stylistic/eslint-plugin";
3
- import jsdoc from "eslint-plugin-jsdoc";
4
- import esLintConfigLove from "eslint-config-love";
5
1
  import { esLintConfigAIDCToolkit } from "@aidc-toolkit/dev";
6
2
 
7
- export default tseslint.config(
8
- ...tseslint.configs.strictTypeChecked,
9
- stylistic.configs["recommended-flat"],
10
- jsdoc.configs["flat/recommended-typescript"],
11
- esLintConfigLove,
12
- ...esLintConfigAIDCToolkit
13
- );
3
+ export default esLintConfigAIDCToolkit;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aidc-toolkit/utility",
3
- "version": "0.9.2",
3
+ "version": "0.9.4",
4
4
  "description": "Foundational utilities for AIDC Toolkit",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -21,22 +21,19 @@
21
21
  "scripts": {
22
22
  "eslint": "eslint .",
23
23
  "build": "tsup src/index.ts --clean --format cjs,esm --dts",
24
- "build-dev": "npm run build && tsc src/index.ts --outDir dist --target esnext --moduleResolution nodenext --module nodenext --emitDeclarationOnly --declaration --declarationMap",
24
+ "build-doc": "npm run build && tsc src/index.ts --outDir dist --target esnext --moduleResolution nodenext --module nodenext --emitDeclarationOnly --declaration --declarationMap",
25
25
  "test": "vitest run"
26
26
  },
27
27
  "devDependencies": {
28
- "@aidc-toolkit/dev": "^0.9.2",
29
- "@stylistic/eslint-plugin": "^2.10.1",
30
- "eslint-config-love": "^98.0.2",
31
- "eslint-plugin-jsdoc": "^50.5.0",
28
+ "@aidc-toolkit/dev": "^0.9.4",
29
+ "eslint": "^9.15.0",
32
30
  "ts-node": "^10.9.2",
33
31
  "tsup": "^8.3.5",
34
32
  "typescript": "^5.6.3",
35
- "typescript-eslint": "^8.14.0",
36
- "vitest": "^2.1.4"
33
+ "vitest": "^2.1.5"
37
34
  },
38
35
  "dependencies": {
39
- "@aidc-toolkit/core": "^0.9.2",
36
+ "@aidc-toolkit/core": "^0.9.4",
40
37
  "i18next": "^23.16.5"
41
38
  }
42
39
  }
package/src/index.ts CHANGED
@@ -1,3 +1,4 @@
1
+ export * from "./iterator_proxy.js";
1
2
  export * from "./sequencer.js";
2
3
  export * from "./transformer.js";
3
4
  export type * from "./string.js";
@@ -0,0 +1,529 @@
1
+ /**
2
+ * Determine if Iterator variable is supported.
3
+ *
4
+ * @returns
5
+ * True if Iterator variable is supported.
6
+ */
7
+ function isIteratorSupported(): boolean {
8
+ let supported: boolean;
9
+
10
+ try {
11
+ // Not supported if in testing.
12
+ supported = process.env["NODE_ENV"] !== "test";
13
+ } catch (_e) {
14
+ // Assume supported.
15
+ supported = true;
16
+ }
17
+
18
+ if (supported) {
19
+ try {
20
+ Iterator.from([]);
21
+ } catch (_e) {
22
+ supported = false;
23
+ }
24
+ }
25
+
26
+ return supported;
27
+ }
28
+
29
+ /**
30
+ * Iteration source; shortcut for iterator or iterable.
31
+ */
32
+ export type IterationSource<T> = Iterator<T> | Iterable<T>;
33
+
34
+ /**
35
+ * Convert an iteration source to an iterable.
36
+ *
37
+ * @param iterationSource
38
+ * Iteration source.
39
+ *
40
+ * @returns
41
+ * Iteration source if it is already an iterable, otherwise iteration source wrapped in an interable.
42
+ */
43
+ function iterationSourceToIterable<T>(iterationSource: IterationSource<T>): Iterable<T> {
44
+ return Symbol.iterator in iterationSource ?
45
+ iterationSource :
46
+ {
47
+ [Symbol.iterator](): Iterator<T> {
48
+ return iterationSource;
49
+ }
50
+ };
51
+ }
52
+
53
+ /**
54
+ * Iterator proxy base; provides common functionality for all iterator objects.
55
+ */
56
+ abstract class IteratorProxyBase<TInitial, TFinal> implements IteratorObject<TFinal, undefined> {
57
+ /**
58
+ * Initial iterable.
59
+ */
60
+ private readonly _initialIterable: Iterable<TInitial>;
61
+
62
+ /**
63
+ * Initial iterator.
64
+ */
65
+ private _initialIterator?: Iterator<TInitial>;
66
+
67
+ /**
68
+ * Constructor.
69
+ *
70
+ * @param initialIterationSource
71
+ * Initial iteration source.
72
+ */
73
+ constructor(initialIterationSource: IterationSource<TInitial>) {
74
+ this._initialIterable = iterationSourceToIterable(initialIterationSource);
75
+ }
76
+
77
+ /**
78
+ * Get the initial iterable.
79
+ */
80
+ protected get initialIterable(): Iterable<TInitial> {
81
+ return this._initialIterable;
82
+ }
83
+
84
+ /**
85
+ * Get the initial iterator.
86
+ */
87
+ protected get initialIterator(): Iterator<TInitial> {
88
+ if (this._initialIterator === undefined) {
89
+ this._initialIterator = this.initialIterable[Symbol.iterator]();
90
+ }
91
+
92
+ return this._initialIterator;
93
+ }
94
+
95
+ /**
96
+ * @inheritDoc
97
+ */
98
+ get [Symbol.toStringTag](): string {
99
+ return "IteratorProxy";
100
+ }
101
+
102
+ /**
103
+ * @inheritDoc
104
+ */
105
+ [Symbol.dispose](): void {
106
+ }
107
+
108
+ /**
109
+ * @inheritDoc
110
+ */
111
+ [Symbol.iterator](): IteratorObject<TFinal, undefined> {
112
+ return this;
113
+ }
114
+
115
+ /**
116
+ * Get the next result from the initial iterator.
117
+ *
118
+ * @param value
119
+ * Tuple value to be passed to Iterator.next().
120
+ *
121
+ * @returns
122
+ * Next result from the initial iterator.
123
+ */
124
+ protected initialNext(...value: [] | [unknown]): IteratorResult<TInitial, undefined> {
125
+ return this.initialIterator.next(...value);
126
+ }
127
+
128
+ /**
129
+ * @inheritDoc
130
+ */
131
+ abstract next(...value: [] | [unknown]): IteratorResult<TFinal, undefined>;
132
+
133
+ /**
134
+ * @inheritDoc
135
+ */
136
+ map<U>(callbackfn: (value: TFinal, index: number) => U): IteratorObject<U, undefined> {
137
+ return new IteratorMapProxy(this, callbackfn);
138
+ }
139
+
140
+ /**
141
+ * @inheritDoc
142
+ */
143
+ flatMap<U>(callback: (value: TFinal, index: number) => IterationSource<U>): IteratorObject<U, undefined> {
144
+ return new IteratorFlatMapProxy(this, callback);
145
+ }
146
+
147
+ /**
148
+ * @inheritDoc
149
+ */
150
+ filter(predicate: (value: TFinal, index: number) => unknown): IteratorObject<TFinal, undefined> {
151
+ return new IteratorFilterProxy(this, predicate);
152
+ }
153
+
154
+ /**
155
+ * @inheritDoc
156
+ */
157
+ take(limit: number): IteratorObject<TFinal, undefined> {
158
+ return new IteratorTakeProxy(this, limit);
159
+ }
160
+
161
+ /**
162
+ * @inheritDoc
163
+ */
164
+ drop(count: number): IteratorObject<TFinal, undefined> {
165
+ return new IteratorDropProxy(this, count);
166
+ }
167
+
168
+ /**
169
+ * @inheritDoc
170
+ */
171
+ reduce<U>(callbackfn: (previousValue: U, currentValue: TFinal, currentIndex: number) => U, initialValue?: U): U {
172
+ let index = 0;
173
+ let result = initialValue;
174
+
175
+ for (const value of this) {
176
+ if (index === 0 && initialValue === undefined) {
177
+ // Initial value is undefined only when TFinal and U are identical.
178
+ result = value as unknown as U;
179
+ } else {
180
+ // Iteration has occurred at least once so result is of the expected type.
181
+ result = callbackfn(result as U, value, index);
182
+ }
183
+
184
+ index++;
185
+ }
186
+
187
+ if (index === 0 && initialValue === undefined) {
188
+ throw new Error("reduce() of empty iterator with no initial value");
189
+ }
190
+
191
+ // Iteration has occurred at least once so result is of the expected type.
192
+ return result as U;
193
+ }
194
+
195
+ /**
196
+ * @inheritDoc
197
+ */
198
+ toArray(): TFinal[] {
199
+ return Array.from(this);
200
+ }
201
+
202
+ /**
203
+ * @inheritDoc
204
+ */
205
+ forEach(callbackfn: (value: TFinal, index: number) => void): void {
206
+ let index = 0;
207
+
208
+ for (const element of this) {
209
+ callbackfn(element, index++);
210
+ }
211
+ }
212
+
213
+ /**
214
+ * Iterate until the truthy result of the predicate changes or until the iterator is exhausted.
215
+ *
216
+ * @param predicate
217
+ * Predicate.
218
+ *
219
+ * @param initialTruthy
220
+ * Initial truthy result of the predicate.
221
+ *
222
+ * @returns
223
+ * Iterator result.
224
+ */
225
+ private untilChanged(predicate: (value: TFinal, index: number) => unknown, initialTruthy: boolean): IteratorResult<TFinal, undefined> {
226
+ let result: IteratorResult<TFinal, undefined> | undefined;
227
+
228
+ const iterator = this[Symbol.iterator]();
229
+ let index = 0;
230
+
231
+ do {
232
+ result = iterator.next();
233
+
234
+ if (result.done !== true) {
235
+ const truthy = Boolean(predicate(result.value, index++));
236
+
237
+ if (truthy === initialTruthy) {
238
+ result = undefined;
239
+ }
240
+ }
241
+ } while (result === undefined);
242
+
243
+ return result;
244
+ }
245
+
246
+ /**
247
+ * @inheritDoc
248
+ */
249
+ some(predicate: (value: TFinal, index: number) => unknown): boolean {
250
+ // Iterate until predicate returns truthy; return done status.
251
+ return this.untilChanged(predicate, false).done !== true;
252
+ }
253
+
254
+ /**
255
+ * @inheritDoc
256
+ */
257
+ every(predicate: (value: TFinal, index: number) => unknown): boolean {
258
+ // Iterate until predicate returns falsy; return done status.
259
+ return this.untilChanged(predicate, true).done === true;
260
+ }
261
+
262
+ /**
263
+ * @inheritDoc
264
+ */
265
+ find(predicate: (value: TFinal, index: number) => unknown): TFinal | undefined {
266
+ // Iterate until predicate returns truthy; return value.
267
+ return this.untilChanged(predicate, false).value;
268
+ }
269
+ }
270
+
271
+ /**
272
+ * Core iterator proxy object.
273
+ */
274
+ class IteratorProxyObject<T> extends IteratorProxyBase<T, T> {
275
+ /**
276
+ * @inheritDoc
277
+ */
278
+ next(...value: [] | [unknown]): IteratorResult<T, undefined> {
279
+ return this.initialNext(...value);
280
+ }
281
+ }
282
+
283
+ /**
284
+ * Iterator callback proxy base.
285
+ */
286
+ abstract class IteratorCallbackProxyBase<TInitial, TIntermediate, TFinal> extends IteratorProxyBase<TInitial, TFinal> {
287
+ /**
288
+ * Callback.
289
+ */
290
+ private readonly _callback: (element: TInitial, index: number) => TIntermediate;
291
+
292
+ /**
293
+ * Index into initial iteration source.
294
+ */
295
+ private _index: number;
296
+
297
+ /**
298
+ * Constructor.
299
+ *
300
+ * @param initialIterationSource
301
+ * Initial iteration source.
302
+ *
303
+ * @param callback
304
+ * Callback.
305
+ */
306
+ constructor(initialIterationSource: IterationSource<TInitial>, callback: (element: TInitial, index: number) => TIntermediate) {
307
+ super(initialIterationSource);
308
+
309
+ this._callback = callback;
310
+ this._index = 0;
311
+ }
312
+
313
+ /**
314
+ * Get the next result from the intermediate iterator.
315
+ *
316
+ * @param initialResult
317
+ * Next result from the initial iterator.
318
+ *
319
+ * @returns
320
+ * Next result from the intermediate iterator.
321
+ */
322
+ protected intermediateNext(initialResult: IteratorResult<TInitial, undefined>): IteratorResult<TIntermediate, undefined> {
323
+ let intermediateResult: IteratorResult<TIntermediate, undefined>;
324
+
325
+ if (initialResult.done !== true) {
326
+ intermediateResult = {
327
+ done: false,
328
+ value: this._callback(initialResult.value, this._index++)
329
+ };
330
+ } else {
331
+ intermediateResult = {
332
+ done: true,
333
+ value: undefined
334
+ };
335
+ }
336
+
337
+ return intermediateResult;
338
+ }
339
+ }
340
+
341
+ /**
342
+ * Iterator map proxy.
343
+ */
344
+ class IteratorMapProxy<TInitial, TFinal> extends IteratorCallbackProxyBase<TInitial, TFinal, TFinal> {
345
+ /**
346
+ * @inheritDoc
347
+ */
348
+ next(...value: [] | [unknown]): IteratorResult<TFinal, undefined> {
349
+ return this.intermediateNext(this.initialNext(...value));
350
+ }
351
+ }
352
+
353
+ /**
354
+ * Iterator flat map proxy.
355
+ */
356
+ class IteratorFlatMapProxy<TInitial, TFinal> extends IteratorCallbackProxyBase<TInitial, IterationSource<TFinal>, TFinal> {
357
+ private _pendingFinalIterator: Iterator<TFinal, undefined> | undefined;
358
+
359
+ /**
360
+ * @inheritDoc
361
+ */
362
+ next(...value: [] | [unknown]): IteratorResult<TFinal, undefined> {
363
+ let finalResult: IteratorResult<TFinal, undefined> | undefined = undefined;
364
+
365
+ do {
366
+ if (this._pendingFinalIterator === undefined) {
367
+ const intermediateResult = this.intermediateNext(this.initialNext(...value));
368
+
369
+ if (intermediateResult.done === true) {
370
+ finalResult = intermediateResult;
371
+ } else {
372
+ this._pendingFinalIterator = iterationSourceToIterable(intermediateResult.value)[Symbol.iterator]();
373
+ }
374
+ }
375
+
376
+ if (this._pendingFinalIterator !== undefined) {
377
+ const pendingFinalResult = this._pendingFinalIterator.next();
378
+
379
+ if (pendingFinalResult.done === true) {
380
+ this._pendingFinalIterator = undefined;
381
+ } else {
382
+ finalResult = {
383
+ done: false,
384
+ value: pendingFinalResult.value
385
+ };
386
+ }
387
+ }
388
+ } while (finalResult === undefined);
389
+
390
+ return finalResult;
391
+ }
392
+ }
393
+
394
+ /**
395
+ * Iterator filter proxy.
396
+ */
397
+ class IteratorFilterProxy<T> extends IteratorCallbackProxyBase<T, unknown, T> {
398
+ /**
399
+ * @inheritDoc
400
+ */
401
+ next(...value: [] | [unknown]): IteratorResult<T, undefined> {
402
+ let finalResult: IteratorResult<T, undefined> | undefined = undefined;
403
+
404
+ do {
405
+ const initialResult = this.initialNext(...value);
406
+
407
+ if (initialResult.done === true) {
408
+ finalResult = {
409
+ done: true,
410
+ value: undefined
411
+ };
412
+ } else {
413
+ const intermediateResult = this.intermediateNext(initialResult);
414
+ const booleanValue = Boolean(intermediateResult.value);
415
+
416
+ if (booleanValue) {
417
+ finalResult = {
418
+ done: false,
419
+ value: initialResult.value
420
+ };
421
+ }
422
+ }
423
+ } while (finalResult === undefined);
424
+
425
+ return finalResult;
426
+ }
427
+ }
428
+
429
+ /**
430
+ * Iterator count proxy base.
431
+ */
432
+ abstract class IteratorCountProxyBase<T> extends IteratorProxyObject<T> {
433
+ /**
434
+ * Count.
435
+ */
436
+ private _count: number;
437
+
438
+ /**
439
+ * Constructor.
440
+ *
441
+ * @param initialIterationSource
442
+ * Initial iteration source.
443
+ *
444
+ * @param count
445
+ * Count.
446
+ */
447
+ constructor(initialIterationSource: IterationSource<T>, count: number) {
448
+ super(initialIterationSource);
449
+
450
+ if (!Number.isInteger(count) || count < 0) {
451
+ throw new RangeError("Limit must be a positive integer");
452
+ }
453
+
454
+ this._count = count;
455
+ }
456
+
457
+ /**
458
+ * Determine if count is exhausted.
459
+ */
460
+ protected get countExhausted(): boolean {
461
+ // Decrementing the count may go below zero so use less-than-or-equal comparison.
462
+ return this._count-- <= 0;
463
+ }
464
+ }
465
+
466
+ /**
467
+ * Iterator take proxy.
468
+ */
469
+ class IteratorTakeProxy<T> extends IteratorCountProxyBase<T> {
470
+ /**
471
+ * @inheritDoc
472
+ */
473
+ override next(...value: [] | [unknown]): IteratorResult<T, undefined> {
474
+ return this.countExhausted ?
475
+ {
476
+ done: true,
477
+ value: undefined
478
+ } :
479
+ super.next(...value);
480
+ }
481
+ }
482
+
483
+ /**
484
+ * Iterator drop proxy.
485
+ */
486
+ class IteratorDropProxy<T> extends IteratorCountProxyBase<T> {
487
+ /**
488
+ * @inheritDoc
489
+ */
490
+ override next(...value: [] | [unknown]): IteratorResult<T, undefined> {
491
+ let result: IteratorResult<T, undefined> | undefined = undefined;
492
+
493
+ do {
494
+ result = super.next(...value);
495
+
496
+ if (result.done !== true && !this.countExhausted) {
497
+ // Discard result.
498
+ result = undefined;
499
+ }
500
+ } while (result === undefined);
501
+
502
+ return result;
503
+ }
504
+ }
505
+
506
+ /**
507
+ * Iterator type.
508
+ */
509
+ type IteratorType = typeof Iterator;
510
+
511
+ /**
512
+ * Iterator proxy is interested only in the "from" method.
513
+ */
514
+ type IteratorProxyType = Pick<IteratorType, "from">;
515
+
516
+ /**
517
+ * Iterator proxy. In environments where the Iterator variable is supported, this is the Iterator variable. Otherwise,
518
+ * it's an implementation of "from" that uses an internally-defined iterator proxy object.
519
+ */
520
+ export const IteratorProxy: IteratorProxyType = isIteratorSupported() ?
521
+ Iterator :
522
+ {
523
+ /**
524
+ * @inheritDoc
525
+ */
526
+ from<T>(value: IterationSource<T>): IteratorObject<T, undefined> {
527
+ return value instanceof IteratorProxyBase ? value : new IteratorProxyObject(value);
528
+ }
529
+ };
@@ -0,0 +1,32 @@
1
+ export const localeStrings = {
2
+ Transformer: {
3
+ domainMustBeGreaterThanZero: "Le domaine {{domain}} doit être supérieur à 0",
4
+ tweakMustBeGreaterThanOrEqualToZero: "Le réglage {{tweak}} doit être supérieur ou égal à 0",
5
+ valueMustBeGreaterThanOrEqualToZero: "La valeur {{value}} doit être supérieure ou égale à 0",
6
+ valueMustBeLessThan: "La valeur {{value}} doit être inférieure à {{domain}}",
7
+ minValueMustBeGreaterThanOrEqualToZero: "La valeur minimale {{minValue}} doit être supérieure ou égale à 0",
8
+ maxValueMustBeLessThan: "La valeur maximale {{maxValue}} doit être inférieure à {{domain}}"
9
+ },
10
+ RegExpValidator: {
11
+ stringDoesNotMatchPattern: "La chaîne {{s}} ne correspond pas au modèle"
12
+ },
13
+ CharacterSetValidator: {
14
+ firstZeroFirstCharacter: "Le jeu de caractères doit prendre en charge zéro comme premier caractère",
15
+ allNumericAllNumericCharacters: "Le jeu de caractères doit prendre en charge tous les caractères numériques en séquence",
16
+ stringMustNotBeAllNumeric: "La chaîne ne doit pas être entièrement numérique",
17
+ lengthMustBeGreaterThanOrEqualTo: "La longueur {{length}} doit être supérieure ou égale à {{minimumLength}}",
18
+ lengthMustBeLessThanOrEqualTo: "La longueur {{length}} doit être inférieure ou égale à {{maximumLength}}",
19
+ lengthMustBeEqualTo: "La longueur {{length}} doit être égale à {{exactLength}}",
20
+ lengthOfComponentMustBeGreaterThanOrEqualTo: "La longueur {{length}} de {{component}} doit être supérieure ou égale à {{minimumLength}}",
21
+ lengthOfComponentMustBeLessThanOrEqualTo: "La longueur {{length}} de {{component}} doit être inférieure ou égale à {{maximumLength}}",
22
+ lengthOfComponentMustBeEqualTo: "La longueur {{length}} de {{component}} doit être égale à {{exactLength}}",
23
+ invalidCharacterAtPosition: "Caractère non valide '{{c}}' à la position {{position}}",
24
+ invalidCharacterAtPositionOfComponent: "Caractère non valide '{{c}}' à la position {{position}} de {{component}}",
25
+ exclusionNotSupported: "La valeur d'exclusion de {{exclusion}} n'est pas prise en charge",
26
+ invalidTweakWithAllNumericExclusion: "Le réglage ne doit pas être utilisé avec une exclusion entièrement numérique",
27
+ endSequenceValueMustBeLessThanOrEqualTo: "La valeur de la séquence de fin (valeur de la séquence de début + nombre - 1) doit être inférieure à {{domaine}}"
28
+ },
29
+ RecordValidator: {
30
+ typeNameKeyNotFound: "{{typeName}} \"{{key}}\" introuvable"
31
+ }
32
+ } as const;
@@ -1,8 +1,12 @@
1
- import { i18nAddResourceBundle, i18next } from "@aidc-toolkit/core";
1
+ import { i18nAddResourceBundle, i18nAssertValidResources, i18next } from "@aidc-toolkit/core";
2
2
  import { localeStrings as enLocaleStrings } from "./en/locale_strings.js";
3
+ import { localeStrings as frLocaleStrings } from "./fr/locale_strings.js";
3
4
 
4
5
  export const utilityNS = "aidct_utility";
5
6
 
7
+ i18nAssertValidResources(enLocaleStrings, "fr", frLocaleStrings);
8
+
6
9
  i18nAddResourceBundle("en", utilityNS, enLocaleStrings);
10
+ i18nAddResourceBundle("fr", utilityNS, frLocaleStrings);
7
11
 
8
12
  export default i18next;
@@ -1,3 +1,4 @@
1
+ import { IteratorProxy } from "./iterator_proxy.js";
1
2
  import i18next, { utilityNS } from "./locale/i18n.js";
2
3
  import { Sequencer } from "./sequencer.js";
3
4
 
@@ -266,18 +267,18 @@ export abstract class Transformer {
266
267
  }
267
268
 
268
269
  result = transformationCallback === undefined ?
269
- Iterator.from(valueOrValues).map(value => this.doForward(value)) :
270
- Iterator.from(valueOrValues).map((value, index) => transformationCallback(this.doForward(value), index));
270
+ IteratorProxy.from(valueOrValues).map(value => this.doForward(value)) :
271
+ IteratorProxy.from(valueOrValues).map((value, index) => transformationCallback(this.doForward(value), index));
271
272
  } else {
272
273
  result = transformationCallback === undefined ?
273
- Iterator.from(valueOrValues).map((value) => {
274
+ IteratorProxy.from(valueOrValues).map((value) => {
274
275
  const valueN = BigInt(value);
275
276
 
276
277
  this.validate(valueN);
277
278
 
278
279
  return this.doForward(valueN);
279
280
  }) :
280
- Iterator.from(valueOrValues).map((value, index) => {
281
+ IteratorProxy.from(valueOrValues).map((value, index) => {
281
282
  const valueN = BigInt(value);
282
283
 
283
284
  this.validate(valueN);
@@ -6,6 +6,7 @@ import {
6
6
  CharacterSetCreator,
7
7
  Exclusion,
8
8
  HEXADECIMAL_CREATOR,
9
+ IteratorProxy,
9
10
  NUMERIC_CREATOR,
10
11
  Sequencer
11
12
  } from "../src/index.js";
@@ -64,7 +65,7 @@ function testCharacterSetCreator(name: string, characterSetCreator: CharacterSet
64
65
  break;
65
66
  }
66
67
 
67
- const sequence = Iterator.from(characterSetCreator.create(length, new Sequencer(0n, domain), exclusion));
68
+ const sequence = IteratorProxy.from(characterSetCreator.create(length, new Sequencer(0n, domain), exclusion));
68
69
 
69
70
  let previousS = "";
70
71
 
@@ -85,7 +86,7 @@ function testCharacterSetCreator(name: string, characterSetCreator: CharacterSet
85
86
 
86
87
  expect(() => characterSetCreator.create(length, domain, exclusion)).toThrow(`Value ${domain} must be less than ${domain}`);
87
88
 
88
- const sparseSequence = Iterator.from(characterSetCreator.create(length, new Sequencer(domain - 1, -domain), exclusion, 123456n));
89
+ const sparseSequence = IteratorProxy.from(characterSetCreator.create(length, new Sequencer(domain - 1, -domain), exclusion, 123456n));
89
90
 
90
91
  let sequential = true;
91
92
  previousS = "~";
@@ -0,0 +1,220 @@
1
+ import { describe, expect, test } from "vitest";
2
+ import { type IterationSource, IteratorProxy } from "../src/index.js";
3
+
4
+ const source: readonly string[] = [
5
+ "1", "2", "3", "4", "5"
6
+ ];
7
+
8
+ function iterableSource(): Iterable<string> {
9
+ return [...source];
10
+ }
11
+
12
+ function arraySource(): string[] {
13
+ return [...source];
14
+ }
15
+
16
+ function iteratorSource(): Iterator<string> {
17
+ return [...source][Symbol.iterator]();
18
+ }
19
+
20
+ function * generatorSource(): Generator<string> {
21
+ for (const s of source) {
22
+ yield s;
23
+ }
24
+ }
25
+
26
+ function callbackSource(): string[] {
27
+ return [...source];
28
+ }
29
+
30
+ describe("Basic", () => {
31
+ function validateIterable(iterationSource: IterationSource<string>): void {
32
+ const iteratorProxy = IteratorProxy.from(iterationSource);
33
+
34
+ expect(IteratorProxy.from(iteratorProxy)).toBe(iteratorProxy);
35
+
36
+ // @ts-expect-error -- Property exists.
37
+ expect(iteratorProxy["_initialIterable"]).toBe(iterationSource);
38
+
39
+ expect(Array.from(iteratorProxy)).toStrictEqual(source);
40
+ }
41
+
42
+ test("Iterator proxy", () => {
43
+ expect(IteratorProxy).not.toBe(Iterator);
44
+ });
45
+
46
+ test("Iterable", () => {
47
+ validateIterable(iterableSource());
48
+ });
49
+
50
+ test("Array", () => {
51
+ validateIterable(arraySource());
52
+ });
53
+
54
+ test("Iterator", () => {
55
+ validateIterable(iteratorSource());
56
+ });
57
+
58
+ test("Generator", () => {
59
+ validateIterable(generatorSource());
60
+ });
61
+
62
+ test("Callback", () => {
63
+ validateIterable(callbackSource());
64
+ });
65
+ });
66
+
67
+ describe("Helpers", () => {
68
+ test("Map", () => {
69
+ let count = 0;
70
+
71
+ const mapIteratorProxy = IteratorProxy.from(source).map((element, index) => {
72
+ expect(Number(element)).toBe(index + 1);
73
+ expect(index).toBe(count++);
74
+
75
+ return -count;
76
+ });
77
+
78
+ expect(count).toBe(0);
79
+
80
+ let negativeCount = 0;
81
+
82
+ for (const element of mapIteratorProxy) {
83
+ expect(element).toBe(--negativeCount);
84
+ }
85
+
86
+ expect(count).toBe(source.length);
87
+ });
88
+
89
+ test("Flat map", () => {
90
+ let count = 0;
91
+
92
+ const flatMapIteratorProxy = IteratorProxy.from(source).flatMap((element, index) => {
93
+ expect(Number(element)).toBe(index + 1);
94
+ expect(index).toBe(count++);
95
+
96
+ return [count, -count];
97
+ });
98
+
99
+ expect(count).toBe(0);
100
+
101
+ let index = 0;
102
+
103
+ for (const element of flatMapIteratorProxy) {
104
+ const absoluteElement = Math.floor(index / 2) + 1;
105
+
106
+ expect(element).toBe(index % 2 === 0 ? absoluteElement : -absoluteElement);
107
+ index++;
108
+ }
109
+
110
+ expect(count).toBe(source.length);
111
+ });
112
+
113
+ test("Filter", () => {
114
+ let count = 0;
115
+
116
+ const filteredIterable = IteratorProxy.from(source).filter((element, index) => {
117
+ expect(Number(element)).toBe(index + 1);
118
+ expect(index).toBe(count++);
119
+
120
+ return Number(element) % 2 === 0;
121
+ });
122
+
123
+ expect(count).toBe(0);
124
+
125
+ let evenCount = 0;
126
+
127
+ for (const element of filteredIterable) {
128
+ const n = Number(element);
129
+
130
+ expect(n % 2).toBe(0);
131
+ expect(Math.floor((n - 1) / 2)).toBe(evenCount++);
132
+ }
133
+
134
+ expect(count).toBe(source.length);
135
+ expect(evenCount).toBe(Math.floor(source.length / 2));
136
+ });
137
+
138
+ test("Take", () => {
139
+ let count = 0;
140
+
141
+ for (const element of IteratorProxy.from(source).take(3)) {
142
+ expect(element).toBe(String(++count));
143
+ }
144
+
145
+ expect(count).toBe(3);
146
+ });
147
+
148
+ test("Drop", () => {
149
+ let count = 0;
150
+
151
+ for (const element of IteratorProxy.from(source).drop(3)) {
152
+ expect(element).toBe(String(++count + 3));
153
+ }
154
+
155
+ expect(count).toBe(source.length - 3);
156
+ });
157
+
158
+ test("To array", () => {
159
+ const sourceToArray = IteratorProxy.from(source).toArray();
160
+
161
+ expect(sourceToArray).not.toBe(source);
162
+ expect(sourceToArray).toStrictEqual(source);
163
+ });
164
+
165
+ test("For each", () => {
166
+ let count = 0;
167
+
168
+ IteratorProxy.from(source).forEach((value, index) => {
169
+ expect(Number(value)).toBe(index + 1);
170
+ expect(index).toBe(count++);
171
+ });
172
+
173
+ expect(count).toBe(source.length);
174
+ });
175
+
176
+ test("Reduce no initial value", () => {
177
+ let count = 0;
178
+
179
+ expect(IteratorProxy.from(source).reduce((previousValue, currentValue, currentIndex) => {
180
+ expect(Number(currentValue)).toBe(currentIndex + 1);
181
+ expect(currentIndex - 1).toBe(count++);
182
+
183
+ return previousValue + currentValue;
184
+ })).toBe("".concat(...source));
185
+
186
+ expect(count).toBe(source.length - 1);
187
+
188
+ expect(() => IteratorProxy.from<string>([]).reduce(() => "")).toThrow("reduce() of empty iterator with no initial value");
189
+ });
190
+
191
+ test("Reduce initial value", () => {
192
+ let count = 0;
193
+
194
+ expect(IteratorProxy.from(source).reduce((previousValue, currentValue, currentIndex) => {
195
+ expect(Number(currentValue)).toBe(currentIndex + 1);
196
+ expect(currentIndex).toBe(count++);
197
+
198
+ return previousValue + currentValue;
199
+ }, "0")).toBe("0".concat(...source));
200
+
201
+ expect(count).toBe(source.length);
202
+
203
+ expect(IteratorProxy.from<string>([]).reduce(() => "", "0")).toBe("0");
204
+ });
205
+
206
+ test("Some", () => {
207
+ expect(IteratorProxy.from(source).some(value => value === "3")).toBe(true);
208
+ expect(IteratorProxy.from(source).some(value => value === "6")).toBe(false);
209
+ });
210
+
211
+ test("Every", () => {
212
+ expect(IteratorProxy.from(source).every(value => Number(value) > 0)).toBe(true);
213
+ expect(IteratorProxy.from(source).every(value => Number(value) < Number(source[source.length - 1]))).toBe(false);
214
+ });
215
+
216
+ test("Find", () => {
217
+ expect(IteratorProxy.from(source).find(value => Number(value) % 3 === 0)).toBe("3");
218
+ expect(IteratorProxy.from(source).find(value => Number(value) % 7 === 0)).toBeUndefined();
219
+ });
220
+ });
@@ -1,6 +1,6 @@
1
1
  import { I18NEnvironment, i18nInit } from "@aidc-toolkit/core";
2
2
  import { describe, expect, test } from "vitest";
3
- import { Sequencer } from "../src/index.js";
3
+ import { IteratorProxy, Sequencer } from "../src/index.js";
4
4
 
5
5
  await i18nInit(I18NEnvironment.CLI, true);
6
6
 
@@ -29,7 +29,7 @@ describe("Sequence", () => {
29
29
  expectedValue = 10n;
30
30
  count = 0;
31
31
 
32
- for (const value of Iterator.from(sequencer1)) {
32
+ for (const value of IteratorProxy.from(sequencer1)) {
33
33
  expect(value).toBe(expectedValue);
34
34
 
35
35
  expectedValue++;
@@ -41,7 +41,7 @@ describe("Sequence", () => {
41
41
  expectedValue = 29n;
42
42
  count = 0;
43
43
 
44
- for (const value of Iterator.from(sequencer2)) {
44
+ for (const value of IteratorProxy.from(sequencer2)) {
45
45
  expect(value).toBe(expectedValue);
46
46
 
47
47
  expectedValue--;
@@ -60,7 +60,7 @@ describe("Sequence", () => {
60
60
 
61
61
  sequencer1.reset();
62
62
 
63
- for (const value of Iterator.from(sequencer1)) {
63
+ for (const value of IteratorProxy.from(sequencer1)) {
64
64
  expect(value).toBe(expectedValue);
65
65
 
66
66
  expectedValue++;
@@ -1,6 +1,6 @@
1
1
  import { I18NEnvironment, i18nInit } from "@aidc-toolkit/core";
2
2
  import { describe, expect, test } from "vitest";
3
- import { Sequencer, EncryptionTransformer, IdentityTransformer, Transformer } from "../src/index.js";
3
+ import { EncryptionTransformer, IdentityTransformer, Sequencer, Transformer } from "../src/index.js";
4
4
 
5
5
  await i18nInit(I18NEnvironment.CLI, true);
6
6