@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.
Files changed (110) hide show
  1. package/README.md +83 -19
  2. package/dist/async/debounce.d.ts +20 -0
  3. package/dist/async/debounce.d.ts.map +1 -0
  4. package/dist/async/debounce.js +44 -0
  5. package/dist/async/index.d.ts +7 -0
  6. package/dist/async/index.d.ts.map +1 -0
  7. package/dist/async/index.js +6 -0
  8. package/dist/async/memoize.d.ts +19 -0
  9. package/dist/async/memoize.d.ts.map +1 -0
  10. package/dist/async/memoize.js +25 -0
  11. package/dist/async/retry.d.ts +25 -0
  12. package/dist/async/retry.d.ts.map +1 -0
  13. package/dist/async/retry.js +33 -0
  14. package/dist/async/sleep.d.ts +9 -0
  15. package/dist/async/sleep.d.ts.map +1 -0
  16. package/dist/async/sleep.js +10 -0
  17. package/dist/async/throttle.d.ts +20 -0
  18. package/dist/async/throttle.d.ts.map +1 -0
  19. package/dist/async/throttle.js +43 -0
  20. package/dist/async/withTimeout.d.ts +19 -0
  21. package/dist/async/withTimeout.d.ts.map +1 -0
  22. package/dist/async/withTimeout.js +34 -0
  23. package/dist/classes/array.d.ts +88 -0
  24. package/dist/classes/array.d.ts.map +1 -1
  25. package/dist/classes/array.js +74 -0
  26. package/dist/classes/index.d.ts +2 -0
  27. package/dist/classes/index.d.ts.map +1 -0
  28. package/dist/classes/index.js +1 -0
  29. package/dist/date/formatRelativeTime.d.ts +13 -0
  30. package/dist/date/formatRelativeTime.d.ts.map +1 -0
  31. package/dist/date/formatRelativeTime.js +30 -0
  32. package/dist/date/index.d.ts +4 -0
  33. package/dist/date/index.d.ts.map +1 -0
  34. package/dist/date/index.js +3 -0
  35. package/dist/date/isSameDay.d.ts +11 -0
  36. package/dist/date/isSameDay.d.ts.map +1 -0
  37. package/dist/date/isSameDay.js +14 -0
  38. package/dist/date/startOfDay.d.ts +10 -0
  39. package/dist/date/startOfDay.d.ts.map +1 -0
  40. package/dist/date/startOfDay.js +13 -0
  41. package/dist/files/downloadText.d.ts +2 -1
  42. package/dist/files/downloadText.d.ts.map +1 -1
  43. package/dist/files/downloadText.js +3 -2
  44. package/dist/files/index.d.ts +2 -0
  45. package/dist/files/index.d.ts.map +1 -0
  46. package/dist/files/index.js +1 -0
  47. package/dist/id/index.d.ts +2 -0
  48. package/dist/id/index.d.ts.map +1 -0
  49. package/dist/id/index.js +1 -0
  50. package/dist/id/randomId.d.ts +10 -0
  51. package/dist/id/randomId.d.ts.map +1 -0
  52. package/dist/id/randomId.js +33 -0
  53. package/dist/index.d.ts +10 -1
  54. package/dist/index.d.ts.map +1 -1
  55. package/dist/index.js +15 -1
  56. package/dist/number/clamp.d.ts +13 -0
  57. package/dist/number/clamp.d.ts.map +1 -0
  58. package/dist/number/clamp.js +14 -0
  59. package/dist/number/formatBytes.d.ts +12 -0
  60. package/dist/number/formatBytes.d.ts.map +1 -0
  61. package/dist/number/formatBytes.js +19 -0
  62. package/dist/number/formatDuration.d.ts +12 -0
  63. package/dist/number/formatDuration.d.ts.map +1 -0
  64. package/dist/number/formatDuration.js +29 -0
  65. package/dist/number/index.d.ts +5 -0
  66. package/dist/number/index.d.ts.map +1 -0
  67. package/dist/number/index.js +4 -0
  68. package/dist/number/round.d.ts +12 -0
  69. package/dist/number/round.d.ts.map +1 -0
  70. package/dist/number/round.js +14 -0
  71. package/dist/object/deepClone.d.ts +12 -0
  72. package/dist/object/deepClone.d.ts.map +1 -0
  73. package/dist/object/deepClone.js +16 -0
  74. package/dist/object/deepEqual.d.ts +12 -0
  75. package/dist/object/deepEqual.d.ts.map +1 -0
  76. package/dist/object/deepEqual.js +31 -0
  77. package/dist/object/groupBy.d.ts +12 -0
  78. package/dist/object/groupBy.d.ts.map +1 -0
  79. package/dist/object/groupBy.js +18 -0
  80. package/dist/object/index.d.ts +8 -0
  81. package/dist/object/index.d.ts.map +1 -0
  82. package/dist/object/index.js +7 -0
  83. package/dist/object/mapValues.d.ts +11 -0
  84. package/dist/object/mapValues.d.ts.map +1 -0
  85. package/dist/object/mapValues.js +16 -0
  86. package/dist/object/omit.d.ts +11 -0
  87. package/dist/object/omit.d.ts.map +1 -0
  88. package/dist/object/omit.js +16 -0
  89. package/dist/object/pick.d.ts +11 -0
  90. package/dist/object/pick.d.ts.map +1 -0
  91. package/dist/object/pick.js +18 -0
  92. package/dist/object/uniqueBy.d.ts +13 -0
  93. package/dist/object/uniqueBy.d.ts.map +1 -0
  94. package/dist/object/uniqueBy.js +23 -0
  95. package/dist/string/capitalize.d.ts +11 -0
  96. package/dist/string/capitalize.d.ts.map +1 -0
  97. package/dist/string/capitalize.js +14 -0
  98. package/dist/string/index.d.ts +5 -0
  99. package/dist/string/index.d.ts.map +1 -0
  100. package/dist/string/index.js +4 -0
  101. package/dist/string/pluralize.d.ts +17 -0
  102. package/dist/string/pluralize.d.ts.map +1 -0
  103. package/dist/string/pluralize.js +26 -0
  104. package/dist/string/slugify.d.ts +12 -0
  105. package/dist/string/slugify.d.ts.map +1 -0
  106. package/dist/string/slugify.js +18 -0
  107. package/dist/string/truncate.d.ts +13 -0
  108. package/dist/string/truncate.d.ts.map +1 -0
  109. package/dist/string/truncate.js +17 -0
  110. package/package.json +3 -2
