@aidc-toolkit/utility 0.9.0 → 0.9.2
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/.idea/runConfigurations/build_dev.xml +12 -0
- package/eslint.config.js +1 -3
- package/package.json +13 -13
- package/src/character_set.ts +135 -144
- package/src/index.ts +2 -2
- package/src/locale/en/locale_strings.ts +6 -4
- package/src/locale/i18next.d.ts +6 -0
- package/src/record.ts +2 -4
- package/src/reg_exp.ts +3 -0
- package/src/sequencer.ts +149 -0
- package/src/string.ts +3 -3
- package/src/transformer.ts +121 -108
- package/test/character_set.test.ts +51 -22
- package/test/record.test.ts +3 -3
- package/test/reg_exp.test.ts +3 -3
- package/test/sequencer.test.ts +72 -0
- package/test/transformer.test.ts +12 -14
- package/typedoc.json +2 -1
- package/.idea/runConfigurations/Test_iteration.xml +0 -12
- package/src/iteration.ts +0 -343
- package/test/iteration.test.ts +0 -282
|
@@ -6,13 +6,12 @@ import {
|
|
|
6
6
|
CharacterSetCreator,
|
|
7
7
|
Exclusion,
|
|
8
8
|
HEXADECIMAL_CREATOR,
|
|
9
|
-
|
|
10
|
-
|
|
9
|
+
NUMERIC_CREATOR,
|
|
10
|
+
Sequencer
|
|
11
11
|
} from "../src/index.js";
|
|
12
12
|
|
|
13
13
|
await i18nInit(I18NEnvironment.CLI, true);
|
|
14
14
|
|
|
15
|
-
// eslint-disable-next-line jsdoc/require-jsdoc
|
|
16
15
|
function testCharacterSetCreator(name: string, characterSetCreator: CharacterSetCreator, characterSetSize: number, length: number, excludeFirstZero: boolean, excludeAllNumeric: boolean): void {
|
|
17
16
|
describe(name, () => {
|
|
18
17
|
test("Character set", () => {
|
|
@@ -46,7 +45,6 @@ function testCharacterSetCreator(name: string, characterSetCreator: CharacterSet
|
|
|
46
45
|
expect(characterSetCreator.exclusionSupport.includes(Exclusion.AllNumeric)).toBe(excludeAllNumeric);
|
|
47
46
|
});
|
|
48
47
|
|
|
49
|
-
// eslint-disable-next-line jsdoc/require-jsdoc
|
|
50
48
|
function testCreate(exclusion: Exclusion): void {
|
|
51
49
|
let domain: number;
|
|
52
50
|
|
|
@@ -66,7 +64,7 @@ function testCharacterSetCreator(name: string, characterSetCreator: CharacterSet
|
|
|
66
64
|
break;
|
|
67
65
|
}
|
|
68
66
|
|
|
69
|
-
const sequence =
|
|
67
|
+
const sequence = Iterator.from(characterSetCreator.create(length, new Sequencer(0n, domain), exclusion));
|
|
70
68
|
|
|
71
69
|
let previousS = "";
|
|
72
70
|
|
|
@@ -78,26 +76,26 @@ function testCharacterSetCreator(name: string, characterSetCreator: CharacterSet
|
|
|
78
76
|
|
|
79
77
|
expect(s.length).toBe(length);
|
|
80
78
|
|
|
81
|
-
expect(characterSetCreator.
|
|
79
|
+
expect(characterSetCreator.valueFor(s, exclusion)).toBe(BigInt(index));
|
|
82
80
|
|
|
83
81
|
sequenceCount++;
|
|
84
82
|
});
|
|
85
83
|
|
|
86
84
|
expect(sequenceCount).toBe(domain);
|
|
87
85
|
|
|
88
|
-
expect(() => characterSetCreator.create(length, domain, exclusion)).toThrow(
|
|
86
|
+
expect(() => characterSetCreator.create(length, domain, exclusion)).toThrow(`Value ${domain} must be less than ${domain}`);
|
|
89
87
|
|
|
90
|
-
const sparseSequence =
|
|
88
|
+
const sparseSequence = Iterator.from(characterSetCreator.create(length, new Sequencer(domain - 1, -domain), exclusion, 123456n));
|
|
91
89
|
|
|
92
90
|
let sequential = true;
|
|
93
|
-
previousS = "";
|
|
91
|
+
previousS = "~";
|
|
94
92
|
|
|
95
93
|
const sequenceSet = new Set<string>();
|
|
96
94
|
|
|
97
95
|
sequenceCount = 0;
|
|
98
96
|
|
|
99
97
|
sparseSequence.forEach((s, index) => {
|
|
100
|
-
sequential = sequential && s
|
|
98
|
+
sequential = sequential && s < previousS;
|
|
101
99
|
previousS = s;
|
|
102
100
|
|
|
103
101
|
expect(s.length).toBe(length);
|
|
@@ -105,7 +103,7 @@ function testCharacterSetCreator(name: string, characterSetCreator: CharacterSet
|
|
|
105
103
|
expect(sequenceSet.has(s)).toBe(false);
|
|
106
104
|
sequenceSet.add(s);
|
|
107
105
|
|
|
108
|
-
expect(characterSetCreator.
|
|
106
|
+
expect(characterSetCreator.valueFor(s, exclusion, 123456n)).toBe(BigInt(domain - index - 1));
|
|
109
107
|
|
|
110
108
|
sequenceCount++;
|
|
111
109
|
});
|
|
@@ -125,12 +123,19 @@ function testCharacterSetCreator(name: string, characterSetCreator: CharacterSet
|
|
|
125
123
|
sparseRandomValues.push(characterSetCreator.create(length, randomValue, exclusion, 123456n));
|
|
126
124
|
}
|
|
127
125
|
|
|
128
|
-
expect(Array.from(characterSetCreator.
|
|
129
|
-
expect(Array.from(characterSetCreator.
|
|
126
|
+
expect(Array.from(characterSetCreator.create(length, randomValues, exclusion))).toStrictEqual(straightRandomValues);
|
|
127
|
+
expect(Array.from(characterSetCreator.create(length, randomValues, exclusion, 123456n))).toStrictEqual(sparseRandomValues);
|
|
130
128
|
|
|
131
|
-
expect(() => characterSetCreator.create(length,
|
|
129
|
+
expect(() => characterSetCreator.create(length, domain, exclusion, 123456n)).toThrow(`Value ${domain} must be less than ${domain}`);
|
|
132
130
|
}
|
|
133
131
|
|
|
132
|
+
test("Length", () => {
|
|
133
|
+
expect(() => characterSetCreator.create(0, 0)).not.toThrow(RangeError);
|
|
134
|
+
expect(() => characterSetCreator.create(-1, 0)).toThrow("Length -1 must be greater than or equal to 0");
|
|
135
|
+
expect(() => characterSetCreator.create(40, 0)).not.toThrow(RangeError);
|
|
136
|
+
expect(() => characterSetCreator.create(41, 0)).toThrow("Length 41 must be less than or equal to 40");
|
|
137
|
+
});
|
|
138
|
+
|
|
134
139
|
test("Create sequence", () => {
|
|
135
140
|
testCreate(Exclusion.None);
|
|
136
141
|
});
|
|
@@ -139,8 +144,8 @@ function testCharacterSetCreator(name: string, characterSetCreator: CharacterSet
|
|
|
139
144
|
test("Create sequence, exclude first zero", () => {
|
|
140
145
|
testCreate(Exclusion.FirstZero);
|
|
141
146
|
|
|
142
|
-
expect(() => characterSetCreator.
|
|
143
|
-
expect(() => characterSetCreator.
|
|
147
|
+
expect(() => characterSetCreator.valueFor("0000", Exclusion.FirstZero)).toThrow("Invalid character '0' at position 1");
|
|
148
|
+
expect(() => characterSetCreator.valueFor("1000", Exclusion.FirstZero)).not.toThrow(RangeError);
|
|
144
149
|
});
|
|
145
150
|
}
|
|
146
151
|
|
|
@@ -148,17 +153,41 @@ function testCharacterSetCreator(name: string, characterSetCreator: CharacterSet
|
|
|
148
153
|
test("Create sequence, exclude all numeric", () => {
|
|
149
154
|
testCreate(Exclusion.AllNumeric);
|
|
150
155
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
expect(() => characterSetCreator.
|
|
155
|
-
|
|
156
|
-
expect(() => characterSetCreator.
|
|
156
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion -- Nine is known to be present in the character set.
|
|
157
|
+
const afterNine = characterSetCreator.character(characterSetCreator.characterIndex("9")! + 1);
|
|
158
|
+
|
|
159
|
+
expect(() => characterSetCreator.valueFor("0000", Exclusion.AllNumeric)).toThrow("String must not be all numeric");
|
|
160
|
+
expect(() => characterSetCreator.valueFor(`000${afterNine}`, Exclusion.AllNumeric)).not.toThrow(RangeError);
|
|
161
|
+
expect(() => characterSetCreator.valueFor("9999", Exclusion.AllNumeric)).toThrow("String must not be all numeric");
|
|
162
|
+
expect(() => characterSetCreator.valueFor(`999${afterNine}`, Exclusion.AllNumeric)).not.toThrow(RangeError);
|
|
157
163
|
});
|
|
158
164
|
}
|
|
159
165
|
});
|
|
160
166
|
}
|
|
161
167
|
|
|
168
|
+
describe("Exclusion", () => {
|
|
169
|
+
test("First zero", () => {
|
|
170
|
+
expect(() => new CharacterSetCreator([
|
|
171
|
+
"0", "1", "2", "3", "4", "5", "6", "7", "8", "9"
|
|
172
|
+
], Exclusion.FirstZero)).not.toThrow(RangeError);
|
|
173
|
+
expect(() => new CharacterSetCreator([
|
|
174
|
+
"1", "2", "3", "4", "5", "6", "7", "8", "9", "0"
|
|
175
|
+
], Exclusion.FirstZero)).toThrow("Character set must support zero as first character");
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
test("All numeric", () => {
|
|
179
|
+
expect(() => new CharacterSetCreator([
|
|
180
|
+
"!", "#", "/", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D"
|
|
181
|
+
], Exclusion.AllNumeric)).not.toThrow(RangeError);
|
|
182
|
+
expect(() => new CharacterSetCreator([
|
|
183
|
+
"!", "#", "/", "0", "1", "2", "3", "A", "B", "C", "D"
|
|
184
|
+
], Exclusion.AllNumeric)).toThrow("Character set must support all numeric characters in sequence");
|
|
185
|
+
expect(() => new CharacterSetCreator([
|
|
186
|
+
"!", "#", "/", "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "A", "B", "C", "D"
|
|
187
|
+
], Exclusion.AllNumeric)).toThrow("Character set must support all numeric characters in sequence");
|
|
188
|
+
});
|
|
189
|
+
});
|
|
190
|
+
|
|
162
191
|
testCharacterSetCreator("Numeric", NUMERIC_CREATOR, 10, 4, true, false);
|
|
163
192
|
testCharacterSetCreator("Hexadecimal", HEXADECIMAL_CREATOR, 16, 4, true, true);
|
|
164
193
|
testCharacterSetCreator("Alphabetic", ALPHABETIC_CREATOR, 26, 3, false, false);
|
package/test/record.test.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import {I18NEnvironment, i18nInit} from "@aidc-toolkit/core";
|
|
2
|
-
import {describe, expect, test} from "vitest";
|
|
3
|
-
import {RecordValidator} from "../src/index.js";
|
|
1
|
+
import { I18NEnvironment, i18nInit } from "@aidc-toolkit/core";
|
|
2
|
+
import { describe, expect, test } from "vitest";
|
|
3
|
+
import { RecordValidator } from "../src/index.js";
|
|
4
4
|
|
|
5
5
|
await i18nInit(I18NEnvironment.CLI, true);
|
|
6
6
|
|
package/test/reg_exp.test.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import {I18NEnvironment, i18nInit} from "@aidc-toolkit/core";
|
|
2
|
-
import {describe, expect, test} from "vitest";
|
|
3
|
-
import {RegExpValidator} from "../src/index.js";
|
|
1
|
+
import { I18NEnvironment, i18nInit } from "@aidc-toolkit/core";
|
|
2
|
+
import { describe, expect, test } from "vitest";
|
|
3
|
+
import { RegExpValidator } from "../src/index.js";
|
|
4
4
|
|
|
5
5
|
await i18nInit(I18NEnvironment.CLI, true);
|
|
6
6
|
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { I18NEnvironment, i18nInit } from "@aidc-toolkit/core";
|
|
2
|
+
import { describe, expect, test } from "vitest";
|
|
3
|
+
import { Sequencer } from "../src/index.js";
|
|
4
|
+
|
|
5
|
+
await i18nInit(I18NEnvironment.CLI, true);
|
|
6
|
+
|
|
7
|
+
describe("Sequence", () => {
|
|
8
|
+
const sequencer1 = new Sequencer(10, 20);
|
|
9
|
+
const sequencer2 = new Sequencer(29, -20);
|
|
10
|
+
|
|
11
|
+
test("Structure", () => {
|
|
12
|
+
expect(sequencer1.startValue).toBe(10n);
|
|
13
|
+
expect(sequencer1.endValue).toBe(30n);
|
|
14
|
+
expect(sequencer1.count).toBe(20);
|
|
15
|
+
expect(sequencer1.minValue).toBe(10n);
|
|
16
|
+
expect(sequencer1.maxValue).toBe(29n);
|
|
17
|
+
|
|
18
|
+
expect(sequencer2.startValue).toBe(29n);
|
|
19
|
+
expect(sequencer2.endValue).toBe(9n);
|
|
20
|
+
expect(sequencer2.count).toBe(-20);
|
|
21
|
+
expect(sequencer2.minValue).toBe(10n);
|
|
22
|
+
expect(sequencer2.maxValue).toBe(29n);
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
test("Iteration", () => {
|
|
26
|
+
let expectedValue: bigint;
|
|
27
|
+
let count: number;
|
|
28
|
+
|
|
29
|
+
expectedValue = 10n;
|
|
30
|
+
count = 0;
|
|
31
|
+
|
|
32
|
+
for (const value of Iterator.from(sequencer1)) {
|
|
33
|
+
expect(value).toBe(expectedValue);
|
|
34
|
+
|
|
35
|
+
expectedValue++;
|
|
36
|
+
count++;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
expect(count).toBe(20);
|
|
40
|
+
|
|
41
|
+
expectedValue = 29n;
|
|
42
|
+
count = 0;
|
|
43
|
+
|
|
44
|
+
for (const value of Iterator.from(sequencer2)) {
|
|
45
|
+
expect(value).toBe(expectedValue);
|
|
46
|
+
|
|
47
|
+
expectedValue--;
|
|
48
|
+
count++;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
expect(count).toBe(20);
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
test("Reset", () => {
|
|
55
|
+
let expectedValue: bigint;
|
|
56
|
+
let count: number;
|
|
57
|
+
|
|
58
|
+
expectedValue = 10n;
|
|
59
|
+
count = 0;
|
|
60
|
+
|
|
61
|
+
sequencer1.reset();
|
|
62
|
+
|
|
63
|
+
for (const value of Iterator.from(sequencer1)) {
|
|
64
|
+
expect(value).toBe(expectedValue);
|
|
65
|
+
|
|
66
|
+
expectedValue++;
|
|
67
|
+
count++;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
expect(count).toBe(20);
|
|
71
|
+
});
|
|
72
|
+
});
|
package/test/transformer.test.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import {I18NEnvironment, i18nInit} from "@aidc-toolkit/core";
|
|
2
|
-
import {describe, expect, test} from "vitest";
|
|
3
|
-
import { EncryptionTransformer, IdentityTransformer,
|
|
1
|
+
import { I18NEnvironment, i18nInit } from "@aidc-toolkit/core";
|
|
2
|
+
import { describe, expect, test } from "vitest";
|
|
3
|
+
import { Sequencer, EncryptionTransformer, IdentityTransformer, Transformer } from "../src/index.js";
|
|
4
4
|
|
|
5
5
|
await i18nInit(I18NEnvironment.CLI, true);
|
|
6
6
|
|
|
@@ -11,7 +11,7 @@ function testTransformer(domain: number, tweak?: number, callback?: (value: bigi
|
|
|
11
11
|
|
|
12
12
|
const transformedValuesSet = new Set<bigint>();
|
|
13
13
|
|
|
14
|
-
|
|
14
|
+
Iterator.from(transformer.forward(new Sequencer(0n, domain))).forEach((transformedValue, index) => {
|
|
15
15
|
const indexN = BigInt(index);
|
|
16
16
|
|
|
17
17
|
if (sequential && transformedValue !== indexN) {
|
|
@@ -40,12 +40,14 @@ function testTransformer(domain: number, tweak?: number, callback?: (value: bigi
|
|
|
40
40
|
transformedRandomValues.push(transformer.forward(randomValue));
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
-
expect(Array.from(transformer.
|
|
43
|
+
expect(Array.from(transformer.forward(randomValues))).toStrictEqual(transformedRandomValues);
|
|
44
44
|
|
|
45
|
-
expect(() => transformer.forward(
|
|
46
|
-
expect(() => transformer.
|
|
47
|
-
expect(() => transformer.
|
|
48
|
-
expect(() => transformer.
|
|
45
|
+
expect(() => transformer.forward(domain)).toThrow(`Value ${domain} must be less than ${domain}`);
|
|
46
|
+
expect(() => transformer.forward(new Sequencer(domain, 0))).not.toThrow(RangeError);
|
|
47
|
+
expect(() => transformer.forward(new Sequencer(domain - 1, 1))).not.toThrow(RangeError);
|
|
48
|
+
expect(() => transformer.forward(new Sequencer(domain, 1))).toThrow(`Maximum value ${domain} must be less than ${domain}`);
|
|
49
|
+
expect(() => transformer.forward(new Sequencer(0, -1))).not.toThrow(RangeError);
|
|
50
|
+
expect(() => transformer.forward(new Sequencer(-1, -1))).toThrow("Minimum value -1 must be greater than or equal to 0");
|
|
49
51
|
}
|
|
50
52
|
|
|
51
53
|
describe("Identity", () => {
|
|
@@ -117,13 +119,9 @@ describe("Encryption", () => {
|
|
|
117
119
|
});
|
|
118
120
|
|
|
119
121
|
test("Byte boundary", () => {
|
|
120
|
-
// eslint-disable-next-line @typescript-eslint/dot-notation
|
|
121
122
|
expect((Transformer.get(256n, 1n) as EncryptionTransformer)["_domainBytes"]).toBe(1);
|
|
122
|
-
// eslint-disable-next-line @typescript-eslint/dot-notation
|
|
123
123
|
expect((Transformer.get(257n, 1n) as EncryptionTransformer)["_domainBytes"]).toBe(2);
|
|
124
|
-
// eslint-disable-next-line @typescript-eslint/dot-notation
|
|
125
124
|
expect((Transformer.get(65536n, 1n) as EncryptionTransformer)["_domainBytes"]).toBe(2);
|
|
126
|
-
// eslint-disable-next-line @typescript-eslint/dot-notation
|
|
127
125
|
expect((Transformer.get(65537n, 1n) as EncryptionTransformer)["_domainBytes"]).toBe(3);
|
|
128
126
|
|
|
129
127
|
testTransformer(256, 1);
|
|
@@ -131,7 +129,7 @@ describe("Encryption", () => {
|
|
|
131
129
|
});
|
|
132
130
|
|
|
133
131
|
test("Tweak variation", () => {
|
|
134
|
-
expect(Array.from(Transformer.get(1000, 1235).
|
|
132
|
+
expect(Array.from(Transformer.get(1000, 1235).forward(new Sequencer(0n, 1000)))).not.toStrictEqual(Array.from(Transformer.get(1000, 1234).forward(new Sequencer(0n, 1000))));
|
|
135
133
|
});
|
|
136
134
|
|
|
137
135
|
test("Consistency", () => {
|
package/typedoc.json
CHANGED
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
<component name="ProjectRunConfigurationManager">
|
|
2
|
-
<configuration default="false" name="Test iteration" 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/iteration.test.ts" />
|
|
10
|
-
<method v="2" />
|
|
11
|
-
</configuration>
|
|
12
|
-
</component>
|
package/src/iteration.ts
DELETED
|
@@ -1,343 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Iteration source type. The underlying source is an iterable or iterator or a callback to an iterable or iterator.
|
|
3
|
-
*/
|
|
4
|
-
export type IterationSource<T> = Iterable<T> | Iterator<T> | (() => (Iterable<T> | Iterator<T>));
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* Iteration proxy class for applying a callback for mapping or filtering.
|
|
8
|
-
*/
|
|
9
|
-
class IterationProxy<T, U, V> implements IterableIterator<V> {
|
|
10
|
-
/**
|
|
11
|
-
* Proxied iterable iterator.
|
|
12
|
-
*/
|
|
13
|
-
private readonly _proxiedIterableIterator: IterableIterator<T>;
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* Callback for map or filter.
|
|
17
|
-
*/
|
|
18
|
-
private readonly _callback: (element: T, index: number) => U;
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* If true, callback is a predicate for a filter.
|
|
22
|
-
*/
|
|
23
|
-
private readonly _isPredicate: boolean;
|
|
24
|
-
|
|
25
|
-
/**
|
|
26
|
-
* Index into proxied iterable iterator.
|
|
27
|
-
*/
|
|
28
|
-
private _index: number;
|
|
29
|
-
|
|
30
|
-
/**
|
|
31
|
-
* Constructor.
|
|
32
|
-
*
|
|
33
|
-
* @param proxiedIterableIterator
|
|
34
|
-
* Proxied iterable iterator.
|
|
35
|
-
*
|
|
36
|
-
* @param callback
|
|
37
|
-
* Callback for map or filter.
|
|
38
|
-
*
|
|
39
|
-
* @param isPredicate
|
|
40
|
-
* If true, callback is a predicate for a filter.
|
|
41
|
-
*/
|
|
42
|
-
constructor(proxiedIterableIterator: IterableIterator<T>, callback: (element: T, index: number) => U, isPredicate: boolean) {
|
|
43
|
-
this._proxiedIterableIterator = proxiedIterableIterator;
|
|
44
|
-
this._callback = callback;
|
|
45
|
-
this._isPredicate = isPredicate;
|
|
46
|
-
|
|
47
|
-
this._index = 0;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
/**
|
|
51
|
-
* {@link Iterable} interface implementation.
|
|
52
|
-
*
|
|
53
|
-
* @returns
|
|
54
|
-
* Iterable iterator.
|
|
55
|
-
*/
|
|
56
|
-
[Symbol.iterator](): IterableIterator<V> {
|
|
57
|
-
return this;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
/**
|
|
61
|
-
* {@link Iterator} interface implementation.
|
|
62
|
-
*
|
|
63
|
-
* @param args
|
|
64
|
-
* Arguments.
|
|
65
|
-
*
|
|
66
|
-
* @returns
|
|
67
|
-
* Next element or number or total number of elements if none.
|
|
68
|
-
*/
|
|
69
|
-
next(...args: [] | [undefined]): IteratorResult<V, number> {
|
|
70
|
-
let done = false;
|
|
71
|
-
let value: V | undefined;
|
|
72
|
-
|
|
73
|
-
let callbackDone: boolean;
|
|
74
|
-
|
|
75
|
-
do {
|
|
76
|
-
const proxiedNext = this._proxiedIterableIterator.next(...args);
|
|
77
|
-
|
|
78
|
-
if (!(proxiedNext.done ?? false)) {
|
|
79
|
-
const proxiedValue = proxiedNext.value;
|
|
80
|
-
const callbackValue = this._callback(proxiedValue, this._index++);
|
|
81
|
-
|
|
82
|
-
if (!this._isPredicate) {
|
|
83
|
-
// Types U and V are known to be identical.
|
|
84
|
-
value = callbackValue as unknown as V;
|
|
85
|
-
|
|
86
|
-
callbackDone = true;
|
|
87
|
-
} else {
|
|
88
|
-
callbackDone = callbackValue as boolean;
|
|
89
|
-
|
|
90
|
-
if (callbackDone) {
|
|
91
|
-
// Types T and V are known to be identical.
|
|
92
|
-
value = proxiedValue as unknown as V;
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
} else {
|
|
96
|
-
done = true;
|
|
97
|
-
callbackDone = true;
|
|
98
|
-
}
|
|
99
|
-
} while (!callbackDone);
|
|
100
|
-
|
|
101
|
-
return done ?
|
|
102
|
-
{
|
|
103
|
-
done: true,
|
|
104
|
-
value: this._index
|
|
105
|
-
} :
|
|
106
|
-
{
|
|
107
|
-
done: false,
|
|
108
|
-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
109
|
-
value: value!
|
|
110
|
-
};
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
/**
|
|
115
|
-
* Iteration helper. Adds array-like functionality through {@link forEach}, {@link map}, {@link filter}, and
|
|
116
|
-
* {@link reduce} methods. Likely to be refactored as {@link
|
|
117
|
-
* https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Iterator#iterator_helpers | iterator
|
|
118
|
-
* helpers} are more widely deployed.
|
|
119
|
-
*/
|
|
120
|
-
export class IterationHelper<T> implements IterableIterator<T> {
|
|
121
|
-
/**
|
|
122
|
-
* Iteration source.
|
|
123
|
-
*/
|
|
124
|
-
private readonly _iterationSource: IterationSource<T>;
|
|
125
|
-
|
|
126
|
-
/**
|
|
127
|
-
* Iterable extracted from iteration source.
|
|
128
|
-
*/
|
|
129
|
-
private _iterable?: Iterable<T>;
|
|
130
|
-
|
|
131
|
-
/**
|
|
132
|
-
* Iterator extracted from iteration source.
|
|
133
|
-
*/
|
|
134
|
-
private _iterator?: Iterator<T>;
|
|
135
|
-
|
|
136
|
-
/**
|
|
137
|
-
* Constructor.
|
|
138
|
-
*
|
|
139
|
-
* @param iterationSource
|
|
140
|
-
* Iteration source.
|
|
141
|
-
*/
|
|
142
|
-
private constructor(iterationSource: IterationSource<T>) {
|
|
143
|
-
this._iterationSource = iterationSource;
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
/**
|
|
147
|
-
* Get an iteration helper from an iteration source. If the iteration source is itself an iteration helper, it is
|
|
148
|
-
* returned verbatim, otherwise a new iteration helper is constructed.
|
|
149
|
-
*
|
|
150
|
-
* @param iterationSource
|
|
151
|
-
* Iteration source.
|
|
152
|
-
*
|
|
153
|
-
* @returns
|
|
154
|
-
* Iteration helper.
|
|
155
|
-
*/
|
|
156
|
-
static from<T>(iterationSource: IterationSource<T>): IterationHelper<T> {
|
|
157
|
-
return iterationSource instanceof IterationHelper ? iterationSource as IterationHelper<T> : new IterationHelper(iterationSource);
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
/**
|
|
161
|
-
* Get the iteration source.
|
|
162
|
-
*/
|
|
163
|
-
get iterationSource(): IterationSource<T> {
|
|
164
|
-
return this._iterationSource;
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
/**
|
|
168
|
-
* Get the iteration source as an iterable.
|
|
169
|
-
*
|
|
170
|
-
* @returns
|
|
171
|
-
* Iterable.
|
|
172
|
-
*/
|
|
173
|
-
asIterable(): Iterable<T> {
|
|
174
|
-
if (this._iterable === undefined) {
|
|
175
|
-
const resolvedIterationSource = typeof this.iterationSource === "function" ? this.iterationSource() : this.iterationSource;
|
|
176
|
-
|
|
177
|
-
this._iterable = Symbol.iterator in resolvedIterationSource ?
|
|
178
|
-
resolvedIterationSource :
|
|
179
|
-
{
|
|
180
|
-
[Symbol.iterator](): Iterator<T> {
|
|
181
|
-
return resolvedIterationSource;
|
|
182
|
-
}
|
|
183
|
-
};
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
return this._iterable;
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
/**
|
|
190
|
-
* Get the iteration source as an array.
|
|
191
|
-
*
|
|
192
|
-
* @returns
|
|
193
|
-
* Array.
|
|
194
|
-
*/
|
|
195
|
-
asArray(): readonly T[] {
|
|
196
|
-
const iterable = this.asIterable();
|
|
197
|
-
|
|
198
|
-
// Return iterable as array.
|
|
199
|
-
return Array.isArray(iterable) ? iterable as readonly T[] : Array.from(iterable);
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
/**
|
|
203
|
-
* Get the iteration source as an iterator.
|
|
204
|
-
*
|
|
205
|
-
* @returns
|
|
206
|
-
* Iterator.
|
|
207
|
-
*/
|
|
208
|
-
asIterator(): Iterator<T> {
|
|
209
|
-
if (this._iterator === undefined) {
|
|
210
|
-
this._iterator = this.asIterable()[Symbol.iterator]();
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
return this._iterator;
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
/**
|
|
217
|
-
* Get the iteration source as a callback.
|
|
218
|
-
*
|
|
219
|
-
* @returns
|
|
220
|
-
* Callback.
|
|
221
|
-
*/
|
|
222
|
-
asCallback(): () => IterationSource<T> {
|
|
223
|
-
return typeof this._iterationSource === "function" ? this._iterationSource : () => this._iterationSource;
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
/**
|
|
227
|
-
* {@link Iterable} interface implementation.
|
|
228
|
-
*
|
|
229
|
-
* @returns
|
|
230
|
-
* Iterable iterator.
|
|
231
|
-
*/
|
|
232
|
-
[Symbol.iterator](): IterableIterator<T> {
|
|
233
|
-
return this;
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
/**
|
|
237
|
-
* {@link Iterator} interface implementation.
|
|
238
|
-
*
|
|
239
|
-
* @param args
|
|
240
|
-
* Arguments.
|
|
241
|
-
*
|
|
242
|
-
* @returns
|
|
243
|
-
* Next element.
|
|
244
|
-
*/
|
|
245
|
-
next(...args: [] | [undefined]): IteratorResult<T, unknown> {
|
|
246
|
-
return this.asIterator().next(...args);
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
/**
|
|
250
|
-
* Perform an action for each element in the iteration helper.
|
|
251
|
-
*
|
|
252
|
-
* @param callback
|
|
253
|
-
* Callback that processes the element and its index in the iteration sequence.
|
|
254
|
-
*/
|
|
255
|
-
forEach(callback: (element: T, index: number) => void): void {
|
|
256
|
-
let index = 0;
|
|
257
|
-
|
|
258
|
-
for (const element of this) {
|
|
259
|
-
callback(element, index++);
|
|
260
|
-
}
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
/**
|
|
264
|
-
* Map the iteration helper to another iteration helper by performing an action for each element in the iteration
|
|
265
|
-
* helper along the way.
|
|
266
|
-
*
|
|
267
|
-
* @param callback
|
|
268
|
-
* Callback that processes the element and its index in the iteration sequence.
|
|
269
|
-
*
|
|
270
|
-
* @returns
|
|
271
|
-
* Iterable iterator over callback results.
|
|
272
|
-
*/
|
|
273
|
-
map<U>(callback: (element: T, index: number) => U): IterableIterator<U> {
|
|
274
|
-
return new IterationProxy(this, callback, false);
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
/**
|
|
278
|
-
* Filter the iteration helper based on the condition specified in a predicate. Each call to `.next()` will iterate
|
|
279
|
-
* as far as necessary until it reaches an element that satisfies the predicate. Care should be taken when working
|
|
280
|
-
* with large iterators and infrequently truthy predicates.
|
|
281
|
-
*
|
|
282
|
-
* @param predicate
|
|
283
|
-
* Predicate that processes the element and its index in the iteration sequence.
|
|
284
|
-
*
|
|
285
|
-
* @returns
|
|
286
|
-
* Iterator iterable over elements that satisfy the predicate.
|
|
287
|
-
*/
|
|
288
|
-
filter(predicate: (element: T, index: number) => boolean): IterableIterator<T> {
|
|
289
|
-
return new IterationProxy(this, predicate, true);
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
/**
|
|
293
|
-
* Reduce the iterator to a single value by applying a callback.
|
|
294
|
-
*
|
|
295
|
-
* @param callback
|
|
296
|
-
* Callback that processes the previous return value of the callback, the current value of the iterator, and the
|
|
297
|
-
* current index. The initial value is considered to be the first element in the iteration helper.
|
|
298
|
-
*
|
|
299
|
-
* @returns
|
|
300
|
-
* Reduced value.
|
|
301
|
-
*/
|
|
302
|
-
reduce(callback: (previousValue: T, currentValue: T, currentIndex: number) => T): T;
|
|
303
|
-
|
|
304
|
-
/**
|
|
305
|
-
* Reduce the iterator to a single value by applying a callback.
|
|
306
|
-
*
|
|
307
|
-
* @param callback
|
|
308
|
-
* Callback that processes the previous return value of the callback, the current value of the iterator, and the
|
|
309
|
-
* current index.
|
|
310
|
-
*
|
|
311
|
-
* @param initialValue
|
|
312
|
-
* Initial value, passed as the first previous return value of the callback.
|
|
313
|
-
*
|
|
314
|
-
* @returns
|
|
315
|
-
* Reduced value.
|
|
316
|
-
*/
|
|
317
|
-
reduce(callback: (previousValue: T, currentValue: T, currentIndex: number) => T, initialValue: T): T;
|
|
318
|
-
|
|
319
|
-
reduce<U>(callback: (previousValue: U, currentValue: T, currentIndex: number) => U, initialValue?: U): U {
|
|
320
|
-
let index = 0;
|
|
321
|
-
let result = initialValue;
|
|
322
|
-
|
|
323
|
-
for (const value of this) {
|
|
324
|
-
if (index === 0 && initialValue === undefined) {
|
|
325
|
-
result = value as unknown as U;
|
|
326
|
-
} else {
|
|
327
|
-
// Iteration has occurred at least once so result is of the expected type.
|
|
328
|
-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
329
|
-
result = callback(result!, value, index);
|
|
330
|
-
}
|
|
331
|
-
|
|
332
|
-
index++;
|
|
333
|
-
}
|
|
334
|
-
|
|
335
|
-
if (index === 0 && initialValue === undefined) {
|
|
336
|
-
throw new Error("reduce() of empty iterator with no initial value");
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
// Iteration has occurred at least once so result is of the expected type.
|
|
340
|
-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
341
|
-
return result!;
|
|
342
|
-
}
|
|
343
|
-
}
|