@blwatkins/utils 0.1.0-alpha.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 ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024-2026 Brittni Watkins
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,79 @@
1
+ # TypeScript Utilities
2
+
3
+ A growing toolkit of reusable, domain-agnostic TypeScript and JavaScript utilities for everyday development.
4
+
5
+ ## Documentation
6
+
7
+ - [Latest Release](https://blwatkins.github.io/typescript-utils/doc/index.html)
8
+ - [Documentation by Version Number](https://blwatkins.github.io/typescript-utils/releases.html)
9
+
10
+ ## License
11
+
12
+ The source code of this project is licensed under the [MIT License](https://opensource.org/license/mit).
13
+ The full text of the license is included with the project source code.
14
+
15
+ ## Project Status Badges
16
+
17
+ ### [npm](https://www.npmjs.com/package/@blwatkins/utils)
18
+
19
+ ![npm License](https://badgen.net/npm/license/@blwatkins/utils/alpha)
20
+ ![npm Version](https://badgen.net/npm/v/@blwatkins/utils)
21
+ ![npm @alpha Version](https://badgen.net/npm/v/@blwatkins/utils/alpha)
22
+ ![npm Types](https://badgen.net/npm/types/@blwatkins/utils)
23
+ ![npm Node Version](https://badgen.net/npm/node/@blwatkins/utils)
24
+ ![npm Weekly Downloads](https://badgen.net/npm/dw/@blwatkins/utils)
25
+ ![npm Total Downloads](https://badgen.net/npm/dt/@blwatkins/utils)
26
+ ![npm Last Update](https://img.shields.io/npm/last-update/%40blwatkins%2Futils)
27
+ ![npm Unpacked Size](https://img.shields.io/npm/unpacked-size/%40blwatkins%2Futils)
28
+
29
+ ### [Socket](https://socket.dev/npm/package/@blwatkins/utils)
30
+
31
+ ![Socket Score](https://badge.socket.dev/npm/package/@blwatkins/utils)
32
+
33
+ ### [Bundlephobia](https://bundlephobia.com/package/@blwatkins/utils)
34
+
35
+ ![Bundlephobia Tree Shaking](https://badgen.net/bundlephobia/tree-shaking/@blwatkins/utils)
36
+ ![Bundlephobia Dependency Count](https://badgen.net/bundlephobia/dependency-count/@blwatkins/utils)
37
+ ![Bundlephobia Minified](https://badgen.net/bundlephobia/min/@blwatkins/utils)
38
+ ![Bundlephobia Minified + gzip](https://badgen.net/bundlephobia/minzip/@blwatkins/utils)
39
+
40
+ ### [Package Phobia](https://packagephobia.com/result?p=%40blwatkins%2Futils)
41
+
42
+ ![Package Phobia Install Size](https://badgen.net/packagephobia/install/@blwatkins/utils)
43
+ ![Package Phobia Publish Size](https://badgen.net/packagephobia/publish/@blwatkins/utils)
44
+
45
+ ### [GitHub](https://github.com/blwatkins/typescript-utils)
46
+
47
+ ![GitHub License](https://badgen.net/github/license/blwatkins/typescript-utils)
48
+ ![GitHub Dependabot](https://badgen.net/github/dependabot/blwatkins/typescript-utils)
49
+ ![GitHub Latest Release](https://badgen.net/github/release/blwatkins/typescript-utils)
50
+ ![GitHub Releases](https://badgen.net/github/releases/blwatkins/typescript-utils)
51
+ ![GitHub Last Commit](https://badgen.net/github/last-commit/blwatkins/typescript-utils)
52
+ ![GitHub Commits](https://badgen.net/github/commits/blwatkins/typescript-utils)
53
+ ![GitHub Commit Activity](https://img.shields.io/github/commit-activity/y/blwatkins/typescript-utils)
54
+ ![GitHub Code Size in Bytes](https://img.shields.io/github/languages/code-size/blwatkins/typescript-utils)
55
+ ![GitHub Repo Size](https://img.shields.io/github/repo-size/blwatkins/typescript-utils)
56
+ ![GitHub Repo File or Directory Count](https://img.shields.io/github/directory-file-count/blwatkins/typescript-utils)
57
+ ![GitHub Language Count](https://img.shields.io/github/languages/count/blwatkins/typescript-utils)
58
+
59
+ ### GitHub Actions
60
+
61
+ ![CodeQL](https://github.com/blwatkins/typescript-utils/actions/workflows/codeql.yml/badge.svg)
62
+ ![npm Lint, Build, and Test](https://github.com/blwatkins/typescript-utils/actions/workflows/npm-test.yml/badge.svg)
63
+ ![Deploy GitHub Pages with Jekyll](https://github.com/blwatkins/typescript-utils/actions/workflows/gh-pages-jekyll.yml/badge.svg)
64
+
65
+ ## Sources and Technical Notes
66
+
67
+ - [Demonstrated Portfolio Skills](https://blwatkins.github.io/typescript-utils/portfolio-skills.html)
68
+ - [Resources and References](https://blwatkins.github.io/typescript-utils/resources-and-references.html)
69
+
70
+ ## Thank Yous
71
+
72
+ A huge thank you to all the open source contributors who have made this project possible by creating and maintaining the libraries and tools used in this project, and to the open source community for fostering collaboration and innovation.
73
+
74
+ A special thank you to all the educators, mentors, and content creators who have shared their knowledge and expertise in the fields of web development and computer science.
75
+ Thank you for giving me the tools, resources, opportunities, support, and inspiration to learn and grow as a developer.
76
+
77
+ ----
78
+
79
+ Copyright © 2024-2026 Brittni Watkins.
@@ -0,0 +1,413 @@
1
+ import { Static, Type } from "typebox";
2
+
3
+ //#region src/discriminator/discriminated.d.ts
4
+ /**
5
+ * TypeBox schema for validating that an object implements the {@link Discriminated} type.
6
+ *
7
+ * @public
8
+ * @since 0.1.0
9
+ */
10
+ declare const discriminatedSchema: Type.TObject<{
11
+ /**
12
+ * The discriminator value that identifies the type of a {@link Discriminated} object.
13
+ * This value must be unique across all registered discriminators.
14
+ *
15
+ * @since 0.1.0
16
+ * @type {string}
17
+ */
18
+ discriminator: Type.TReadonly<Type.TString>;
19
+ }>;
20
+ /**
21
+ * Discriminated objects can be type checked using the discriminator registry.
22
+ *
23
+ * @public
24
+ * @since 0.1.0
25
+ */
26
+ type Discriminated = Static<typeof discriminatedSchema>;
27
+ //#endregion
28
+ //#region src/discriminator/discriminator-registry.d.ts
29
+ /**
30
+ * A type guard function that checks if an input is of a specific {@link Discriminated} type.
31
+ *
32
+ * @public
33
+ * @since 0.1.0
34
+ */
35
+ type TypeGuard<T extends Discriminated> = (input: unknown) => input is T;
36
+ /**
37
+ * A registration for a discriminator to the {@link DiscriminatorRegistry}.
38
+ *
39
+ * @public
40
+ * @since 0.1.0
41
+ */
42
+ interface DiscriminatorRegistration {
43
+ /**
44
+ * The discriminator value that identifies the type of a {@link Discriminated} object.
45
+ * This value must be unique across all registered discriminators.
46
+ *
47
+ * @readonly
48
+ * @since 0.1.0
49
+ * @type {string}
50
+ */
51
+ readonly discriminator: string;
52
+ /**
53
+ * A method that validates whether an input matches the type associated with the {@link discriminator}.
54
+ *
55
+ * @param {unknown} input - The input to validate.
56
+ *
57
+ * @returns {boolean} - `true` if the input matches the type associated with the discriminator, `false` otherwise.
58
+ *
59
+ * @readonly
60
+ * @since 0.1.0
61
+ * @type {(input: unknown) => boolean}
62
+ */
63
+ readonly validator: (input: unknown) => boolean;
64
+ }
65
+ /**
66
+ * Static registry for managing discriminators and their associated type guards.
67
+ * Discriminators are used to identify the type of a {@link Discriminated} object and validate it using a registered type guard function.
68
+ *
69
+ * @public
70
+ * @since 0.1.0
71
+ */
72
+ declare class DiscriminatorRegistry {
73
+ #private;
74
+ /**
75
+ * @throws {Error} DiscriminatorRegistry is a static class and cannot be instantiated.
76
+ *
77
+ * @private
78
+ */
79
+ private constructor();
80
+ /**
81
+ * Checks if a discriminator is already registered.
82
+ *
83
+ * @param {string} discriminator - The discriminator value to check.
84
+ * @returns {boolean} - `true` if the discriminator is registered, `false` otherwise.
85
+ *
86
+ * @public
87
+ * @since 0.1.0
88
+ */
89
+ static has(discriminator: string): boolean;
90
+ /**
91
+ * Registers a new discriminator and its associated validation function.
92
+ *
93
+ * @param {DiscriminatorRegistration} registration - The registration details for the discriminator.
94
+ * @returns {TypeGuard<T>} - A type guard function for the registered type.
95
+ *
96
+ * @throws {TypeError} If the given input is not an object.
97
+ * @throws {TypeError} If the {@link DiscriminatorRegistration.discriminator} is not a non-empty single line trimmed string.
98
+ * @throws {TypeError} If the {@link DiscriminatorRegistration.validator} property is not a function.
99
+ * @throws {Error} If the {@link DiscriminatorRegistration.discriminator} is already registered.
100
+ *
101
+ * @public
102
+ * @since 0.1.0
103
+ */
104
+ static register<T extends Discriminated>(registration: DiscriminatorRegistration): TypeGuard<T>;
105
+ }
106
+ //#endregion
107
+ //#region src/number/number-utility.d.ts
108
+ /**
109
+ * Static properties and methods for validating number types.
110
+ *
111
+ * @since 0.1.0
112
+ */
113
+ declare class NumberUtility {
114
+ /**
115
+ * @throws {Error} - NumberUtility is a static class and cannot be instantiated.
116
+ *
117
+ * @private
118
+ */
119
+ private constructor();
120
+ /**
121
+ * Is the given input a finite number?
122
+ *
123
+ * @param {unknown} input
124
+ *
125
+ * @returns {input is number} `true` when the input is a finite number; `false` otherwise.
126
+ *
127
+ * @public
128
+ * @since 0.1.0
129
+ */
130
+ static isFiniteNumber(input: unknown): input is number;
131
+ /**
132
+ * Is the given input an integer?
133
+ *
134
+ * @param {unknown} input
135
+ *
136
+ * @returns {input is number} `true` when the input is an integer; `false` otherwise.
137
+ *
138
+ * @public
139
+ * @since 0.1.0
140
+ */
141
+ static isInteger(input: unknown): input is number;
142
+ /**
143
+ * Is the given input a positive integer?
144
+ *
145
+ * @param {unknown} input
146
+ * @param {boolean} zeroInclusive - `true` if zero should be considered a valid input.
147
+ * `false` if zero should be considered an invalid input.
148
+ *
149
+ * @returns {input is number} `true` if the given input is a positive integer, or zero when `zeroInclusive` is `true`; `false` otherwise.
150
+ *
151
+ * @public
152
+ * @since 0.1.0
153
+ */
154
+ static isPositiveInteger(input: unknown, zeroInclusive?: boolean): input is number;
155
+ }
156
+ //#endregion
157
+ //#region src/random/seeded-random/seeded-random-number-generator.d.ts
158
+ /**
159
+ * Deterministic seeded pseudorandom number generator.
160
+ * This generator utilizes the xoshiro128** algorithm, which is a pseudorandom number generator suitable for general-purpose use.
161
+ *
162
+ * @since 0.1.0
163
+ */
164
+ declare class SeededRandomNumberGenerator {
165
+ #private;
166
+ /**
167
+ * @param {[number, number, number, number]} state - Initial 128-bit state.
168
+ * Must be an array with 4 32-bit unsigned integers, where at least one element is greater than 0.
169
+ *
170
+ * @throws {TypeError} If state is not an array with 4 elements.
171
+ * @throws {RangeError} If each element of state is not a 32-bit unsigned integer.
172
+ * @throws {RangeError} If state does not have at least one element that is greater than 0.
173
+ *
174
+ * @public
175
+ * @since 0.1.0
176
+ */
177
+ constructor(state: [number, number, number, number]);
178
+ /**
179
+ * @remarks This method advances the internal 128-bit xoshiro128** state by one step.
180
+ * Successive calls produce an independent, uniformly distributed sequence.
181
+ *
182
+ * @returns {number} - The next pseudorandom float in the range [0, 1).
183
+ *
184
+ * @public
185
+ * @since 0.1.0
186
+ */
187
+ next(): number;
188
+ }
189
+ //#endregion
190
+ //#region src/random/seeded-random/random-number-generator-factory.d.ts
191
+ /**
192
+ * A static factory class for creating a {@link SeededRandomNumberGenerator} object.
193
+ *
194
+ * @since 0.1.0
195
+ */
196
+ declare class RandomNumberGeneratorFactory {
197
+ #private;
198
+ /**
199
+ * @throws {Error} - RandomNumberGeneratorFactory is a static class and cannot be instantiated.
200
+ *
201
+ * @private
202
+ * @since 0.1.0
203
+ */
204
+ private constructor();
205
+ /**
206
+ * Build a {@link SeededRandomNumberGenerator} object with the given seed, namespace, and version.
207
+ *
208
+ * @see {@link SeedVersions.size}
209
+ * @see {@link SeedVersions.isValidIndex}
210
+ *
211
+ * @param {string} seed - The primary input to determine the random number sequence.
212
+ * @param {string|undefined} namespace - Namespace to create different sequences from the same seed.
213
+ * @param {number|undefined} version - The {@link SeedVersions} index to use for selecting the offsets for hashing.
214
+ * Changing the version number will result in a different sequence of random numbers for the same seed and namespace.
215
+ *
216
+ * @returns {SeededRandomNumberGenerator}
217
+ *
218
+ * @throws {TypeError} - When the given seed is not a string.
219
+ * @throws {TypeError} - When the given namespace is not a string.
220
+ * @throws {TypeError} - When the given version is not an integer.
221
+ * @throws {RangeError} - When the given version is not a valid {@link SeedVersions} index.
222
+ *
223
+ * @public
224
+ * @since 0.1.0
225
+ */
226
+ static build(seed: string, namespace?: string, version?: number): SeededRandomNumberGenerator;
227
+ /**
228
+ * Build a {@link SeededRandomNumberGenerator} object with the given seed and namespace from an asynchronous hashing algorithm.
229
+ *
230
+ * @remarks This method relies on the Web Crypto API via `crypto.subtle`.
231
+ * In Node.js environments, ensure you are using a version where the Web Crypto API is available.
232
+ *
233
+ * @param {string} seed - The primary input to determine the random number sequence.
234
+ * @param {string|undefined} namespace - Namespace to create different sequences from the same seed.
235
+ *
236
+ * @returns {Promise<SeededRandomNumberGenerator>}
237
+ *
238
+ * @throws {TypeError} - When the given seed is not a string.
239
+ * @throws {TypeError} - When the given namespace is not a string.
240
+ *
241
+ * @public
242
+ * @since 0.1.0
243
+ */
244
+ static asyncBuild(seed: string, namespace?: string): Promise<SeededRandomNumberGenerator>;
245
+ }
246
+ //#endregion
247
+ //#region src/random/seeded-random/seed-versions.d.ts
248
+ /**
249
+ * A seed version defines a specific set of offsets for the FNV-1a hashing algorithm.
250
+ * A different set of offsets results in a different hash for the same input, which results in a different initial state for the seeded random number generator, which results in a different sequence of pseudorandom numbers.
251
+ *
252
+ * @since 0.1.0
253
+ */
254
+ interface SeedVersion {
255
+ /**
256
+ * A collection of offset values for the FNV-1a hashing algorithm.
257
+ * Each offset value should be a 32-bit unsigned integer.
258
+ *
259
+ * @readonly
260
+ * @since 0.1.0
261
+ */
262
+ readonly offsets: readonly [number, number, number, number];
263
+ }
264
+ /**
265
+ * A static class for accessing different seed versions.
266
+ * Each seed version index is guaranteed to always return the same seed version object, so that the same seed and version will always produce the same sequence of pseudorandom numbers.
267
+ *
268
+ * @since 0.1.0
269
+ */
270
+ declare class SeedVersions {
271
+ /**
272
+ * @throws {Error} - SeedVersions is a static class and cannot be instantiated.
273
+ *
274
+ * @private
275
+ */
276
+ private constructor();
277
+ /**
278
+ * @returns {number} - The total number of seed versions that currently exist.
279
+ *
280
+ * @public
281
+ * @since 0.1.0
282
+ */
283
+ static get size(): number;
284
+ /**
285
+ * Is the given index a valid seed version?
286
+ *
287
+ * @param {number} index - The index to check.
288
+ *
289
+ * @returns {boolean}
290
+ *
291
+ * @public
292
+ * @since 0.1.0
293
+ */
294
+ static isValidIndex(index: number): boolean;
295
+ /**
296
+ * @param {number} index - The index of the seed version to retrieve.
297
+ * Must be a valid {@link SeedVersions} index.
298
+ *
299
+ * @returns {SeedVersion} - The seed version with the given index.
300
+ *
301
+ * @throws {RangeError} - If the index is not a valid seed version index.
302
+ *
303
+ * @public
304
+ * @since 0.1.0
305
+ */
306
+ static getVersion(index: number): SeedVersion;
307
+ }
308
+ //#endregion
309
+ //#region src/string/string-utility.d.ts
310
+ /**
311
+ * Static properties and methods for validating string types.
312
+ *
313
+ * @since 0.1.0
314
+ */
315
+ declare class StringUtility {
316
+ /**
317
+ * @throws {Error} - StringUtility is a static class and cannot be instantiated.
318
+ *
319
+ * @private
320
+ */
321
+ private constructor();
322
+ /**
323
+ * @remarks This expression does not allow tab breaks, new lines, leading whitespace, trailing whitespace, or consecutive spaces within the string.
324
+ *
325
+ * @returns {RegExp} Regular expression pattern for validating single-line lowercase strings.
326
+ *
327
+ * @public
328
+ * @since 0.1.0
329
+ */
330
+ static get singleLineLowercaseTrimmedPattern(): RegExp;
331
+ /**
332
+ * @remarks This expression does not allow tab breaks, new lines, leading whitespace, trailing whitespace, or consecutive spaces within the string.
333
+ *
334
+ * @returns {RegExp} Regular expression pattern for validating single-line uppercase strings.
335
+ *
336
+ * @public
337
+ * @since 0.1.0
338
+ */
339
+ static get singleLineUppercaseTrimmedPattern(): RegExp;
340
+ /**
341
+ * @remarks This expression does not allow tab breaks, new lines, leading whitespace, trailing whitespace, or consecutive spaces within the string.
342
+ *
343
+ * @returns {RegExp} Regular expression pattern for validating single-line mixed-case strings.
344
+ *
345
+ * @public
346
+ * @since 0.1.0
347
+ */
348
+ static get singleLineTrimmedPattern(): RegExp;
349
+ /**
350
+ * Is the given input a string?
351
+ *
352
+ * @param {unknown} input
353
+ *
354
+ * @returns {input is string}
355
+ *
356
+ * @public
357
+ * @since 0.1.0
358
+ */
359
+ static isString(input: unknown): input is string;
360
+ /**
361
+ * Is the given input a non-empty string?
362
+ * Non-empty strings must contain at least one non-whitespace character.
363
+ *
364
+ * @param {unknown} input
365
+ *
366
+ * @returns {boolean}
367
+ *
368
+ * @public
369
+ * @since 0.1.0
370
+ */
371
+ static isNonEmptyString(input: unknown): boolean;
372
+ /**
373
+ * Is the given input a single-line lowercase string that is trimmed (no leading or trailing whitespace)?
374
+ *
375
+ * @see {@link StringUtility.singleLineLowercaseTrimmedPattern}
376
+ *
377
+ * @param {unknown} input
378
+ *
379
+ * @returns {boolean}
380
+ *
381
+ * @public
382
+ * @since 0.1.0
383
+ */
384
+ static isSingleLineLowercaseTrimmedString(input: unknown): boolean;
385
+ /**
386
+ * Is the given input a single-line uppercase string that is trimmed (no leading or trailing whitespace)?
387
+ *
388
+ * @see {@link StringUtility.singleLineUppercaseTrimmedPattern}
389
+ *
390
+ * @param {unknown} input
391
+ *
392
+ * @returns {boolean}
393
+ *
394
+ * @public
395
+ * @since 0.1.0
396
+ */
397
+ static isSingleLineUppercaseTrimmedString(input: unknown): boolean;
398
+ /**
399
+ * Is the given input a single-line string that is trimmed (no leading or trailing whitespace)?
400
+ *
401
+ * @see {@link StringUtility.singleLineTrimmedPattern}
402
+ *
403
+ * @param {unknown} input
404
+ *
405
+ * @returns {boolean}
406
+ *
407
+ * @public
408
+ * @since 0.1.0
409
+ */
410
+ static isSingleLineTrimmedString(input: unknown): boolean;
411
+ }
412
+ //#endregion
413
+ export { Discriminated, DiscriminatorRegistration, DiscriminatorRegistry, NumberUtility, RandomNumberGeneratorFactory, SeedVersion, SeedVersions, SeededRandomNumberGenerator, StringUtility, TypeGuard, discriminatedSchema };
@@ -0,0 +1,679 @@
1
+ import { Type } from "typebox";
2
+
3
+ //#region src/discriminator/discriminated.ts
4
+ /**
5
+ * TypeBox schema for validating that an object implements the {@link Discriminated} type.
6
+ *
7
+ * @public
8
+ * @since 0.1.0
9
+ */
10
+ const discriminatedSchema = Type.Object({
11
+ /**
12
+ * The discriminator value that identifies the type of a {@link Discriminated} object.
13
+ * This value must be unique across all registered discriminators.
14
+ *
15
+ * @since 0.1.0
16
+ * @type {string}
17
+ */
18
+ discriminator: Type.Readonly(Type.String()) });
19
+
20
+ //#endregion
21
+ //#region src/string/string-utility.ts
22
+ const RegularExpressions = {
23
+ singleLineLowercaseTrimmed: /^(?!\s)(?!.*\s$)(?!.*\p{Lu})(?!.* {2})[^\t\r\n]+$/u,
24
+ singleLineUppercaseTrimmed: /^(?!\s)(?!.*\s$)(?!.*\p{Ll})(?!.* {2})[^\t\r\n]+$/u,
25
+ singleLineTrimmed: /^(?!\s)(?!.*\s$)(?!.* {2})[^\t\r\n]+$/
26
+ };
27
+ /**
28
+ * Static properties and methods for validating string types.
29
+ *
30
+ * @since 0.1.0
31
+ */
32
+ var StringUtility = class StringUtility {
33
+ /**
34
+ * @throws {Error} - StringUtility is a static class and cannot be instantiated.
35
+ *
36
+ * @private
37
+ */
38
+ constructor() {
39
+ throw new Error("StringUtility is a static class and cannot be instantiated.");
40
+ }
41
+ /**
42
+ * @remarks This expression does not allow tab breaks, new lines, leading whitespace, trailing whitespace, or consecutive spaces within the string.
43
+ *
44
+ * @returns {RegExp} Regular expression pattern for validating single-line lowercase strings.
45
+ *
46
+ * @public
47
+ * @since 0.1.0
48
+ */
49
+ static get singleLineLowercaseTrimmedPattern() {
50
+ return RegularExpressions.singleLineLowercaseTrimmed;
51
+ }
52
+ /**
53
+ * @remarks This expression does not allow tab breaks, new lines, leading whitespace, trailing whitespace, or consecutive spaces within the string.
54
+ *
55
+ * @returns {RegExp} Regular expression pattern for validating single-line uppercase strings.
56
+ *
57
+ * @public
58
+ * @since 0.1.0
59
+ */
60
+ static get singleLineUppercaseTrimmedPattern() {
61
+ return RegularExpressions.singleLineUppercaseTrimmed;
62
+ }
63
+ /**
64
+ * @remarks This expression does not allow tab breaks, new lines, leading whitespace, trailing whitespace, or consecutive spaces within the string.
65
+ *
66
+ * @returns {RegExp} Regular expression pattern for validating single-line mixed-case strings.
67
+ *
68
+ * @public
69
+ * @since 0.1.0
70
+ */
71
+ static get singleLineTrimmedPattern() {
72
+ return RegularExpressions.singleLineTrimmed;
73
+ }
74
+ /**
75
+ * Is the given input a string?
76
+ *
77
+ * @param {unknown} input
78
+ *
79
+ * @returns {input is string}
80
+ *
81
+ * @public
82
+ * @since 0.1.0
83
+ */
84
+ static isString(input) {
85
+ return typeof input === "string";
86
+ }
87
+ /**
88
+ * Is the given input a non-empty string?
89
+ * Non-empty strings must contain at least one non-whitespace character.
90
+ *
91
+ * @param {unknown} input
92
+ *
93
+ * @returns {boolean}
94
+ *
95
+ * @public
96
+ * @since 0.1.0
97
+ */
98
+ static isNonEmptyString(input) {
99
+ return StringUtility.isString(input) && input.trim().length > 0;
100
+ }
101
+ /**
102
+ * Is the given input a single-line lowercase string that is trimmed (no leading or trailing whitespace)?
103
+ *
104
+ * @see {@link StringUtility.singleLineLowercaseTrimmedPattern}
105
+ *
106
+ * @param {unknown} input
107
+ *
108
+ * @returns {boolean}
109
+ *
110
+ * @public
111
+ * @since 0.1.0
112
+ */
113
+ static isSingleLineLowercaseTrimmedString(input) {
114
+ return StringUtility.isString(input) && StringUtility.singleLineLowercaseTrimmedPattern.test(input);
115
+ }
116
+ /**
117
+ * Is the given input a single-line uppercase string that is trimmed (no leading or trailing whitespace)?
118
+ *
119
+ * @see {@link StringUtility.singleLineUppercaseTrimmedPattern}
120
+ *
121
+ * @param {unknown} input
122
+ *
123
+ * @returns {boolean}
124
+ *
125
+ * @public
126
+ * @since 0.1.0
127
+ */
128
+ static isSingleLineUppercaseTrimmedString(input) {
129
+ return StringUtility.isString(input) && StringUtility.singleLineUppercaseTrimmedPattern.test(input);
130
+ }
131
+ /**
132
+ * Is the given input a single-line string that is trimmed (no leading or trailing whitespace)?
133
+ *
134
+ * @see {@link StringUtility.singleLineTrimmedPattern}
135
+ *
136
+ * @param {unknown} input
137
+ *
138
+ * @returns {boolean}
139
+ *
140
+ * @public
141
+ * @since 0.1.0
142
+ */
143
+ static isSingleLineTrimmedString(input) {
144
+ return StringUtility.isString(input) && StringUtility.singleLineTrimmedPattern.test(input);
145
+ }
146
+ };
147
+
148
+ //#endregion
149
+ //#region src/discriminator/discriminator-registry.ts
150
+ /**
151
+ * Static registry for managing discriminators and their associated type guards.
152
+ * Discriminators are used to identify the type of a {@link Discriminated} object and validate it using a registered type guard function.
153
+ *
154
+ * @public
155
+ * @since 0.1.0
156
+ */
157
+ var DiscriminatorRegistry = class DiscriminatorRegistry {
158
+ /**
159
+ * A map of discriminator values to their corresponding validation functions.
160
+ *
161
+ * @readonly
162
+ * @private
163
+ * @type {Map<string, (input: unknown) => boolean>}
164
+ */
165
+ static #discriminators = /* @__PURE__ */ new Map();
166
+ /**
167
+ * @throws {Error} DiscriminatorRegistry is a static class and cannot be instantiated.
168
+ *
169
+ * @private
170
+ */
171
+ constructor() {
172
+ throw new Error("DiscriminatorRegistry is a static class and cannot be instantiated.");
173
+ }
174
+ /**
175
+ * Checks if a discriminator is already registered.
176
+ *
177
+ * @param {string} discriminator - The discriminator value to check.
178
+ * @returns {boolean} - `true` if the discriminator is registered, `false` otherwise.
179
+ *
180
+ * @public
181
+ * @since 0.1.0
182
+ */
183
+ static has(discriminator) {
184
+ return DiscriminatorRegistry.#discriminators.has(discriminator);
185
+ }
186
+ /**
187
+ * Registers a new discriminator and its associated validation function.
188
+ *
189
+ * @param {DiscriminatorRegistration} registration - The registration details for the discriminator.
190
+ * @returns {TypeGuard<T>} - A type guard function for the registered type.
191
+ *
192
+ * @throws {TypeError} If the given input is not an object.
193
+ * @throws {TypeError} If the {@link DiscriminatorRegistration.discriminator} is not a non-empty single line trimmed string.
194
+ * @throws {TypeError} If the {@link DiscriminatorRegistration.validator} property is not a function.
195
+ * @throws {Error} If the {@link DiscriminatorRegistration.discriminator} is already registered.
196
+ *
197
+ * @public
198
+ * @since 0.1.0
199
+ */
200
+ static register(registration) {
201
+ DiscriminatorRegistry.#validateRegistration(registration);
202
+ DiscriminatorRegistry.#discriminators.set(registration.discriminator, registration.validator);
203
+ return (input) => {
204
+ return DiscriminatorRegistry.#validate(input, registration.discriminator);
205
+ };
206
+ }
207
+ /**
208
+ * Validates a discriminator registration object to ensure it has the required properties and that the discriminator value is unique.
209
+ *
210
+ * @see {@link StringUtility.isSingleLineTrimmedString}
211
+ *
212
+ * @param {unknown} input - The input to validate.
213
+ *
214
+ * @returns {void}
215
+ *
216
+ * @throws {TypeError} If the given input is not an object.
217
+ * @throws {TypeError} If the {@link DiscriminatorRegistration.discriminator} is not a non-empty single line trimmed string.
218
+ * @throws {TypeError} If the {@link DiscriminatorRegistration.validator} property is not a function.
219
+ * @throws {Error} If the {@link DiscriminatorRegistration.discriminator} is already registered.
220
+ *
221
+ * @private
222
+ */
223
+ static #validateRegistration(input) {
224
+ if (!input || typeof input !== "object" || Array.isArray(input)) throw new TypeError("Registration must be an object.");
225
+ const registration = input;
226
+ if (!StringUtility.isSingleLineTrimmedString(registration.discriminator)) throw new TypeError(`Discriminator '${registration.discriminator}' must be a non-empty single line trimmed string.`);
227
+ if (typeof registration.validator !== "function") throw new TypeError(`Discriminator '${registration.discriminator}' must have a validator function.`);
228
+ if (DiscriminatorRegistry.has(registration.discriminator)) throw new Error(`Discriminator '${registration.discriminator}' is already registered.`);
229
+ }
230
+ /**
231
+ * Validates an input against a specific discriminator.
232
+ *
233
+ * @param {unknown} input - The input to validate.
234
+ * @param {string} discriminator - The discriminator value to check.
235
+ *
236
+ * @returns {boolean} - `true` if the input matches the type associated with the discriminator, `false` otherwise.
237
+ *
238
+ * @private
239
+ */
240
+ static #validate(input, discriminator) {
241
+ if (!DiscriminatorRegistry.#isDiscriminated(input, discriminator)) return false;
242
+ const validator = DiscriminatorRegistry.#discriminators.get(discriminator);
243
+ if (validator) return validator(input);
244
+ return false;
245
+ }
246
+ /**
247
+ * Is the given input an object with a discriminator property that matches the given discriminator value?
248
+ *
249
+ * @param {unknown} input - The input to check.
250
+ * @param {string} discriminator - The discriminator value to match.
251
+ *
252
+ * @returns {boolean} - `true` if the input is an object with a discriminator property that matches the given discriminator value, `false` otherwise.
253
+ *
254
+ * @private
255
+ */
256
+ static #isDiscriminated(input, discriminator) {
257
+ if (input && typeof input === "object") return input.discriminator === discriminator;
258
+ return false;
259
+ }
260
+ };
261
+
262
+ //#endregion
263
+ //#region src/number/number-utility.ts
264
+ /**
265
+ * Static properties and methods for validating number types.
266
+ *
267
+ * @since 0.1.0
268
+ */
269
+ var NumberUtility = class NumberUtility {
270
+ /**
271
+ * @throws {Error} - NumberUtility is a static class and cannot be instantiated.
272
+ *
273
+ * @private
274
+ */
275
+ constructor() {
276
+ throw new Error("NumberUtility is a static class and cannot be instantiated.");
277
+ }
278
+ /**
279
+ * Is the given input a finite number?
280
+ *
281
+ * @param {unknown} input
282
+ *
283
+ * @returns {input is number} `true` when the input is a finite number; `false` otherwise.
284
+ *
285
+ * @public
286
+ * @since 0.1.0
287
+ */
288
+ static isFiniteNumber(input) {
289
+ return Number.isFinite(input);
290
+ }
291
+ /**
292
+ * Is the given input an integer?
293
+ *
294
+ * @param {unknown} input
295
+ *
296
+ * @returns {input is number} `true` when the input is an integer; `false` otherwise.
297
+ *
298
+ * @public
299
+ * @since 0.1.0
300
+ */
301
+ static isInteger(input) {
302
+ return Number.isInteger(input);
303
+ }
304
+ /**
305
+ * Is the given input a positive integer?
306
+ *
307
+ * @param {unknown} input
308
+ * @param {boolean} zeroInclusive - `true` if zero should be considered a valid input.
309
+ * `false` if zero should be considered an invalid input.
310
+ *
311
+ * @returns {input is number} `true` if the given input is a positive integer, or zero when `zeroInclusive` is `true`; `false` otherwise.
312
+ *
313
+ * @public
314
+ * @since 0.1.0
315
+ */
316
+ static isPositiveInteger(input, zeroInclusive) {
317
+ if (!NumberUtility.isInteger(input)) return false;
318
+ if (zeroInclusive === true) return input >= 0;
319
+ return input > 0;
320
+ }
321
+ };
322
+
323
+ //#endregion
324
+ //#region src/random/seeded-random/seed-versions.ts
325
+ /**
326
+ * @remarks Once a seed version has been published, it should <b>NEVER</b> be changed or updated.
327
+ * The order of seed versions should <b>NEVER</b> be changed.
328
+ * New seed versions can only be added to the end of the array.
329
+ * Each element in the offsets array should be unique.
330
+ *
331
+ * @constant
332
+ */
333
+ const seedVersions = [{ offsets: Object.freeze([
334
+ 1779033703,
335
+ 3144134277,
336
+ 1013904242,
337
+ 2773480762
338
+ ]) }, { offsets: Object.freeze([
339
+ 2166136261,
340
+ 55548468,
341
+ 2712847316,
342
+ 1584364171
343
+ ]) }];
344
+ /**
345
+ * A static class for accessing different seed versions.
346
+ * Each seed version index is guaranteed to always return the same seed version object, so that the same seed and version will always produce the same sequence of pseudorandom numbers.
347
+ *
348
+ * @since 0.1.0
349
+ */
350
+ var SeedVersions = class SeedVersions {
351
+ /**
352
+ * @throws {Error} - SeedVersions is a static class and cannot be instantiated.
353
+ *
354
+ * @private
355
+ */
356
+ constructor() {
357
+ throw new Error("SeedVersions is a static class and cannot be instantiated.");
358
+ }
359
+ /**
360
+ * @returns {number} - The total number of seed versions that currently exist.
361
+ *
362
+ * @public
363
+ * @since 0.1.0
364
+ */
365
+ static get size() {
366
+ return seedVersions.length;
367
+ }
368
+ /**
369
+ * Is the given index a valid seed version?
370
+ *
371
+ * @param {number} index - The index to check.
372
+ *
373
+ * @returns {boolean}
374
+ *
375
+ * @public
376
+ * @since 0.1.0
377
+ */
378
+ static isValidIndex(index) {
379
+ return NumberUtility.isPositiveInteger(index, true) && index < seedVersions.length;
380
+ }
381
+ /**
382
+ * @param {number} index - The index of the seed version to retrieve.
383
+ * Must be a valid {@link SeedVersions} index.
384
+ *
385
+ * @returns {SeedVersion} - The seed version with the given index.
386
+ *
387
+ * @throws {RangeError} - If the index is not a valid seed version index.
388
+ *
389
+ * @public
390
+ * @since 0.1.0
391
+ */
392
+ static getVersion(index) {
393
+ if (!SeedVersions.isValidIndex(index)) throw new RangeError(`SeedVersion ${index} does not exist`);
394
+ return seedVersions[index];
395
+ }
396
+ };
397
+
398
+ //#endregion
399
+ //#region src/random/seeded-random/seeded-random-number-generator.ts
400
+ /**
401
+ * Deterministic seeded pseudorandom number generator.
402
+ * This generator utilizes the xoshiro128** algorithm, which is a pseudorandom number generator suitable for general-purpose use.
403
+ *
404
+ * @since 0.1.0
405
+ */
406
+ var SeededRandomNumberGenerator = class SeededRandomNumberGenerator {
407
+ /**
408
+ * Internal xoshiro128** state (4 x 32-bit unsigned integers).
409
+ *
410
+ * @private
411
+ * @readonly
412
+ * @type {[number, number, number, number]}
413
+ */
414
+ #state;
415
+ /**
416
+ * @param {[number, number, number, number]} state - Initial 128-bit state.
417
+ * Must be an array with 4 32-bit unsigned integers, where at least one element is greater than 0.
418
+ *
419
+ * @throws {TypeError} If state is not an array with 4 elements.
420
+ * @throws {RangeError} If each element of state is not a 32-bit unsigned integer.
421
+ * @throws {RangeError} If state does not have at least one element that is greater than 0.
422
+ *
423
+ * @public
424
+ * @since 0.1.0
425
+ */
426
+ constructor(state) {
427
+ this.#validateState(state);
428
+ this.#state = [
429
+ state[0],
430
+ state[1],
431
+ state[2],
432
+ state[3]
433
+ ];
434
+ }
435
+ /**
436
+ * @remarks This method advances the internal 128-bit xoshiro128** state by one step.
437
+ * Successive calls produce an independent, uniformly distributed sequence.
438
+ *
439
+ * @returns {number} - The next pseudorandom float in the range [0, 1).
440
+ *
441
+ * @public
442
+ * @since 0.1.0
443
+ */
444
+ next() {
445
+ const result = SeededRandomNumberGenerator.#rotl(Math.imul(this.#state[1], 5), 7);
446
+ const output = (Math.imul(result, 9) >>> 0) / 4294967296;
447
+ const t = this.#state[1] << 9 >>> 0;
448
+ this.#state[2] ^= this.#state[0];
449
+ this.#state[3] ^= this.#state[1];
450
+ this.#state[1] ^= this.#state[2];
451
+ this.#state[0] ^= this.#state[3];
452
+ this.#state[2] ^= t;
453
+ this.#state[3] = SeededRandomNumberGenerator.#rotl(this.#state[3], 11);
454
+ return output;
455
+ }
456
+ /**
457
+ * Rotates the bits of a 32-bit unsigned integer left by k positions.
458
+ *
459
+ * @param {number} x - The number to rotate. Must be a 32-bit unsigned integer.
460
+ * @param {number} k - The number of bits to rotate.
461
+ *
462
+ * @returns {number}
463
+ *
464
+ * @private
465
+ */
466
+ static #rotl(x, k) {
467
+ return (x << k | x >>> 32 - k) >>> 0;
468
+ }
469
+ /**
470
+ * The maximum valid value in the state array.
471
+ *
472
+ * @returns {number} 0xFFFFFFFF
473
+ *
474
+ * @private
475
+ */
476
+ static get #maxStateValue() {
477
+ return 4294967295;
478
+ }
479
+ /**
480
+ * Validate that state is an array with 4 32-bit unsigned integers, where at least one element is greater than 0.
481
+ *
482
+ * @param {[number, number, number, number]} state - The state to validate.
483
+ *
484
+ * @throws {TypeError} If state is not an array with 4 elements.
485
+ * @throws {RangeError} If each element of state is not a 32-bit unsigned integer
486
+ * @throws {RangeError} If state does not have at least one element that is greater than 0.
487
+ *
488
+ * @private
489
+ */
490
+ #validateState(state) {
491
+ if (!Array.isArray(state) || state.length !== 4) throw new TypeError("State must be an array with 4 elements.");
492
+ for (const value of state) if (!NumberUtility.isPositiveInteger(value, true) || value > SeededRandomNumberGenerator.#maxStateValue) throw new RangeError("Elements of state must be 32-bit unsigned integers (maximum value 0xFFFFFFFF).");
493
+ if (state[0] === 0 && state[1] === 0 && state[2] === 0 && state[3] === 0) throw new RangeError("State must have at least one element that is greater than 0.");
494
+ }
495
+ };
496
+
497
+ //#endregion
498
+ //#region src/random/seeded-random/random-number-generator-factory.ts
499
+ /**
500
+ * @type {TextEncoder}
501
+ */
502
+ const textEncoder = new TextEncoder();
503
+ /**
504
+ * A static factory class for creating a {@link SeededRandomNumberGenerator} object.
505
+ *
506
+ * @since 0.1.0
507
+ */
508
+ var RandomNumberGeneratorFactory = class RandomNumberGeneratorFactory {
509
+ /**
510
+ * @throws {Error} - RandomNumberGeneratorFactory is a static class and cannot be instantiated.
511
+ *
512
+ * @private
513
+ * @since 0.1.0
514
+ */
515
+ constructor() {
516
+ throw new Error("RandomNumberGeneratorFactory is a static class and cannot be instantiated.");
517
+ }
518
+ /**
519
+ * Prime number for FNV-1a hashing algorithm.
520
+ * This number is an algorithmic constant; it must not change.
521
+ *
522
+ * @returns {number} - 0x01000193
523
+ *
524
+ * @private
525
+ */
526
+ static get #fnvPrime() {
527
+ return 16777619;
528
+ }
529
+ /**
530
+ * Build a {@link SeededRandomNumberGenerator} object with the given seed, namespace, and version.
531
+ *
532
+ * @see {@link SeedVersions.size}
533
+ * @see {@link SeedVersions.isValidIndex}
534
+ *
535
+ * @param {string} seed - The primary input to determine the random number sequence.
536
+ * @param {string|undefined} namespace - Namespace to create different sequences from the same seed.
537
+ * @param {number|undefined} version - The {@link SeedVersions} index to use for selecting the offsets for hashing.
538
+ * Changing the version number will result in a different sequence of random numbers for the same seed and namespace.
539
+ *
540
+ * @returns {SeededRandomNumberGenerator}
541
+ *
542
+ * @throws {TypeError} - When the given seed is not a string.
543
+ * @throws {TypeError} - When the given namespace is not a string.
544
+ * @throws {TypeError} - When the given version is not an integer.
545
+ * @throws {RangeError} - When the given version is not a valid {@link SeedVersions} index.
546
+ *
547
+ * @public
548
+ * @since 0.1.0
549
+ */
550
+ static build(seed, namespace, version) {
551
+ RandomNumberGeneratorFactory.#validateBuildInputs(seed, namespace, version);
552
+ const input = RandomNumberGeneratorFactory.#buildInputString(seed, namespace);
553
+ return new SeededRandomNumberGenerator(RandomNumberGeneratorFactory.#generateFnvHashState(input, version));
554
+ }
555
+ /**
556
+ * Build a {@link SeededRandomNumberGenerator} object with the given seed and namespace from an asynchronous hashing algorithm.
557
+ *
558
+ * @remarks This method relies on the Web Crypto API via `crypto.subtle`.
559
+ * In Node.js environments, ensure you are using a version where the Web Crypto API is available.
560
+ *
561
+ * @param {string} seed - The primary input to determine the random number sequence.
562
+ * @param {string|undefined} namespace - Namespace to create different sequences from the same seed.
563
+ *
564
+ * @returns {Promise<SeededRandomNumberGenerator>}
565
+ *
566
+ * @throws {TypeError} - When the given seed is not a string.
567
+ * @throws {TypeError} - When the given namespace is not a string.
568
+ *
569
+ * @public
570
+ * @since 0.1.0
571
+ */
572
+ static async asyncBuild(seed, namespace) {
573
+ RandomNumberGeneratorFactory.#validateBuildInputs(seed, namespace);
574
+ const input = RandomNumberGeneratorFactory.#buildInputString(seed, namespace);
575
+ return new SeededRandomNumberGenerator(await RandomNumberGeneratorFactory.#generateSha256HashState(input));
576
+ }
577
+ /**
578
+ * @see {@link SeedVersions.isValidIndex}
579
+ *
580
+ * @param {string} seed - seed to validate
581
+ * @param {string|undefined} namespace - namespace to validate
582
+ * @param {number|undefined} version - version to validate
583
+ *
584
+ * @throws {TypeError} - When the given seed is not a string.
585
+ * @throws {TypeError} - When the given namespace is not a string.
586
+ * @throws {TypeError} - When the given version is not an integer.
587
+ * @throws {RangeError} - When the given version is not a valid {@link SeedVersions} index.
588
+ *
589
+ * @private
590
+ */
591
+ static #validateBuildInputs(seed, namespace, version) {
592
+ if (!StringUtility.isString(seed)) throw new TypeError("Seed must be a string.");
593
+ if (namespace !== void 0 && !StringUtility.isString(namespace)) throw new TypeError("Namespace must be a string.");
594
+ if (version !== void 0 && !NumberUtility.isInteger(version)) throw new TypeError("Version must be an integer.");
595
+ if (version !== void 0 && !SeedVersions.isValidIndex(version)) throw new RangeError("Version must be a valid seed versions index.");
596
+ }
597
+ /**
598
+ * Build the hash algorithm input string from the given seed and namespace.
599
+ *
600
+ * @param {string} seed
601
+ * @param {string|undefined} namespace - Optional namespace to create different sequences from the same seed.
602
+ *
603
+ * @returns {string}
604
+ *
605
+ * @private
606
+ */
607
+ static #buildInputString(seed, namespace) {
608
+ if (StringUtility.isString(namespace)) return `${namespace}\x00${seed}`;
609
+ else return seed;
610
+ }
611
+ /**
612
+ * Create a state array from the given input using the FNV-1a hashing algorithm.
613
+ * The state is generated by hashing the input string with four different offsets, which are determined by the given version number.
614
+ *
615
+ * @see {@link SeedVersions.size}
616
+ * @see {@link SeedVersions.isValidIndex}
617
+ *
618
+ * @param {string} input - Input to be hashed and converted into the initial state of the random number generator.
619
+ * @param {number} version - The {@link SeedVersions} index to use for selecting the offsets for hashing.
620
+ * Changing the version number will result in a different sequence of random numbers for the same input.
621
+ * Default value is 0.
622
+ *
623
+ * @returns {[number, number, number, number]}
624
+ *
625
+ * @throws {TypeError} - When the given input is not a string.
626
+ * @throws {TypeError} - When the given version is not an integer.
627
+ * @throws {RangeError} - When the given version is not a valid {@link SeedVersions} index.
628
+ *
629
+ * @private
630
+ */
631
+ static #generateFnvHashState(input, version = 0) {
632
+ RandomNumberGeneratorFactory.#validateBuildInputs(input, void 0, version);
633
+ const bytes = textEncoder.encode(input);
634
+ const [o0, o1, o2, o3] = SeedVersions.getVersion(version).offsets;
635
+ const hash = (offset) => {
636
+ let h = offset;
637
+ for (const byte of bytes) {
638
+ h ^= byte;
639
+ h = Math.imul(h, RandomNumberGeneratorFactory.#fnvPrime);
640
+ }
641
+ return h >>> 0;
642
+ };
643
+ return [
644
+ hash(o0),
645
+ hash(o1),
646
+ hash(o2),
647
+ hash(o3)
648
+ ];
649
+ }
650
+ /**
651
+ * Create a state array from the given input using the SHA-256 hashing algorithm.
652
+ *
653
+ * @remarks This method hashes the given input with SHA-256 and folds the 256-bit output into 128 bits by XOR-ing the two 128-bit halves together, fully utilizing all output bits.<hr/>
654
+ * This method relies on the Web Crypto API via `crypto.subtle`.
655
+ * In Node.js environments, ensure you are using a version where the Web Crypto API is available.
656
+ *
657
+ * @param {string} input - Input to be hashed and converted into the initial state of the random number generator.
658
+ *
659
+ * @returns {[number, number, number, number]}
660
+ *
661
+ * @throws {TypeError} - When the given input is not a string.
662
+ *
663
+ * @private
664
+ */
665
+ static async #generateSha256HashState(input) {
666
+ RandomNumberGeneratorFactory.#validateBuildInputs(input);
667
+ const hashBuffer = await crypto.subtle.digest("SHA-256", textEncoder.encode(input));
668
+ const v = new DataView(hashBuffer);
669
+ return [
670
+ (v.getUint32(0, false) ^ v.getUint32(16, false)) >>> 0,
671
+ (v.getUint32(4, false) ^ v.getUint32(20, false)) >>> 0,
672
+ (v.getUint32(8, false) ^ v.getUint32(24, false)) >>> 0,
673
+ (v.getUint32(12, false) ^ v.getUint32(28, false)) >>> 0
674
+ ];
675
+ }
676
+ };
677
+
678
+ //#endregion
679
+ export { DiscriminatorRegistry, NumberUtility, RandomNumberGeneratorFactory, SeedVersions, SeededRandomNumberGenerator, StringUtility, discriminatedSchema };
package/package.json ADDED
@@ -0,0 +1,89 @@
1
+ {
2
+ "name": "@blwatkins/utils",
3
+ "version": "0.1.0-alpha.0",
4
+ "license": "MIT",
5
+ "private": false,
6
+ "description": "A growing toolkit of reusable, domain-agnostic TypeScript and JavaScript utilities for everyday development.",
7
+ "author": {
8
+ "name": "Brittni Watkins",
9
+ "url": "https://blwatkins.github.io/"
10
+ },
11
+ "homepage": "https://blwatkins.github.io/typescript-utils/",
12
+ "repository": {
13
+ "type": "git",
14
+ "url": "git+https://github.com/blwatkins/typescript-utils.git"
15
+ },
16
+ "bugs": {
17
+ "url": "https://github.com/blwatkins/typescript-utils/issues"
18
+ },
19
+ "type": "module",
20
+ "engines": {
21
+ "node": "^22.22.0 || >=24"
22
+ },
23
+ "types": "./_dist/index.d.mts",
24
+ "module": "./_dist/index.mjs",
25
+ "main": "./_dist/index.mjs",
26
+ "exports": {
27
+ ".": {
28
+ "types": "./_dist/index.d.mts",
29
+ "default": "./_dist/index.mjs"
30
+ }
31
+ },
32
+ "files": [
33
+ "./_dist",
34
+ "./LICENSE",
35
+ "./package.json",
36
+ "./README.md"
37
+ ],
38
+ "scripts": {
39
+ "lint:js": "eslint -c eslint.config.js.mjs .",
40
+ "lint:ts": "eslint -c eslint.config.ts.mjs .",
41
+ "lint:all": "npm run lint:js && npm run lint:ts",
42
+ "test": "vitest run",
43
+ "test:watch": "vitest",
44
+ "test:ui": "vitest --ui",
45
+ "test:coverage": "vitest run --coverage",
46
+ "docs": "typedoc",
47
+ "build": "tsdown",
48
+ "prepack": "npm run build"
49
+ },
50
+ "dependencies": {
51
+ "typebox": "^1.3.2"
52
+ },
53
+ "devDependencies": {
54
+ "@eslint/js": "^10.0.1",
55
+ "@stylistic/eslint-plugin": "^5.10.0",
56
+ "@types/node": "^26.0.1",
57
+ "@vitest/coverage-v8": "^4.1.9",
58
+ "@vitest/ui": "^4.1.9",
59
+ "eslint": "^10.6.0",
60
+ "eslint-plugin-es-x": "^9.7.0",
61
+ "globals": "^17.7.0",
62
+ "tsdown": "^0.22.3",
63
+ "typedoc": "^0.28.19",
64
+ "typescript": "^6.0.3",
65
+ "typescript-eslint": "^8.62.1",
66
+ "vitest": "^4.1.9"
67
+ },
68
+ "keywords": [
69
+ "typescript",
70
+ "javascript",
71
+ "utility",
72
+ "utilities",
73
+ "utils",
74
+ "type-guard",
75
+ "type-guards",
76
+ "validation",
77
+ "validator",
78
+ "discriminator",
79
+ "number",
80
+ "string",
81
+ "random",
82
+ "seeded-random",
83
+ "prng",
84
+ "pseudorandom",
85
+ "deterministic",
86
+ "xoshiro",
87
+ "esm"
88
+ ]
89
+ }