@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.
Files changed (60) hide show
  1. package/dist/parse.d.ts +62 -44
  2. package/dist/parse.d.ts.map +1 -1
  3. package/dist/parse.js +95 -59
  4. package/dist/parse.js.map +1 -1
  5. package/dist/range.d.ts +2 -10
  6. package/dist/range.d.ts.map +1 -1
  7. package/dist/range.js +0 -12
  8. package/dist/range.js.map +1 -1
  9. package/dist/stopwatch.d.ts +2 -2
  10. package/dist/stopwatch.d.ts.map +1 -1
  11. package/dist/stopwatch.js +9 -9
  12. package/dist/stopwatch.js.map +1 -1
  13. package/dist/string.d.ts +10 -0
  14. package/dist/string.d.ts.map +1 -1
  15. package/dist/string.js +20 -0
  16. package/dist/string.js.map +1 -1
  17. package/dist/timespan.d.ts +10 -15
  18. package/dist/timespan.d.ts.map +1 -1
  19. package/dist/timespan.js +10 -15
  20. package/dist/timespan.js.map +1 -1
  21. package/dist/triggers.d.ts +6 -15
  22. package/dist/triggers.d.ts.map +1 -1
  23. package/dist/triggers.js +8 -22
  24. package/dist/triggers.js.map +1 -1
  25. package/dist/uuid.d.ts +11 -11
  26. package/dist/uuid.d.ts.map +1 -1
  27. package/dist/uuid.js +18 -18
  28. package/dist/uuid.js.map +1 -1
  29. package/package.json +1 -1
  30. package/src/array.ts +0 -0
  31. package/src/async-pool-executor.ts +0 -0
  32. package/src/browser/form-utilities.ts +0 -0
  33. package/src/browser/geo-location.ts +0 -0
  34. package/src/browser/index.ts +0 -0
  35. package/src/browser/native-bridge.ts +0 -0
  36. package/src/cancellable-promise.ts +0 -0
  37. package/src/clean-url-search-params.ts +0 -0
  38. package/src/collections/cache-dictionary.ts +0 -0
  39. package/src/collections/default-map.ts +0 -0
  40. package/src/collections/default-weak-map.ts +0 -0
  41. package/src/collections/index.ts +0 -0
  42. package/src/draft.ts +0 -0
  43. package/src/event-bus.ts +0 -0
  44. package/src/functions.ts +0 -0
  45. package/src/key-value-pair.ts +0 -0
  46. package/src/lazy.ts +0 -0
  47. package/src/math.ts +0 -0
  48. package/src/order.ts +0 -0
  49. package/src/parse.ts +108 -66
  50. package/src/patch.ts +0 -0
  51. package/src/range.ts +2 -16
  52. package/src/stopwatch.ts +9 -9
  53. package/src/string.ts +24 -0
  54. package/src/timer.ts +0 -0
  55. package/src/timespan.ts +10 -15
  56. package/src/triggers.ts +10 -24
  57. package/src/type-code.ts +0 -0
  58. package/src/type.ts +0 -0
  59. package/src/types.ts +0 -0
  60. 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.parseOrThrow}.
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
- * @returns The UUID, or `null` when the value is not one.
63
+ * @throws {SyntaxError} If the value is not a UUID.
64
64
  */
