@develia/commons 0.4.26 → 0.6.1
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/dist/array.d.ts +10 -0
- package/dist/array.d.ts.map +1 -1
- package/dist/array.js +18 -0
- package/dist/array.js.map +1 -1
- package/dist/async-pool-executor.d.ts +3 -5
- package/dist/async-pool-executor.d.ts.map +1 -1
- package/dist/async-pool-executor.js +9 -9
- package/dist/async-pool-executor.js.map +1 -1
- package/dist/browser/form-utilities.d.ts +0 -2
- package/dist/browser/form-utilities.d.ts.map +1 -1
- package/dist/browser/form-utilities.js +3 -23
- package/dist/browser/form-utilities.js.map +1 -1
- package/dist/browser/geo-location.js +3 -3
- package/dist/browser/geo-location.js.map +1 -1
- package/dist/browser/native-bridge.d.ts +1 -1
- package/dist/browser/native-bridge.d.ts.map +1 -1
- package/dist/browser/native-bridge.js +2 -2
- package/dist/browser/native-bridge.js.map +1 -1
- package/dist/cancellable-promise.d.ts +8 -2
- package/dist/cancellable-promise.d.ts.map +1 -1
- package/dist/cancellable-promise.js +9 -3
- package/dist/cancellable-promise.js.map +1 -1
- package/dist/clean-url-search-params.js +3 -3
- package/dist/clean-url-search-params.js.map +1 -1
- package/dist/collections/from.d.ts +16 -5
- package/dist/collections/from.d.ts.map +1 -1
- package/dist/collections/from.js +69 -38
- package/dist/collections/from.js.map +1 -1
- package/dist/draft.d.ts +2 -2
- package/dist/draft.d.ts.map +1 -1
- package/dist/draft.js +2 -2
- package/dist/draft.js.map +1 -1
- package/dist/functions.d.ts +2 -2
- package/dist/functions.d.ts.map +1 -1
- package/dist/functions.js +1 -1
- package/dist/functions.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/math.d.ts +14 -5
- package/dist/math.d.ts.map +1 -1
- package/dist/math.js +16 -5
- package/dist/math.js.map +1 -1
- package/dist/parse.d.ts +62 -44
- package/dist/parse.d.ts.map +1 -1
- package/dist/parse.js +95 -59
- package/dist/parse.js.map +1 -1
- package/dist/range.d.ts +2 -10
- package/dist/range.d.ts.map +1 -1
- package/dist/range.js +0 -12
- package/dist/range.js.map +1 -1
- package/dist/stopwatch.d.ts +2 -2
- package/dist/stopwatch.d.ts.map +1 -1
- package/dist/stopwatch.js +9 -9
- package/dist/stopwatch.js.map +1 -1
- package/dist/string.d.ts +15 -0
- package/dist/string.d.ts.map +1 -1
- package/dist/string.js +28 -3
- package/dist/string.js.map +1 -1
- package/dist/timespan.d.ts +10 -15
- package/dist/timespan.d.ts.map +1 -1
- package/dist/timespan.js +10 -15
- package/dist/timespan.js.map +1 -1
- package/dist/triggers.d.ts +6 -15
- package/dist/triggers.d.ts.map +1 -1
- package/dist/triggers.js +8 -22
- package/dist/triggers.js.map +1 -1
- package/dist/types.d.ts +0 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/dist/utilities.d.ts +7 -6
- package/dist/utilities.d.ts.map +1 -1
- package/dist/utilities.js +19 -20
- package/dist/utilities.js.map +1 -1
- package/dist/uuid.d.ts +73 -0
- package/dist/uuid.d.ts.map +1 -0
- package/dist/uuid.js +129 -0
- package/dist/uuid.js.map +1 -0
- package/package.json +5 -1
- package/src/array.ts +19 -0
- package/src/async-pool-executor.ts +9 -9
- package/src/browser/form-utilities.ts +3 -32
- package/src/browser/geo-location.ts +3 -3
- package/src/browser/index.ts +0 -0
- package/src/browser/native-bridge.ts +2 -2
- package/src/cancellable-promise.ts +9 -3
- package/src/clean-url-search-params.ts +3 -3
- package/src/collections/cache-dictionary.ts +0 -0
- package/src/collections/default-map.ts +0 -0
- package/src/collections/default-weak-map.ts +0 -0
- package/src/collections/from.ts +70 -38
- package/src/collections/index.ts +0 -0
- package/src/draft.ts +3 -3
- package/src/event-bus.ts +0 -0
- package/src/functions.ts +3 -3
- package/src/index.ts +2 -1
- package/src/key-value-pair.ts +0 -0
- package/src/lazy.ts +0 -0
- package/src/math.ts +17 -5
- package/src/order.ts +0 -0
- package/src/parse.ts +108 -66
- package/src/patch.ts +0 -0
- package/src/range.ts +2 -16
- package/src/stopwatch.ts +9 -9
- package/src/string.ts +33 -3
- package/src/timer.ts +0 -0
- package/src/timespan.ts +10 -15
- package/src/triggers.ts +10 -24
- package/src/type-code.ts +0 -0
- package/src/type.ts +0 -0
- package/src/types.ts +0 -1
- package/src/utilities.ts +22 -25
- package/src/uuid.ts +153 -0
package/src/range.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export default class Range<T> {
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
public readonly start: T;
|
|
4
|
+
public readonly end: T;
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* Range constructor.
|
|
@@ -86,20 +86,6 @@ export default class Range<T> {
|
|
|
86
86
|
(this.start < obj && this.end > obj);
|
|
87
87
|
}
|
|
88
88
|
|
|
89
|
-
/**
|
|
90
|
-
* @returns T
|
|
91
|
-
*/
|
|
92
|
-
public getStart(): T {
|
|
93
|
-
return this.start;
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
/**
|
|
97
|
-
* @returns T
|
|
98
|
-
*/
|
|
99
|
-
public getEnd(): T {
|
|
100
|
-
return this.end;
|
|
101
|
-
}
|
|
102
|
-
|
|
103
89
|
/**
|
|
104
90
|
* Finds the intersection between two sets of ranges.
|
|
105
91
|
*
|
package/src/stopwatch.ts
CHANGED
|
@@ -2,7 +2,7 @@ import TimeSpan from "./timespan.js";
|
|
|
2
2
|
|
|
3
3
|
export default class Stopwatch {
|
|
4
4
|
private startTime: number | null = null;
|
|
5
|
-
private
|
|
5
|
+
private elapsedMilliseconds: number | null = null;
|
|
6
6
|
|
|
7
7
|
/*static measure<T>(callback: () => T, measure: { value: TimeSpan | null }): T {
|
|
8
8
|
const stopwatch = new Stopwatch();
|
|
@@ -11,33 +11,33 @@ export default class Stopwatch {
|
|
|
11
11
|
return callback();
|
|
12
12
|
} finally {
|
|
13
13
|
stopwatch.stop();
|
|
14
|
-
measure.value = stopwatch.
|
|
14
|
+
measure.value = stopwatch.elapsedTime;
|
|
15
15
|
}
|
|
16
16
|
}*/
|
|
17
17
|
|
|
18
18
|
start(): void {
|
|
19
19
|
this.startTime = performance.now();
|
|
20
|
-
this.
|
|
20
|
+
this.elapsedMilliseconds = null;
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
stop(): void {
|
|
24
24
|
if (this.startTime !== null) {
|
|
25
|
-
this.
|
|
25
|
+
this.elapsedMilliseconds = performance.now() - this.startTime;
|
|
26
26
|
this.startTime = null;
|
|
27
27
|
}
|
|
28
28
|
}
|
|
29
29
|
|
|
30
30
|
resume(): void {
|
|
31
|
-
if (this.
|
|
32
|
-
this.startTime = performance.now() - this.
|
|
33
|
-
this.
|
|
31
|
+
if (this.elapsedMilliseconds !== null) {
|
|
32
|
+
this.startTime = performance.now() - this.elapsedMilliseconds;
|
|
33
|
+
this.elapsedMilliseconds = null;
|
|
34
34
|
}
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
-
|
|
37
|
+
get elapsedTime(): TimeSpan {
|
|
38
38
|
if (this.startTime !== null) {
|
|
39
39
|
return TimeSpan.fromMilliseconds(performance.now() - this.startTime);
|
|
40
40
|
}
|
|
41
|
-
return TimeSpan.fromMilliseconds(this.
|
|
41
|
+
return TimeSpan.fromMilliseconds(this.elapsedMilliseconds ?? 0.0);
|
|
42
42
|
}
|
|
43
43
|
}
|
package/src/string.ts
CHANGED
|
@@ -26,14 +26,20 @@ export function ensureSuffix(str: string, suffix: string): string {
|
|
|
26
26
|
return str
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
+
/**
|
|
30
|
+
* Null for an empty string, as `Str::nullIfEmpty()` of develia/commons for PHP; any other string as it is. Compose it
|
|
31
|
+
* with `trim()` to turn a blank string into null: `nullIfEmpty(value.trim())`.
|
|
32
|
+
*/
|
|
33
|
+
export function nullIfEmpty(str: string | null | undefined): string | null {
|
|
34
|
+
return str === undefined || str === null || str === '' ? null : str;
|
|
35
|
+
}
|
|
36
|
+
|
|
29
37
|
/**
|
|
30
38
|
* Normalizes Unicode whitespaces into single ASCII spaces and removes leading
|
|
31
39
|
* and trailing whitespace.
|
|
32
40
|
*/
|
|
33
41
|
export function normalizeWhitespaces(str: string): string {
|
|
34
|
-
return str.
|
|
35
|
-
.replace(/[\s\u200B\uFEFF]+/gu, ' ')
|
|
36
|
-
.trim()
|
|
42
|
+
return str.replace(/[\s\u200B\uFEFF]+/gu, ' ').trim();
|
|
37
43
|
}
|
|
38
44
|
|
|
39
45
|
/**
|
|
@@ -151,3 +157,27 @@ export function toPascalCase(str: string): string {
|
|
|
151
157
|
export function toConstantCase(str: string): string {
|
|
152
158
|
return toWords(str).join('_').toUpperCase();
|
|
153
159
|
}
|
|
160
|
+
|
|
161
|
+
const FILE_SIZE_UNITS: readonly string[] = Object.freeze(['B', 'KB', 'MB', 'GB', 'TB', 'PB']);
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Formats a size in bytes with the largest unit it fills, in powers of 1024: `340 B`, `1.2 MB`.
|
|
165
|
+
*
|
|
166
|
+
* Bytes are written whole and the larger units with at most one decimal, with the separators of the locale.
|
|
167
|
+
*
|
|
168
|
+
* @param {number} bytes - The size in bytes; a negative or non-finite size is formatted as zero.
|
|
169
|
+
* @param {string} [locale] - The locale of the number, such as `es-ES`; the one of the runtime when omitted.
|
|
170
|
+
* @return {string} The number, a space and the unit.
|
|
171
|
+
*/
|
|
172
|
+
export function formatFileSize(bytes: number, locale?: string): string {
|
|
173
|
+
let size = Number.isFinite(bytes) && bytes > 0 ? bytes : 0;
|
|
174
|
+
let unitIndex = 0;
|
|
175
|
+
while (size >= 1024 && unitIndex < FILE_SIZE_UNITS.length - 1) {
|
|
176
|
+
size /= 1024;
|
|
177
|
+
unitIndex++;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
const maximumFractionDigits = unitIndex === 0 ? 0 : 1;
|
|
181
|
+
|
|
182
|
+
return `${size.toLocaleString(locale, {maximumFractionDigits})} ${FILE_SIZE_UNITS[unitIndex]}`;
|
|
183
|
+
}
|
package/src/timer.ts
CHANGED
|
File without changes
|
package/src/timespan.ts
CHANGED
|
@@ -10,42 +10,37 @@ export default class TimeSpan {
|
|
|
10
10
|
|
|
11
11
|
|
|
12
12
|
/**
|
|
13
|
-
*
|
|
14
|
-
* @returns {number} The duration in seconds.
|
|
13
|
+
* The whole duration in seconds.
|
|
15
14
|
*/
|
|
16
|
-
|
|
15
|
+
get totalSeconds(): number {
|
|
17
16
|
return this.milliseconds / 1000;
|
|
18
17
|
}
|
|
19
18
|
|
|
20
19
|
/**
|
|
21
|
-
*
|
|
22
|
-
* @returns {number} The duration in minutes.
|
|
20
|
+
* The whole duration in minutes.
|
|
23
21
|
*/
|
|
24
|
-
|
|
22
|
+
get totalMinutes(): number {
|
|
25
23
|
return this.milliseconds / (1000 * 60);
|
|
26
24
|
}
|
|
27
25
|
|
|
28
26
|
/**
|
|
29
|
-
*
|
|
30
|
-
* @returns {number} The duration in hours.
|
|
27
|
+
* The whole duration in hours.
|
|
31
28
|
*/
|
|
32
|
-
|
|
29
|
+
get totalHours(): number {
|
|
33
30
|
return this.milliseconds / (1000 * 60 * 60);
|
|
34
31
|
}
|
|
35
32
|
|
|
36
33
|
/**
|
|
37
|
-
*
|
|
38
|
-
* @returns {number} The duration in days.
|
|
34
|
+
* The whole duration in days.
|
|
39
35
|
*/
|
|
40
|
-
|
|
36
|
+
get totalDays(): number {
|
|
41
37
|
return this.milliseconds / (1000 * 60 * 60 * 24);
|
|
42
38
|
}
|
|
43
39
|
|
|
44
40
|
/**
|
|
45
|
-
*
|
|
46
|
-
* @returns {number} The duration in weeks.
|
|
41
|
+
* The whole duration in weeks.
|
|
47
42
|
*/
|
|
48
|
-
|
|
43
|
+
get totalWeeks(): number {
|
|
49
44
|
return this.milliseconds / (1000 * 60 * 60 * 24 * 7);
|
|
50
45
|
}
|
|
51
46
|
|
package/src/triggers.ts
CHANGED
|
@@ -7,7 +7,8 @@ import type {Nullable, Supplier} from './types.js';
|
|
|
7
7
|
*/
|
|
8
8
|
export class TimeTrigger<T> {
|
|
9
9
|
private callback: Supplier<T | Promise<T>>;
|
|
10
|
-
|
|
10
|
+
/** Time that must pass between two runs. */
|
|
11
|
+
public interval: TimeSpan;
|
|
11
12
|
private lastRunTime: Nullable<number>;
|
|
12
13
|
|
|
13
14
|
public constructor(callback: Supplier<T | Promise<T>>, interval: TimeSpan) {
|
|
@@ -46,19 +47,6 @@ export class TimeTrigger<T> {
|
|
|
46
47
|
this.callback = callback;
|
|
47
48
|
}
|
|
48
49
|
|
|
49
|
-
/**
|
|
50
|
-
* Gets the configured time interval.
|
|
51
|
-
*/
|
|
52
|
-
public getInterval(): TimeSpan {
|
|
53
|
-
return this.interval;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
/**
|
|
57
|
-
* Replaces the configured time interval.
|
|
58
|
-
*/
|
|
59
|
-
public setInterval(interval: TimeSpan): void {
|
|
60
|
-
this.interval = interval;
|
|
61
|
-
}
|
|
62
50
|
|
|
63
51
|
|
|
64
52
|
}
|
|
@@ -68,12 +56,12 @@ export class TimeTrigger<T> {
|
|
|
68
56
|
*/
|
|
69
57
|
export class CountTrigger<T> {
|
|
70
58
|
private callback: Supplier<T | Promise<T>>;
|
|
71
|
-
private
|
|
59
|
+
private _interval: number = 1;
|
|
72
60
|
private checkCount: number = 0;
|
|
73
61
|
|
|
74
62
|
public constructor(callback: Supplier<T | Promise<T>>, interval: number) {
|
|
75
63
|
this.callback = callback;
|
|
76
|
-
this.
|
|
64
|
+
this.interval = interval;
|
|
77
65
|
}
|
|
78
66
|
|
|
79
67
|
/**
|
|
@@ -82,7 +70,7 @@ export class CountTrigger<T> {
|
|
|
82
70
|
public async check(): Promise<Result<T>> {
|
|
83
71
|
this.checkCount++;
|
|
84
72
|
|
|
85
|
-
if (this.checkCount >= this.
|
|
73
|
+
if (this.checkCount >= this._interval) {
|
|
86
74
|
return {success: true, output: await this.trigger()};
|
|
87
75
|
}
|
|
88
76
|
|
|
@@ -105,22 +93,20 @@ export class CountTrigger<T> {
|
|
|
105
93
|
}
|
|
106
94
|
|
|
107
95
|
/**
|
|
108
|
-
*
|
|
96
|
+
* Number of checks after which the callback runs.
|
|
109
97
|
*/
|
|
110
|
-
public
|
|
111
|
-
return this.
|
|
98
|
+
public get interval(): number {
|
|
99
|
+
return this._interval;
|
|
112
100
|
}
|
|
113
101
|
|
|
114
102
|
/**
|
|
115
|
-
* Replaces the configured check interval.
|
|
116
|
-
*
|
|
117
103
|
* @throws {RangeError} When the interval is less than one.
|
|
118
104
|
*/
|
|
119
|
-
public
|
|
105
|
+
public set interval(interval: number) {
|
|
120
106
|
if (interval < 1) {
|
|
121
107
|
throw new RangeError('Interval must be greater than zero.');
|
|
122
108
|
}
|
|
123
109
|
|
|
124
|
-
this.
|
|
110
|
+
this._interval = interval;
|
|
125
111
|
}
|
|
126
112
|
}
|
package/src/type-code.ts
CHANGED
|
File without changes
|
package/src/type.ts
CHANGED
|
File without changes
|
package/src/types.ts
CHANGED
package/src/utilities.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
|
|
4
4
|
import KeyValuePair from "./key-value-pair.js";
|
|
5
|
-
import type {
|
|
5
|
+
import type {Constructor} from "./types.js";
|
|
6
6
|
import Type from "./type.js";
|
|
7
7
|
|
|
8
8
|
export function lazy<T extends object>(factory: () => T): T {
|
|
@@ -185,8 +185,8 @@ function _deepEqual(
|
|
|
185
185
|
}
|
|
186
186
|
|
|
187
187
|
const objB = b as object;
|
|
188
|
-
const keysA =
|
|
189
|
-
const keysB =
|
|
188
|
+
const keysA = _collectComparableKeys(objA, includeInherited);
|
|
189
|
+
const keysB = _collectComparableKeys(objB, includeInherited);
|
|
190
190
|
if (keysA.length !== keysB.length) return false;
|
|
191
191
|
|
|
192
192
|
for (const key of keysA) {
|
|
@@ -265,7 +265,7 @@ function _deepEqualSet(
|
|
|
265
265
|
return true;
|
|
266
266
|
}
|
|
267
267
|
|
|
268
|
-
function
|
|
268
|
+
function _collectComparableKeys(value: object, includeInherited: boolean): PropertyKey[] {
|
|
269
269
|
const keys: PropertyKey[] = [];
|
|
270
270
|
const seen = new Set<PropertyKey>();
|
|
271
271
|
const addKey = (key: PropertyKey) => {
|
|
@@ -529,29 +529,26 @@ export function pick<T extends object, K extends keyof T>(obj: T, props: K[]): P
|
|
|
529
529
|
}
|
|
530
530
|
|
|
531
531
|
/**
|
|
532
|
-
*
|
|
533
|
-
*
|
|
534
|
-
*
|
|
535
|
-
* @
|
|
532
|
+
* Turns a function that takes a Node-style callback as its last argument (`(error, result) => void`) into one that
|
|
533
|
+
* returns a promise, as Node's `util.promisify`: it resolves with the result, or rejects with the error the callback
|
|
534
|
+
* receives. The function keeps the `this` it is called with.
|
|
535
|
+
* @param fn - The function to wrap.
|
|
536
|
+
* @returns The function that returns a promise.
|
|
536
537
|
*/
|
|
537
|
-
export function promisify<
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
}
|
|
549
|
-
reject(error);
|
|
550
|
-
}
|
|
538
|
+
export function promisify<TArgs extends unknown[], TResult>(
|
|
539
|
+
fn: (...args: [...TArgs, (error: unknown, result: TResult) => void]) => void,
|
|
540
|
+
): (...args: TArgs) => Promise<TResult> {
|
|
541
|
+
return function (this: unknown, ...args: TArgs): Promise<TResult> {
|
|
542
|
+
return new Promise<TResult>((resolve, reject) => {
|
|
543
|
+
fn.call(this, ...args, (error: unknown, result: TResult) => {
|
|
544
|
+
if (error !== null && error !== undefined) {
|
|
545
|
+
reject(error);
|
|
546
|
+
} else {
|
|
547
|
+
resolve(result);
|
|
548
|
+
}
|
|
549
|
+
});
|
|
551
550
|
});
|
|
552
|
-
}
|
|
553
|
-
|
|
554
|
-
return Promise.resolve(thing);
|
|
551
|
+
};
|
|
555
552
|
}
|
|
556
553
|
|
|
557
554
|
|
package/src/uuid.ts
ADDED
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
import {Nullable} from './types.js';
|
|
2
|
+
|
|
3
|
+
/** Versions 1 to 8 with the RFC variant, plus the Nil and Max UUIDs of RFC 9562 sections 5.9 and 5.10. */
|
|
4
|
+
const PATTERN = /^(?:[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}|0{8}-0{4}-0{4}-0{4}-0{12}|f{8}-f{4}-f{4}-f{4}-f{12})$/i;
|
|
5
|
+
|
|
6
|
+
/** Largest Unix timestamp in milliseconds that fits in the 48 bits of a version 7 UUID. */
|
|
7
|
+
const MAX_V7_MILLISECONDS = 0xffffffffffff;
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Immutable universally unique identifier (RFC 9562), kept in lowercase canonical form,
|
|
11
|
+
* such as `019a4f8b-4bae-7d2e-9f10-4a5b6c7d8e9f`.
|
|
12
|
+
*
|
|
13
|
+
* It serializes to its string with `JSON.stringify`, so DTOs carry it as text; read those
|
|
14
|
+
* strings back with {@link Uuid.parse} or {@link Uuid.tryParse}.
|
|
15
|
+
*/
|
|
16
|
+
export default class Uuid {
|
|
17
|
+
private readonly value: string;
|
|
18
|
+
|
|
19
|
+
private constructor(value: string) {
|
|
20
|
+
this.value = value;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Generates a version 4 UUID from 122 random bits of a cryptographically secure source.
|
|
25
|
+
*
|
|
26
|
+
* Unlike `crypto.randomUUID()`, it also works in browser contexts that are not secure, such as
|
|
27
|
+
* a development site served over HTTP.
|
|
28
|
+
*/
|
|
29
|
+
static generateV4(): Uuid {
|
|
30
|
+
const bytes = new Uint8Array(16);
|
|
31
|
+
globalThis.crypto.getRandomValues(bytes);
|
|
32
|
+
|
|
33
|
+
return Uuid.createWithVersion(bytes, 4);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Generates a version 7 UUID: a 48-bit Unix timestamp in milliseconds followed by 74 random bits
|
|
38
|
+
* from a cryptographically secure source, so identifiers sort by creation time.
|
|
39
|
+
*
|
|
40
|
+
* Identifiers generated within the same millisecond keep no particular order among themselves.
|
|
41
|
+
*
|
|
42
|
+
* @param date Moment the UUID encodes; the current time when omitted.
|
|
43
|
+
* @throws {RangeError} If the date is invalid, before the Unix epoch or beyond the 48-bit range.
|
|
44
|
+
*/
|
|
45
|
+
static generateV7(date: Date = new Date()): Uuid {
|
|
46
|
+
const milliseconds = date.getTime();
|
|
47
|
+
if (!Number.isInteger(milliseconds) || milliseconds < 0 || milliseconds > MAX_V7_MILLISECONDS) {
|
|
48
|
+
throw new RangeError('A version 7 UUID can only encode a valid date from the Unix epoch on.');
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const bytes = new Uint8Array(16);
|
|
52
|
+
globalThis.crypto.getRandomValues(bytes);
|
|
53
|
+
let remaining = milliseconds;
|
|
54
|
+
for (let index = 5; index >= 0; index--) {
|
|
55
|
+
bytes[index] = remaining % 256;
|
|
56
|
+
remaining = Math.floor(remaining / 256);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return Uuid.createWithVersion(bytes, 7);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Checks if a value is a UUID in canonical form, in any letter case: one of RFC 9562 versions 1 to 8,
|
|
64
|
+
* or its Nil UUID (all zeros) or Max UUID (all ones).
|
|
65
|
+
*
|
|
66
|
+
* It returns a plain boolean rather than a type guard: a string that is not a UUID is still a
|
|
67
|
+
* string, which a guard would narrow to `never`.
|
|
68
|
+
*/
|
|
69
|
+
static isValid(value: unknown): boolean {
|
|
70
|
+
return typeof value === 'string' && PATTERN.test(value);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Reads a UUID in canonical form, in any letter case, as {@link Uuid.isValid} accepts it.
|
|
75
|
+
*
|
|
76
|
+
* @throws {SyntaxError} If the value is not a UUID.
|
|
77
|
+
*/
|
|
78
|
+
static parse(value: string): Uuid {
|
|
79
|
+
const uuid = Uuid.tryParse(value);
|
|
80
|
+
if (uuid === undefined) {
|
|
81
|
+
throw new SyntaxError(`Invalid UUID "${value}".`);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
return uuid;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Reads a UUID in canonical form, in any letter case, as {@link Uuid.isValid} accepts it.
|
|
89
|
+
*
|
|
90
|
+
* @returns The UUID, or `undefined` when the value is not one.
|
|
91
|
+
*/
|
|
92
|
+
static tryParse(value: unknown): Uuid | undefined {
|
|
93
|
+
if (typeof value !== 'string' || !Uuid.isValid(value)) {
|
|
94
|
+
return undefined;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
return new Uuid(value.toLowerCase());
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* The RFC 9562 version, from 1 to 8, or 0 for the Nil UUID and 15 for the Max UUID.
|
|
102
|
+
*/
|
|
103
|
+
get version(): number {
|
|
104
|
+
return Number.parseInt(this.value.charAt(14), 16);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* The moment a version 7 UUID encodes, with millisecond precision.
|
|
109
|
+
*
|
|
110
|
+
* It is `null` for other versions.
|
|
111
|
+
*/
|
|
112
|
+
get date(): Nullable<Date> {
|
|
113
|
+
if (this.version !== 7) {
|
|
114
|
+
return null;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const milliseconds = Number.parseInt(this.value.slice(0, 8) + this.value.slice(9, 13), 16);
|
|
118
|
+
return new Date(milliseconds);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
equals(other: Uuid): boolean {
|
|
122
|
+
return this.value === other.value;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Returns the UUID in lowercase canonical form.
|
|
127
|
+
*/
|
|
128
|
+
toString(): string {
|
|
129
|
+
return this.value;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
toJSON(): string {
|
|
133
|
+
return this.value;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Sets the version and the RFC 9562 variant bits of 16 bytes and wraps them as a UUID.
|
|
138
|
+
*
|
|
139
|
+
* @param bytes Exactly 16 bytes; only the version and variant bits are overwritten.
|
|
140
|
+
* @param version From 1 to 8.
|
|
141
|
+
*/
|
|
142
|
+
private static createWithVersion(bytes: Uint8Array, version: number): Uuid {
|
|
143
|
+
bytes[6] = (bytes[6] & 0x0f) | (version << 4);
|
|
144
|
+
bytes[8] = (bytes[8] & 0x3f) | 0x80;
|
|
145
|
+
|
|
146
|
+
let hex = '';
|
|
147
|
+
for (const byte of bytes) {
|
|
148
|
+
hex += byte.toString(16).padStart(2, '0');
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
return new Uuid(`${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`);
|
|
152
|
+
}
|
|
153
|
+
}
|