@brandup/ui-helpers 2.0.2 → 2.0.4

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 CHANGED
@@ -129,6 +129,9 @@ const data = await FuncHelper.minWaitAsync(() => loadData(), 1000);
129
129
 
130
130
  // Reject with TimeoutError if the request takes longer than 5000ms
131
131
  const result = await FuncHelper.timeout(fetch("/api"), 5000);
132
+
133
+ // Make the wait abortable without stopping the underlying work
134
+ const value = await FuncHelper.abortable(longRunning(), abortController.signal);
132
135
  ```
133
136
 
134
137
  Detect a timeout by checking the error type:
@@ -146,7 +149,18 @@ try {
146
149
  ```
147
150
 
148
151
  - `minWait(func, minTime?)` — wraps a callback so it runs no sooner than `minTime` ms after wrapping.
149
- - `minWaitAsync(func, minTime?, abort?)` — awaits an async operation, padding so it settles no sooner than `minTime` ms.
150
- - `delay(time, abort?)` — a promise resolved after `time` ms; rejects on abort.
151
- - `timeout(promise, timeout, abort?)` — races `promise` against `timeout` ms; rejects with a `TimeoutError` on timeout. Throws synchronously if `timeout ≤ 0`.
152
+ - `minWaitAsync(func, minTime?, abort?)` — awaits an async operation, padding so it settles no sooner than `minTime` ms. An already-aborted signal rejects immediately, before `func` runs.
153
+ - `delay(ms, abort?)` — a promise resolved after `ms` ms; rejects on abort. Throws synchronously if `ms` is negative (`0` is allowed).
154
+ - `timeout(promise, ms, abort?)` — races `promise` against `ms` ms; rejects with a `TimeoutError` on timeout, or with the signal's reason on abort. Throws synchronously if `ms ≤ 0`. The underlying `promise` is not cancelled — only the wait ends.
155
+ - `abortable(promise, abort?)` — makes *waiting* for `promise` abortable: rejects with the signal's reason on abort. Does not stop the underlying work; without a signal the promise is awaited as-is.
152
156
  - `TimeoutError` — error class thrown by `timeout` when the time limit is exceeded.