package/README.md CHANGED
@@ -1,11 +1,8 @@
1
1
  # @forthtilliath/ts-kit
2
2
 
3
- Framework-agnostic TypeScript utility functions — arrays, maths, browser file
4
- downloads, and a couple of `Array` extensions with better type-narrowing.
5
-
6
- > **Status**: not currently consumed by any other package in this monorepo.
7
- > Built, linted, tested and type-checked like everything else, but nothing
8
- > imports it yet.
3
+ Framework-agnostic TypeScript utility functions — arrays, objects, strings,
4
+ async control-flow, dates, numbers, browser file downloads, and a couple of
5
+ `Array` extensions with better type-narrowing.
9
6
 
10
7
  ## Install
11
8
 
@@ -21,19 +18,19 @@ Within the workspace:
21
18
 
22
19
  ## Usage
23
20
 
24
- Each module is exported individually — import the file you need directly
25
- rather than a single barrel:
21
+ Import everything you need from the package root — a single barrel re-exports
22
+ every category, so there's no need to remember (or type out) one import line
23
+ per module:
24
+
25
+ ```ts
26
+ import { chunk, clamp, debounce, pick, slugify } from "@forthtilliath/ts-kit";
27
+ ```
28
+
29
+ Subpath imports still work if you'd rather import one module at a time (e.g.
30
+ to keep a bundle's included code obvious at a glance):
26
31
 
27
32
  ```ts
28
33
  import { chunk } from "@forthtilliath/ts-kit/array/chunk";
29
- import { flattenDeep } from "@forthtilliath/ts-kit/array/flattenDeep";
30
- import { FArray } from "@forthtilliath/ts-kit/classes/array";
31
- import {
32
- downloadText,
33
- downloadTextBlob,
34
- } from "@forthtilliath/ts-kit/files/downloadText";
35
- import { avg } from "@forthtilliath/ts-kit/maths/average";
36
- import { sum } from "@forthtilliath/ts-kit/maths/sum";
37
34
  ```
38
35
 
39
36
  ### `array`
@@ -41,11 +38,46 @@ import { sum } from "@forthtilliath/ts-kit/maths/sum";
41
38
  - `chunk(array, size)` — splits an array into chunks of a given size.
42
39
  - `flattenDeep(array)` — recursively flattens nested arrays.
43
40
 
41
+ ### `async`
42
+
43
+ - `sleep(ms)` — resolves after a delay.
44
+ - `retry(fn, options)` — retries an async operation with exponential backoff.
45
+ - `debounce(fn, wait)` — delays `fn` until calls stop for `wait` ms; exposes
46
+ `cancel()` / `flush()`.
47
+ - `throttle(fn, wait)` — runs `fn` at most once per `wait` ms (leading +
48
+ trailing edge); exposes `cancel()`.
49
+ - `memoize(fn, getKey?)` — caches `fn`'s results per argument tuple; exposes
50
+ the underlying `cache` map.
51
+ - `withTimeout(promise, ms, message?)` — rejects with a `TimeoutError` if
52
+ `promise` doesn't settle in time.
53
+
44
54
  ### `classes`
