@forthtilliath/ts-kit 0.9.0 → 0.11.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 +80 -22
- package/dist/array/createColumnStorage.d.ts +30 -0
- package/dist/array/createColumnStorage.d.ts.map +1 -0
- package/dist/array/createColumnStorage.js +51 -0
- package/dist/array/flattenDeep.d.ts +7 -5
- package/dist/array/flattenDeep.d.ts.map +1 -1
- package/dist/array/flattenDeep.js +8 -5
- package/dist/array/index.d.ts +1 -0
- package/dist/array/index.d.ts.map +1 -1
- package/dist/array/index.js +1 -0
- package/dist/async/abortReason.d.ts +9 -0
- package/dist/async/abortReason.d.ts.map +1 -0
- package/dist/async/abortReason.js +11 -0
- package/dist/async/memoize.d.ts +2 -18
- package/dist/async/memoize.d.ts.map +1 -1
- package/dist/async/memoize.js +4 -25
- package/dist/async/retry.d.ts +15 -3
- package/dist/async/retry.d.ts.map +1 -1
- package/dist/async/retry.js +15 -11
- package/dist/async/sleep.d.ts +2 -1
- package/dist/async/sleep.d.ts.map +1 -1
- package/dist/async/sleep.js +23 -2
- package/dist/async/withTimeout.d.ts +11 -4
- package/dist/async/withTimeout.d.ts.map +1 -1
- package/dist/async/withTimeout.js +31 -9
- package/dist/csv/escapeCsvField.d.ts +21 -0
- package/dist/csv/escapeCsvField.d.ts.map +1 -0
- package/dist/csv/escapeCsvField.js +28 -0
- package/dist/csv/formatCsvNumber.d.ts +14 -0
- package/dist/csv/formatCsvNumber.d.ts.map +1 -0
- package/dist/csv/formatCsvNumber.js +15 -0
- package/dist/csv/index.d.ts +2 -0
- package/dist/csv/index.d.ts.map +1 -1
- package/dist/csv/index.js +2 -0
- package/dist/date/dateInTimeZone.d.ts +22 -0
- package/dist/date/dateInTimeZone.d.ts.map +1 -0
- package/dist/date/dateInTimeZone.js +35 -0
- package/dist/date/daysUntil.d.ts +15 -0
- package/dist/date/daysUntil.d.ts.map +1 -0
- package/dist/date/daysUntil.js +22 -0
- package/dist/date/index.d.ts +3 -0
- package/dist/date/index.d.ts.map +1 -1
- package/dist/date/index.js +3 -0
- package/dist/date/todayInTimeZone.d.ts +14 -0
- package/dist/date/todayInTimeZone.d.ts.map +1 -0
- package/dist/date/todayInTimeZone.js +15 -0
- package/dist/files/downloadText.d.ts.map +1 -1
- package/dist/files/downloadText.js +9 -1
- package/dist/files/index.d.ts +1 -0
- package/dist/files/index.d.ts.map +1 -1
- package/dist/files/index.js +1 -0
- package/dist/files/sanitizeFileName.d.ts +14 -0
- package/dist/files/sanitizeFileName.d.ts.map +1 -0
- package/dist/files/sanitizeFileName.js +18 -0
- package/dist/function/index.d.ts +2 -0
- package/dist/function/index.d.ts.map +1 -0
- package/dist/function/index.js +1 -0
- package/dist/function/memoize.d.ts +34 -0
- package/dist/function/memoize.d.ts.map +1 -0
- package/dist/function/memoize.js +50 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/maths/average.d.ts +2 -7
- package/dist/maths/average.d.ts.map +1 -1
- package/dist/maths/average.js +4 -10
- package/dist/maths/sum.d.ts +2 -7
- package/dist/maths/sum.d.ts.map +1 -1
- package/dist/maths/sum.js +4 -9
- package/dist/number/average.d.ts +13 -0
- package/dist/number/average.d.ts.map +1 -0
- package/dist/number/average.js +15 -0
- package/dist/number/formatBytes.d.ts.map +1 -1
- package/dist/number/formatBytes.js +6 -2
- package/dist/number/formatCsvNumber.d.ts +2 -13
- package/dist/number/formatCsvNumber.d.ts.map +1 -1
- package/dist/number/formatCsvNumber.js +4 -15
- package/dist/number/index.d.ts +3 -0
- package/dist/number/index.d.ts.map +1 -1
- package/dist/number/index.js +3 -0
- package/dist/number/padNumber.d.ts +15 -0
- package/dist/number/padNumber.d.ts.map +1 -0
- package/dist/number/padNumber.js +16 -0
- package/dist/number/sum.d.ts +10 -0
- package/dist/number/sum.d.ts.map +1 -0
- package/dist/number/sum.js +11 -0
- package/dist/object/deepEqual.d.ts +6 -0
- package/dist/object/deepEqual.d.ts.map +1 -1
- package/dist/object/deepEqual.js +45 -11
- package/dist/string/escapeCsvField.d.ts +2 -13
- package/dist/string/escapeCsvField.d.ts.map +1 -1
- package/dist/string/escapeCsvField.js +4 -17
- package/dist/string/escapeHtml.d.ts +6 -4
- package/dist/string/escapeHtml.d.ts.map +1 -1
- package/dist/string/escapeHtml.js +8 -5
- package/dist/string/pluralize.d.ts +30 -7
- package/dist/string/pluralize.d.ts.map +1 -1
- package/dist/string/pluralize.js +48 -15
- package/dist/version/compareVersions.d.ts +8 -1
- package/dist/version/compareVersions.d.ts.map +1 -1
- package/dist/version/compareVersions.js +63 -9
- package/package.json +7 -6
|
@@ -9,26 +9,48 @@ export class TimeoutError extends Error {
|
|
|
9
9
|
}
|
|
10
10
|
}
|
|
11
11
|
/**
|
|
12
|
-
* Rejects with a {@link TimeoutError} if `
|
|
12
|
+
* Rejects with a {@link TimeoutError} if `operation` has not settled within
|
|
13
13
|
* `ms` milliseconds.
|
|
14
14
|
*
|
|
15
|
-
*
|
|
15
|
+
* Pass a function rather than a promise to also **cancel** the operation on
|
|
16
|
+
* timeout: it receives an `AbortSignal`, aborted (with the `TimeoutError`)
|
|
17
|
+
* when time runs out — hand it to `fetch` or any abortable API. A plain
|
|
18
|
+
* promise can only be given up on, not stopped.
|
|
19
|
+
*
|
|
20
|
+
* @param operation - The promise to race against the timeout, or a function
|
|
21
|
+
* starting the operation with the given `AbortSignal`.
|
|
16
22
|
* @param ms - The timeout in milliseconds.
|
|
17
23
|
* @param message - Optional error message (defaults to a generic one).
|
|
18
24
|
* @example
|
|
19
|
-
* await withTimeout(fetch("/api"), 5000);
|
|
25
|
+
* await withTimeout((signal) => fetch("/api", { signal }), 5000); // aborted on timeout
|
|
26
|
+
* await withTimeout(somePromise, 5000); // only stops waiting
|
|
20
27
|
*/
|
|
21
|
-
export function withTimeout(
|
|
28
|
+
export function withTimeout(operation, ms, message) {
|
|
29
|
+
const controller = new AbortController();
|
|
22
30
|
return new Promise((resolve, reject) => {
|
|
23
31
|
const timer = setTimeout(() => {
|
|
24
|
-
|
|
32
|
+
const error = new TimeoutError(message);
|
|
33
|
+
controller.abort(error);
|
|
34
|
+
reject(error);
|
|
25
35
|
}, ms);
|
|
36
|
+
const fail = (error) => {
|
|
37
|
+
clearTimeout(timer);
|
|
38
|
+
reject(error instanceof Error ? error : new Error(String(error)));
|
|
39
|
+
};
|
|
40
|
+
let promise;
|
|
41
|
+
try {
|
|
42
|
+
promise =
|
|
43
|
+
typeof operation === "function"
|
|
44
|
+
? operation(controller.signal)
|
|
45
|
+
: operation;
|
|
46
|
+
}
|
|
47
|
+
catch (error) {
|
|
48
|
+
fail(error);
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
26
51
|
promise.then((value) => {
|
|
27
52
|
clearTimeout(timer);
|
|
28
53
|
resolve(value);
|
|
29
|
-
},
|
|
30
|
-
clearTimeout(timer);
|
|
31
|
-
reject(error instanceof Error ? error : new Error(String(error)));
|
|
32
|
-
});
|
|
54
|
+
}, fail);
|
|
33
55
|
});
|
|
34
56
|
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Escapes a single CSV field per RFC 4180, quoting it only when needed: if it
|
|
3
|
+
* contains a double quote, the delimiter or a line break, it's wrapped in
|
|
4
|
+
* double quotes with any internal double quote doubled; otherwise it's
|
|
5
|
+
* returned as-is.
|
|
6
|
+
*
|
|
7
|
+
* Builds rows cell by cell. To serialize whole rows at once, see `toCsv`
|
|
8
|
+
* (which always quotes, and defaults to `,`).
|
|
9
|
+
*
|
|
10
|
+
* @param value - The raw field value.
|
|
11
|
+
* @param delimiter - The cell separator the field will be joined with.
|
|
12
|
+
* Defaults to `";"`, what French-locale Excel expects.
|
|
13
|
+
* @returns The value, quoted and escaped only if needed.
|
|
14
|
+
* @example
|
|
15
|
+
* escapeCsvField("Apple"); // => "Apple"
|
|
16
|
+
* escapeCsvField("a;b"); // => '"a;b"'
|
|
17
|
+
* escapeCsvField("a,b", ","); // => '"a,b"'
|
|
18
|
+
* escapeCsvField('He said "hi"'); // => '"He said ""hi"""'
|
|
19
|
+
*/
|
|
20
|
+
export declare function escapeCsvField(value: string, delimiter?: string): string;
|
|
21
|
+
//# sourceMappingURL=escapeCsvField.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"escapeCsvField.d.ts","sourceRoot":"","sources":["../../src/csv/escapeCsvField.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,EAAE,SAAS,SAAM,GAAG,MAAM,CAQrE"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Escapes a single CSV field per RFC 4180, quoting it only when needed: if it
|
|
3
|
+
* contains a double quote, the delimiter or a line break, it's wrapped in
|
|
4
|
+
* double quotes with any internal double quote doubled; otherwise it's
|
|
5
|
+
* returned as-is.
|
|
6
|
+
*
|
|
7
|
+
* Builds rows cell by cell. To serialize whole rows at once, see `toCsv`
|
|
8
|
+
* (which always quotes, and defaults to `,`).
|
|
9
|
+
*
|
|
10
|
+
* @param value - The raw field value.
|
|
11
|
+
* @param delimiter - The cell separator the field will be joined with.
|
|
12
|
+
* Defaults to `";"`, what French-locale Excel expects.
|
|
13
|
+
* @returns The value, quoted and escaped only if needed.
|
|
14
|
+
* @example
|
|
15
|
+
* escapeCsvField("Apple"); // => "Apple"
|
|
16
|
+
* escapeCsvField("a;b"); // => '"a;b"'
|
|
17
|
+
* escapeCsvField("a,b", ","); // => '"a,b"'
|
|
18
|
+
* escapeCsvField('He said "hi"'); // => '"He said ""hi"""'
|
|
19
|
+
*/
|
|
20
|
+
export function escapeCsvField(value, delimiter = ";") {
|
|
21
|
+
const needsQuoting = value.includes('"') ||
|
|
22
|
+
value.includes(delimiter) ||
|
|
23
|
+
value.includes("\n") ||
|
|
24
|
+
value.includes("\r");
|
|
25
|
+
if (!needsQuoting)
|
|
26
|
+
return value;
|
|
27
|
+
return `"${value.replace(/"/g, '""')}"`;
|
|
28
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Formats a number as a raw value (no unit suffix) for use in a spreadsheet
|
|
3
|
+
* (sum, average...), with a comma decimal separator as expected by
|
|
4
|
+
* French-locale spreadsheets instead of JS's dot.
|
|
5
|
+
*
|
|
6
|
+
* @param value - The number to format.
|
|
7
|
+
* @param decimals - Number of decimal places. Defaults to `1`.
|
|
8
|
+
* @returns The formatted number, comma as decimal separator.
|
|
9
|
+
* @example
|
|
10
|
+
* formatCsvNumber(1.5); // => "1,5"
|
|
11
|
+
* formatCsvNumber(12.345, 2); // => "12,35"
|
|
12
|
+
*/
|
|
13
|
+
export declare function formatCsvNumber(value: number, decimals?: number): string;
|
|
14
|
+
//# sourceMappingURL=formatCsvNumber.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"formatCsvNumber.d.ts","sourceRoot":"","sources":["../../src/csv/formatCsvNumber.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,SAAI,GAAG,MAAM,CAEnE"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Formats a number as a raw value (no unit suffix) for use in a spreadsheet
|
|
3
|
+
* (sum, average...), with a comma decimal separator as expected by
|
|
4
|
+
* French-locale spreadsheets instead of JS's dot.
|
|
5
|
+
*
|
|
6
|
+
* @param value - The number to format.
|
|
7
|
+
* @param decimals - Number of decimal places. Defaults to `1`.
|
|
8
|
+
* @returns The formatted number, comma as decimal separator.
|
|
9
|
+
* @example
|
|
10
|
+
* formatCsvNumber(1.5); // => "1,5"
|
|
11
|
+
* formatCsvNumber(12.345, 2); // => "12,35"
|
|
12
|
+
*/
|
|
13
|
+
export function formatCsvNumber(value, decimals = 1) {
|
|
14
|
+
return value.toFixed(decimals).replace(".", ",");
|
|
15
|
+
}
|
package/dist/csv/index.d.ts
CHANGED
package/dist/csv/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/csv/index.ts"],"names":[],"mappings":"AAAA,cAAc,kBAAkB,CAAC;AACjC,cAAc,mBAAmB,CAAC;AAClC,cAAc,YAAY,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/csv/index.ts"],"names":[],"mappings":"AAAA,cAAc,kBAAkB,CAAC;AACjC,cAAc,qBAAqB,CAAC;AACpC,cAAc,sBAAsB,CAAC;AACrC,cAAc,mBAAmB,CAAC;AAClC,cAAc,YAAY,CAAC"}
|
package/dist/csv/index.js
CHANGED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export interface CalendarDate {
|
|
2
|
+
year: number;
|
|
3
|
+
/** 1–12. */
|
|
4
|
+
month: number;
|
|
5
|
+
day: number;
|
|
6
|
+
/** `YYYY-MM-DD`. */
|
|
7
|
+
iso: string;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Returns the calendar date of an instant in a given time zone, whatever the
|
|
11
|
+
* machine's own time zone (e.g. a server running in UTC).
|
|
12
|
+
*
|
|
13
|
+
* @param date - The instant (a `Date`, a timestamp in ms or a parsable string).
|
|
14
|
+
* @param timeZone - An IANA time zone, e.g. `"Europe/Paris"`.
|
|
15
|
+
* @returns `{ year, month, day, iso }`, `month` from 1 to 12.
|
|
16
|
+
* @throws {RangeError} If the time zone or the date is invalid.
|
|
17
|
+
* @example
|
|
18
|
+
* dateInTimeZone("2026-03-14T23:30:00Z", "Europe/Paris");
|
|
19
|
+
* // => { year: 2026, month: 3, day: 15, iso: "2026-03-15" }
|
|
20
|
+
*/
|
|
21
|
+
export declare function dateInTimeZone(date: Date | number | string, timeZone: string): CalendarDate;
|
|
22
|
+
//# sourceMappingURL=dateInTimeZone.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dateInTimeZone.d.ts","sourceRoot":"","sources":["../../src/date/dateInTimeZone.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,YAAY;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;IACZ,oBAAoB;IACpB,GAAG,EAAE,MAAM,CAAC;CACb;AAkBD;;;;;;;;;;;GAWG;AACH,wBAAgB,cAAc,CAC5B,IAAI,EAAE,IAAI,GAAG,MAAM,GAAG,MAAM,EAC5B,QAAQ,EAAE,MAAM,GACf,YAAY,CASd"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
const formatters = new Map();
|
|
2
|
+
function formatter(timeZone) {
|
|
3
|
+
let result = formatters.get(timeZone);
|
|
4
|
+
if (!result) {
|
|
5
|
+
result = new Intl.DateTimeFormat("en-US", {
|
|
6
|
+
timeZone,
|
|
7
|
+
year: "numeric",
|
|
8
|
+
month: "numeric",
|
|
9
|
+
day: "numeric",
|
|
10
|
+
});
|
|
11
|
+
formatters.set(timeZone, result);
|
|
12
|
+
}
|
|
13
|
+
return result;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Returns the calendar date of an instant in a given time zone, whatever the
|
|
17
|
+
* machine's own time zone (e.g. a server running in UTC).
|
|
18
|
+
*
|
|
19
|
+
* @param date - The instant (a `Date`, a timestamp in ms or a parsable string).
|
|
20
|
+
* @param timeZone - An IANA time zone, e.g. `"Europe/Paris"`.
|
|
21
|
+
* @returns `{ year, month, day, iso }`, `month` from 1 to 12.
|
|
22
|
+
* @throws {RangeError} If the time zone or the date is invalid.
|
|
23
|
+
* @example
|
|
24
|
+
* dateInTimeZone("2026-03-14T23:30:00Z", "Europe/Paris");
|
|
25
|
+
* // => { year: 2026, month: 3, day: 15, iso: "2026-03-15" }
|
|
26
|
+
*/
|
|
27
|
+
export function dateInTimeZone(date, timeZone) {
|
|
28
|
+
const parts = formatter(timeZone).formatToParts(new Date(date));
|
|
29
|
+
const get = (type) => Number(parts.find((p) => p.type === type)?.value);
|
|
30
|
+
const year = get("year");
|
|
31
|
+
const month = get("month");
|
|
32
|
+
const day = get("day");
|
|
33
|
+
const iso = `${String(year).padStart(4, "0")}-${String(month).padStart(2, "0")}-${String(day).padStart(2, "0")}`;
|
|
34
|
+
return { year, month, day, iso };
|
|
35
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Counts the calendar days between today and a date, both taken in a given
|
|
3
|
+
* time zone: the time of day doesn't matter, only the date on the calendar.
|
|
4
|
+
*
|
|
5
|
+
* @param date - The target instant (a `Date`, a timestamp in ms or a parsable string).
|
|
6
|
+
* @param timeZone - An IANA time zone, e.g. `"Europe/Paris"`.
|
|
7
|
+
* @param now - The reference instant (defaults to now).
|
|
8
|
+
* @returns `0` today, `1` tomorrow, `-1` yesterday…
|
|
9
|
+
* @throws {RangeError} If the time zone or the date is invalid.
|
|
10
|
+
* @example
|
|
11
|
+
* // It's 2026-03-14 23:00 in Paris
|
|
12
|
+
* daysUntil("2026-03-15T08:00:00+01:00", "Europe/Paris"); // => 1
|
|
13
|
+
*/
|
|
14
|
+
export declare function daysUntil(date: Date | number | string, timeZone: string, now?: Date): number;
|
|
15
|
+
//# sourceMappingURL=daysUntil.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"daysUntil.d.ts","sourceRoot":"","sources":["../../src/date/daysUntil.ts"],"names":[],"mappings":"AASA;;;;;;;;;;;;GAYG;AACH,wBAAgB,SAAS,CACvB,IAAI,EAAE,IAAI,GAAG,MAAM,GAAG,MAAM,EAC5B,QAAQ,EAAE,MAAM,EAChB,GAAG,GAAE,IAAiB,GACrB,MAAM,CAER"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { dateInTimeZone } from "./dateInTimeZone.js";
|
|
2
|
+
const DAY_MS = 24 * 60 * 60 * 1000;
|
|
3
|
+
function dayNumber(date, timeZone) {
|
|
4
|
+
const { year, month, day } = dateInTimeZone(date, timeZone);
|
|
5
|
+
return Date.UTC(year, month - 1, day) / DAY_MS;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Counts the calendar days between today and a date, both taken in a given
|
|
9
|
+
* time zone: the time of day doesn't matter, only the date on the calendar.
|
|
10
|
+
*
|
|
11
|
+
* @param date - The target instant (a `Date`, a timestamp in ms or a parsable string).
|
|
12
|
+
* @param timeZone - An IANA time zone, e.g. `"Europe/Paris"`.
|
|
13
|
+
* @param now - The reference instant (defaults to now).
|
|
14
|
+
* @returns `0` today, `1` tomorrow, `-1` yesterday…
|
|
15
|
+
* @throws {RangeError} If the time zone or the date is invalid.
|
|
16
|
+
* @example
|
|
17
|
+
* // It's 2026-03-14 23:00 in Paris
|
|
18
|
+
* daysUntil("2026-03-15T08:00:00+01:00", "Europe/Paris"); // => 1
|
|
19
|
+
*/
|
|
20
|
+
export function daysUntil(date, timeZone, now = new Date()) {
|
|
21
|
+
return dayNumber(date, timeZone) - dayNumber(now, timeZone);
|
|
22
|
+
}
|
package/dist/date/index.d.ts
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
|
+
export * from "./dateInTimeZone.js";
|
|
2
|
+
export * from "./daysUntil.js";
|
|
1
3
|
export * from "./formatRelativeTime.js";
|
|
2
4
|
export * from "./getPeriodStartMs.js";
|
|
3
5
|
export * from "./isSameDay.js";
|
|
4
6
|
export * from "./startOfDay.js";
|
|
7
|
+
export * from "./todayInTimeZone.js";
|
|
5
8
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/date/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/date/index.ts"],"names":[],"mappings":"AAAA,cAAc,yBAAyB,CAAC;AACxC,cAAc,uBAAuB,CAAC;AACtC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,iBAAiB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/date/index.ts"],"names":[],"mappings":"AAAA,cAAc,qBAAqB,CAAC;AACpC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,yBAAyB,CAAC;AACxC,cAAc,uBAAuB,CAAC;AACtC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,iBAAiB,CAAC;AAChC,cAAc,sBAAsB,CAAC"}
|
package/dist/date/index.js
CHANGED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { CalendarDate } from "./dateInTimeZone.js";
|
|
2
|
+
/**
|
|
3
|
+
* Returns today's calendar date in a given time zone — on a server running in
|
|
4
|
+
* UTC, `new Date()` is still "yesterday" in Paris between midnight and 1–2 am.
|
|
5
|
+
*
|
|
6
|
+
* @param timeZone - An IANA time zone, e.g. `"Europe/Paris"`.
|
|
7
|
+
* @param now - The reference instant (defaults to now).
|
|
8
|
+
* @returns `{ year, month, day, iso }`, `month` from 1 to 12.
|
|
9
|
+
* @throws {RangeError} If the time zone is invalid.
|
|
10
|
+
* @example
|
|
11
|
+
* todayInTimeZone("Europe/Paris").iso; // => "2026-09-30"
|
|
12
|
+
*/
|
|
13
|
+
export declare function todayInTimeZone(timeZone: string, now?: Date): CalendarDate;
|
|
14
|
+
//# sourceMappingURL=todayInTimeZone.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"todayInTimeZone.d.ts","sourceRoot":"","sources":["../../src/date/todayInTimeZone.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAGxD;;;;;;;;;;GAUG;AACH,wBAAgB,eAAe,CAC7B,QAAQ,EAAE,MAAM,EAChB,GAAG,GAAE,IAAiB,GACrB,YAAY,CAEd"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { dateInTimeZone } from "./dateInTimeZone.js";
|
|
2
|
+
/**
|
|
3
|
+
* Returns today's calendar date in a given time zone — on a server running in
|
|
4
|
+
* UTC, `new Date()` is still "yesterday" in Paris between midnight and 1–2 am.
|
|
5
|
+
*
|
|
6
|
+
* @param timeZone - An IANA time zone, e.g. `"Europe/Paris"`.
|
|
7
|
+
* @param now - The reference instant (defaults to now).
|
|
8
|
+
* @returns `{ year, month, day, iso }`, `month` from 1 to 12.
|
|
9
|
+
* @throws {RangeError} If the time zone is invalid.
|
|
10
|
+
* @example
|
|
11
|
+
* todayInTimeZone("Europe/Paris").iso; // => "2026-09-30"
|
|
12
|
+
*/
|
|
13
|
+
export function todayInTimeZone(timeZone, now = new Date()) {
|
|
14
|
+
return dateInTimeZone(now, timeZone);
|
|
15
|
+
}
|
|
@@ -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;
|
|
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;AASD;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAC9B,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,MAAM,EACZ,QAAQ,SAAe,GACtB,IAAI,CAgBN"}
|
|
@@ -12,6 +12,12 @@ export function downloadText(filename, text) {
|
|
|
12
12
|
element.click();
|
|
13
13
|
document.body.removeChild(element);
|
|
14
14
|
}
|
|
15
|
+
/**
|
|
16
|
+
* How long the object URL stays valid after the click. Revoking it right
|
|
17
|
+
* away can cancel the download in Firefox and Safari, which start it
|
|
18
|
+
* asynchronously; FileSaver.js uses the same 40 s delay.
|
|
19
|
+
*/
|
|
20
|
+
const REVOKE_DELAY_MS = 40_000;
|
|
15
21
|
/**
|
|
16
22
|
* Downloads a text blob as a file.
|
|
17
23
|
* @param filename The filename to save to.
|
|
@@ -26,6 +32,8 @@ export function downloadTextBlob(filename, text, mimeType = "text/plain") {
|
|
|
26
32
|
a.style.setProperty("display", "none");
|
|
27
33
|
document.body.appendChild(a);
|
|
28
34
|
a.click();
|
|
29
|
-
URL.revokeObjectURL(url);
|
|
30
35
|
document.body.removeChild(a);
|
|
36
|
+
setTimeout(() => {
|
|
37
|
+
URL.revokeObjectURL(url);
|
|
38
|
+
}, REVOKE_DELAY_MS);
|
|
31
39
|
}
|
package/dist/files/index.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/files/index.ts"],"names":[],"mappings":"AAAA,cAAc,mBAAmB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/files/index.ts"],"names":[],"mappings":"AAAA,cAAc,mBAAmB,CAAC;AAClC,cAAc,uBAAuB,CAAC"}
|
package/dist/files/index.js
CHANGED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Makes a string safe to use as a file name: characters forbidden on Windows
|
|
3
|
+
* (`\ / : * ? " < > |`) become hyphens, whitespace runs collapse into a single
|
|
4
|
+
* space and the result is trimmed. Also prevents a `/` from creating a
|
|
5
|
+
* sub-folder inside a ZIP archive.
|
|
6
|
+
*
|
|
7
|
+
* @param name - The raw file name.
|
|
8
|
+
* @returns The sanitized file name.
|
|
9
|
+
* @example
|
|
10
|
+
* sanitizeFileName("AC/DC: Live?"); // => "AC-DC- Live-"
|
|
11
|
+
* sanitizeFileName(" My file "); // => "My file"
|
|
12
|
+
*/
|
|
13
|
+
export declare function sanitizeFileName(name: string): string;
|
|
14
|
+
//# sourceMappingURL=sanitizeFileName.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sanitizeFileName.d.ts","sourceRoot":"","sources":["../../src/files/sanitizeFileName.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAKrD"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Makes a string safe to use as a file name: characters forbidden on Windows
|
|
3
|
+
* (`\ / : * ? " < > |`) become hyphens, whitespace runs collapse into a single
|
|
4
|
+
* space and the result is trimmed. Also prevents a `/` from creating a
|
|
5
|
+
* sub-folder inside a ZIP archive.
|
|
6
|
+
*
|
|
7
|
+
* @param name - The raw file name.
|
|
8
|
+
* @returns The sanitized file name.
|
|
9
|
+
* @example
|
|
10
|
+
* sanitizeFileName("AC/DC: Live?"); // => "AC-DC- Live-"
|
|
11
|
+
* sanitizeFileName(" My file "); // => "My file"
|
|
12
|
+
*/
|
|
13
|
+
export function sanitizeFileName(name) {
|
|
14
|
+
return name
|
|
15
|
+
.replace(/[\\/:*?"<>|]/g, "-")
|
|
16
|
+
.replace(/\s+/g, " ")
|
|
17
|
+
.trim();
|
|
18
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/function/index.ts"],"names":[],"mappings":"AAAA,cAAc,cAAc,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./memoize.js";
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
export interface MemoizedFunction<Args extends unknown[], R> {
|
|
2
|
+
(...args: Args): R;
|
|
3
|
+
/** The underlying cache, exposed for inspection or manual clearing. */
|
|
4
|
+
cache: Map<string, R>;
|
|
5
|
+
}
|
|
6
|
+
export interface MemoizeOptions<Args extends unknown[]> {
|
|
7
|
+
/** Computes the cache key for a set of arguments (defaults to `JSON.stringify`). */
|
|
8
|
+
getKey?: (...args: Args) => string;
|
|
9
|
+
/**
|
|
10
|
+
* Max number of cached results; beyond it, the least recently used one is
|
|
11
|
+
* evicted. Unbounded by default — set it for a long-lived function called
|
|
12
|
+
* with many distinct arguments.
|
|
13
|
+
*/
|
|
14
|
+
maxSize?: number;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Caches the result of `fn` per argument tuple (JSON-stringified by
|
|
18
|
+
* default).
|
|
19
|
+
*
|
|
20
|
+
* A returned promise that rejects is evicted from the cache, so a failed
|
|
21
|
+
* async call is retried on the next invocation instead of failing forever.
|
|
22
|
+
*
|
|
23
|
+
* @param fn - The function to memoize.
|
|
24
|
+
* @param getKeyOrOptions - A key function, or `{ getKey, maxSize }`.
|
|
25
|
+
* @returns A memoized version of `fn`, with a `cache` map exposed.
|
|
26
|
+
* @example
|
|
27
|
+
* const square = memoize((n: number) => expensiveSquare(n));
|
|
28
|
+
* square(4); // computed
|
|
29
|
+
* square(4); // cached
|
|
30
|
+
*
|
|
31
|
+
* const getUser = memoize(fetchUser, { maxSize: 100 });
|
|
32
|
+
*/
|
|
33
|
+
export declare function memoize<Args extends unknown[], R>(fn: (...args: Args) => R, getKeyOrOptions?: ((...args: Args) => string) | MemoizeOptions<Args>): MemoizedFunction<Args, R>;
|
|
34
|
+
//# sourceMappingURL=memoize.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"memoize.d.ts","sourceRoot":"","sources":["../../src/function/memoize.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,gBAAgB,CAAC,IAAI,SAAS,OAAO,EAAE,EAAE,CAAC;IACzD,CAAC,GAAG,IAAI,EAAE,IAAI,GAAG,CAAC,CAAC;IACnB,uEAAuE;IACvE,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;CACvB;AAED,MAAM,WAAW,cAAc,CAAC,IAAI,SAAS,OAAO,EAAE;IACpD,oFAAoF;IACpF,MAAM,CAAC,EAAE,CAAC,GAAG,IAAI,EAAE,IAAI,KAAK,MAAM,CAAC;IACnC;;;;OAIG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,OAAO,CAAC,IAAI,SAAS,OAAO,EAAE,EAAE,CAAC,EAC/C,EAAE,EAAE,CAAC,GAAG,IAAI,EAAE,IAAI,KAAK,CAAC,EACxB,eAAe,GAAE,CAAC,CAAC,GAAG,IAAI,EAAE,IAAI,KAAK,MAAM,CAAC,GAAG,cAAc,CAAC,IAAI,CAAM,GACvE,gBAAgB,CAAC,IAAI,EAAE,CAAC,CAAC,CAqC3B"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Caches the result of `fn` per argument tuple (JSON-stringified by
|
|
3
|
+
* default).
|
|
4
|
+
*
|
|
5
|
+
* A returned promise that rejects is evicted from the cache, so a failed
|
|
6
|
+
* async call is retried on the next invocation instead of failing forever.
|
|
7
|
+
*
|
|
8
|
+
* @param fn - The function to memoize.
|
|
9
|
+
* @param getKeyOrOptions - A key function, or `{ getKey, maxSize }`.
|
|
10
|
+
* @returns A memoized version of `fn`, with a `cache` map exposed.
|
|
11
|
+
* @example
|
|
12
|
+
* const square = memoize((n: number) => expensiveSquare(n));
|
|
13
|
+
* square(4); // computed
|
|
14
|
+
* square(4); // cached
|
|
15
|
+
*
|
|
16
|
+
* const getUser = memoize(fetchUser, { maxSize: 100 });
|
|
17
|
+
*/
|
|
18
|
+
export function memoize(fn, getKeyOrOptions = {}) {
|
|
19
|
+
const { getKey = (...args) => JSON.stringify(args), maxSize = Infinity, } = typeof getKeyOrOptions === "function"
|
|
20
|
+
? { getKey: getKeyOrOptions }
|
|
21
|
+
: getKeyOrOptions;
|
|
22
|
+
const cache = new Map();
|
|
23
|
+
const memoized = ((...args) => {
|
|
24
|
+
const key = getKey(...args);
|
|
25
|
+
if (cache.has(key)) {
|
|
26
|
+
const cached = cache.get(key);
|
|
27
|
+
// Re-insert to mark it as the most recently used.
|
|
28
|
+
cache.delete(key);
|
|
29
|
+
cache.set(key, cached);
|
|
30
|
+
return cached;
|
|
31
|
+
}
|
|
32
|
+
const result = fn(...args);
|
|
33
|
+
cache.set(key, result);
|
|
34
|
+
if (cache.size > maxSize) {
|
|
35
|
+
const oldestKey = cache.keys().next().value;
|
|
36
|
+
if (oldestKey !== undefined)
|
|
37
|
+
cache.delete(oldestKey);
|
|
38
|
+
}
|
|
39
|
+
if (result instanceof Promise) {
|
|
40
|
+
result.catch(() => {
|
|
41
|
+
// Only if it's still this call's promise (not a newer one).
|
|
42
|
+
if (cache.get(key) === result)
|
|
43
|
+
cache.delete(key);
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
return result;
|
|
47
|
+
});
|
|
48
|
+
memoized.cache = cache;
|
|
49
|
+
return memoized;
|
|
50
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -4,10 +4,10 @@ export * from "./classes/index.js";
|
|
|
4
4
|
export * from "./csv/index.js";
|
|
5
5
|
export * from "./date/index.js";
|
|
6
6
|
export * from "./files/index.js";
|
|
7
|
+
export * from "./function/index.js";
|
|
7
8
|
export * from "./id/index.js";
|
|
8
9
|
export * from "./image/index.js";
|
|
9
10
|
export * from "./markdown/index.js";
|
|
10
|
-
export * from "./maths/index.js";
|
|
11
11
|
export * from "./number/index.js";
|
|
12
12
|
export * from "./object/index.js";
|
|
13
13
|
export * from "./string/index.js";
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
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,gBAAgB,CAAC;AAC/B,cAAc,iBAAiB,CAAC;AAChC,cAAc,kBAAkB,CAAC;AACjC,cAAc,eAAe,CAAC;AAC9B,cAAc,kBAAkB,CAAC;AACjC,cAAc,qBAAqB,CAAC;AACpC,cAAc,
|
|
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,gBAAgB,CAAC;AAC/B,cAAc,iBAAiB,CAAC;AAChC,cAAc,kBAAkB,CAAC;AACjC,cAAc,qBAAqB,CAAC;AACpC,cAAc,eAAe,CAAC;AAC9B,cAAc,kBAAkB,CAAC;AACjC,cAAc,qBAAqB,CAAC;AACpC,cAAc,mBAAmB,CAAC;AAClC,cAAc,mBAAmB,CAAC;AAClC,cAAc,mBAAmB,CAAC;AAClC,cAAc,oBAAoB,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -9,10 +9,10 @@ export * from "./classes/index.js";
|
|
|
9
9
|
export * from "./csv/index.js";
|
|
10
10
|
export * from "./date/index.js";
|
|
11
11
|
export * from "./files/index.js";
|
|
12
|
+
export * from "./function/index.js";
|
|
12
13
|
export * from "./id/index.js";
|
|
13
14
|
export * from "./image/index.js";
|
|
14
15
|
export * from "./markdown/index.js";
|
|
15
|
-
export * from "./maths/index.js";
|
|
16
16
|
export * from "./number/index.js";
|
|
17
17
|
export * from "./object/index.js";
|
|
18
18
|
export * from "./string/index.js";
|
package/dist/maths/average.d.ts
CHANGED
|
@@ -1,8 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
|
|
3
|
-
*
|
|
4
|
-
* @param {number[]} arr - The array of numbers to average.
|
|
5
|
-
* @return {number} The average of all numbers in the array.
|
|
6
|
-
*/
|
|
7
|
-
export declare function avg(arr: number[]): number;
|
|
1
|
+
/** @deprecated Import from `@forthtilliath/ts-kit/number/average` instead. */
|
|
2
|
+
export { avg } from "../number/average.js";
|
|
8
3
|
//# sourceMappingURL=average.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"average.d.ts","sourceRoot":"","sources":["../../src/maths/average.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"average.d.ts","sourceRoot":"","sources":["../../src/maths/average.ts"],"names":[],"mappings":"AAGA,8EAA8E;AAC9E,OAAO,EAAE,GAAG,EAAE,MAAM,sBAAsB,CAAC"}
|
package/dist/maths/average.js
CHANGED
|
@@ -1,10 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
* @param {number[]} arr - The array of numbers to average.
|
|
6
|
-
* @return {number} The average of all numbers in the array.
|
|
7
|
-
*/
|
|
8
|
-
export function avg(arr) {
|
|
9
|
-
return sum(arr) / arr.length;
|
|
10
|
-
}
|
|
1
|
+
// `maths/` duplicated `number/`: `avg` moved there. Re-exported so the
|
|
2
|
+
// `@forthtilliath/ts-kit/maths/average` deep import keeps working.
|
|
3
|
+
/** @deprecated Import from `@forthtilliath/ts-kit/number/average` instead. */
|
|
4
|
+
export { avg } from "../number/average.js";
|
package/dist/maths/sum.d.ts
CHANGED
|
@@ -1,8 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
|
|
3
|
-
*
|
|
4
|
-
* @param {number[]} arr - The array of numbers to sum.
|
|
5
|
-
* @return {number} The sum of all numbers in the array.
|
|
6
|
-
*/
|
|
7
|
-
export declare function sum(arr: number[]): number;
|
|
1
|
+
/** @deprecated Import from `@forthtilliath/ts-kit/number/sum` instead. */
|
|
2
|
+
export { sum } from "../number/sum.js";
|
|
8
3
|
//# sourceMappingURL=sum.d.ts.map
|
package/dist/maths/sum.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sum.d.ts","sourceRoot":"","sources":["../../src/maths/sum.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"sum.d.ts","sourceRoot":"","sources":["../../src/maths/sum.ts"],"names":[],"mappings":"AAGA,0EAA0E;AAC1E,OAAO,EAAE,GAAG,EAAE,MAAM,kBAAkB,CAAC"}
|
package/dist/maths/sum.js
CHANGED
|
@@ -1,9 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
* @return {number} The sum of all numbers in the array.
|
|
6
|
-
*/
|
|
7
|
-
export function sum(arr) {
|
|
8
|
-
return arr.reduce((total, n) => total + n, 0);
|
|
9
|
-
}
|
|
1
|
+
// `maths/` duplicated `number/`: `sum` moved there. Re-exported so the
|
|
2
|
+
// `@forthtilliath/ts-kit/maths/sum` deep import keeps working.
|
|
3
|
+
/** @deprecated Import from `@forthtilliath/ts-kit/number/sum` instead. */
|
|
4
|
+
export { sum } from "../number/sum.js";
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Calculates the average (arithmetic mean) of all numbers in an array.
|
|
3
|
+
*
|
|
4
|
+
* @param arr - The numbers to average.
|
|
5
|
+
* @returns The average, or `NaN` for an empty array — there's no meaningful
|
|
6
|
+
* average of nothing, and `0` would be indistinguishable from a real one
|
|
7
|
+
* (same as lodash's `mean`). Check `arr.length` first if you need a fallback.
|
|
8
|
+
* @example
|
|
9
|
+
* avg([1, 2, 3]); // => 2
|
|
10
|
+
* avg([]); // => NaN
|
|
11
|
+
*/
|
|
12
|
+
export declare function avg(arr: readonly number[]): number;
|
|
13
|
+
//# sourceMappingURL=average.d.ts.map
|