65
65
  static parse(value) {
66
- if (typeof value !== 'string' || !Uuid.isValid(value)) {
67
- return null;
66
+ const uuid = Uuid.tryParse(value);
67
+ if (uuid === undefined) {
68
+ throw new SyntaxError(`Invalid UUID "${value}".`);
68
69
  }
69
- return new Uuid(value.toLowerCase());
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
- * @throws {SyntaxError} If the value is not a UUID.
75
+ * @returns The UUID, or `undefined` when the value is not one.
75
76
  */
76
- static parseOrThrow(value) {
77
- const uuid = Uuid.parse(value);
78
- if (uuid === null) {
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 uuid;
81
+ return new Uuid(value.toLowerCase());
82
82
  }
83
83
  /**
84
- * Returns the RFC 9562 version, from 1 to 8, or 0 for the Nil UUID and 15 for the Max UUID.
84
+ * The RFC 9562 version, from 1 to 8, or 0 for the Nil UUID and 15 for the Max UUID.
85
85
  */
86
- getVersion() {
86
+ get version() {
87
87
  return Number.parseInt(this.value.charAt(14), 16);
88
88
  }
89
89
  /**
90
- * Returns the moment a version 7 UUID encodes, with millisecond precision.
90
+ * The moment a version 7 UUID encodes, with millisecond precision.
91
91
  *
92
- * @returns The moment, or `null` for other versions.
92
+ * It is `null` for other versions.
93
93
  */
94
- getDate() {
95
- if (this.getVersion() !== 7) {
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":"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","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.parseOrThrow}.\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 * @returns The UUID, or `null` when the value is not one.\n */\n static parse(value: unknown): Nullable<Uuid> {\n if (typeof value !== 'string' || !Uuid.isValid(value)) {\n return null;\n }\n\n return new Uuid(value.toLowerCase());\n }\n\n /**\n * Reads a UUID in canonical form, in any letter case.\n *\n * @throws {SyntaxError} If the value is not a UUID.\n */\n static parseOrThrow(value: string): Uuid {\n const uuid = Uuid.parse(value);\n if (uuid === null) {\n throw new SyntaxError(`Invalid UUID \"${value}\".`);\n }\n\n return uuid;\n }\n\n /**\n * Returns the RFC 9562 version, from 1 to 8, or 0 for the Nil UUID and 15 for the Max UUID.\n */\n getVersion(): number {\n return Number.parseInt(this.value.charAt(14), 16);\n }\n\n /**\n * Returns the moment a version 7 UUID encodes, with millisecond precision.\n *\n * @returns The moment, or `null` for other versions.\n */\n getDate(): Nullable<Date> {\n if (this.getVersion() !== 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"]}
1
+ {"version":3,"file":"uuid.js","sourceRoot":"","sources":["../src/uuid.ts"],"names":[],"mappings":"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","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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@develia/commons",
3
- "version": "0.5.0",
3
+ "version": "0.6.1",
4
4
  "author": {
5
5
  "name": "Antonio Gil Espinosa",
6
6
  "email": "antonio.gil.espinosa@gmail.com",
package/src/array.ts CHANGED
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
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
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 `null` if the value cannot be parsed.
83
+ * Returns `undefined` if the value cannot be parsed.
84
84
  *
85
- * - `null` or `undefined` → `null`
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 `null` on failure.
91
+ * @returns The parsed integer, or `undefined` on failure.
92
92
  *
93
93
  * @example
94
94
  * ```ts
95
- * parseInteger(42) // 42
96
- * parseInteger(3.7) // 3
97
- * parseInteger(null) // null
98
- * parseInteger(undefined) // null
99
- * parseInteger("42") // 42
100
- * parseInteger("1.234.567", { thousandsSeparator: "." }) // 1234567
101
- * parseInteger("FF", { radix: 16 }) // 255
102
- * parseInteger("abc") // null
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 parseInteger(
105
+ export function tryParseInteger(
106
106
  value: string | number | null | undefined,
107
107
  options: ParseIntegerOptions = {}
108
- ): number | null {
109
- if (value === null || value === undefined) return null;
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) : null;
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 null;
120
- if (!isValidInteger(cleaned, radix)) return null;
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) ? null : 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 `null` if the value cannot be parsed.
147
+ * Returns `undefined` if the value cannot be parsed.
134
148
  *
135
- * - `null` or `undefined` → `null`
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 `null` on failure.
158
+ * @returns The parsed float, or `undefined` on failure.
145
159
  *
146
160
  * @example
147
161
  * ```ts
148
- * parseDecimal(3.14) // 3.14
149
- * parseDecimal(null) // null
150
- * parseDecimal(undefined) // null
151
- * parseDecimal("3.14") // 3.14
152
- * parseDecimal("3,14", { decimalSeparator: "," }) // 3.14
153
- * parseDecimal("1.234,56", { decimalSeparator: ",", thousandsSeparator: "." }) // 1234.56
154
- * parseDecimal("abc") // null
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 parseDecimal(
171
+ export function tryParseDecimal(
158
172
  value: string | number | null | undefined,
159
173
  options: ParseDecimalOptions = {}
160
- ): number | null {
161
- if (value === null || value === undefined) return null;
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 : null;
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 null;
185
+ if (cleaned === "") return undefined;
172
186
 
173
187
  if (decimalSeparator === ",") {
174
188
  const commaCount = (cleaned.match(/,/g) || []).length;
175
- if (commaCount > 1) return null;
189
+ if (commaCount > 1) return undefined;
176
190
  cleaned = cleaned.replace(",", ".");
177
191
  }
178
192
 
179
- if (!/^[+-]?\d+(\.\d+)?$/.test(cleaned)) return null;
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) ? null : 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 `null` if the value cannot be interpreted as boolean.
223
+ * Returns `undefined` if the value cannot be interpreted as boolean.
196
224
  *
197
- * - `null` or `undefined` → `null`
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 `null` on failure.
235
+ * @returns The parsed boolean, or `undefined` on failure.
208
236
  *
209
237
  * @example
210
238
  * ```ts
211
- * parseBoolean(true) // true
212
- * parseBoolean(false) // false
213
- * parseBoolean(1) // true
214
- * parseBoolean(0) // false
215
- * parseBoolean(null) // null
216
- * parseBoolean(undefined) // null
217
- * parseBoolean("yes") // true
218
- * parseBoolean("no") // false
219
- * parseBoolean("sí") // true
220
- * parseBoolean("TRUE") // true
221
- * parseBoolean("oui") // null
222
- * parseBoolean("oui", { extraTruthy: ["oui"] }) // true
223
- * parseBoolean("nein", { extraFalsy: ["nein"] }) // false
224
- * parseBoolean("whatever") // null
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 parseBoolean(
255
+ export function tryParseBoolean(
228
256
  value: string | number | boolean | null | undefined,
229
257
  options: ParseBooleanOptions = {}
230
- ): boolean | null {
231
- if (value === null || value === undefined) return null;
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 : null;
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 null;
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 null;
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
- private readonly start: T;
4
- private readonly end: T;
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 elapsedTime: number | null = null;
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.getElapsedTime();
14
+ measure.value = stopwatch.elapsedTime;
15
15
  }
16
16
  }*/
17
17
 
18
18
  start(): void {
19
19
  this.startTime = performance.now();
20
- this.elapsedTime = null;
20
+ this.elapsedMilliseconds = null;
21
21
  }
22
22
 
23
23
  stop(): void {
24
24
  if (this.startTime !== null) {
25
- this.elapsedTime = performance.now() - this.startTime;
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.elapsedTime !== null) {
32
- this.startTime = performance.now() - this.elapsedTime;
33
- this.elapsedTime = null;
31
+ if (this.elapsedMilliseconds !== null) {
32
+ this.startTime = performance.now() - this.elapsedMilliseconds;
33
+ this.elapsedMilliseconds = null;
34
34
  }
35
35
  }
36
36
 
37
- getElapsedTime(): TimeSpan {
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.elapsedTime ?? 0.0);
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
- * Gets the time interval in seconds.
14
- * @returns {number} The duration in seconds.
13
+ * The whole duration in seconds.
15
14
  */
16
- getTotalSeconds(): number {
15
+ get totalSeconds(): number {
17
16
  return this.milliseconds / 1000;
18
17
  }
19
18
 
20
19
  /**
21
- * Gets the time interval in minutes.
22
- * @returns {number} The duration in minutes.
20
+ * The whole duration in minutes.
23
21
  */
24
- getTotalMinutes(): number {
22
+ get totalMinutes(): number {
25
23
  return this.milliseconds / (1000 * 60);
26
24
  }
27
25
 
28
26
  /**
29
- * Gets the time interval in hours.
30
- * @returns {number} The duration in hours.
27
+ * The whole duration in hours.
31
28
  */
32
- getTotalHours(): number {
29
+ get totalHours(): number {
33
30
  return this.milliseconds / (1000 * 60 * 60);
34
31
  }
35
32
 
36
33
  /**
37
- * Gets the time interval in days.
38
- * @returns {number} The duration in days.
34
+ * The whole duration in days.
39
35
  */
40
- getTotalDays(): number {
36
+ get totalDays(): number {
41
37
  return this.milliseconds / (1000 * 60 * 60 * 24);
42
38
  }
43
39
 
44
40
  /**
45
- * Gets the time interval in weeks.
46
- * @returns {number} The duration in weeks.
41
+ * The whole duration in weeks.
47
42
  */
48
- getTotalWeeks(): number {
43
+ get totalWeeks(): number {
49
44
  return this.milliseconds / (1000 * 60 * 60 * 24 * 7);
50
45
  }
51
46