@develia/commons 0.5.0 → 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/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 +10 -0
- package/dist/string.d.ts.map +1 -1
- package/dist/string.js +20 -0
- 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/uuid.d.ts +11 -11
- package/dist/uuid.d.ts.map +1 -1
- package/dist/uuid.js +18 -18
- package/dist/uuid.js.map +1 -1
- package/package.json +1 -1
- package/src/array.ts +0 -0
- package/src/async-pool-executor.ts +0 -0
- package/src/browser/form-utilities.ts +0 -0
- package/src/browser/geo-location.ts +0 -0
- package/src/browser/index.ts +0 -0
- package/src/browser/native-bridge.ts +0 -0
- package/src/cancellable-promise.ts +0 -0
- package/src/clean-url-search-params.ts +0 -0
- 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/index.ts +0 -0
- package/src/draft.ts +0 -0
- package/src/event-bus.ts +0 -0
- package/src/functions.ts +0 -0
- package/src/key-value-pair.ts +0 -0
- package/src/lazy.ts +0 -0
- package/src/math.ts +0 -0
- 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 +24 -0
- 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 -0
- package/src/uuid.ts +19 -19
package/dist/uuid.js
CHANGED
|
@@ -7,7 +7,7 @@ const MAX_V7_MILLISECONDS = 0xffffffffffff;
|
|
|
7
7
|
* such as `019a4f8b-4bae-7d2e-9f10-4a5b6c7d8e9f`.
|
|
8
8
|
*
|
|
9
9
|
* It serializes to its string with `JSON.stringify`, so DTOs carry it as text; read those
|
|
10
|
-
* strings back with {@link Uuid.parse} or {@link Uuid.
|
|
10
|
+
* strings back with {@link Uuid.parse} or {@link Uuid.tryParse}.
|
|
11
11
|
*/
|
|
12
12
|
export default class Uuid {
|
|
13
13
|
constructor(value) {
|
|
@@ -60,39 +60,39 @@ export default class Uuid {
|
|
|
60
60
|
/**
|
|
61
61
|
* Reads a UUID in canonical form, in any letter case, as {@link Uuid.isValid} accepts it.
|
|
62
62
|
*
|
|
63
|
-
* @
|
|
63
|
+
* @throws {SyntaxError} If the value is not a UUID.
|
|
64
64
|
*/
|
|
65
65
|
static parse(value) {
|
|
66
|
-
|
|
67
|
-
|
|
66
|
+
const uuid = Uuid.tryParse(value);
|
|
67
|
+
if (uuid === undefined) {
|
|
68
|
+
throw new SyntaxError(`Invalid UUID "${value}".`);
|
|
68
69
|
}
|
|
69
|
-
return
|
|
70
|
+
return uuid;
|
|
70
71
|
}
|
|
71
72
|
/**
|
|
72
|
-
* Reads a UUID in canonical form, in any letter case.
|
|
73
|
+
* Reads a UUID in canonical form, in any letter case, as {@link Uuid.isValid} accepts it.
|
|
73
74
|
*
|
|
74
|
-
* @
|
|
75
|
+
* @returns The UUID, or `undefined` when the value is not one.
|
|
75
76
|
*/
|
|
76
|
-
static
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
throw new SyntaxError(`Invalid UUID "${value}".`);
|
|
77
|
+
static tryParse(value) {
|
|
78
|
+
if (typeof value !== 'string' || !Uuid.isValid(value)) {
|
|
79
|
+
return undefined;
|
|
80
80
|
}
|
|
81
|
-
return
|
|
81
|
+
return new Uuid(value.toLowerCase());
|
|
82
82
|
}
|
|
83
83
|
/**
|
|
84
|
-
*
|
|
84
|
+
* The RFC 9562 version, from 1 to 8, or 0 for the Nil UUID and 15 for the Max UUID.
|
|
85
85
|
*/
|
|
86
|
-
|
|
86
|
+
get version() {
|
|
87
87
|
return Number.parseInt(this.value.charAt(14), 16);
|
|
88
88
|
}
|
|
89
89
|
/**
|
|
90
|
-
*
|
|
90
|
+
* The moment a version 7 UUID encodes, with millisecond precision.
|
|
91
91
|
*
|
|
92
|
-
*
|
|
92
|
+
* It is `null` for other versions.
|
|
93
93
|
*/
|
|
94
|
-
|
|
95
|
-
if (this.
|
|
94
|
+
get date() {
|
|
95
|
+
if (this.version !== 7) {
|
|
96
96
|
return null;
|
|
97
97
|
}
|
|
98
98
|
const milliseconds = Number.parseInt(this.value.slice(0, 8) + this.value.slice(9, 13), 16);
|
package/dist/uuid.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"uuid.js","sourceRoot":"","sources":["../src/uuid.ts"],"names":[],"mappings":"AAEA,0GAA0G;AAC1G,MAAM,OAAO,GAAG,oIAAoI,CAAC;AAErJ,2FAA2F;AAC3F,MAAM,mBAAmB,GAAG,cAAc,CAAC;AAE3C;;;;;;GAMG;AACH,MAAM,CAAC,OAAO,OAAO,IAAI;IAGrB,YAAoB,KAAa;QAC7B,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;IACvB,CAAC;IAED;;;;;OAKG;IACH,MAAM,CAAC,UAAU;QACb,MAAM,KAAK,GAAG,IAAI,UAAU,CAAC,EAAE,CAAC,CAAC;QACjC,UAAU,CAAC,MAAM,CAAC,eAAe,CAAC,KAAK,CAAC,CAAC;QAEzC,OAAO,IAAI,CAAC,iBAAiB,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;IAC5C,CAAC;IAED;;;;;;;;OAQG;IACH,MAAM,CAAC,UAAU,CAAC,OAAa,IAAI,IAAI,EAAE;QACrC,MAAM,YAAY,GAAG,IAAI,CAAC,OAAO,EAAE,CAAC;QACpC,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,YAAY,CAAC,IAAI,YAAY,GAAG,CAAC,IAAI,YAAY,GAAG,mBAAmB,EAAE,CAAC;YAC5F,MAAM,IAAI,UAAU,CAAC,uEAAuE,CAAC,CAAC;QAClG,CAAC;QAED,MAAM,KAAK,GAAG,IAAI,UAAU,CAAC,EAAE,CAAC,CAAC;QACjC,UAAU,CAAC,MAAM,CAAC,eAAe,CAAC,KAAK,CAAC,CAAC;QACzC,IAAI,SAAS,GAAG,YAAY,CAAC;QAC7B,KAAK,IAAI,KAAK,GAAG,CAAC,EAAE,KAAK,IAAI,CAAC,EAAE,KAAK,EAAE,EAAE,CAAC;YACtC,KAAK,CAAC,KAAK,CAAC,GAAG,SAAS,GAAG,GAAG,CAAC;YAC/B,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC,SAAS,GAAG,GAAG,CAAC,CAAC;QAC5C,CAAC;QAED,OAAO,IAAI,CAAC,iBAAiB,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;IAC5C,CAAC;IAED;;;;;;OAMG;IACH,MAAM,CAAC,OAAO,CAAC,KAAc;QACzB,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAC5D,CAAC;IAED;;;;OAIG;IACH,MAAM,CAAC,KAAK,CAAC,
|
|
1
|
+
{"version":3,"file":"uuid.js","sourceRoot":"","sources":["../src/uuid.ts"],"names":[],"mappings":"AAEA,0GAA0G;AAC1G,MAAM,OAAO,GAAG,oIAAoI,CAAC;AAErJ,2FAA2F;AAC3F,MAAM,mBAAmB,GAAG,cAAc,CAAC;AAE3C;;;;;;GAMG;AACH,MAAM,CAAC,OAAO,OAAO,IAAI;IAGrB,YAAoB,KAAa;QAC7B,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;IACvB,CAAC;IAED;;;;;OAKG;IACH,MAAM,CAAC,UAAU;QACb,MAAM,KAAK,GAAG,IAAI,UAAU,CAAC,EAAE,CAAC,CAAC;QACjC,UAAU,CAAC,MAAM,CAAC,eAAe,CAAC,KAAK,CAAC,CAAC;QAEzC,OAAO,IAAI,CAAC,iBAAiB,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;IAC5C,CAAC;IAED;;;;;;;;OAQG;IACH,MAAM,CAAC,UAAU,CAAC,OAAa,IAAI,IAAI,EAAE;QACrC,MAAM,YAAY,GAAG,IAAI,CAAC,OAAO,EAAE,CAAC;QACpC,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,YAAY,CAAC,IAAI,YAAY,GAAG,CAAC,IAAI,YAAY,GAAG,mBAAmB,EAAE,CAAC;YAC5F,MAAM,IAAI,UAAU,CAAC,uEAAuE,CAAC,CAAC;QAClG,CAAC;QAED,MAAM,KAAK,GAAG,IAAI,UAAU,CAAC,EAAE,CAAC,CAAC;QACjC,UAAU,CAAC,MAAM,CAAC,eAAe,CAAC,KAAK,CAAC,CAAC;QACzC,IAAI,SAAS,GAAG,YAAY,CAAC;QAC7B,KAAK,IAAI,KAAK,GAAG,CAAC,EAAE,KAAK,IAAI,CAAC,EAAE,KAAK,EAAE,EAAE,CAAC;YACtC,KAAK,CAAC,KAAK,CAAC,GAAG,SAAS,GAAG,GAAG,CAAC;YAC/B,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC,SAAS,GAAG,GAAG,CAAC,CAAC;QAC5C,CAAC;QAED,OAAO,IAAI,CAAC,iBAAiB,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;IAC5C,CAAC;IAED;;;;;;OAMG;IACH,MAAM,CAAC,OAAO,CAAC,KAAc;QACzB,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAC5D,CAAC;IAED;;;;OAIG;IACH,MAAM,CAAC,KAAK,CAAC,KAAa;QACtB,MAAM,IAAI,GAAG,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;QAClC,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;YACrB,MAAM,IAAI,WAAW,CAAC,iBAAiB,KAAK,IAAI,CAAC,CAAC;QACtD,CAAC;QAED,OAAO,IAAI,CAAC;IAChB,CAAC;IAED;;;;OAIG;IACH,MAAM,CAAC,QAAQ,CAAC,KAAc;QAC1B,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;YACpD,OAAO,SAAS,CAAC;QACrB,CAAC;QAED,OAAO,IAAI,IAAI,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC,CAAC;IACzC,CAAC;IAED;;OAEG;IACH,IAAI,OAAO;QACP,OAAO,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC;IACtD,CAAC;IAED;;;;OAIG;IACH,IAAI,IAAI;QACJ,IAAI,IAAI,CAAC,OAAO,KAAK,CAAC,EAAE,CAAC;YACrB,OAAO,IAAI,CAAC;QAChB,CAAC;QAED,MAAM,YAAY,GAAG,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC;QAC3F,OAAO,IAAI,IAAI,CAAC,YAAY,CAAC,CAAC;IAClC,CAAC;IAED,MAAM,CAAC,KAAW;QACd,OAAO,IAAI,CAAC,KAAK,KAAK,KAAK,CAAC,KAAK,CAAC;IACtC,CAAC;IAED;;OAEG;IACH,QAAQ;QACJ,OAAO,IAAI,CAAC,KAAK,CAAC;IACtB,CAAC;IAED,MAAM;QACF,OAAO,IAAI,CAAC,KAAK,CAAC;IACtB,CAAC;IAED;;;;;OAKG;IACK,MAAM,CAAC,iBAAiB,CAAC,KAAiB,EAAE,OAAe;QAC/D,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,OAAO,IAAI,CAAC,CAAC,CAAC;QAC9C,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,GAAG,IAAI,CAAC;QAEpC,IAAI,GAAG,GAAG,EAAE,CAAC;QACb,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YACvB,GAAG,IAAI,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;QAC9C,CAAC;QAED,OAAO,IAAI,IAAI,CAAC,GAAG,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,GAAG,CAAC,KAAK,CAAC,EAAE,EAAE,EAAE,CAAC,IAAI,GAAG,CAAC,KAAK,CAAC,EAAE,EAAE,EAAE,CAAC,IAAI,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC;IACzH,CAAC;CACJ","sourcesContent":["import {Nullable} from './types.js';\n\n/** Versions 1 to 8 with the RFC variant, plus the Nil and Max UUIDs of RFC 9562 sections 5.9 and 5.10. */\nconst 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;\n\n/** Largest Unix timestamp in milliseconds that fits in the 48 bits of a version 7 UUID. */\nconst MAX_V7_MILLISECONDS = 0xffffffffffff;\n\n/**\n * Immutable universally unique identifier (RFC 9562), kept in lowercase canonical form,\n * such as `019a4f8b-4bae-7d2e-9f10-4a5b6c7d8e9f`.\n *\n * It serializes to its string with `JSON.stringify`, so DTOs carry it as text; read those\n * strings back with {@link Uuid.parse} or {@link Uuid.tryParse}.\n */\nexport default class Uuid {\n private readonly value: string;\n\n private constructor(value: string) {\n this.value = value;\n }\n\n /**\n * Generates a version 4 UUID from 122 random bits of a cryptographically secure source.\n *\n * Unlike `crypto.randomUUID()`, it also works in browser contexts that are not secure, such as\n * a development site served over HTTP.\n */\n static generateV4(): Uuid {\n const bytes = new Uint8Array(16);\n globalThis.crypto.getRandomValues(bytes);\n\n return Uuid.createWithVersion(bytes, 4);\n }\n\n /**\n * Generates a version 7 UUID: a 48-bit Unix timestamp in milliseconds followed by 74 random bits\n * from a cryptographically secure source, so identifiers sort by creation time.\n *\n * Identifiers generated within the same millisecond keep no particular order among themselves.\n *\n * @param date Moment the UUID encodes; the current time when omitted.\n * @throws {RangeError} If the date is invalid, before the Unix epoch or beyond the 48-bit range.\n */\n static generateV7(date: Date = new Date()): Uuid {\n const milliseconds = date.getTime();\n if (!Number.isInteger(milliseconds) || milliseconds < 0 || milliseconds > MAX_V7_MILLISECONDS) {\n throw new RangeError('A version 7 UUID can only encode a valid date from the Unix epoch on.');\n }\n\n const bytes = new Uint8Array(16);\n globalThis.crypto.getRandomValues(bytes);\n let remaining = milliseconds;\n for (let index = 5; index >= 0; index--) {\n bytes[index] = remaining % 256;\n remaining = Math.floor(remaining / 256);\n }\n\n return Uuid.createWithVersion(bytes, 7);\n }\n\n /**\n * Checks if a value is a UUID in canonical form, in any letter case: one of RFC 9562 versions 1 to 8,\n * or its Nil UUID (all zeros) or Max UUID (all ones).\n *\n * It returns a plain boolean rather than a type guard: a string that is not a UUID is still a\n * string, which a guard would narrow to `never`.\n */\n static isValid(value: unknown): boolean {\n return typeof value === 'string' && PATTERN.test(value);\n }\n\n /**\n * Reads a UUID in canonical form, in any letter case, as {@link Uuid.isValid} accepts it.\n *\n * @throws {SyntaxError} If the value is not a UUID.\n */\n static parse(value: string): Uuid {\n const uuid = Uuid.tryParse(value);\n if (uuid === undefined) {\n throw new SyntaxError(`Invalid UUID \"${value}\".`);\n }\n\n return uuid;\n }\n\n /**\n * Reads a UUID in canonical form, in any letter case, as {@link Uuid.isValid} accepts it.\n *\n * @returns The UUID, or `undefined` when the value is not one.\n */\n static tryParse(value: unknown): Uuid | undefined {\n if (typeof value !== 'string' || !Uuid.isValid(value)) {\n return undefined;\n }\n\n return new Uuid(value.toLowerCase());\n }\n\n /**\n * The RFC 9562 version, from 1 to 8, or 0 for the Nil UUID and 15 for the Max UUID.\n */\n get version(): number {\n return Number.parseInt(this.value.charAt(14), 16);\n }\n\n /**\n * The moment a version 7 UUID encodes, with millisecond precision.\n *\n * It is `null` for other versions.\n */\n get date(): Nullable<Date> {\n if (this.version !== 7) {\n return null;\n }\n\n const milliseconds = Number.parseInt(this.value.slice(0, 8) + this.value.slice(9, 13), 16);\n return new Date(milliseconds);\n }\n\n equals(other: Uuid): boolean {\n return this.value === other.value;\n }\n\n /**\n * Returns the UUID in lowercase canonical form.\n */\n toString(): string {\n return this.value;\n }\n\n toJSON(): string {\n return this.value;\n }\n\n /**\n * Sets the version and the RFC 9562 variant bits of 16 bytes and wraps them as a UUID.\n *\n * @param bytes Exactly 16 bytes; only the version and variant bits are overwritten.\n * @param version From 1 to 8.\n */\n private static createWithVersion(bytes: Uint8Array, version: number): Uuid {\n bytes[6] = (bytes[6] & 0x0f) | (version << 4);\n bytes[8] = (bytes[8] & 0x3f) | 0x80;\n\n let hex = '';\n for (const byte of bytes) {\n hex += byte.toString(16).padStart(2, '0');\n }\n\n return new Uuid(`${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`);\n }\n}\n"]}
|
package/package.json
CHANGED
package/src/array.ts
CHANGED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
package/src/browser/index.ts
CHANGED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
package/src/collections/index.ts
CHANGED
|
File without changes
|
package/src/draft.ts
CHANGED
|
File without changes
|
package/src/event-bus.ts
CHANGED
|
File without changes
|
package/src/functions.ts
CHANGED
|
File without changes
|
package/src/key-value-pair.ts
CHANGED
|
File without changes
|
package/src/lazy.ts
CHANGED
|
File without changes
|
package/src/math.ts
CHANGED
|
File without changes
|
package/src/order.ts
CHANGED
|
File without changes
|
package/src/parse.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// parse.ts
|
|
2
2
|
/**
|
|
3
|
-
* Options for {@link parseBoolean}.
|
|
3
|
+
* Options for {@link tryParseBoolean} and {@link parseBoolean}.
|
|
4
4
|
*/
|
|
5
5
|
export interface ParseBooleanOptions {
|
|
6
6
|
/**
|
|
@@ -19,7 +19,7 @@ export interface ParseBooleanOptions {
|
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
/**
|
|
22
|
-
* Options for {@link parseInteger}.
|
|
22
|
+
* Options for {@link tryParseInteger} and {@link parseInteger}.
|
|
23
23
|
*/
|
|
24
24
|
export interface ParseIntegerOptions {
|
|
25
25
|
/**
|
|
@@ -37,7 +37,7 @@ export interface ParseIntegerOptions {
|
|
|
37
37
|
}
|
|
38
38
|
|
|
39
39
|
/**
|
|
40
|
-
* Options for {@link parseDecimal}.
|
|
40
|
+
* Options for {@link tryParseDecimal} and {@link parseDecimal}.
|
|
41
41
|
*/
|
|
42
42
|
export interface ParseDecimalOptions {
|
|
43
43
|
/**
|
|
@@ -75,64 +75,78 @@ function isValidInteger(value: string, radix: number): boolean {
|
|
|
75
75
|
}
|
|
76
76
|
|
|
77
77
|
// ============================================
|
|
78
|
-
// parseInteger
|
|
78
|
+
// tryParseInteger / parseInteger
|
|
79
79
|
// ============================================
|
|
80
80
|
|
|
81
81
|
/**
|
|
82
82
|
* Safely parses a value into an integer.
|
|
83
|
-
* Returns `
|
|
83
|
+
* Returns `undefined` if the value cannot be parsed.
|
|
84
84
|
*
|
|
85
|
-
* - `null` or `undefined` → `
|
|
85
|
+
* - `null` or `undefined` → `undefined`
|
|
86
86
|
* - `number` → returned as-is (floats are truncated)
|
|
87
87
|
* - `string` → parsed with the given options
|
|
88
88
|
*
|
|
89
89
|
* @param value - The value to parse.
|
|
90
90
|
* @param options - Optional parsing configuration.
|
|
91
|
-
* @returns The parsed integer, or `
|
|
91
|
+
* @returns The parsed integer, or `undefined` on failure.
|
|
92
92
|
*
|
|
93
93
|
* @example
|
|
94
94
|
* ```ts
|
|
95
|
-
*
|
|
96
|
-
*
|
|
97
|
-
*
|
|
98
|
-
*
|
|
99
|
-
*
|
|
100
|
-
*
|
|
101
|
-
*
|
|
102
|
-
*
|
|
95
|
+
* tryParseInteger(42) // 42
|
|
96
|
+
* tryParseInteger(3.7) // 3
|
|
97
|
+
* tryParseInteger(null) // undefined
|
|
98
|
+
* tryParseInteger(undefined) // undefined
|
|
99
|
+
* tryParseInteger("42") // 42
|
|
100
|
+
* tryParseInteger("1.234.567", { thousandsSeparator: "." }) // 1234567
|
|
101
|
+
* tryParseInteger("FF", { radix: 16 }) // 255
|
|
102
|
+
* tryParseInteger("abc") // undefined
|
|
103
103
|
* ```
|
|
104
104
|
*/
|
|
105
|
-
export function
|
|
105
|
+
export function tryParseInteger(
|
|
106
106
|
value: string | number | null | undefined,
|
|
107
107
|
options: ParseIntegerOptions = {}
|
|
108
|
-
): number |
|
|
109
|
-
if (value === null || value === undefined) return
|
|
108
|
+
): number | undefined {
|
|
109
|
+
if (value === null || value === undefined) return undefined;
|
|
110
110
|
|
|
111
111
|
if (typeof value === "number") {
|
|
112
|
-
return Number.isFinite(value) ? Math.trunc(value) :
|
|
112
|
+
return Number.isFinite(value) ? Math.trunc(value) : undefined;
|
|
113
113
|
}
|
|
114
114
|
|
|
115
115
|
const { radix = 10, thousandsSeparator } = options;
|
|
116
116
|
|
|
117
117
|
const cleaned = stripThousands(value.trim(), thousandsSeparator);
|
|
118
118
|
|
|
119
|
-
if (cleaned === "") return
|
|
120
|
-
if (!isValidInteger(cleaned, radix)) return
|
|
119
|
+
if (cleaned === "") return undefined;
|
|
120
|
+
if (!isValidInteger(cleaned, radix)) return undefined;
|
|
121
121
|
|
|
122
122
|
const parsed = Number.parseInt(cleaned, radix);
|
|
123
123
|
|
|
124
|
-
return Number.isNaN(parsed) ?
|
|
124
|
+
return Number.isNaN(parsed) ? undefined : parsed;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Parses a value into an integer as {@link tryParseInteger} does.
|
|
129
|
+
*
|
|
130
|
+
* @throws {SyntaxError} If the value cannot be parsed.
|
|
131
|
+
*/
|
|
132
|
+
export function parseInteger(value: string | number, options: ParseIntegerOptions = {}): number {
|
|
133
|
+
const parsed = tryParseInteger(value, options);
|
|
134
|
+
if (parsed === undefined) {
|
|
135
|
+
throw new SyntaxError(`Invalid integer "${value}".`);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
return parsed;
|
|
125
139
|
}
|
|
126
140
|
|
|
127
141
|
// ============================================
|
|
128
|
-
// parseDecimal
|
|
142
|
+
// tryParseDecimal / parseDecimal
|
|
129
143
|
// ============================================
|
|
130
144
|
|
|
131
145
|
/**
|
|
132
146
|
* Safely parses a value into a floating-point number.
|
|
133
|
-
* Returns `
|
|
147
|
+
* Returns `undefined` if the value cannot be parsed.
|
|
134
148
|
*
|
|
135
|
-
* - `null` or `undefined` → `
|
|
149
|
+
* - `null` or `undefined` → `undefined`
|
|
136
150
|
* - `number` → returned as-is (if finite)
|
|
137
151
|
* - `string` → parsed with the given options
|
|
138
152
|
*
|
|
@@ -141,50 +155,64 @@ export function parseInteger(
|
|
|
141
155
|
*
|
|
142
156
|
* @param value - The value to parse.
|
|
143
157
|
* @param options - Optional parsing configuration.
|
|
144
|
-
* @returns The parsed float, or `
|
|
158
|
+
* @returns The parsed float, or `undefined` on failure.
|
|
145
159
|
*
|
|
146
160
|
* @example
|
|
147
161
|
* ```ts
|
|
148
|
-
*
|
|
149
|
-
*
|
|
150
|
-
*
|
|
151
|
-
*
|
|
152
|
-
*
|
|
153
|
-
*
|
|
154
|
-
*
|
|
162
|
+
* tryParseDecimal(3.14) // 3.14
|
|
163
|
+
* tryParseDecimal(null) // undefined
|
|
164
|
+
* tryParseDecimal(undefined) // undefined
|
|
165
|
+
* tryParseDecimal("3.14") // 3.14
|
|
166
|
+
* tryParseDecimal("3,14", { decimalSeparator: "," }) // 3.14
|
|
167
|
+
* tryParseDecimal("1.234,56", { decimalSeparator: ",", thousandsSeparator: "." }) // 1234.56
|
|
168
|
+
* tryParseDecimal("abc") // undefined
|
|
155
169
|
* ```
|
|
156
170
|
*/
|
|
157
|
-
export function
|
|
171
|
+
export function tryParseDecimal(
|
|
158
172
|
value: string | number | null | undefined,
|
|
159
173
|
options: ParseDecimalOptions = {}
|
|
160
|
-
): number |
|
|
161
|
-
if (value === null || value === undefined) return
|
|
174
|
+
): number | undefined {
|
|
175
|
+
if (value === null || value === undefined) return undefined;
|
|
162
176
|
|
|
163
177
|
if (typeof value === "number") {
|
|
164
|
-
return Number.isFinite(value) ? value :
|
|
178
|
+
return Number.isFinite(value) ? value : undefined;
|
|
165
179
|
}
|
|
166
180
|
|
|
167
181
|
const { decimalSeparator = ".", thousandsSeparator } = options;
|
|
168
182
|
|
|
169
183
|
let cleaned = stripThousands(value.trim(), thousandsSeparator);
|
|
170
184
|
|
|
171
|
-
if (cleaned === "") return
|
|
185
|
+
if (cleaned === "") return undefined;
|
|
172
186
|
|
|
173
187
|
if (decimalSeparator === ",") {
|
|
174
188
|
const commaCount = (cleaned.match(/,/g) || []).length;
|
|
175
|
-
if (commaCount > 1) return
|
|
189
|
+
if (commaCount > 1) return undefined;
|
|
176
190
|
cleaned = cleaned.replace(",", ".");
|
|
177
191
|
}
|
|
178
192
|
|
|
179
|
-
if (!/^[+-]?\d+(\.\d+)?$/.test(cleaned)) return
|
|
193
|
+
if (!/^[+-]?\d+(\.\d+)?$/.test(cleaned)) return undefined;
|
|
180
194
|
|
|
181
195
|
const parsed = Number.parseFloat(cleaned);
|
|
182
196
|
|
|
183
|
-
return Number.isNaN(parsed) || !Number.isFinite(parsed) ?
|
|
197
|
+
return Number.isNaN(parsed) || !Number.isFinite(parsed) ? undefined : parsed;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* Parses a value into a decimal number as {@link tryParseDecimal} does.
|
|
202
|
+
*
|
|
203
|
+
* @throws {SyntaxError} If the value cannot be parsed.
|
|
204
|
+
*/
|
|
205
|
+
export function parseDecimal(value: string | number, options: ParseDecimalOptions = {}): number {
|
|
206
|
+
const parsed = tryParseDecimal(value, options);
|
|
207
|
+
if (parsed === undefined) {
|
|
208
|
+
throw new SyntaxError(`Invalid decimal number "${value}".`);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
return parsed;
|
|
184
212
|
}
|
|
185
213
|
|
|
186
214
|
// ============================================
|
|
187
|
-
// parseBoolean
|
|
215
|
+
// tryParseBoolean / parseBoolean
|
|
188
216
|
// ============================================
|
|
189
217
|
|
|
190
218
|
const DEFAULT_TRUTHY = ["true", "1", "yes", "si", "sí", "on"];
|
|
@@ -192,9 +220,9 @@ const DEFAULT_FALSY = ["false", "0", "no", "off"];
|
|
|
192
220
|
|
|
193
221
|
/**
|
|
194
222
|
* Safely parses a value into a boolean.
|
|
195
|
-
* Returns `
|
|
223
|
+
* Returns `undefined` if the value cannot be interpreted as boolean.
|
|
196
224
|
*
|
|
197
|
-
* - `null` or `undefined` → `
|
|
225
|
+
* - `null` or `undefined` → `undefined`
|
|
198
226
|
* - `boolean` → returned as-is
|
|
199
227
|
* - `number` → `0` is `false`, any other finite number is `true`
|
|
200
228
|
* - `string` → matched case-insensitively against known truthy/falsy values
|
|
@@ -204,43 +232,43 @@ const DEFAULT_FALSY = ["false", "0", "no", "off"];
|
|
|
204
232
|
*
|
|
205
233
|
* @param value - The value to parse.
|
|
206
234
|
* @param options - Optional parsing configuration.
|
|
207
|
-
* @returns The parsed boolean, or `
|
|
235
|
+
* @returns The parsed boolean, or `undefined` on failure.
|
|
208
236
|
*
|
|
209
237
|
* @example
|
|
210
238
|
* ```ts
|
|
211
|
-
*
|
|
212
|
-
*
|
|
213
|
-
*
|
|
214
|
-
*
|
|
215
|
-
*
|
|
216
|
-
*
|
|
217
|
-
*
|
|
218
|
-
*
|
|
219
|
-
*
|
|
220
|
-
*
|
|
221
|
-
*
|
|
222
|
-
*
|
|
223
|
-
*
|
|
224
|
-
*
|
|
239
|
+
* tryParseBoolean(true) // true
|
|
240
|
+
* tryParseBoolean(false) // false
|
|
241
|
+
* tryParseBoolean(1) // true
|
|
242
|
+
* tryParseBoolean(0) // false
|
|
243
|
+
* tryParseBoolean(null) // undefined
|
|
244
|
+
* tryParseBoolean(undefined) // undefined
|
|
245
|
+
* tryParseBoolean("yes") // true
|
|
246
|
+
* tryParseBoolean("no") // false
|
|
247
|
+
* tryParseBoolean("sí") // true
|
|
248
|
+
* tryParseBoolean("TRUE") // true
|
|
249
|
+
* tryParseBoolean("oui") // undefined
|
|
250
|
+
* tryParseBoolean("oui", { extraTruthy: ["oui"] }) // true
|
|
251
|
+
* tryParseBoolean("nein", { extraFalsy: ["nein"] }) // false
|
|
252
|
+
* tryParseBoolean("whatever") // undefined
|
|
225
253
|
* ```
|
|
226
254
|
*/
|
|
227
|
-
export function
|
|
255
|
+
export function tryParseBoolean(
|
|
228
256
|
value: string | number | boolean | null | undefined,
|
|
229
257
|
options: ParseBooleanOptions = {}
|
|
230
|
-
): boolean |
|
|
231
|
-
if (value === null || value === undefined) return
|
|
258
|
+
): boolean | undefined {
|
|
259
|
+
if (value === null || value === undefined) return undefined;
|
|
232
260
|
|
|
233
261
|
if (typeof value === "boolean") return value;
|
|
234
262
|
|
|
235
263
|
if (typeof value === "number") {
|
|
236
|
-
return Number.isFinite(value) ? value !== 0 :
|
|
264
|
+
return Number.isFinite(value) ? value !== 0 : undefined;
|
|
237
265
|
}
|
|
238
266
|
|
|
239
267
|
const { extraTruthy = [], extraFalsy = [] } = options;
|
|
240
268
|
|
|
241
269
|
const normalized = value.trim().toLowerCase();
|
|
242
270
|
|
|
243
|
-
if (normalized === "") return
|
|
271
|
+
if (normalized === "") return undefined;
|
|
244
272
|
|
|
245
273
|
const truthy = [...DEFAULT_TRUTHY, ...extraTruthy.map((s) => s.toLowerCase())];
|
|
246
274
|
const falsy = [...DEFAULT_FALSY, ...extraFalsy.map((s) => s.toLowerCase())];
|
|
@@ -248,5 +276,19 @@ export function parseBoolean(
|
|
|
248
276
|
if (truthy.includes(normalized)) return true;
|
|
249
277
|
if (falsy.includes(normalized)) return false;
|
|
250
278
|
|
|
251
|
-
return
|
|
252
|
-
}
|
|
279
|
+
return undefined;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
/**
|
|
283
|
+
* Parses a value into a boolean as {@link tryParseBoolean} does.
|
|
284
|
+
*
|
|
285
|
+
* @throws {SyntaxError} If the value cannot be parsed.
|
|
286
|
+
*/
|
|
287
|
+
export function parseBoolean(value: string | number | boolean, options: ParseBooleanOptions = {}): boolean {
|
|
288
|
+
const parsed = tryParseBoolean(value, options);
|
|
289
|
+
if (parsed === undefined) {
|
|
290
|
+
throw new SyntaxError(`Invalid boolean "${value}".`);
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
return parsed;
|
|
294
|
+
}
|
package/src/patch.ts
CHANGED
|
File without changes
|
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
|
@@ -157,3 +157,27 @@ export function toPascalCase(str: string): string {
|
|
|
157
157
|
export function toConstantCase(str: string): string {
|
|
158
158
|
return toWords(str).join('_').toUpperCase();
|
|
159
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
|
|