@aidc-toolkit/utility 0.9.3 → 0.9.5

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aidc-toolkit/utility",
3
- "version": "0.9.3",
3
+ "version": "0.9.5",
4
4
  "description": "Foundational utilities for AIDC Toolkit",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -25,15 +25,16 @@
25
25
  "test": "vitest run"
26
26
  },
27
27
  "devDependencies": {
28
- "@aidc-toolkit/dev": "^0.9.3",
29
- "eslint": "^9.14.0",
28
+ "@aidc-toolkit/dev": "^0.9.5",
29
+ "eslint": "^9.15.0",
30
30
  "ts-node": "^10.9.2",
31
31
  "tsup": "^8.3.5",
32
32
  "typescript": "^5.6.3",
33
33
  "vitest": "^2.1.5"
34
34
  },
35
35
  "dependencies": {
36
- "@aidc-toolkit/core": "^0.9.3",
37
- "i18next": "^23.16.5"
36
+ "@aidc-toolkit/core": "^0.9.5",
37
+ "@rollup/rollup-linux-x64-gnu": "^4.27.3",
38
+ "i18next": "^23.16.8"
38
39
  }
39
40
  }
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,517 @@
1
+ /**
2
+ * Iteration source; shortcut for iterator or iterable.
3
+ *
4
+ * Client applications should **not** rely on long-term availability of this variable as it will be removed once there
5
+ * is widespread support for iterator helpers.
6
+ */
7
+ type IterationSource<T> = Iterator<T> | Iterable<T>;
8
+
9
+ /**
10
+ * Iterator proxy base; provides common functionality for all iterator objects.
11
+ */
12
+ abstract class IteratorProxyBase<TInitial, TFinal> implements IteratorObject<TFinal, undefined> {
13
+ /**
14
+ * Convert an iteration source to an iterable.
15
+ *
16
+ * @param iterationSource
17
+ * Iteration source.
18
+ *
19
+ * @returns
20
+ * Iteration source if it is already an iterable, otherwise iteration source wrapped in an iterable.
21
+ */
22
+ protected static toIterable<T>(iterationSource: IterationSource<T>): Iterable<T> {
23
+ return Symbol.iterator in iterationSource ?
24
+ iterationSource :
25
+ {
26
+ [Symbol.iterator](): Iterator<T> {
27
+ return iterationSource;
28
+ }
29
+ };
30
+ }
31
+
32
+ /**
33
+ * Initial iterable.
34
+ */
35
+ private readonly _initialIterable: Iterable<TInitial>;
36
+
37
+ /**
38
+ * Initial iterator.
39
+ */
40
+ private _initialIterator?: Iterator<TInitial>;
41
+
42
+ /**
43
+ * Constructor.
44
+ *
45
+ * @param initialIterationSource
46
+ * Initial iteration source.
47
+ */
48
+ constructor(initialIterationSource: IterationSource<TInitial>) {
49
+ this._initialIterable = IteratorProxyBase.toIterable(initialIterationSource);
50
+ }
51
+
52
+ /**
53
+ * Get the initial iterable.
54
+ */
55
+ protected get initialIterable(): Iterable<TInitial> {
56
+ return this._initialIterable;
57
+ }
58
+
59
+ /**
60
+ * Get the initial iterator.
61
+ */
62
+ protected get initialIterator(): Iterator<TInitial> {
63
+ if (this._initialIterator === undefined) {
64
+ this._initialIterator = this.initialIterable[Symbol.iterator]();
65
+ }
66
+
67
+ return this._initialIterator;
68
+ }
69
+
70
+ /**
71
+ * @inheritDoc
72
+ */
73
+ get [Symbol.toStringTag](): string {
74
+ return "IteratorProxy";
75
+ }
76
+
77
+ /**
78
+ * @inheritDoc
79
+ */
80
+ [Symbol.dispose](): void {
81
+ }
82
+
83
+ /**
84
+ * @inheritDoc
85
+ */
86
+ [Symbol.iterator](): IteratorObject<TFinal, undefined> {
87
+ return this;
88
+ }
89
+
90
+ /**
91
+ * Get the next result from the initial iterator.
92
+ *
93
+ * @param value
94
+ * Tuple value to be passed to Iterator.next().
95
+ *
96
+ * @returns
97
+ * Next result from the initial iterator.
98
+ */
99
+ protected initialNext(...value: [] | [unknown]): IteratorResult<TInitial, undefined> {
100
+ return this.initialIterator.next(...value);
101
+ }
102
+
103
+ /**
104
+ * @inheritDoc
105
+ */
106
+ abstract next(...value: [] | [unknown]): IteratorResult<TFinal, undefined>;
107
+
108
+ /**
109
+ * @inheritDoc
110
+ */
111
+ map<U>(callback: (value: TFinal, index: number) => U): IteratorObject<U, undefined> {
112
+ return new IteratorMapProxy(this, callback);
113
+ }
114
+
115
+ /**
116
+ * @inheritDoc
117
+ */
118
+ flatMap<U>(callback: (value: TFinal, index: number) => IterationSource<U>): IteratorObject<U, undefined> {
119
+ return new IteratorFlatMapProxy(this, callback);
120
+ }
121
+
122
+ /**
123
+ * @inheritDoc
124
+ */
125
+ filter(predicate: (value: TFinal, index: number) => unknown): IteratorObject<TFinal, undefined> {
126
+ return new IteratorFilterProxy(this, predicate, true);
127
+ }
128
+
129
+ /**
130
+ * @inheritDoc
131
+ */
132
+ take(limit: number): IteratorObject<TFinal, undefined> {
133
+ return new IteratorTakeProxy(this, limit);
134
+ }
135
+
136
+ /**
137
+ * @inheritDoc
138
+ */
139
+ drop(count: number): IteratorObject<TFinal, undefined> {
140
+ return new IteratorDropProxy(this, count);
141
+ }
142
+
143
+ /**
144
+ * @inheritDoc
145
+ */
146
+ reduce<U>(callback: (previousValue: U, currentValue: TFinal, currentIndex: number) => U, initialValue?: U): U {
147
+ let index = 0;
148
+ let result = initialValue;
149
+
150
+ for (const value of this) {
151
+ // Need to check arguments length as U could include undefined.
152
+ if (index === 0 && arguments.length === 1) {
153
+ // Initial value is not supplied only when U is identical to TFinal.
154
+ result = value as unknown as U;
155
+ } else {
156
+ // Iteration has occurred at least once so result is of the expected type.
157
+ result = callback(result as U, value, index);
158
+ }
159
+
160
+ index++;
161
+ }
162
+
163
+ if (index === 0 && arguments.length === 1) {
164
+ throw new Error("reduce() of empty iterator with no initial value");
165
+ }
166
+
167
+ // Iteration has occurred at least once so result is of the expected type.
168
+ return result as U;
169
+ }
170
+
171
+ /**
172
+ * @inheritDoc
173
+ */
174
+ toArray(): TFinal[] {
175
+ return Array.from(this);
176
+ }
177
+
178
+ /**
179
+ * @inheritDoc
180
+ */
181
+ forEach(callback: (value: TFinal, index: number) => void): void {
182
+ let index = 0;
183
+
184
+ for (const element of this) {
185
+ callback(element, index++);
186
+ }
187
+ }
188
+
189
+ /**
190
+ * @inheritDoc
191
+ */
192
+ some(predicate: (value: TFinal, index: number) => unknown): boolean {
193
+ // Filter until predicate returns truthy; return true if found.
194
+ return new IteratorFilterProxy(this, predicate, true).next().done !== true;
195
+ }
196
+
197
+ /**
198
+ * @inheritDoc
199
+ */
200
+ every(predicate: (value: TFinal, index: number) => unknown): boolean {
201
+ // Filter until predicate returns falsy; return false if found.
202
+ return new IteratorFilterProxy(this, predicate, false).next().done === true;
203
+ }
204
+
205
+ /**
206
+ * @inheritDoc
207
+ */
208
+ find(predicate: (value: TFinal, index: number) => unknown): TFinal | undefined {
209
+ // Filter until predicate returns truthy; return value.
210
+ return new IteratorFilterProxy(this, predicate, true).next().value;
211
+ }
212
+ }
213
+
214
+ /**
215
+ * Core iterator proxy object.
216
+ */
217
+ class IteratorProxyObject<T> extends IteratorProxyBase<T, T> {
218
+ /**
219
+ * @inheritDoc
220
+ */
221
+ next(...value: [] | [unknown]): IteratorResult<T, undefined> {
222
+ // Initial result is the final result.
223
+ return this.initialNext(...value);
224
+ }
225
+ }
226
+
227
+ /**
228
+ * Iterator map proxy base.
229
+ */
230
+ abstract class IteratorMapProxyBase<TInitial, TIntermediate, TFinal> extends IteratorProxyBase<TInitial, TFinal> {
231
+ /**
232
+ * Callback.
233
+ */
234
+ private readonly _callback: (element: TInitial, index: number) => TIntermediate;
235
+
236
+ /**
237
+ * Index into initial iteration source.
238
+ */
239
+ private _index: number;
240
+
241
+ /**
242
+ * Constructor.
243
+ *
244
+ * @param initialIterationSource
245
+ * Initial iteration source.
246
+ *
247
+ * @param callback
248
+ * Callback.
249
+ */
250
+ constructor(initialIterationSource: IterationSource<TInitial>, callback: (element: TInitial, index: number) => TIntermediate) {
251
+ super(initialIterationSource);
252
+
253
+ this._callback = callback;
254
+ this._index = 0;
255
+ }
256
+
257
+ /**
258
+ * Get the next result from the intermediate iterator.
259
+ *
260
+ * @param value
261
+ * Tuple value to be passed to Iterator.next().
262
+ *
263
+ * @returns
264
+ * Next result from the intermediate iterator.
265
+ */
266
+ protected intermediateNext(...value: [] | [unknown]): IteratorResult<TIntermediate, undefined> {
267
+ const initialResult = this.initialNext(...value);
268
+
269
+ return initialResult.done !== true ?
270
+ {
271
+ value: this._callback(initialResult.value, this._index++)
272
+ } :
273
+ {
274
+ done: true,
275
+ value: undefined
276
+ };
277
+ }
278
+ }
279
+
280
+ /**
281
+ * Iterator map proxy.
282
+ */
283
+ class IteratorMapProxy<TInitial, TFinal> extends IteratorMapProxyBase<TInitial, TFinal, TFinal> {
284
+ /**
285
+ * @inheritDoc
286
+ */
287
+ next(...value: [] | [unknown]): IteratorResult<TFinal, undefined> {
288
+ // Intermediate result is the final result.
289
+ return this.intermediateNext(...value);
290
+ }
291
+ }
292
+
293
+ /**
294
+ * Iterator flat map proxy.
295
+ */
296
+ class IteratorFlatMapProxy<TInitial, TFinal> extends IteratorMapProxyBase<TInitial, IterationSource<TFinal>, TFinal> {
297
+ private _intermediateIterator: Iterator<TFinal, undefined> | undefined;
298
+
299
+ /**
300
+ * @inheritDoc
301
+ */
302
+ next(...value: [] | [unknown]): IteratorResult<TFinal, undefined> {
303
+ let finalResult: IteratorResult<TFinal, undefined> | undefined = undefined;
304
+
305
+ do {
306
+ if (this._intermediateIterator === undefined) {
307
+ const intermediateResult = this.intermediateNext(...value);
308
+
309
+ if (intermediateResult.done === true) {
310
+ finalResult = intermediateResult;
311
+ } else {
312
+ this._intermediateIterator = IteratorProxyBase.toIterable(intermediateResult.value)[Symbol.iterator]();
313
+ }
314
+ } else {
315
+ const pendingFinalResult = this._intermediateIterator.next();
316
+
317
+ if (pendingFinalResult.done === true) {
318
+ this._intermediateIterator = undefined;
319
+ } else {
320
+ finalResult = pendingFinalResult;
321
+ }
322
+ }
323
+ } while (finalResult === undefined);
324
+
325
+ return finalResult;
326
+ }
327
+ }
328
+
329
+ /**
330
+ * Iterator filter proxy.
331
+ */
332
+ class IteratorFilterProxy<T> extends IteratorProxyBase<T, T> {
333
+ /**
334
+ * Predicate.
335
+ */
336
+ private readonly _predicate: (value: T, index: number) => unknown;
337
+
338
+ /**
339
+ * Expected truthy result of the predicate.
340
+ */
341
+ private readonly _expectedTruthy: boolean;
342
+
343
+ /**
344
+ * Index into iteration source.
345
+ */
346
+ private _index: number;
347
+
348
+ /**
349
+ * Constructor.
350
+ *
351
+ * @param iterationSource
352
+ * Iteration source.
353
+ *
354
+ * @param predicate
355
+ * Predicate.
356
+ *
357
+ * @param expectedTruthy
358
+ * Expected truthy result of the predicate.
359
+ */
360
+ constructor(iterationSource: IterationSource<T>, predicate: (element: T, index: number) => unknown, expectedTruthy: boolean) {
361
+ super(iterationSource);
362
+
363
+ this._predicate = predicate;
364
+ this._expectedTruthy = expectedTruthy;
365
+
366
+ this._index = 0;
367
+ }
368
+
369
+ /**
370
+ * @inheritDoc
371
+ */
372
+ next(...value: [] | [unknown]): IteratorResult<T, undefined> {
373
+ let result: IteratorResult<T, undefined> | undefined;
374
+
375
+ const expectedTruthy = this._expectedTruthy;
376
+
377
+ do {
378
+ result = this.initialNext(...value);
379
+ } while (result.done !== true && Boolean(this._predicate(result.value, this._index++)) !== expectedTruthy);
380
+
381
+ return result;
382
+ }
383
+ }
384
+
385
+ /**
386
+ * Iterator count proxy base.
387
+ */
388
+ abstract class IteratorCountProxyBase<T> extends IteratorProxyObject<T> {
389
+ /**
390
+ * Count.
391
+ */
392
+ private _count: number;
393
+
394
+ /**
395
+ * Constructor.
396
+ *
397
+ * @param initialIterationSource
398
+ * Initial iteration source.
399
+ *
400
+ * @param count
401
+ * Count.
402
+ */
403
+ constructor(initialIterationSource: IterationSource<T>, count: number) {
404
+ super(initialIterationSource);
405
+
406
+ if (!Number.isInteger(count) || count < 0) {
407
+ throw new RangeError("Count must be a positive integer");
408
+ }
409
+
410
+ this._count = count;
411
+ }
412
+
413
+ /**
414
+ * Determine if iterator is exhausted (by count or by iterator itself).
415
+ */
416
+ protected get exhausted(): boolean {
417
+ return this._count <= 0;
418
+ }
419
+
420
+ /**
421
+ * @inheritDoc
422
+ */
423
+ override next(...value: [] | [unknown]): IteratorResult<T, undefined> {
424
+ const result = super.next(...value);
425
+
426
+ if (result.done !== true) {
427
+ this._count--;
428
+ } else {
429
+ // Iterator exhausted before count.
430
+ this._count = 0;
431
+ }
432
+
433
+ return result;
434
+ }
435
+ }
436
+
437
+ /**
438
+ * Iterator take proxy.
439
+ */
440
+ class IteratorTakeProxy<T> extends IteratorCountProxyBase<T> {
441
+ /**
442
+ * @inheritDoc
443
+ */
444
+ override next(...value: [] | [unknown]): IteratorResult<T, undefined> {
445
+ return !this.exhausted ?
446
+ super.next(...value) :
447
+ {
448
+ done: true,
449
+ value: undefined
450
+ };
451
+ }
452
+ }
453
+
454
+ /**
455
+ * Iterator drop proxy.
456
+ */
457
+ class IteratorDropProxy<T> extends IteratorCountProxyBase<T> {
458
+ /**
459
+ * @inheritDoc
460
+ */
461
+ override next(...value: [] | [unknown]): IteratorResult<T, undefined> {
462
+ while (!this.exhausted) {
463
+ super.next(...value);
464
+ }
465
+
466
+ return super.next(...value);
467
+ }
468
+ }
469
+
470
+ /**
471
+ * Get Iterator variable if supported or a proxy for it if not.
472
+ *
473
+ * @returns
474
+ * Iterator variable if supported or a proxy for it if not.
475
+ */
476
+ function iteratorProxy(): Pick<typeof Iterator, "from"> {
477
+ let supported: boolean;
478
+
479
+ try {
480
+ // Not supported if in testing.
481
+ supported = process.env["NODE_ENV"] !== "test";
482
+ } catch (_e) {
483
+ // Assume supported.
484
+ supported = true;
485
+ }
486
+
487
+ if (supported) {
488
+ try {
489
+ // This will throw a ReferenceError if Iterator variable is not supported.
490
+ Iterator.from([]);
491
+ } catch (_e) {
492
+ supported = false;
493
+ }
494
+ }
495
+
496
+ return supported ?
497
+ Iterator :
498
+ {
499
+ /**
500
+ * @inheritDoc
501
+ */
502
+ from<T>(value: Iterator<T> | Iterable<T>): IteratorObject<T, undefined> {
503
+ return value instanceof IteratorProxyBase ? value : new IteratorProxyObject(value);
504
+ }
505
+ };
506
+ }
507
+
508
+ /**
509
+ * Iterator proxy. In environments where
510
+ * {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Iterator#iterator_helpers |
511
+ * iterator helpers} are supported, this references the {@link Iterator} variable directly. Otherwise, it references an
512
+ * implementation of "from" that uses an internally-defined iterator proxy object.
513
+ *
514
+ * Client applications should **not** rely on long-term availability of this variable as it will be removed once there
515
+ * is widespread support for iterator helpers.
516
+ */
517
+ export const IteratorProxy = iteratorProxy();
@@ -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,11 +6,12 @@ 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";
12
13
 
13
- await i18nInit(I18NEnvironment.CLI, true);
14
+ await i18nInit(I18NEnvironment.CLI);
14
15
 
15
16
  function testCharacterSetCreator(name: string, characterSetCreator: CharacterSetCreator, characterSetSize: number, length: number, excludeFirstZero: boolean, excludeAllNumeric: boolean): void {
16
17
  describe(name, () => {
@@ -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 = "~";
@@ -193,7 +194,7 @@ testCharacterSetCreator("Hexadecimal", HEXADECIMAL_CREATOR, 16, 4, true, true);
193
194
  testCharacterSetCreator("Alphabetic", ALPHABETIC_CREATOR, 26, 3, false, false);
194
195
  testCharacterSetCreator("Alphanumeric", ALPHANUMERIC_CREATOR, 36, 3, true, true);
195
196
  testCharacterSetCreator("Middle numeric", new CharacterSetCreator([
196
- "(", ")", "*", "+",
197
+ "(", ")",
197
198
  "0", "1", "2", "3", "4", "5", "6", "7", "8", "9",
198
- ":", ";", "<", ">"
199
- ], Exclusion.AllNumeric), 18, 4, false, true);
199
+ "<", ">"
200
+ ], Exclusion.AllNumeric), 14, 4, false, true);