@forthtilliath/ts-kit 0.1.0 → 0.3.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/README.md +83 -19
- package/dist/async/debounce.d.ts +20 -0
- package/dist/async/debounce.d.ts.map +1 -0
- package/dist/async/debounce.js +44 -0
- package/dist/async/index.d.ts +7 -0
- package/dist/async/index.d.ts.map +1 -0
- package/dist/async/index.js +6 -0
- package/dist/async/memoize.d.ts +19 -0
- package/dist/async/memoize.d.ts.map +1 -0
- package/dist/async/memoize.js +25 -0
- package/dist/async/retry.d.ts +25 -0
- package/dist/async/retry.d.ts.map +1 -0
- package/dist/async/retry.js +33 -0
- package/dist/async/sleep.d.ts +9 -0
- package/dist/async/sleep.d.ts.map +1 -0
- package/dist/async/sleep.js +10 -0
- package/dist/async/throttle.d.ts +20 -0
- package/dist/async/throttle.d.ts.map +1 -0
- package/dist/async/throttle.js +43 -0
- package/dist/async/withTimeout.d.ts +19 -0
- package/dist/async/withTimeout.d.ts.map +1 -0
- package/dist/async/withTimeout.js +34 -0
- package/dist/classes/array.d.ts +88 -0
- package/dist/classes/array.d.ts.map +1 -1
- package/dist/classes/array.js +74 -0
- package/dist/classes/index.d.ts +2 -0
- package/dist/classes/index.d.ts.map +1 -0
- package/dist/classes/index.js +1 -0
- package/dist/date/formatRelativeTime.d.ts +13 -0
- package/dist/date/formatRelativeTime.d.ts.map +1 -0
- package/dist/date/formatRelativeTime.js +30 -0
- package/dist/date/index.d.ts +4 -0
- package/dist/date/index.d.ts.map +1 -0
- package/dist/date/index.js +3 -0
- package/dist/date/isSameDay.d.ts +11 -0
- package/dist/date/isSameDay.d.ts.map +1 -0
- package/dist/date/isSameDay.js +14 -0
- package/dist/date/startOfDay.d.ts +10 -0
- package/dist/date/startOfDay.d.ts.map +1 -0
- package/dist/date/startOfDay.js +13 -0
- package/dist/files/downloadText.d.ts +2 -1
- package/dist/files/downloadText.d.ts.map +1 -1
- package/dist/files/downloadText.js +3 -2
- package/dist/files/index.d.ts +2 -0
- package/dist/files/index.d.ts.map +1 -0
- package/dist/files/index.js +1 -0
- package/dist/id/index.d.ts +2 -0
- package/dist/id/index.d.ts.map +1 -0
- package/dist/id/index.js +1 -0
- package/dist/id/randomId.d.ts +10 -0
- package/dist/id/randomId.d.ts.map +1 -0
- package/dist/id/randomId.js +33 -0
- package/dist/index.d.ts +10 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +15 -1
- package/dist/number/clamp.d.ts +13 -0
- package/dist/number/clamp.d.ts.map +1 -0
- package/dist/number/clamp.js +14 -0
- package/dist/number/formatBytes.d.ts +12 -0
- package/dist/number/formatBytes.d.ts.map +1 -0
- package/dist/number/formatBytes.js +19 -0
- package/dist/number/formatDuration.d.ts +12 -0
- package/dist/number/formatDuration.d.ts.map +1 -0
- package/dist/number/formatDuration.js +29 -0
- package/dist/number/index.d.ts +5 -0
- package/dist/number/index.d.ts.map +1 -0
- package/dist/number/index.js +4 -0
- package/dist/number/round.d.ts +12 -0
- package/dist/number/round.d.ts.map +1 -0
- package/dist/number/round.js +14 -0
- package/dist/object/deepClone.d.ts +12 -0
- package/dist/object/deepClone.d.ts.map +1 -0
- package/dist/object/deepClone.js +16 -0
- package/dist/object/deepEqual.d.ts +12 -0
- package/dist/object/deepEqual.d.ts.map +1 -0
- package/dist/object/deepEqual.js +31 -0
- package/dist/object/groupBy.d.ts +12 -0
- package/dist/object/groupBy.d.ts.map +1 -0
- package/dist/object/groupBy.js +18 -0
- package/dist/object/index.d.ts +8 -0
- package/dist/object/index.d.ts.map +1 -0
- package/dist/object/index.js +7 -0
- package/dist/object/mapValues.d.ts +11 -0
- package/dist/object/mapValues.d.ts.map +1 -0
- package/dist/object/mapValues.js +16 -0
- package/dist/object/omit.d.ts +11 -0
- package/dist/object/omit.d.ts.map +1 -0
- package/dist/object/omit.js +16 -0
- package/dist/object/pick.d.ts +11 -0
- package/dist/object/pick.d.ts.map +1 -0
- package/dist/object/pick.js +18 -0
- package/dist/object/uniqueBy.d.ts +13 -0
- package/dist/object/uniqueBy.d.ts.map +1 -0
- package/dist/object/uniqueBy.js +23 -0
- package/dist/string/capitalize.d.ts +11 -0
- package/dist/string/capitalize.d.ts.map +1 -0
- package/dist/string/capitalize.js +14 -0
- package/dist/string/index.d.ts +5 -0
- package/dist/string/index.d.ts.map +1 -0
- package/dist/string/index.js +4 -0
- package/dist/string/pluralize.d.ts +17 -0
- package/dist/string/pluralize.d.ts.map +1 -0
- package/dist/string/pluralize.js +26 -0
- package/dist/string/slugify.d.ts +12 -0
- package/dist/string/slugify.d.ts.map +1 -0
- package/dist/string/slugify.js +18 -0
- package/dist/string/truncate.d.ts +13 -0
- package/dist/string/truncate.d.ts.map +1 -0
- package/dist/string/truncate.js +17 -0
- package/package.json +3 -2
package/dist/classes/array.js
CHANGED
|
@@ -52,4 +52,78 @@ export class FArray extends Array {
|
|
|
52
52
|
// @ts-expect-error searchElement is unknown
|
|
53
53
|
return super.includes(searchElement, fromIndex);
|
|
54
54
|
}
|
|
55
|
+
/**
|
|
56
|
+
* Same as `Array.indexOf`, but accepts a search value outside of `T`
|
|
57
|
+
* instead of rejecting it at compile-time (the native typings only accept
|
|
58
|
+
* `T`, exactly like `Array.includes` before the override above).
|
|
59
|
+
*
|
|
60
|
+
* @param searchElement The value to search for — no longer restricted to `T`.
|
|
61
|
+
* @param fromIndex The position in this array at which to begin searching for searchElement.
|
|
62
|
+
* @returns The index of the first match, or -1 if not found.
|
|
63
|
+
*
|
|
64
|
+
* @example
|
|
65
|
+
* // Normal operation of Array.indexOf
|
|
66
|
+
* const buz = "buz";
|
|
67
|
+
* type FBB = "foo" | "bar" | "baz";
|
|
68
|
+
* const arr: FBB[] = ["foo", "bar", "baz"];
|
|
69
|
+
* arr.indexOf(buz);
|
|
70
|
+
* // -----------^
|
|
71
|
+
* // The argument of type 'buz' is not assignable to the parameter of type 'FBB'.
|
|
72
|
+
*
|
|
73
|
+
* @example
|
|
74
|
+
* // FArray.indexOf
|
|
75
|
+
* const buz = "buz";
|
|
76
|
+
* type FBB = "foo" | "bar" | "baz";
|
|
77
|
+
* const arr = new FArray<FBB>("foo", "bar", "baz");
|
|
78
|
+
* arr.indexOf(buz); // compiles fine, returns -1
|
|
79
|
+
*/
|
|
80
|
+
indexOf(searchElement, fromIndex = 0) {
|
|
81
|
+
// @ts-expect-error searchElement is unknown
|
|
82
|
+
return super.indexOf(searchElement, fromIndex);
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Same as `Array.lastIndexOf`, but accepts a search value outside of `T`
|
|
86
|
+
* instead of rejecting it at compile-time. See {@link FArray.indexOf}.
|
|
87
|
+
*
|
|
88
|
+
* @param searchElement The value to search for — no longer restricted to `T`.
|
|
89
|
+
* @param fromIndex The position in this array at which to start searching backwards. Defaults to the last index, like the native method.
|
|
90
|
+
* @returns The index of the last match, or -1 if not found.
|
|
91
|
+
*/
|
|
92
|
+
lastIndexOf(searchElement, fromIndex) {
|
|
93
|
+
if (fromIndex === undefined) {
|
|
94
|
+
// @ts-expect-error searchElement is unknown
|
|
95
|
+
return super.lastIndexOf(searchElement);
|
|
96
|
+
}
|
|
97
|
+
// @ts-expect-error searchElement is unknown
|
|
98
|
+
return super.lastIndexOf(searchElement, fromIndex);
|
|
99
|
+
}
|
|
100
|
+
filter(predicate, thisArg) {
|
|
101
|
+
return super.filter(predicate, thisArg);
|
|
102
|
+
}
|
|
103
|
+
static from(iterable, mapfn, thisArg) {
|
|
104
|
+
return (mapfn ? super.from(iterable, mapfn, thisArg) : super.from(iterable));
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Same as `Array.of`, but returns an `FArray`. See {@link FArray.from}.
|
|
108
|
+
*
|
|
109
|
+
* @example
|
|
110
|
+
* const fa = FArray.of(1, 2, 3); // FArray<number>, not number[]
|
|
111
|
+
*/
|
|
112
|
+
static of(...items) {
|
|
113
|
+
return super.of(...items);
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Converts this `FArray` back into a plain `Array` instance — the
|
|
117
|
+
* reverse of {@link FArray.from}/{@link FArray.of}. `FArray` is already
|
|
118
|
+
* assignable to `T[]` anywhere a plain array is expected (it extends
|
|
119
|
+
* `Array`), so this is mostly useful when the runtime type matters too
|
|
120
|
+
* (e.g. a library that special-cases `Array` and rejects subclasses).
|
|
121
|
+
*
|
|
122
|
+
* @example
|
|
123
|
+
* const fa = new FArray(1, 2, 3);
|
|
124
|
+
* const plain = fa.toArray(); // Array(3) [1, 2, 3], not instanceof FArray
|
|
125
|
+
*/
|
|
126
|
+
toArray() {
|
|
127
|
+
return [...this];
|
|
128
|
+
}
|
|
55
129
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/classes/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./array.js";
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Formats the difference between two dates as a human-readable relative
|
|
3
|
+
* string ("3 hours ago", "in 2 days"), backed by `Intl.RelativeTimeFormat`.
|
|
4
|
+
*
|
|
5
|
+
* @param date - The date to describe.
|
|
6
|
+
* @param baseDate - The reference date to compare against (defaults to now).
|
|
7
|
+
* @param locale - The locale to format with (defaults to `"en"`).
|
|
8
|
+
* @returns The formatted relative time.
|
|
9
|
+
* @example
|
|
10
|
+
* formatRelativeTime(new Date(Date.now() - 3_600_000)); // => "1 hour ago"
|
|
11
|
+
*/
|
|
12
|
+
export declare function formatRelativeTime(date: Date, baseDate?: Date, locale?: string): string;
|
|
13
|
+
//# sourceMappingURL=formatRelativeTime.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"formatRelativeTime.d.ts","sourceRoot":"","sources":["../../src/date/formatRelativeTime.ts"],"names":[],"mappings":"AAUA;;;;;;;;;;GAUG;AACH,wBAAgB,kBAAkB,CAChC,IAAI,EAAE,IAAI,EACV,QAAQ,GAAE,IAAiB,EAC3B,MAAM,SAAO,GACZ,MAAM,CAWR"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
const UNITS = [
|
|
2
|
+
{ unit: "year", ms: 365 * 24 * 60 * 60 * 1000 },
|
|
3
|
+
{ unit: "month", ms: 30 * 24 * 60 * 60 * 1000 },
|
|
4
|
+
{ unit: "week", ms: 7 * 24 * 60 * 60 * 1000 },
|
|
5
|
+
{ unit: "day", ms: 24 * 60 * 60 * 1000 },
|
|
6
|
+
{ unit: "hour", ms: 60 * 60 * 1000 },
|
|
7
|
+
{ unit: "minute", ms: 60 * 1000 },
|
|
8
|
+
{ unit: "second", ms: 1000 },
|
|
9
|
+
];
|
|
10
|
+
/**
|
|
11
|
+
* Formats the difference between two dates as a human-readable relative
|
|
12
|
+
* string ("3 hours ago", "in 2 days"), backed by `Intl.RelativeTimeFormat`.
|
|
13
|
+
*
|
|
14
|
+
* @param date - The date to describe.
|
|
15
|
+
* @param baseDate - The reference date to compare against (defaults to now).
|
|
16
|
+
* @param locale - The locale to format with (defaults to `"en"`).
|
|
17
|
+
* @returns The formatted relative time.
|
|
18
|
+
* @example
|
|
19
|
+
* formatRelativeTime(new Date(Date.now() - 3_600_000)); // => "1 hour ago"
|
|
20
|
+
*/
|
|
21
|
+
export function formatRelativeTime(date, baseDate = new Date(), locale = "en") {
|
|
22
|
+
const diffMs = date.getTime() - baseDate.getTime();
|
|
23
|
+
const rtf = new Intl.RelativeTimeFormat(locale, { numeric: "auto" });
|
|
24
|
+
for (const { unit, ms } of UNITS) {
|
|
25
|
+
if (Math.abs(diffMs) >= ms || unit === "second") {
|
|
26
|
+
return rtf.format(Math.round(diffMs / ms), unit);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
return rtf.format(0, "second");
|
|
30
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/date/index.ts"],"names":[],"mappings":"AAAA,cAAc,yBAAyB,CAAC;AACxC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,iBAAiB,CAAC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Checks whether two dates fall on the same local calendar day.
|
|
3
|
+
*
|
|
4
|
+
* @param a - The first date.
|
|
5
|
+
* @param b - The second date.
|
|
6
|
+
* @returns `true` if both dates share the same year, month and day.
|
|
7
|
+
* @example
|
|
8
|
+
* isSameDay(new Date("2026-03-14T08:00"), new Date("2026-03-14T22:00")); // => true
|
|
9
|
+
*/
|
|
10
|
+
export declare function isSameDay(a: Date, b: Date): boolean;
|
|
11
|
+
//# sourceMappingURL=isSameDay.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"isSameDay.d.ts","sourceRoot":"","sources":["../../src/date/isSameDay.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,wBAAgB,SAAS,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,IAAI,GAAG,OAAO,CAMnD"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Checks whether two dates fall on the same local calendar day.
|
|
3
|
+
*
|
|
4
|
+
* @param a - The first date.
|
|
5
|
+
* @param b - The second date.
|
|
6
|
+
* @returns `true` if both dates share the same year, month and day.
|
|
7
|
+
* @example
|
|
8
|
+
* isSameDay(new Date("2026-03-14T08:00"), new Date("2026-03-14T22:00")); // => true
|
|
9
|
+
*/
|
|
10
|
+
export function isSameDay(a, b) {
|
|
11
|
+
return (a.getFullYear() === b.getFullYear() &&
|
|
12
|
+
a.getMonth() === b.getMonth() &&
|
|
13
|
+
a.getDate() === b.getDate());
|
|
14
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Returns a new `Date` set to midnight (00:00:00.000) of the same local day.
|
|
3
|
+
*
|
|
4
|
+
* @param date - The reference date (defaults to now).
|
|
5
|
+
* @returns A new `Date` at the start of that day.
|
|
6
|
+
* @example
|
|
7
|
+
* startOfDay(new Date("2026-03-14T18:30:00")); // => 2026-03-14T00:00:00.000
|
|
8
|
+
*/
|
|
9
|
+
export declare function startOfDay(date?: Date): Date;
|
|
10
|
+
//# sourceMappingURL=startOfDay.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"startOfDay.d.ts","sourceRoot":"","sources":["../../src/date/startOfDay.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,wBAAgB,UAAU,CAAC,IAAI,GAAE,IAAiB,GAAG,IAAI,CAIxD"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Returns a new `Date` set to midnight (00:00:00.000) of the same local day.
|
|
3
|
+
*
|
|
4
|
+
* @param date - The reference date (defaults to now).
|
|
5
|
+
* @returns A new `Date` at the start of that day.
|
|
6
|
+
* @example
|
|
7
|
+
* startOfDay(new Date("2026-03-14T18:30:00")); // => 2026-03-14T00:00:00.000
|
|
8
|
+
*/
|
|
9
|
+
export function startOfDay(date = new Date()) {
|
|
10
|
+
const result = new Date(date);
|
|
11
|
+
result.setHours(0, 0, 0, 0);
|
|
12
|
+
return result;
|
|
13
|
+
}
|
|
@@ -8,6 +8,7 @@ export declare function downloadText(filename: string, text: string): void;
|
|
|
8
8
|
* Downloads a text blob as a file.
|
|
9
9
|
* @param filename The filename to save to.
|
|
10
10
|
* @param text The text to save.
|
|
11
|
+
* @param mimeType The MIME type of the blob (defaults to `"text/plain"`).
|
|
11
12
|
*/
|
|
12
|
-
export declare function downloadTextBlob(filename: string, text: string): void;
|
|
13
|
+
export declare function downloadTextBlob(filename: string, text: string, mimeType?: string): void;
|
|
13
14
|
//# sourceMappingURL=downloadText.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"downloadText.d.ts","sourceRoot":"","sources":["../../src/files/downloadText.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAcjE;AAED
|
|
1
|
+
{"version":3,"file":"downloadText.d.ts","sourceRoot":"","sources":["../../src/files/downloadText.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAcjE;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAC9B,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,MAAM,EACZ,QAAQ,SAAe,GACtB,IAAI,CAcN"}
|
|
@@ -16,9 +16,10 @@ export function downloadText(filename, text) {
|
|
|
16
16
|
* Downloads a text blob as a file.
|
|
17
17
|
* @param filename The filename to save to.
|
|
18
18
|
* @param text The text to save.
|
|
19
|
+
* @param mimeType The MIME type of the blob (defaults to `"text/plain"`).
|
|
19
20
|
*/
|
|
20
|
-
export function downloadTextBlob(filename, text) {
|
|
21
|
-
const url = URL.createObjectURL(new Blob([text], { type:
|
|
21
|
+
export function downloadTextBlob(filename, text, mimeType = "text/plain") {
|
|
22
|
+
const url = URL.createObjectURL(new Blob([text], { type: `${mimeType};charset=utf-8` }));
|
|
22
23
|
const a = document.createElement("a");
|
|
23
24
|
a.setAttribute("download", filename);
|
|
24
25
|
a.setAttribute("href", url);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/files/index.ts"],"names":[],"mappings":"AAAA,cAAc,mBAAmB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./downloadText.js";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/id/index.ts"],"names":[],"mappings":"AAAA,cAAc,eAAe,CAAC"}
|
package/dist/id/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./randomId.js";
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `crypto.randomUUID()` with a fallback. That API only exists in a secure
|
|
3
|
+
* context (HTTPS or `localhost`) and is missing on a few older browsers. The
|
|
4
|
+
* fallbacks are not cryptographically secure but are good enough for local
|
|
5
|
+
* identifiers.
|
|
6
|
+
*
|
|
7
|
+
* @returns {string} A UUID-v4-shaped string, or a timestamp-based id as a last resort.
|
|
8
|
+
*/
|
|
9
|
+
export declare function randomId(): string;
|
|
10
|
+
//# sourceMappingURL=randomId.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"randomId.d.ts","sourceRoot":"","sources":["../../src/id/randomId.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,wBAAgB,QAAQ,IAAI,MAAM,CA6BjC"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `crypto.randomUUID()` with a fallback. That API only exists in a secure
|
|
3
|
+
* context (HTTPS or `localhost`) and is missing on a few older browsers. The
|
|
4
|
+
* fallbacks are not cryptographically secure but are good enough for local
|
|
5
|
+
* identifiers.
|
|
6
|
+
*
|
|
7
|
+
* @returns {string} A UUID-v4-shaped string, or a timestamp-based id as a last resort.
|
|
8
|
+
*/
|
|
9
|
+
export function randomId() {
|
|
10
|
+
// `globalThis.crypto` is typed as always present by lib.dom, but it can be
|
|
11
|
+
// missing at runtime (older browser, insecure context) — cast it away so
|
|
12
|
+
// the checks below are not flagged as unnecessary.
|
|
13
|
+
const c = globalThis.crypto;
|
|
14
|
+
if (typeof c?.randomUUID === "function") {
|
|
15
|
+
return c.randomUUID();
|
|
16
|
+
}
|
|
17
|
+
if (typeof c?.getRandomValues === "function") {
|
|
18
|
+
const bytes = c.getRandomValues(new Uint8Array(16));
|
|
19
|
+
bytes[6] = ((bytes[6] ?? 0) & 0x0f) | 0x40; // version 4
|
|
20
|
+
bytes[8] = ((bytes[8] ?? 0) & 0x3f) | 0x80; // variant
|
|
21
|
+
const hex = Array.from(bytes, (b) => b.toString(16).padStart(2, "0"));
|
|
22
|
+
return (hex.slice(0, 4).join("") +
|
|
23
|
+
"-" +
|
|
24
|
+
hex.slice(4, 6).join("") +
|
|
25
|
+
"-" +
|
|
26
|
+
hex.slice(6, 8).join("") +
|
|
27
|
+
"-" +
|
|
28
|
+
hex.slice(8, 10).join("") +
|
|
29
|
+
"-" +
|
|
30
|
+
hex.slice(10, 16).join(""));
|
|
31
|
+
}
|
|
32
|
+
return `id-${Date.now().toString(16)}-${Math.random().toString(16).slice(2, 10)}`;
|
|
33
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,2 +1,11 @@
|
|
|
1
|
-
export
|
|
1
|
+
export * from "./array/index.js";
|
|
2
|
+
export * from "./async/index.js";
|
|
3
|
+
export * from "./classes/index.js";
|
|
4
|
+
export * from "./date/index.js";
|
|
5
|
+
export * from "./files/index.js";
|
|
6
|
+
export * from "./id/index.js";
|
|
7
|
+
export * from "./maths/index.js";
|
|
8
|
+
export * from "./number/index.js";
|
|
9
|
+
export * from "./object/index.js";
|
|
10
|
+
export * from "./string/index.js";
|
|
2
11
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAKA,cAAc,kBAAkB,CAAC;AACjC,cAAc,kBAAkB,CAAC;AACjC,cAAc,oBAAoB,CAAC;AACnC,cAAc,iBAAiB,CAAC;AAChC,cAAc,kBAAkB,CAAC;AACjC,cAAc,eAAe,CAAC;AAC9B,cAAc,kBAAkB,CAAC;AACjC,cAAc,mBAAmB,CAAC;AAClC,cAAc,mBAAmB,CAAC;AAClC,cAAc,mBAAmB,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1 +1,15 @@
|
|
|
1
|
-
|
|
1
|
+
// Barrel: re-exports every category so consumers can do a single
|
|
2
|
+
// import { chunk, pick, sleep, clamp } from "@forthtilliath/ts-kit";
|
|
3
|
+
// instead of one import line per module. Subpath imports
|
|
4
|
+
// (`@forthtilliath/ts-kit/array/chunk`) still work if you want to keep
|
|
5
|
+
// bundles minimal, but this is the default, low-friction entry point.
|
|
6
|
+
export * from "./array/index.js";
|
|
7
|
+
export * from "./async/index.js";
|
|
8
|
+
export * from "./classes/index.js";
|
|
9
|
+
export * from "./date/index.js";
|
|
10
|
+
export * from "./files/index.js";
|
|
11
|
+
export * from "./id/index.js";
|
|
12
|
+
export * from "./maths/index.js";
|
|
13
|
+
export * from "./number/index.js";
|
|
14
|
+
export * from "./object/index.js";
|
|
15
|
+
export * from "./string/index.js";
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Clamps a number between a minimum and maximum value.
|
|
3
|
+
*
|
|
4
|
+
* @param value - The value to clamp.
|
|
5
|
+
* @param min - The lower bound.
|
|
6
|
+
* @param max - The upper bound.
|
|
7
|
+
* @returns `value`, or the nearest bound if it falls outside `[min, max]`.
|
|
8
|
+
* @example
|
|
9
|
+
* clamp(15, 0, 10); // => 10
|
|
10
|
+
* clamp(-5, 0, 10); // => 0
|
|
11
|
+
*/
|
|
12
|
+
export declare function clamp(value: number, min: number, max: number): number;
|
|
13
|
+
//# sourceMappingURL=clamp.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"clamp.d.ts","sourceRoot":"","sources":["../../src/number/clamp.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,wBAAgB,KAAK,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAErE"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Clamps a number between a minimum and maximum value.
|
|
3
|
+
*
|
|
4
|
+
* @param value - The value to clamp.
|
|
5
|
+
* @param min - The lower bound.
|
|
6
|
+
* @param max - The upper bound.
|
|
7
|
+
* @returns `value`, or the nearest bound if it falls outside `[min, max]`.
|
|
8
|
+
* @example
|
|
9
|
+
* clamp(15, 0, 10); // => 10
|
|
10
|
+
* clamp(-5, 0, 10); // => 0
|
|
11
|
+
*/
|
|
12
|
+
export function clamp(value, min, max) {
|
|
13
|
+
return Math.min(Math.max(value, min), max);
|
|
14
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Formats a byte count as a human-readable string (`"1.5 MB"`).
|
|
3
|
+
*
|
|
4
|
+
* @param bytes - The number of bytes.
|
|
5
|
+
* @param decimals - The number of decimal places to keep (default: 2).
|
|
6
|
+
* @returns The formatted string.
|
|
7
|
+
* @example
|
|
8
|
+
* formatBytes(1536); // => "1.5 KB"
|
|
9
|
+
* formatBytes(0); // => "0 B"
|
|
10
|
+
*/
|
|
11
|
+
export declare function formatBytes(bytes: number, decimals?: number): string;
|
|
12
|
+
//# sourceMappingURL=formatBytes.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"formatBytes.d.ts","sourceRoot":"","sources":["../../src/number/formatBytes.ts"],"names":[],"mappings":"AAEA;;;;;;;;;GASG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,SAAI,GAAG,MAAM,CAW/D"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
const UNITS = ["B", "KB", "MB", "GB", "TB", "PB"];
|
|
2
|
+
/**
|
|
3
|
+
* Formats a byte count as a human-readable string (`"1.5 MB"`).
|
|
4
|
+
*
|
|
5
|
+
* @param bytes - The number of bytes.
|
|
6
|
+
* @param decimals - The number of decimal places to keep (default: 2).
|
|
7
|
+
* @returns The formatted string.
|
|
8
|
+
* @example
|
|
9
|
+
* formatBytes(1536); // => "1.5 KB"
|
|
10
|
+
* formatBytes(0); // => "0 B"
|
|
11
|
+
*/
|
|
12
|
+
export function formatBytes(bytes, decimals = 2) {
|
|
13
|
+
if (bytes === 0)
|
|
14
|
+
return "0 B";
|
|
15
|
+
const exponent = Math.min(Math.floor(Math.log(Math.abs(bytes)) / Math.log(1024)), UNITS.length - 1);
|
|
16
|
+
const value = bytes / 1024 ** exponent;
|
|
17
|
+
const formatted = value.toFixed(Math.max(decimals, 0)).replace(/\.?0+$/, "");
|
|
18
|
+
return `${formatted} ${UNITS[exponent]}`;
|
|
19
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Formats a duration in milliseconds as a human-readable string
|
|
3
|
+
* (`"1d 1h 1m 1s"`, `"500ms"`).
|
|
4
|
+
*
|
|
5
|
+
* @param ms - The duration in milliseconds.
|
|
6
|
+
* @returns The formatted string.
|
|
7
|
+
* @example
|
|
8
|
+
* formatDuration(90_061_000); // => "1d 1h 1m 1s"
|
|
9
|
+
* formatDuration(500); // => "500ms"
|
|
10
|
+
*/
|
|
11
|
+
export declare function formatDuration(ms: number): string;
|
|
12
|
+
//# sourceMappingURL=formatDuration.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"formatDuration.d.ts","sourceRoot":"","sources":["../../src/number/formatDuration.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,wBAAgB,cAAc,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,CAgBjD"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Formats a duration in milliseconds as a human-readable string
|
|
3
|
+
* (`"1d 1h 1m 1s"`, `"500ms"`).
|
|
4
|
+
*
|
|
5
|
+
* @param ms - The duration in milliseconds.
|
|
6
|
+
* @returns The formatted string.
|
|
7
|
+
* @example
|
|
8
|
+
* formatDuration(90_061_000); // => "1d 1h 1m 1s"
|
|
9
|
+
* formatDuration(500); // => "500ms"
|
|
10
|
+
*/
|
|
11
|
+
export function formatDuration(ms) {
|
|
12
|
+
if (ms < 1000)
|
|
13
|
+
return `${Math.round(ms)}ms`;
|
|
14
|
+
const totalSeconds = Math.floor(ms / 1000);
|
|
15
|
+
const days = Math.floor(totalSeconds / 86400);
|
|
16
|
+
const hours = Math.floor((totalSeconds % 86400) / 3600);
|
|
17
|
+
const minutes = Math.floor((totalSeconds % 3600) / 60);
|
|
18
|
+
const seconds = totalSeconds % 60;
|
|
19
|
+
const parts = [];
|
|
20
|
+
if (days > 0)
|
|
21
|
+
parts.push(`${days}d`);
|
|
22
|
+
if (hours > 0)
|
|
23
|
+
parts.push(`${hours}h`);
|
|
24
|
+
if (minutes > 0)
|
|
25
|
+
parts.push(`${minutes}m`);
|
|
26
|
+
if (seconds > 0 || parts.length === 0)
|
|
27
|
+
parts.push(`${seconds}s`);
|
|
28
|
+
return parts.join(" ");
|
|
29
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/number/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B,cAAc,kBAAkB,CAAC;AACjC,cAAc,qBAAqB,CAAC;AACpC,cAAc,YAAY,CAAC"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Rounds a number to a given number of decimal places.
|
|
3
|
+
*
|
|
4
|
+
* @param value - The number to round.
|
|
5
|
+
* @param decimals - The number of decimal places (default: 0).
|
|
6
|
+
* @returns The rounded number.
|
|
7
|
+
* @example
|
|
8
|
+
* round(1.2345, 2); // => 1.23
|
|
9
|
+
* round(1.005, 2); // => 1.01
|
|
10
|
+
*/
|
|
11
|
+
export declare function round(value: number, decimals?: number): number;
|
|
12
|
+
//# sourceMappingURL=round.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"round.d.ts","sourceRoot":"","sources":["../../src/number/round.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,wBAAgB,KAAK,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,SAAI,GAAG,MAAM,CAGzD"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Rounds a number to a given number of decimal places.
|
|
3
|
+
*
|
|
4
|
+
* @param value - The number to round.
|
|
5
|
+
* @param decimals - The number of decimal places (default: 0).
|
|
6
|
+
* @returns The rounded number.
|
|
7
|
+
* @example
|
|
8
|
+
* round(1.2345, 2); // => 1.23
|
|
9
|
+
* round(1.005, 2); // => 1.01
|
|
10
|
+
*/
|
|
11
|
+
export function round(value, decimals = 0) {
|
|
12
|
+
const factor = 10 ** decimals;
|
|
13
|
+
return Math.round((value + Number.EPSILON) * factor) / factor;
|
|
14
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Deep clones a value.
|
|
3
|
+
*
|
|
4
|
+
* Uses the native `structuredClone` when available (handles `Date`, `Map`,
|
|
5
|
+
* `Set`, circular references, etc.), and falls back to a JSON round-trip
|
|
6
|
+
* otherwise — which loses `undefined` values, functions, and non-JSON types.
|
|
7
|
+
*
|
|
8
|
+
* @param value - The value to clone.
|
|
9
|
+
* @returns A deep copy of `value`.
|
|
10
|
+
*/
|
|
11
|
+
export declare function deepClone<T>(value: T): T;
|
|
12
|
+
//# sourceMappingURL=deepClone.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"deepClone.d.ts","sourceRoot":"","sources":["../../src/object/deepClone.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,wBAAgB,SAAS,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,GAAG,CAAC,CAKxC"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Deep clones a value.
|
|
3
|
+
*
|
|
4
|
+
* Uses the native `structuredClone` when available (handles `Date`, `Map`,
|
|
5
|
+
* `Set`, circular references, etc.), and falls back to a JSON round-trip
|
|
6
|
+
* otherwise — which loses `undefined` values, functions, and non-JSON types.
|
|
7
|
+
*
|
|
8
|
+
* @param value - The value to clone.
|
|
9
|
+
* @returns A deep copy of `value`.
|
|
10
|
+
*/
|
|
11
|
+
export function deepClone(value) {
|
|
12
|
+
if (typeof structuredClone === "function") {
|
|
13
|
+
return structuredClone(value);
|
|
14
|
+
}
|
|
15
|
+
return JSON.parse(JSON.stringify(value));
|
|
16
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Recursively compares two values for structural equality.
|
|
3
|
+
*
|
|
4
|
+
* @param a - The first value.
|
|
5
|
+
* @param b - The second value.
|
|
6
|
+
* @returns `true` if `a` and `b` are deeply equal.
|
|
7
|
+
* @example
|
|
8
|
+
* deepEqual({ a: [1, 2] }, { a: [1, 2] }); // => true
|
|
9
|
+
* deepEqual({ a: 1 }, { a: 2 }); // => false
|
|
10
|
+
*/
|
|
11
|
+
export declare function deepEqual(a: unknown, b: unknown): boolean;
|
|
12
|
+
//# sourceMappingURL=deepEqual.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"deepEqual.d.ts","sourceRoot":"","sources":["../../src/object/deepEqual.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,wBAAgB,SAAS,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,OAAO,GAAG,OAAO,CAwBzD"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Recursively compares two values for structural equality.
|
|
3
|
+
*
|
|
4
|
+
* @param a - The first value.
|
|
5
|
+
* @param b - The second value.
|
|
6
|
+
* @returns `true` if `a` and `b` are deeply equal.
|
|
7
|
+
* @example
|
|
8
|
+
* deepEqual({ a: [1, 2] }, { a: [1, 2] }); // => true
|
|
9
|
+
* deepEqual({ a: 1 }, { a: 2 }); // => false
|
|
10
|
+
*/
|
|
11
|
+
export function deepEqual(a, b) {
|
|
12
|
+
if (Object.is(a, b))
|
|
13
|
+
return true;
|
|
14
|
+
if (typeof a !== typeof b || a === null || b === null)
|
|
15
|
+
return false;
|
|
16
|
+
if (Array.isArray(a) || Array.isArray(b)) {
|
|
17
|
+
if (!Array.isArray(a) || !Array.isArray(b))
|
|
18
|
+
return false;
|
|
19
|
+
if (a.length !== b.length)
|
|
20
|
+
return false;
|
|
21
|
+
return a.every((item, index) => deepEqual(item, b[index]));
|
|
22
|
+
}
|
|
23
|
+
if (typeof a === "object" && typeof b === "object") {
|
|
24
|
+
const aKeys = Object.keys(a);
|
|
25
|
+
const bKeys = Object.keys(b);
|
|
26
|
+
if (aKeys.length !== bKeys.length)
|
|
27
|
+
return false;
|
|
28
|
+
return aKeys.every((key) => deepEqual(a[key], b[key]));
|
|
29
|
+
}
|
|
30
|
+
return false;
|
|
31
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Groups the elements of an array by a key derived from each element.
|
|
3
|
+
*
|
|
4
|
+
* @param array - The array to group.
|
|
5
|
+
* @param getKey - Computes the group key for an element.
|
|
6
|
+
* @returns A record mapping each key to the array of matching elements.
|
|
7
|
+
* @example
|
|
8
|
+
* groupBy([1, 2, 3, 4], (n) => (n % 2 === 0 ? "even" : "odd"));
|
|
9
|
+
* // => { odd: [1, 3], even: [2, 4] }
|
|
10
|
+
*/
|
|
11
|
+
export declare function groupBy<T, K extends PropertyKey>(array: readonly T[], getKey: (item: T) => K): Record<K, T[]>;
|
|
12
|
+
//# sourceMappingURL=groupBy.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"groupBy.d.ts","sourceRoot":"","sources":["../../src/object/groupBy.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,wBAAgB,OAAO,CAAC,CAAC,EAAE,CAAC,SAAS,WAAW,EAC9C,KAAK,EAAE,SAAS,CAAC,EAAE,EACnB,MAAM,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GACrB,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAOhB"}
|