157
+
158
+ ## Polyfills
159
+
160
+ An opt-in, side-effect-only entry point fills in `AbortSignal` APIs that older runtimes may lack — `AbortSignal.prototype.throwIfAborted`, `AbortSignal.timeout` and `AbortSignal.any`. Import it once, as early as possible (e.g. in your entry module):
161
+
162
+ ```TypeScript
163
+ import "@brandup/ui-helpers/polyfill";
164
+ ```
165
+
166
+ Each implementation installs only when missing, so native behaviour is preserved where available. Types ship with the TypeScript `ESNext` lib; this module provides just the runtime. It is excluded from tree-shaking (`sideEffects`), so a bare import is never dropped by the bundler.
@@ -31,13 +31,17 @@ const minWait = (func, minTime) => {
31
31
  * Useful for keeping spinners/loading states visible for a minimum duration. If `minTime`
32
32
  * is omitted or falsy, `func` is awaited and its result returned without padding.
33
33
  *
34
+ * An already-aborted signal rejects immediately, before `func` runs; aborting later cancels
35
+ * the padding delay (but not `func` itself, which can't be cancelled here).
36
+ *
34
37
  * @typeParam TResult Result type produced by `func`.
35
38
  * @param func Factory returning the promise to await.
36
39
  * @param minTime Minimum total duration in milliseconds.
37
- * @param abort Optional signal used to cancel the padding delay.
40
+ * @param abort Optional signal used to cancel the wait.
38
41
  * @returns The result produced by `func`.
39
42
  */
40
43
  async function minWaitAsync(func, minTime, abort) {
44
+ abort?.throwIfAborted();
41
45
  if (!minTime)
42
46
  return func();
43
47
  const beginTime = Date.now();
@@ -51,6 +55,8 @@ async function minWaitAsync(func, minTime, abort) {
51
55
  const getRightTime = (start, minTime) => {
52
56
  const finishTime = Date.now();
53
57
  const w = minTime - (finishTime - start);
58
+ // Skip padding when the leftover is within 10% of `minTime`: the wait is already
59
+ // "close enough", and a sub-tick delay would only add jitter without a visible effect.
54
60
  return w > minTime * 0.1 ? w : 0;
55
61
  };
56
62
  /**
@@ -59,21 +65,26 @@ const getRightTime = (start, minTime) => {
59
65
  * If an already-aborted signal is supplied the promise rejects immediately; otherwise
60
66
  * aborting before the delay elapses clears the timer and rejects with the abort reason.
61
67
  *
62
- * @param time Delay in milliseconds.
68
+ * @param ms Delay in milliseconds; must not be negative. Values above 2147483647 (~24.8 days)
69
+ * overflow the timer and fire on the next tick — a `setTimeout` limitation.
63
70
  * @param abort Optional signal used to cancel the delay.
64
71
  * @returns A promise that resolves when the delay elapses.
72
+ * @throws {Error} When `ms` is negative.
65
73
  */
66
- function delay(time, abort) {
74
+ function delay(ms, abort) {
75
+ if (ms < 0)
76
+ throw new Error("Invalid delay value.");
67
77
  return new Promise((resolve, reject) => {
68
78
  abort?.throwIfAborted();
69
79
  const onAbort = () => {
70
80
  clearTimeout(timer);
71
- reject(abort?.reason);
81
+ // `onAbort` only runs once the listener has fired, which means `abort` exists.
82
+ reject(abort.reason);
72
83
  };
73
84
  const timer = setTimeout(() => {
74
85
  abort?.removeEventListener("abort", onAbort);
75
86
  resolve();
76
- }, time);
87
+ }, ms);
77
88
  abort?.addEventListener("abort", onAbort, { once: true });
78
89
  });
79
90
  }
@@ -86,32 +97,48 @@ function delay(time, abort) {
86
97
  *
87
98
  * @typeParam T Resolved value type of the wrapped promise.
88
99
  * @param promise Promise to guard with a timeout.
89
- * @param timeout Timeout in milliseconds; must be greater than `0`.
100
+ * @param ms Timeout in milliseconds; must be greater than `0`. Values above 2147483647
101
+ * (~24.8 days) overflow the timer and fire on the next tick — a `setTimeout` limitation.
90
102
  * @param abort Optional signal used to cancel the wait.
91
103
  * @returns A promise mirroring `promise` unless the timeout or abort fires first.
92
- * @throws {Error} When `timeout` is not greater than `0`.
104
+ * @throws {Error} When `ms` is not greater than `0`.
93
105
  */
94
- function timeout(promise, timeout, abort) {
95
- if (timeout <= 0)
106
+ function timeout(promise, ms, abort) {
107
+ if (ms <= 0)
96
108
  throw new Error("Invalid timeout value.");
109
+ // Own controller so the `delay` timer can be torn down once the race is decided,
110
+ // independently of the caller's `abort`.
111
+ const timer = new AbortController();
112
+ // Wins the race only by timing out (rejects with TimeoutError). If the timer is cancelled
113
+ // instead — the guarded work settled, or the caller aborted — `delay` rejects, and we turn
114
+ // that into a never-settling promise so `expire` simply stands aside: the race never produces
115
+ // a stray rejection that nobody is listening for.
116
+ const expire = delay(ms, timer.signal).then(() => Promise.reject(new TimeoutError()), () => new Promise(() => { }));
117
+ return abortable(Promise.race([promise, expire]), abort)
118
+ .finally(() => timer.abort());
119
+ }
120
+ /**
121
+ * Makes *waiting* for a promise abortable: rejects with the signal's reason on abort.
122
+ * Does not stop the underlying work the promise performs.
123
+ *
124
+ * If no signal is supplied the promise is awaited as-is. An already-aborted signal rejects
125
+ * immediately; otherwise the abort listener is removed once the promise settles.
126
+ *
127
+ * @typeParam T Resolved value type of the wrapped promise.
128
+ * @param promise Promise (or thenable) whose wait should become abortable.
129
+ * @param abort Optional signal used to cancel the wait.
130
+ * @returns A promise mirroring `promise` unless the abort fires first.
131
+ */
132
+ function abortable(promise, abort) {
133
+ if (!abort)
134
+ return Promise.resolve(promise);
135
+ if (abort.aborted)
136
+ return Promise.reject(abort.reason);
97
137
  return new Promise((resolve, reject) => {
98
- abort?.throwIfAborted();
99
- const onAbort = () => {
100
- clearTimeout(timer);
101
- reject(abort?.reason);
102
- };
103
- const timer = setTimeout(() => {
104
- abort?.removeEventListener("abort", onAbort);
105
- reject(new TimeoutError());
106
- }, timeout);
107
- abort?.addEventListener("abort", onAbort, { once: true });
108
- promise
109
- .then(result => resolve(result))
110
- .catch(reason => reject(reason))
111
- .finally(() => {
112
- clearTimeout(timer);
113
- abort?.removeEventListener("abort", onAbort);
114
- });
138
+ const onAbort = () => reject(abort.reason);
139
+ abort.addEventListener("abort", onAbort, { once: true });
140
+ const cleanup = () => abort.removeEventListener("abort", onAbort);
141
+ Promise.resolve(promise).then(v => { cleanup(); resolve(v); }, e => { cleanup(); reject(e); });
115
142
  });
116
143
  }
117
144
  /** Thrown by {@link timeout} when the time limit is exceeded. */
@@ -123,6 +150,7 @@ class TimeoutError extends Error {
123
150
  }
124
151
 
125
152
  exports.TimeoutError = TimeoutError;
153
+ exports.abortable = abortable;
126
154
  exports.delay = delay;
127
155
  exports.minWait = minWait;
128
156
  exports.minWaitAsync = minWaitAsync;
@@ -1 +1 @@
1
- {"version":3,"file":"func.js","sources":["../../../../source/helpers/func.ts"],"sourcesContent":[null],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"func.js","sources":["../../../../source/helpers/func.ts"],"sourcesContent":[null],"names":[],"mappings":"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"}
@@ -0,0 +1,44 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * Runtime polyfills for `AbortSignal`, installed only when the host lacks them:
5
+ * `AbortSignal.prototype.throwIfAborted`, `AbortSignal.timeout` and `AbortSignal.any`.
6
+ *
7
+ * Import for its side effect — there are no exports:
8
+ *
9
+ * ```TypeScript
10
+ * import "@brandup/ui-helpers/polyfill";
11
+ * ```
12
+ *
13
+ * Types already ship with the TypeScript `ESNext` lib; this module only fills in the
14
+ * implementations missing in older runtimes, matching the standard behaviour.
15
+ */
16
+ if (typeof AbortSignal.prototype.throwIfAborted !== "function") {
17
+ AbortSignal.prototype.throwIfAborted = function () {
18
+ if (this.aborted)
19
+ throw this.reason;
20
+ };
21
+ }
22
+ if (typeof AbortSignal.timeout !== "function") {
23
+ AbortSignal.timeout = (milliseconds) => {
24
+ const controller = new AbortController();
25
+ setTimeout(() => controller.abort(new DOMException("The operation timed out.", "TimeoutError")), milliseconds);
26
+ return controller.signal;
27
+ };
28
+ }
29
+ if (typeof AbortSignal.any !== "function") {
30
+ AbortSignal.any = (signals) => {
31
+ const controller = new AbortController();
32
+ for (const signal of signals) {
33
+ // If one is already aborted, the combined signal aborts immediately with its reason.
34
+ if (signal.aborted) {
35
+ controller.abort(signal.reason);
36
+ break;
37
+ }
38
+ // Tying each listener to the combined signal removes them all once it aborts — no leak.
39
+ signal.addEventListener("abort", () => controller.abort(signal.reason), { signal: controller.signal });
40
+ }
41
+ return controller.signal;
42
+ };
43
+ }
44
+ //# sourceMappingURL=polyfill.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"polyfill.js","sources":["../../../source/polyfill.ts"],"sourcesContent":[null],"names":[],"mappings":";;AAAA;;;;;;;;;;;;AAYG;AAEH,IAAI,OAAO,WAAW,CAAC,SAAS,CAAC,cAAc,KAAK,UAAU,EAAE;AAC/D,IAAA,WAAW,CAAC,SAAS,CAAC,cAAc,GAAG,YAAA;QACtC,IAAI,IAAI,CAAC,OAAO;YACf,MAAM,IAAI,CAAC,MAAM;AACnB,IAAA,CAAC;AACF;AAEA,IAAI,OAAO,WAAW,CAAC,OAAO,KAAK,UAAU,EAAE;AAC9C,IAAA,WAAW,CAAC,OAAO,GAAG,CAAC,YAAoB,KAAiB;AAC3D,QAAA,MAAM,UAAU,GAAG,IAAI,eAAe,EAAE;AACxC,QAAA,UAAU,CAAC,MAAM,UAAU,CAAC,KAAK,CAAC,IAAI,YAAY,CAAC,0BAA0B,EAAE,cAAc,CAAC,CAAC,EAAE,YAAY,CAAC;QAC9G,OAAO,UAAU,CAAC,MAAM;AACzB,IAAA,CAAC;AACF;AAEA,IAAI,OAAO,WAAW,CAAC,GAAG,KAAK,UAAU,EAAE;AAC1C,IAAA,WAAW,CAAC,GAAG,GAAG,CAAC,OAAsB,KAAiB;AACzD,QAAA,MAAM,UAAU,GAAG,IAAI,eAAe,EAAE;AAExC,QAAA,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE;;AAE7B,YAAA,IAAI,MAAM,CAAC,OAAO,EAAE;AACnB,gBAAA,UAAU,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC;gBAC/B;YACD;;YAGA,MAAM,CAAC,gBAAgB,CAAC,OAAO,EAAE,MAAM,UAAU,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,UAAU,CAAC,MAAM,EAAE,CAAC;QACvG;QAEA,OAAO,UAAU,CAAC,MAAM;AACzB,IAAA,CAAC;AACF;;"}
@@ -29,13 +29,17 @@ const minWait = (func, minTime) => {
29
29
  * Useful for keeping spinners/loading states visible for a minimum duration. If `minTime`
30
30
  * is omitted or falsy, `func` is awaited and its result returned without padding.
31
31
  *
32
+ * An already-aborted signal rejects immediately, before `func` runs; aborting later cancels
33
+ * the padding delay (but not `func` itself, which can't be cancelled here).
34
+ *
32
35
  * @typeParam TResult Result type produced by `func`.
33
36
  * @param func Factory returning the promise to await.
34
37
  * @param minTime Minimum total duration in milliseconds.
35
- * @param abort Optional signal used to cancel the padding delay.
38
+ * @param abort Optional signal used to cancel the wait.
36
39
  * @returns The result produced by `func`.
37
40
  */
38
41
  async function minWaitAsync(func, minTime, abort) {
42
+ abort?.throwIfAborted();
39
43
  if (!minTime)
40
44
  return func();
41
45
  const beginTime = Date.now();
@@ -49,6 +53,8 @@ async function minWaitAsync(func, minTime, abort) {
49
53
  const getRightTime = (start, minTime) => {
50
54
  const finishTime = Date.now();
51
55
  const w = minTime - (finishTime - start);
56
+ // Skip padding when the leftover is within 10% of `minTime`: the wait is already
57
+ // "close enough", and a sub-tick delay would only add jitter without a visible effect.
52
58
  return w > minTime * 0.1 ? w : 0;
53
59
  };
54
60
  /**
@@ -57,21 +63,26 @@ const getRightTime = (start, minTime) => {
57
63
  * If an already-aborted signal is supplied the promise rejects immediately; otherwise
58
64
  * aborting before the delay elapses clears the timer and rejects with the abort reason.
59
65
  *
60
- * @param time Delay in milliseconds.
66
+ * @param ms Delay in milliseconds; must not be negative. Values above 2147483647 (~24.8 days)
67
+ * overflow the timer and fire on the next tick — a `setTimeout` limitation.
61
68
  * @param abort Optional signal used to cancel the delay.
62
69
  * @returns A promise that resolves when the delay elapses.
70
+ * @throws {Error} When `ms` is negative.
63
71
  */
64
- function delay(time, abort) {
72
+ function delay(ms, abort) {
73
+ if (ms < 0)
74
+ throw new Error("Invalid delay value.");
65
75
  return new Promise((resolve, reject) => {
66
76
  abort?.throwIfAborted();
67
77
  const onAbort = () => {
68
78
  clearTimeout(timer);
69
- reject(abort?.reason);
79
+ // `onAbort` only runs once the listener has fired, which means `abort` exists.
80
+ reject(abort.reason);
70
81
  };
71
82
  const timer = setTimeout(() => {
72
83
  abort?.removeEventListener("abort", onAbort);
73
84
  resolve();
74
- }, time);
85
+ }, ms);
75
86
  abort?.addEventListener("abort", onAbort, { once: true });
76
87
  });
77
88
  }
@@ -84,32 +95,48 @@ function delay(time, abort) {
84
95
  *
85
96
  * @typeParam T Resolved value type of the wrapped promise.
86
97
  * @param promise Promise to guard with a timeout.
87
- * @param timeout Timeout in milliseconds; must be greater than `0`.
98
+ * @param ms Timeout in milliseconds; must be greater than `0`. Values above 2147483647
99
+ * (~24.8 days) overflow the timer and fire on the next tick — a `setTimeout` limitation.
88
100
  * @param abort Optional signal used to cancel the wait.
89
101
  * @returns A promise mirroring `promise` unless the timeout or abort fires first.
90
- * @throws {Error} When `timeout` is not greater than `0`.
102
+ * @throws {Error} When `ms` is not greater than `0`.
91
103
  */
92
- function timeout(promise, timeout, abort) {
93
- if (timeout <= 0)
104
+ function timeout(promise, ms, abort) {
105
+ if (ms <= 0)
94
106
  throw new Error("Invalid timeout value.");
107
+ // Own controller so the `delay` timer can be torn down once the race is decided,
108
+ // independently of the caller's `abort`.
109
+ const timer = new AbortController();
110
+ // Wins the race only by timing out (rejects with TimeoutError). If the timer is cancelled
111
+ // instead — the guarded work settled, or the caller aborted — `delay` rejects, and we turn
112
+ // that into a never-settling promise so `expire` simply stands aside: the race never produces
113
+ // a stray rejection that nobody is listening for.
114
+ const expire = delay(ms, timer.signal).then(() => Promise.reject(new TimeoutError()), () => new Promise(() => { }));
115
+ return abortable(Promise.race([promise, expire]), abort)
116
+ .finally(() => timer.abort());
117
+ }
118
+ /**
119
+ * Makes *waiting* for a promise abortable: rejects with the signal's reason on abort.
120
+ * Does not stop the underlying work the promise performs.
121
+ *
122
+ * If no signal is supplied the promise is awaited as-is. An already-aborted signal rejects
123
+ * immediately; otherwise the abort listener is removed once the promise settles.
124
+ *
125
+ * @typeParam T Resolved value type of the wrapped promise.
126
+ * @param promise Promise (or thenable) whose wait should become abortable.
127
+ * @param abort Optional signal used to cancel the wait.
128
+ * @returns A promise mirroring `promise` unless the abort fires first.
129
+ */
130
+ function abortable(promise, abort) {
131
+ if (!abort)
132
+ return Promise.resolve(promise);
133
+ if (abort.aborted)
134
+ return Promise.reject(abort.reason);
95
135
  return new Promise((resolve, reject) => {
96
- abort?.throwIfAborted();
97
- const onAbort = () => {
98
- clearTimeout(timer);
99
- reject(abort?.reason);
100
- };
101
- const timer = setTimeout(() => {
102
- abort?.removeEventListener("abort", onAbort);
103
- reject(new TimeoutError());
104
- }, timeout);
105
- abort?.addEventListener("abort", onAbort, { once: true });
106
- promise
107
- .then(result => resolve(result))
108
- .catch(reason => reject(reason))
109
- .finally(() => {
110
- clearTimeout(timer);
111
- abort?.removeEventListener("abort", onAbort);
112
- });
136
+ const onAbort = () => reject(abort.reason);
137
+ abort.addEventListener("abort", onAbort, { once: true });
138
+ const cleanup = () => abort.removeEventListener("abort", onAbort);
139
+ Promise.resolve(promise).then(v => { cleanup(); resolve(v); }, e => { cleanup(); reject(e); });
113
140
  });
114
141
  }
115
142
  /** Thrown by {@link timeout} when the time limit is exceeded. */
@@ -120,5 +147,5 @@ class TimeoutError extends Error {
120
147
  }
121
148
  }
122
149
 
123
- export { TimeoutError, delay, minWait, minWaitAsync, timeout };
150
+ export { TimeoutError, abortable, delay, minWait, minWaitAsync, timeout };
124
151
  //# sourceMappingURL=func.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"func.js","sources":["../../../../source/helpers/func.ts"],"sourcesContent":[null],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"func.js","sources":["../../../../source/helpers/func.ts"],"sourcesContent":[null],"names":[],"mappings":"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"}
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Runtime polyfills for `AbortSignal`, installed only when the host lacks them:
3
+ * `AbortSignal.prototype.throwIfAborted`, `AbortSignal.timeout` and `AbortSignal.any`.
4
+ *
5
+ * Import for its side effect — there are no exports:
6
+ *
7
+ * ```TypeScript
8
+ * import "@brandup/ui-helpers/polyfill";
9
+ * ```
10
+ *
11
+ * Types already ship with the TypeScript `ESNext` lib; this module only fills in the
12
+ * implementations missing in older runtimes, matching the standard behaviour.
13
+ */
14
+ if (typeof AbortSignal.prototype.throwIfAborted !== "function") {
15
+ AbortSignal.prototype.throwIfAborted = function () {
16
+ if (this.aborted)
17
+ throw this.reason;
18
+ };
19
+ }
20
+ if (typeof AbortSignal.timeout !== "function") {
21
+ AbortSignal.timeout = (milliseconds) => {
22
+ const controller = new AbortController();
23
+ setTimeout(() => controller.abort(new DOMException("The operation timed out.", "TimeoutError")), milliseconds);
24
+ return controller.signal;
25
+ };
26
+ }
27
+ if (typeof AbortSignal.any !== "function") {
28
+ AbortSignal.any = (signals) => {
29
+ const controller = new AbortController();
30
+ for (const signal of signals) {
31
+ // If one is already aborted, the combined signal aborts immediately with its reason.
32
+ if (signal.aborted) {
33
+ controller.abort(signal.reason);
34
+ break;
35
+ }
36
+ // Tying each listener to the combined signal removes them all once it aborts — no leak.
37
+ signal.addEventListener("abort", () => controller.abort(signal.reason), { signal: controller.signal });
38
+ }
39
+ return controller.signal;
40
+ };
41
+ }
42
+ //# sourceMappingURL=polyfill.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"polyfill.js","sources":["../../../source/polyfill.ts"],"sourcesContent":[null],"names":[],"mappings":"AAAA;;;;;;;;;;;;AAYG;AAEH,IAAI,OAAO,WAAW,CAAC,SAAS,CAAC,cAAc,KAAK,UAAU,EAAE;AAC/D,IAAA,WAAW,CAAC,SAAS,CAAC,cAAc,GAAG,YAAA;QACtC,IAAI,IAAI,CAAC,OAAO;YACf,MAAM,IAAI,CAAC,MAAM;AACnB,IAAA,CAAC;AACF;AAEA,IAAI,OAAO,WAAW,CAAC,OAAO,KAAK,UAAU,EAAE;AAC9C,IAAA,WAAW,CAAC,OAAO,GAAG,CAAC,YAAoB,KAAiB;AAC3D,QAAA,MAAM,UAAU,GAAG,IAAI,eAAe,EAAE;AACxC,QAAA,UAAU,CAAC,MAAM,UAAU,CAAC,KAAK,CAAC,IAAI,YAAY,CAAC,0BAA0B,EAAE,cAAc,CAAC,CAAC,EAAE,YAAY,CAAC;QAC9G,OAAO,UAAU,CAAC,MAAM;AACzB,IAAA,CAAC;AACF;AAEA,IAAI,OAAO,WAAW,CAAC,GAAG,KAAK,UAAU,EAAE;AAC1C,IAAA,WAAW,CAAC,GAAG,GAAG,CAAC,OAAsB,KAAiB;AACzD,QAAA,MAAM,UAAU,GAAG,IAAI,eAAe,EAAE;AAExC,QAAA,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE;;AAE7B,YAAA,IAAI,MAAM,CAAC,OAAO,EAAE;AACnB,gBAAA,UAAU,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC;gBAC/B;YACD;;YAGA,MAAM,CAAC,gBAAgB,CAAC,OAAO,EAAE,MAAM,UAAU,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,UAAU,CAAC,MAAM,EAAE,CAAC;QACvG;QAEA,OAAO,UAAU,CAAC,MAAM;AACzB,IAAA,CAAC;AACF"}
@@ -0,0 +1,2 @@
1
+
2
+ export { };
package/dist/types.d.ts CHANGED
@@ -74,10 +74,13 @@ declare const minWait: (func: (...args: any[]) => void, minTime?: number) => (..
74
74
  * Useful for keeping spinners/loading states visible for a minimum duration. If `minTime`
75
75
  * is omitted or falsy, `func` is awaited and its result returned without padding.
76
76
  *
77
+ * An already-aborted signal rejects immediately, before `func` runs; aborting later cancels
78
+ * the padding delay (but not `func` itself, which can't be cancelled here).
79
+ *
77
80
  * @typeParam TResult Result type produced by `func`.
78
81
  * @param func Factory returning the promise to await.
79
82
  * @param minTime Minimum total duration in milliseconds.
80
- * @param abort Optional signal used to cancel the padding delay.
83
+ * @param abort Optional signal used to cancel the wait.
81
84
  * @returns The result produced by `func`.
82
85
  */
83
86
  declare function minWaitAsync<TResult = unknown>(func: () => Promise<TResult>, minTime?: number, abort?: AbortSignal): Promise<TResult>;
@@ -87,11 +90,13 @@ declare function minWaitAsync<TResult = unknown>(func: () => Promise<TResult>, m
87
90
  * If an already-aborted signal is supplied the promise rejects immediately; otherwise
88
91
  * aborting before the delay elapses clears the timer and rejects with the abort reason.
89
92
  *
90
- * @param time Delay in milliseconds.
93
+ * @param ms Delay in milliseconds; must not be negative. Values above 2147483647 (~24.8 days)
94
+ * overflow the timer and fire on the next tick — a `setTimeout` limitation.
91
95
  * @param abort Optional signal used to cancel the delay.
92
96
  * @returns A promise that resolves when the delay elapses.
97
+ * @throws {Error} When `ms` is negative.
93
98
  */
94
- declare function delay(time: number, abort?: AbortSignal): Promise<void>;
99
+ declare function delay(ms: number, abort?: AbortSignal): Promise<void>;
95
100
  /**
96
101
  * Races a promise against a timeout.
97
102
  *
@@ -101,12 +106,26 @@ declare function delay(time: number, abort?: AbortSignal): Promise<void>;
101
106
  *
102
107
  * @typeParam T Resolved value type of the wrapped promise.
103
108
  * @param promise Promise to guard with a timeout.
104
- * @param timeout Timeout in milliseconds; must be greater than `0`.
109
+ * @param ms Timeout in milliseconds; must be greater than `0`. Values above 2147483647
110
+ * (~24.8 days) overflow the timer and fire on the next tick — a `setTimeout` limitation.
105
111
  * @param abort Optional signal used to cancel the wait.
106
112
  * @returns A promise mirroring `promise` unless the timeout or abort fires first.
107
- * @throws {Error} When `timeout` is not greater than `0`.
113
+ * @throws {Error} When `ms` is not greater than `0`.
114
+ */
115
+ declare function timeout<T = unknown>(promise: Promise<T>, ms: number, abort?: AbortSignal): Promise<T>;
116
+ /**
117
+ * Makes *waiting* for a promise abortable: rejects with the signal's reason on abort.
118
+ * Does not stop the underlying work the promise performs.
119
+ *
120
+ * If no signal is supplied the promise is awaited as-is. An already-aborted signal rejects
121
+ * immediately; otherwise the abort listener is removed once the promise settles.
122
+ *
123
+ * @typeParam T Resolved value type of the wrapped promise.
124
+ * @param promise Promise (or thenable) whose wait should become abortable.
125
+ * @param abort Optional signal used to cancel the wait.
126
+ * @returns A promise mirroring `promise` unless the abort fires first.
108
127
  */
109
- declare function timeout<T = unknown>(promise: Promise<T>, timeout: number, abort?: AbortSignal): Promise<T>;
128
+ declare function abortable<T>(promise: PromiseLike<T>, abort?: AbortSignal): Promise<T>;
110
129
  /** Thrown by {@link timeout} when the time limit is exceeded. */
111
130
  declare class TimeoutError extends Error {
112
131
  constructor();
@@ -114,6 +133,7 @@ declare class TimeoutError extends Error {
114
133
 
115
134
  type func_TimeoutError = TimeoutError;
116
135
  declare const func_TimeoutError: typeof TimeoutError;
136
+ declare const func_abortable: typeof abortable;
117
137
  declare const func_delay: typeof delay;
118
138
  declare const func_minWait: typeof minWait;
119
139
  declare const func_minWaitAsync: typeof minWaitAsync;
@@ -121,6 +141,7 @@ declare const func_timeout: typeof timeout;
121
141
  declare namespace func {
122
142
  export {
123
143
  func_TimeoutError as TimeoutError,
144
+ func_abortable as abortable,
124
145
  func_delay as delay,
125
146
  func_minWait as minWait,
126
147
  func_minWaitAsync as minWaitAsync,
package/package.json CHANGED
@@ -22,16 +22,24 @@
22
22
  "email": "it@brandup.online"
23
23
  },
24
24
  "license": "Apache-2.0",
25
- "version": "2.0.2",
25
+ "version": "2.0.4",
26
26
  "main": "dist/cjs/index.js",
27
27
  "module": "dist/mjs/index.js",
28
28
  "types": "dist/types.d.ts",
29
- "sideEffects": false,
29
+ "sideEffects": [
30
+ "./dist/cjs/polyfill.js",
31
+ "./dist/mjs/polyfill.js"
32
+ ],
30
33
  "exports": {
31
34
  ".": {
32
35
  "types": "./dist/types.d.ts",
33
36
  "import": "./dist/mjs/index.js",
34
37
  "require": "./dist/cjs/index.js"
38
+ },
39
+ "./polyfill": {
40
+ "types": "./dist/polyfill.d.ts",
41
+ "import": "./dist/mjs/polyfill.js",
42
+ "require": "./dist/cjs/polyfill.js"
35
43
  }
36
44
  },
37
45
  "files": [