45
55
 
46
- - `FArray` — an `Array` subclass whose `includes()` narrows the type of the
47
- searched value when it's found (unlike the native `Array.prototype.includes`,
48
- which only narrows the _array's_ element type).
56
+ - `FArray` — an `Array` subclass that fixes a handful of spots where the
57
+ native `Array` typings are wrong or overly restrictive:
58
+ - `includes()` narrows the type of the searched value when it's found
59
+ (the native `Array.prototype.includes` only narrows the _array's_
60
+ element type).
61
+ - `indexOf()` / `lastIndexOf()` accept a search value outside of the
62
+ array's element type instead of rejecting it at compile-time (same
63
+ root cause as `includes`, fixed the same way).
64
+ - `filter(Boolean)` strips `null`/`undefined` from the result's type
65
+ (the native typings don't recognize `Boolean` as a type guard, so the
66
+ result keeps the nullish types even though they're removed at runtime).
67
+ - `FArray.from(...)` / `FArray.of(...)` are typed to return an `FArray`
68
+ instead of a plain array (the native static typings return a plain
69
+ array even though these methods already construct an instance of the
70
+ calling class at runtime).
71
+ - `toArray()` converts an `FArray` back into a plain `Array` instance —
72
+ rarely needed since `FArray` is already assignable to `T[]` anywhere a
73
+ plain array is expected, but useful when the runtime type matters too.
74
+
75
+ ### `date`
76
+
77
+ - `startOfDay(date?)` — returns a new `Date` at local midnight.
78
+ - `isSameDay(a, b)` — checks whether two dates fall on the same local day.
79
+ - `formatRelativeTime(date, baseDate?, locale?)` — human-readable relative
80
+ time ("3 hours ago"), backed by `Intl.RelativeTimeFormat`.
49
81
 
50
82
  ### `files`
51
83
 
@@ -54,11 +86,43 @@ import { sum } from "@forthtilliath/ts-kit/maths/sum";
54
86
  - `downloadTextBlob(filename, text)` — same, but via a `Blob` + object URL
55
87
  (better for larger content).
56
88
 
89
+ ### `id`
90
+
91
+ - `randomId()` — `crypto.randomUUID()` with fallbacks for older/insecure
92
+ contexts.
93
+
57
94
  ### `maths`
58
95
 
59
96
  - `sum(numbers)` — sum of an array of numbers.
60
97
  - `avg(numbers)` — average of an array of numbers.
61
98
 
99
+ ### `number`
100
+
101
+ - `clamp(value, min, max)` — clamps a number between two bounds.
102
+ - `round(value, decimals?)` — rounds to a given number of decimal places.
103
+ - `formatBytes(bytes, decimals?)` — human-readable byte size ("1.5 MB").
104
+ - `formatDuration(ms)` — human-readable duration ("1d 1h 1m 1s").
105
+
106
+ ### `object`
107
+
108
+ - `pick(obj, keys)` / `omit(obj, keys)` — keep or drop a set of keys.
109
+ - `groupBy(array, getKey)` — groups elements by a computed key.
110
+ - `uniqueBy(array, getKey)` — deduplicates elements by a computed key.
111
+ - `deepClone(value)` — deep clone (via `structuredClone`, with a JSON
112
+ fallback).
113
+ - `deepEqual(a, b)` — recursive structural equality check.
114
+ - `mapValues(obj, fn)` — maps every value of an object through `fn`.
115
+
116
+ ### `string`
117
+
118
+ - `capitalize(str)` — uppercases the first letter, lowercases the rest.
119
+ - `truncate(str, maxLength, suffix?)` — truncates with an ellipsis (or custom
120
+ suffix).
121
+ - `slugify(str)` — URL-friendly slug (accents stripped, lower-cased,
122
+ hyphenated).
123
+ - `pluralize(count, singular, plural?)` — naive English pluralization, with an
124
+ override for irregular words.
125
+
62
126
  ## Scripts
63
127
 
64
128
  ```bash
@@ -0,0 +1,20 @@
1
+ export interface DebouncedFunction<Args extends unknown[]> {
2
+ (...args: Args): void;
3
+ /** Cancels a pending invocation, if any. */
4
+ cancel: () => void;
5
+ /** Immediately invokes a pending call, if any. */
6
+ flush: () => void;
7
+ }
8
+ /**
9
+ * Returns a debounced version of `fn` that only runs after `wait`
10
+ * milliseconds have elapsed without another call.
11
+ *
12
+ * @param fn - The function to debounce.
13
+ * @param wait - The delay in milliseconds.
14
+ * @returns The debounced function, with `cancel()` and `flush()` helpers.
15
+ * @example
16
+ * const onResize = debounce(() => recompute(), 200);
17
+ * window.addEventListener("resize", onResize);
18
+ */
19
+ export declare function debounce<Args extends unknown[]>(fn: (...args: Args) => void, wait: number): DebouncedFunction<Args>;
20
+ //# sourceMappingURL=debounce.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"debounce.d.ts","sourceRoot":"","sources":["../../src/async/debounce.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,iBAAiB,CAAC,IAAI,SAAS,OAAO,EAAE;IACvD,CAAC,GAAG,IAAI,EAAE,IAAI,GAAG,IAAI,CAAC;IACtB,4CAA4C;IAC5C,MAAM,EAAE,MAAM,IAAI,CAAC;IACnB,kDAAkD;IAClD,KAAK,EAAE,MAAM,IAAI,CAAC;CACnB;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,QAAQ,CAAC,IAAI,SAAS,OAAO,EAAE,EAC7C,EAAE,EAAE,CAAC,GAAG,IAAI,EAAE,IAAI,KAAK,IAAI,EAC3B,IAAI,EAAE,MAAM,GACX,iBAAiB,CAAC,IAAI,CAAC,CA+BzB"}
@@ -0,0 +1,44 @@
1
+ /**
2
+ * Returns a debounced version of `fn` that only runs after `wait`
3
+ * milliseconds have elapsed without another call.
4
+ *
5
+ * @param fn - The function to debounce.
6
+ * @param wait - The delay in milliseconds.
7
+ * @returns The debounced function, with `cancel()` and `flush()` helpers.
8
+ * @example
9
+ * const onResize = debounce(() => recompute(), 200);
10
+ * window.addEventListener("resize", onResize);
11
+ */
12
+ export function debounce(fn, wait) {
13
+ let timer;
14
+ let pendingArgs;
15
+ const debounced = ((...args) => {
16
+ pendingArgs = args;
17
+ if (timer !== undefined)
18
+ clearTimeout(timer);
19
+ timer = setTimeout(() => {
20
+ timer = undefined;
21
+ const callArgs = pendingArgs;
22
+ pendingArgs = undefined;
23
+ if (callArgs)
24
+ fn(...callArgs);
25
+ }, wait);
26
+ });
27
+ debounced.cancel = () => {
28
+ if (timer !== undefined)
29
+ clearTimeout(timer);
30
+ timer = undefined;
31
+ pendingArgs = undefined;
32
+ };
33
+ debounced.flush = () => {
34
+ if (timer === undefined)
35
+ return;
36
+ clearTimeout(timer);
37
+ timer = undefined;
38
+ const callArgs = pendingArgs;
39
+ pendingArgs = undefined;
40
+ if (callArgs)
41
+ fn(...callArgs);
42
+ };
43
+ return debounced;
44
+ }
@@ -0,0 +1,7 @@
1
+ export * from "./debounce.js";
2
+ export * from "./memoize.js";
3
+ export * from "./retry.js";
4
+ export * from "./sleep.js";
5
+ export * from "./throttle.js";
6
+ export * from "./withTimeout.js";
7
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/async/index.ts"],"names":[],"mappings":"AAAA,cAAc,eAAe,CAAC;AAC9B,cAAc,cAAc,CAAC;AAC7B,cAAc,YAAY,CAAC;AAC3B,cAAc,YAAY,CAAC;AAC3B,cAAc,eAAe,CAAC;AAC9B,cAAc,kBAAkB,CAAC"}
@@ -0,0 +1,6 @@
1
+ export * from "./debounce.js";
2
+ export * from "./memoize.js";
3
+ export * from "./retry.js";
4
+ export * from "./sleep.js";
5
+ export * from "./throttle.js";
6
+ export * from "./withTimeout.js";
@@ -0,0 +1,19 @@
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
+ /**
7
+ * Caches the result of `fn` per argument tuple (JSON-stringified by
8
+ * default).
9
+ *
10
+ * @param fn - The function to memoize.
11
+ * @param getKey - Computes the cache key for a set of arguments (defaults to `JSON.stringify`).
12
+ * @returns A memoized version of `fn`, with a `cache` map exposed.
13
+ * @example
14
+ * const square = memoize((n: number) => expensiveSquare(n));
15
+ * square(4); // computed
16
+ * square(4); // cached
17
+ */
18
+ export declare function memoize<Args extends unknown[], R>(fn: (...args: Args) => R, getKey?: (...args: Args) => string): MemoizedFunction<Args, R>;
19
+ //# sourceMappingURL=memoize.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"memoize.d.ts","sourceRoot":"","sources":["../../src/async/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;;;;;;;;;;;GAWG;AACH,wBAAgB,OAAO,CAAC,IAAI,SAAS,OAAO,EAAE,EAAE,CAAC,EAC/C,EAAE,EAAE,CAAC,GAAG,IAAI,EAAE,IAAI,KAAK,CAAC,EACxB,MAAM,GAAE,CAAC,GAAG,IAAI,EAAE,IAAI,KAAK,MAA0C,GACpE,gBAAgB,CAAC,IAAI,EAAE,CAAC,CAAC,CAa3B"}
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Caches the result of `fn` per argument tuple (JSON-stringified by
3
+ * default).
4
+ *
5
+ * @param fn - The function to memoize.
6
+ * @param getKey - Computes the cache key for a set of arguments (defaults to `JSON.stringify`).
7
+ * @returns A memoized version of `fn`, with a `cache` map exposed.
8
+ * @example
9
+ * const square = memoize((n: number) => expensiveSquare(n));
10
+ * square(4); // computed
11
+ * square(4); // cached
12
+ */
13
+ export function memoize(fn, getKey = (...args) => JSON.stringify(args)) {
14
+ const cache = new Map();
15
+ const memoized = ((...args) => {
16
+ const key = getKey(...args);
17
+ if (cache.has(key))
18
+ return cache.get(key);
19
+ const result = fn(...args);
20
+ cache.set(key, result);
21
+ return result;
22
+ });
23
+ memoized.cache = cache;
24
+ return memoized;
25
+ }
@@ -0,0 +1,25 @@
1
+ export interface RetryOptions {
2
+ /** Maximum number of attempts, including the first one (default: 3). */
3
+ attempts?: number;
4
+ /** Base delay in milliseconds before retrying (default: 200). */
5
+ delayMs?: number;
6
+ /** Backoff multiplier applied to `delayMs` after each failed attempt (default: 2). */
7
+ backoffFactor?: number;
8
+ /** Called with the error and the 1-based attempt number after each failure. */
9
+ onRetry?: (error: unknown, attempt: number) => void;
10
+ }
11
+ /**
12
+ * Retries an async operation with exponential backoff.
13
+ *
14
+ * @param fn - The operation to retry. Receives the 1-based attempt number.
15
+ * @param options - Retry configuration.
16
+ * @returns The result of the first successful attempt.
17
+ * @throws The last error, if every attempt failed.
18
+ * @example
19
+ * const data = await retry(() => fetch("/api").then((r) => r.json()), {
20
+ * attempts: 5,
21
+ * delayMs: 300,
22
+ * });
23
+ */
24
+ export declare function retry<T>(fn: (attempt: number) => Promise<T>, options?: RetryOptions): Promise<T>;
25
+ //# sourceMappingURL=retry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"retry.d.ts","sourceRoot":"","sources":["../../src/async/retry.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,YAAY;IAC3B,wEAAwE;IACxE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,iEAAiE;IACjE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,sFAAsF;IACtF,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,+EAA+E;IAC/E,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;CACrD;AAED;;;;;;;;;;;;GAYG;AACH,wBAAsB,KAAK,CAAC,CAAC,EAC3B,EAAE,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC,CAAC,CAAC,EACnC,OAAO,GAAE,YAAiB,GACzB,OAAO,CAAC,CAAC,CAAC,CAoBZ"}
@@ -0,0 +1,33 @@
1
+ import { sleep } from "./sleep.js";
2
+ /**
3
+ * Retries an async operation with exponential backoff.
4
+ *
5
+ * @param fn - The operation to retry. Receives the 1-based attempt number.
6
+ * @param options - Retry configuration.
7
+ * @returns The result of the first successful attempt.
8
+ * @throws The last error, if every attempt failed.
9
+ * @example
10
+ * const data = await retry(() => fetch("/api").then((r) => r.json()), {
11
+ * attempts: 5,
12
+ * delayMs: 300,
13
+ * });
14
+ */
15
+ export async function retry(fn, options = {}) {
16
+ const { attempts = 3, delayMs = 200, backoffFactor = 2, onRetry } = options;
17
+ let lastError;
18
+ let currentDelay = delayMs;
19
+ for (let attempt = 1; attempt <= attempts; attempt++) {
20
+ try {
21
+ return await fn(attempt);
22
+ }
23
+ catch (error) {
24
+ lastError = error;
25
+ onRetry?.(error, attempt);
26
+ if (attempt < attempts) {
27
+ await sleep(currentDelay);
28
+ currentDelay *= backoffFactor;
29
+ }
30
+ }
31
+ }
32
+ throw lastError;
33
+ }
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Resolves after the given number of milliseconds.
3
+ *
4
+ * @param ms - The delay in milliseconds.
5
+ * @example
6
+ * await sleep(1000); // waits 1 second
7
+ */
8
+ export declare function sleep(ms: number): Promise<void>;
9
+ //# sourceMappingURL=sleep.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sleep.d.ts","sourceRoot":"","sources":["../../src/async/sleep.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,wBAAgB,KAAK,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAE/C"}
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Resolves after the given number of milliseconds.
3
+ *
4
+ * @param ms - The delay in milliseconds.
5
+ * @example
6
+ * await sleep(1000); // waits 1 second
7
+ */
8
+ export function sleep(ms) {
9
+ return new Promise((resolve) => setTimeout(resolve, ms));
10
+ }
@@ -0,0 +1,20 @@
1
+ export interface ThrottledFunction<Args extends unknown[]> {
2
+ (...args: Args): void;
3
+ /** Cancels any pending trailing invocation. */
4
+ cancel: () => void;
5
+ }
6
+ /**
7
+ * Returns a throttled version of `fn` that runs at most once every `wait`
8
+ * milliseconds: immediately on the first call (leading edge), then once more
9
+ * at the end of the window if further calls came in during it (trailing
10
+ * edge).
11
+ *
12
+ * @param fn - The function to throttle.
13
+ * @param wait - The minimum delay in milliseconds between calls.
14
+ * @returns The throttled function, with a `cancel()` helper.
15
+ * @example
16
+ * const onScroll = throttle(() => updatePosition(), 100);
17
+ * window.addEventListener("scroll", onScroll);
18
+ */
19
+ export declare function throttle<Args extends unknown[]>(fn: (...args: Args) => void, wait: number): ThrottledFunction<Args>;
20
+ //# sourceMappingURL=throttle.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"throttle.d.ts","sourceRoot":"","sources":["../../src/async/throttle.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,iBAAiB,CAAC,IAAI,SAAS,OAAO,EAAE;IACvD,CAAC,GAAG,IAAI,EAAE,IAAI,GAAG,IAAI,CAAC;IACtB,+CAA+C;IAC/C,MAAM,EAAE,MAAM,IAAI,CAAC;CACpB;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,QAAQ,CAAC,IAAI,SAAS,OAAO,EAAE,EAC7C,EAAE,EAAE,CAAC,GAAG,IAAI,EAAE,IAAI,KAAK,IAAI,EAC3B,IAAI,EAAE,MAAM,GACX,iBAAiB,CAAC,IAAI,CAAC,CAkCzB"}
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Returns a throttled version of `fn` that runs at most once every `wait`
3
+ * milliseconds: immediately on the first call (leading edge), then once more
4
+ * at the end of the window if further calls came in during it (trailing
5
+ * edge).
6
+ *
7
+ * @param fn - The function to throttle.
8
+ * @param wait - The minimum delay in milliseconds between calls.
9
+ * @returns The throttled function, with a `cancel()` helper.
10
+ * @example
11
+ * const onScroll = throttle(() => updatePosition(), 100);
12
+ * window.addEventListener("scroll", onScroll);
13
+ */
14
+ export function throttle(fn, wait) {
15
+ let lastCallTime;
16
+ let timer;
17
+ let pendingArgs;
18
+ const throttled = ((...args) => {
19
+ const now = Date.now();
20
+ if (lastCallTime === undefined || now - lastCallTime >= wait) {
21
+ lastCallTime = now;
22
+ fn(...args);
23
+ return;
24
+ }
25
+ pendingArgs = args;
26
+ timer ??= setTimeout(() => {
27
+ lastCallTime = Date.now();
28
+ timer = undefined;
29
+ const callArgs = pendingArgs;
30
+ pendingArgs = undefined;
31
+ if (callArgs)
32
+ fn(...callArgs);
33
+ }, wait - (now - lastCallTime));
34
+ });
35
+ throttled.cancel = () => {
36
+ if (timer !== undefined)
37
+ clearTimeout(timer);
38
+ timer = undefined;
39
+ pendingArgs = undefined;
40
+ lastCallTime = undefined;
41
+ };
42
+ return throttled;
43
+ }
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Thrown by {@link withTimeout} when the wrapped promise does not settle in
3
+ * time.
4
+ */
5
+ export declare class TimeoutError extends Error {
6
+ constructor(message?: string);
7
+ }
8
+ /**
9
+ * Rejects with a {@link TimeoutError} if `promise` has not settled within
10
+ * `ms` milliseconds.
11
+ *
12
+ * @param promise - The promise to race against the timeout.
13
+ * @param ms - The timeout in milliseconds.
14
+ * @param message - Optional error message (defaults to a generic one).
15
+ * @example
16
+ * await withTimeout(fetch("/api"), 5000);
17
+ */
18
+ export declare function withTimeout<T>(promise: Promise<T>, ms: number, message?: string): Promise<T>;
19
+ //# sourceMappingURL=withTimeout.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"withTimeout.d.ts","sourceRoot":"","sources":["../../src/async/withTimeout.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,qBAAa,YAAa,SAAQ,KAAK;gBACzB,OAAO,SAAwB;CAI5C;AAED;;;;;;;;;GASG;AACH,wBAAgB,WAAW,CAAC,CAAC,EAC3B,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,EACnB,EAAE,EAAE,MAAM,EACV,OAAO,CAAC,EAAE,MAAM,GACf,OAAO,CAAC,CAAC,CAAC,CAgBZ"}
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Thrown by {@link withTimeout} when the wrapped promise does not settle in
3
+ * time.
4
+ */
5
+ export class TimeoutError extends Error {
6
+ constructor(message = "Operation timed out") {
7
+ super(message);
8
+ this.name = "TimeoutError";
9
+ }
10
+ }
11
+ /**
12
+ * Rejects with a {@link TimeoutError} if `promise` has not settled within
13
+ * `ms` milliseconds.
14
+ *
15
+ * @param promise - The promise to race against the timeout.
16
+ * @param ms - The timeout in milliseconds.
17
+ * @param message - Optional error message (defaults to a generic one).
18
+ * @example
19
+ * await withTimeout(fetch("/api"), 5000);
20
+ */
21
+ export function withTimeout(promise, ms, message) {
22
+ return new Promise((resolve, reject) => {
23
+ const timer = setTimeout(() => {
24
+ reject(new TimeoutError(message));
25
+ }, ms);
26
+ promise.then((value) => {
27
+ clearTimeout(timer);
28
+ resolve(value);
29
+ }, (error) => {
30
+ clearTimeout(timer);
31
+ reject(error instanceof Error ? error : new Error(String(error)));
32
+ });
33
+ });
34
+ }
@@ -49,5 +49,93 @@ export declare class FArray<T> extends Array<T> {
49
49
  * }
50
50
  */
51
51
  includes<S>(searchElement: S | T, fromIndex?: number): searchElement is S & T;
52
+ /**
53
+ * Same as `Array.indexOf`, but accepts a search value outside of `T`
54
+ * instead of rejecting it at compile-time (the native typings only accept
55
+ * `T`, exactly like `Array.includes` before the override above).
56
+ *
57
+ * @param searchElement The value to search for — no longer restricted to `T`.
58
+ * @param fromIndex The position in this array at which to begin searching for searchElement.
59
+ * @returns The index of the first match, or -1 if not found.
60
+ *
61
+ * @example
62
+ * // Normal operation of Array.indexOf
63
+ * const buz = "buz";
64
+ * type FBB = "foo" | "bar" | "baz";
65
+ * const arr: FBB[] = ["foo", "bar", "baz"];
66
+ * arr.indexOf(buz);
67
+ * // -----------^
68
+ * // The argument of type 'buz' is not assignable to the parameter of type 'FBB'.
69
+ *
70
+ * @example
71
+ * // FArray.indexOf
72
+ * const buz = "buz";
73
+ * type FBB = "foo" | "bar" | "baz";
74
+ * const arr = new FArray<FBB>("foo", "bar", "baz");
75
+ * arr.indexOf(buz); // compiles fine, returns -1
76
+ */
77
+ indexOf(searchElement: unknown, fromIndex?: number): number;
78
+ /**
79
+ * Same as `Array.lastIndexOf`, but accepts a search value outside of `T`
80
+ * instead of rejecting it at compile-time. See {@link FArray.indexOf}.
81
+ *
82
+ * @param searchElement The value to search for — no longer restricted to `T`.
83
+ * @param fromIndex The position in this array at which to start searching backwards. Defaults to the last index, like the native method.
84
+ * @returns The index of the last match, or -1 if not found.
85
+ */
86
+ lastIndexOf(searchElement: unknown, fromIndex?: number): number;
87
+ /**
88
+ * Same as `Array.filter`, but recognizes the `Boolean` global constructor
89
+ * as a type guard — the native typings don't, so `arr.filter(Boolean)`
90
+ * keeps `null`/`undefined` in the result's element type even though
91
+ * they're removed at runtime.
92
+ *
93
+ * Note: like the native runtime behavior, this also drops other falsy
94
+ * values (`0`, `""`, `NaN`, `false`) — but the type only reflects the
95
+ * removal of `null`/`undefined`, since narrowing those further would
96
+ * require them to be literal members of `T`.
97
+ *
98
+ * @example
99
+ * // Normal operation of Array.filter
100
+ * const arr: (number | null | undefined)[] = [1, null, 2, undefined];
101
+ * const result = arr.filter(Boolean); // still (number | null | undefined)[]
102
+ *
103
+ * @example
104
+ * // FArray.filter
105
+ * const arr = new FArray<number | null | undefined>(1, null, 2, undefined);
106
+ * const result = arr.filter(Boolean); // FArray<number>
107
+ */
108
+ filter(predicate: BooleanConstructor): FArray<NonNullable<T>>;
109
+ filter<S extends T>(predicate: (value: T, index: number, array: T[]) => value is S, thisArg?: unknown): FArray<S>;
110
+ filter(predicate: (value: T, index: number, array: T[]) => unknown, thisArg?: unknown): FArray<T>;
111
+ /**
112
+ * Same as `Array.from`, but returns an `FArray` — the native typings
113
+ * return a plain `T[]`/`U[]` even though `Array.from` (and `Array.of`
114
+ * below) already construct an instance of the calling class at runtime.
115
+ *
116
+ * @example
117
+ * const fromPlain = FArray.from([1, 2, 3]); // FArray<number>, not number[]
118
+ */
119
+ static from<T>(iterable: Iterable<T> | ArrayLike<T>): FArray<T>;
120
+ static from<T, U>(iterable: Iterable<T> | ArrayLike<T>, mapfn: (v: T, k: number) => U, thisArg?: unknown): FArray<U>;
121
+ /**
122
+ * Same as `Array.of`, but returns an `FArray`. See {@link FArray.from}.
123
+ *
124
+ * @example
125
+ * const fa = FArray.of(1, 2, 3); // FArray<number>, not number[]
126
+ */
127
+ static of<T>(...items: T[]): FArray<T>;
128
+ /**
129
+ * Converts this `FArray` back into a plain `Array` instance — the
130
+ * reverse of {@link FArray.from}/{@link FArray.of}. `FArray` is already
131
+ * assignable to `T[]` anywhere a plain array is expected (it extends
132
+ * `Array`), so this is mostly useful when the runtime type matters too
133
+ * (e.g. a library that special-cases `Array` and rejects subclasses).
134
+ *
135
+ * @example
136
+ * const fa = new FArray(1, 2, 3);
137
+ * const plain = fa.toArray(); // Array(3) [1, 2, 3], not instanceof FArray
138
+ */
139
+ toArray(): T[];
52
140
  }
53
141
  //# sourceMappingURL=array.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"array.d.ts","sourceRoot":"","sources":["../../src/classes/array.ts"],"names":[],"mappings":"AAAA,qBAAa,MAAM,CAAC,CAAC,CAAE,SAAQ,KAAK,CAAC,CAAC,CAAC;IACrC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAgDG;IACH,QAAQ,CAAC,CAAC,EAAE,aAAa,EAAE,CAAC,GAAG,CAAC,EAAE,SAAS,SAAI,GAAG,aAAa,IAAI,CAAC,GAAG,CAAC;CAIzE"}
1
+ {"version":3,"file":"array.d.ts","sourceRoot":"","sources":["../../src/classes/array.ts"],"names":[],"mappings":"AAAA,qBAAa,MAAM,CAAC,CAAC,CAAE,SAAQ,KAAK,CAAC,CAAC,CAAC;IACrC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAgDG;IACH,QAAQ,CAAC,CAAC,EAAE,aAAa,EAAE,CAAC,GAAG,CAAC,EAAE,SAAS,SAAI,GAAG,aAAa,IAAI,CAAC,GAAG,CAAC;IAKxE;;;;;;;;;;;;;;;;;;;;;;;;OAwBG;IACH,OAAO,CAAC,aAAa,EAAE,OAAO,EAAE,SAAS,SAAI,GAAG,MAAM;IAKtD;;;;;;;OAOG;IACH,WAAW,CAAC,aAAa,EAAE,OAAO,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,MAAM;IAS/D;;;;;;;;;;;;;;;;;;;;OAoBG;IACH,MAAM,CAAC,SAAS,EAAE,kBAAkB,GAAG,MAAM,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC;IAC7D,MAAM,CAAC,CAAC,SAAS,CAAC,EAChB,SAAS,EAAE,CAAC,KAAK,EAAE,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,EAAE,KAAK,KAAK,IAAI,CAAC,EAC9D,OAAO,CAAC,EAAE,OAAO,GAChB,MAAM,CAAC,CAAC,CAAC;IACZ,MAAM,CACJ,SAAS,EAAE,CAAC,KAAK,EAAE,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,EAAE,KAAK,OAAO,EAC3D,OAAO,CAAC,EAAE,OAAO,GAChB,MAAM,CAAC,CAAC,CAAC;IAQZ;;;;;;;OAOG;IACH,MAAM,CAAC,IAAI,CAAC,CAAC,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAAC,CAAC,GAAG,SAAS,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC;IAC/D,MAAM,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC,EACd,QAAQ,EAAE,QAAQ,CAAC,CAAC,CAAC,GAAG,SAAS,CAAC,CAAC,CAAC,EACpC,KAAK,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC,EAC7B,OAAO,CAAC,EAAE,OAAO,GAChB,MAAM,CAAC,CAAC,CAAC;IAWZ;;;;;OAKG;IACH,MAAM,CAAC,EAAE,CAAC,CAAC,EAAE,GAAG,KAAK,EAAE,CAAC,EAAE,GAAG,MAAM,CAAC,CAAC,CAAC;IAItC;;;;;;;;;;OAUG;IACH,OAAO,IAAI,CAAC,EAAE;CAGf"}