actly 1.0.2 → 1.1.5

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 (70) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +612 -0
  3. package/dist/core/act.cjs +187 -37
  4. package/dist/core/act.d.ts +81 -10
  5. package/dist/core/act.d.ts.map +1 -1
  6. package/dist/core/act.js +184 -36
  7. package/dist/core/act.js.map +1 -1
  8. package/dist/core/executor.cjs +30 -4
  9. package/dist/core/executor.d.ts +33 -11
  10. package/dist/core/executor.d.ts.map +1 -1
  11. package/dist/core/executor.js +29 -4
  12. package/dist/core/executor.js.map +1 -1
  13. package/dist/index.cjs +15 -5
  14. package/dist/index.d.ts +9 -4
  15. package/dist/index.d.ts.map +1 -1
  16. package/dist/index.js +9 -5
  17. package/dist/index.js.map +1 -1
  18. package/dist/policies/cache.cjs +91 -7
  19. package/dist/policies/cache.d.ts +30 -3
  20. package/dist/policies/cache.d.ts.map +1 -1
  21. package/dist/policies/cache.js +91 -7
  22. package/dist/policies/cache.js.map +1 -1
  23. package/dist/policies/dedupe.cjs +79 -16
  24. package/dist/policies/dedupe.d.ts +39 -7
  25. package/dist/policies/dedupe.d.ts.map +1 -1
  26. package/dist/policies/dedupe.js +79 -16
  27. package/dist/policies/dedupe.js.map +1 -1
  28. package/dist/policies/retry.cjs +65 -25
  29. package/dist/policies/retry.d.ts +25 -2
  30. package/dist/policies/retry.d.ts.map +1 -1
  31. package/dist/policies/retry.js +65 -25
  32. package/dist/policies/retry.js.map +1 -1
  33. package/dist/policies/timeout.cjs +87 -17
  34. package/dist/policies/timeout.d.ts +28 -8
  35. package/dist/policies/timeout.d.ts.map +1 -1
  36. package/dist/policies/timeout.js +87 -17
  37. package/dist/policies/timeout.js.map +1 -1
  38. package/dist/state/store.cjs +11 -38
  39. package/dist/state/store.d.ts +10 -12
  40. package/dist/state/store.d.ts.map +1 -1
  41. package/dist/state/store.js +10 -37
  42. package/dist/state/store.js.map +1 -1
  43. package/dist/stores/base.cjs +20 -0
  44. package/dist/stores/base.d.ts +88 -0
  45. package/dist/stores/base.d.ts.map +1 -0
  46. package/dist/stores/base.js +17 -0
  47. package/dist/stores/base.js.map +1 -0
  48. package/dist/stores/memory.cjs +140 -0
  49. package/dist/stores/memory.d.ts +79 -0
  50. package/dist/stores/memory.d.ts.map +1 -0
  51. package/dist/stores/memory.js +137 -0
  52. package/dist/stores/memory.js.map +1 -0
  53. package/dist/types/index.d.ts +151 -34
  54. package/dist/types/index.d.ts.map +1 -1
  55. package/dist/utils/abort.cjs +142 -0
  56. package/dist/utils/abort.d.ts +55 -0
  57. package/dist/utils/abort.d.ts.map +1 -0
  58. package/dist/utils/abort.js +136 -0
  59. package/dist/utils/abort.js.map +1 -0
  60. package/dist/utils/backoff.cjs +43 -0
  61. package/dist/utils/backoff.d.ts +14 -0
  62. package/dist/utils/backoff.d.ts.map +1 -0
  63. package/dist/utils/backoff.js +41 -0
  64. package/dist/utils/backoff.js.map +1 -0
  65. package/dist/utils/validate.cjs +79 -0
  66. package/dist/utils/validate.d.ts +15 -0
  67. package/dist/utils/validate.d.ts.map +1 -0
  68. package/dist/utils/validate.js +72 -0
  69. package/dist/utils/validate.js.map +1 -0
  70. package/package.json +19 -37
@@ -0,0 +1,142 @@
1
+ "use strict";
2
+ // ─── AbortSignal helpers ──────────────────────────────────────────────────────
3
+ //
4
+ // Centralised utilities for composing AbortSignals. These exist because
5
+ // Node 18 lacks `AbortSignal.any` (added in Node 20) and we want to keep
6
+ // the `engines` floor at 18 for backwards compatibility with existing
7
+ // consumers.
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.anySignal = anySignal;
10
+ exports.raceAbort = raceAbort;
11
+ exports.sleep = sleep;
12
+ exports.isAbortError = isAbortError;
13
+ exports.linkSignal = linkSignal;
14
+ /**
15
+ * Polyfill for `AbortSignal.any(signals)` (Node 20+).
16
+ *
17
+ * Returns a single signal that aborts when ANY of the input signals aborts,
18
+ * with the same reason. If any input is already aborted, the returned signal
19
+ * is aborted synchronously.
20
+ *
21
+ * Listener registration is `{ once: true }` — once any signal fires, we stop
22
+ * listening on the others. The composite signal cannot be "un-aborted".
23
+ */
24
+ function anySignal(signals) {
25
+ // Fast path: native implementation (Node 20+, modern browsers, Bun).
26
+ // The cast is safe — the runtime check guards the call.
27
+ const native = AbortSignal.any;
28
+ if (typeof native === 'function')
29
+ return native.call(AbortSignal, signals);
30
+ // Polyfill for Node 18.
31
+ const controller = new AbortController();
32
+ for (const signal of signals) {
33
+ if (signal.aborted) {
34
+ controller.abort(signal.reason);
35
+ break;
36
+ }
37
+ signal.addEventListener('abort', () => controller.abort(signal.reason), { once: true });
38
+ }
39
+ return controller.signal;
40
+ }
41
+ /**
42
+ * Race a promise against an AbortSignal.
43
+ *
44
+ * - If the signal is already aborted, rejects immediately with `signal.reason`.
45
+ * - If the signal aborts while the promise is pending, rejects with `signal.reason`.
46
+ * - If the promise settles first, returns its value (or rejects with its error).
47
+ *
48
+ * The listener is registered with `{ once: true }` and never leaks: either
49
+ * the signal fires (listener auto-removed) or the promise settles (the
50
+ * signal will eventually be GC'd along with the listener).
51
+ *
52
+ * Used by `dedupePolicy` so joiners can cancel their own `await` even if the
53
+ * originator's `fn` is still running.
54
+ */
55
+ function raceAbort(promise, signal) {
56
+ if (signal.aborted)
57
+ return Promise.reject(signal.reason);
58
+ return new Promise((resolve, reject) => {
59
+ let settled = false;
60
+ const onAbort = () => {
61
+ if (settled)
62
+ return;
63
+ settled = true;
64
+ reject(signal.reason);
65
+ };
66
+ signal.addEventListener('abort', onAbort, { once: true });
67
+ promise.then((value) => {
68
+ if (settled)
69
+ return;
70
+ settled = true;
71
+ resolve(value);
72
+ }, (error) => {
73
+ if (settled)
74
+ return;
75
+ settled = true;
76
+ reject(error);
77
+ });
78
+ });
79
+ }
80
+ /**
81
+ * Sleep for `ms` milliseconds, but abort early if `signal` fires.
82
+ *
83
+ * Resolves normally on timer expiry. Rejects with `signal.reason` if the
84
+ * signal aborts before the timer fires. If the signal is already aborted
85
+ * when called, rejects synchronously (in microtask).
86
+ *
87
+ * Used by `retryPolicy` to make backoff delays interruptible: when an outer
88
+ * `totalTimeout` fires mid-delay, the delay rejects immediately instead of
89
+ * blocking the retry loop until the timer would have elapsed.
90
+ */
91
+ function sleep(ms, signal) {
92
+ if (ms <= 0) {
93
+ if (signal?.aborted)
94
+ return Promise.reject(signal.reason);
95
+ return Promise.resolve();
96
+ }
97
+ if (signal?.aborted)
98
+ return Promise.reject(signal.reason);
99
+ return new Promise((resolve, reject) => {
100
+ const timer = setTimeout(() => {
101
+ signal?.removeEventListener('abort', onAbort);
102
+ resolve();
103
+ }, ms);
104
+ const onAbort = () => {
105
+ clearTimeout(timer);
106
+ reject(signal.reason);
107
+ };
108
+ signal?.addEventListener('abort', onAbort, { once: true });
109
+ });
110
+ }
111
+ /**
112
+ * True if `err` is an `AbortError` (DOMException name or Error name).
113
+ *
114
+ * The default `shouldRetry` predicate uses this to skip retrying on
115
+ * cancellations — if the caller aborted, retrying would just abort again.
116
+ */
117
+ function isAbortError(err) {
118
+ if (err == null || typeof err !== 'object')
119
+ return false;
120
+ const name = err.name;
121
+ return name === 'AbortError' || name === 'TimeoutError' &&
122
+ err instanceof Error &&
123
+ // DOMException with name 'TimeoutError' is what AbortSignal.timeout throws.
124
+ // Distinguish from our own TimeoutError class by checking for DOMException.
125
+ typeof DOMException !== 'undefined' &&
126
+ err instanceof DOMException;
127
+ }
128
+ /**
129
+ * Link a parent signal to a child controller: when the parent aborts, the
130
+ * child is aborted with the same reason. No-op if the parent is already
131
+ * aborted (the caller should check `parent.aborted` separately if it cares
132
+ * about synchronous abort).
133
+ *
134
+ * The listener is `{ once: true }` — no leak.
135
+ */
136
+ function linkSignal(parent, child) {
137
+ if (parent.aborted) {
138
+ child.abort(parent.reason);
139
+ return;
140
+ }
141
+ parent.addEventListener('abort', () => child.abort(parent.reason), { once: true });
142
+ }
@@ -0,0 +1,55 @@
1
+ /**
2
+ * Polyfill for `AbortSignal.any(signals)` (Node 20+).
3
+ *
4
+ * Returns a single signal that aborts when ANY of the input signals aborts,
5
+ * with the same reason. If any input is already aborted, the returned signal
6
+ * is aborted synchronously.
7
+ *
8
+ * Listener registration is `{ once: true }` — once any signal fires, we stop
9
+ * listening on the others. The composite signal cannot be "un-aborted".
10
+ */
11
+ export declare function anySignal(signals: ReadonlyArray<AbortSignal>): AbortSignal;
12
+ /**
13
+ * Race a promise against an AbortSignal.
14
+ *
15
+ * - If the signal is already aborted, rejects immediately with `signal.reason`.
16
+ * - If the signal aborts while the promise is pending, rejects with `signal.reason`.
17
+ * - If the promise settles first, returns its value (or rejects with its error).
18
+ *
19
+ * The listener is registered with `{ once: true }` and never leaks: either
20
+ * the signal fires (listener auto-removed) or the promise settles (the
21
+ * signal will eventually be GC'd along with the listener).
22
+ *
23
+ * Used by `dedupePolicy` so joiners can cancel their own `await` even if the
24
+ * originator's `fn` is still running.
25
+ */
26
+ export declare function raceAbort<T>(promise: Promise<T>, signal: AbortSignal): Promise<T>;
27
+ /**
28
+ * Sleep for `ms` milliseconds, but abort early if `signal` fires.
29
+ *
30
+ * Resolves normally on timer expiry. Rejects with `signal.reason` if the
31
+ * signal aborts before the timer fires. If the signal is already aborted
32
+ * when called, rejects synchronously (in microtask).
33
+ *
34
+ * Used by `retryPolicy` to make backoff delays interruptible: when an outer
35
+ * `totalTimeout` fires mid-delay, the delay rejects immediately instead of
36
+ * blocking the retry loop until the timer would have elapsed.
37
+ */
38
+ export declare function sleep(ms: number, signal?: AbortSignal): Promise<void>;
39
+ /**
40
+ * True if `err` is an `AbortError` (DOMException name or Error name).
41
+ *
42
+ * The default `shouldRetry` predicate uses this to skip retrying on
43
+ * cancellations — if the caller aborted, retrying would just abort again.
44
+ */
45
+ export declare function isAbortError(err: unknown): boolean;
46
+ /**
47
+ * Link a parent signal to a child controller: when the parent aborts, the
48
+ * child is aborted with the same reason. No-op if the parent is already
49
+ * aborted (the caller should check `parent.aborted` separately if it cares
50
+ * about synchronous abort).
51
+ *
52
+ * The listener is `{ once: true }` — no leak.
53
+ */
54
+ export declare function linkSignal(parent: AbortSignal, child: AbortController): void;
55
+ //# sourceMappingURL=abort.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"abort.d.ts","sourceRoot":"","sources":["../../src/utils/abort.ts"],"names":[],"mappings":"AAOA;;;;;;;;;GASG;AACH,wBAAgB,SAAS,CAAC,OAAO,EAAE,aAAa,CAAC,WAAW,CAAC,GAAG,WAAW,CAwB1E;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,SAAS,CAAC,CAAC,EACzB,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,EACnB,MAAM,EAAE,WAAW,GAClB,OAAO,CAAC,CAAC,CAAC,CA2BZ;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,KAAK,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAqBrE;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CASlD;AAED;;;;;;;GAOG;AACH,wBAAgB,UAAU,CACxB,MAAM,EAAE,WAAW,EACnB,KAAK,EAAE,eAAe,GACrB,IAAI,CAUN"}
@@ -0,0 +1,136 @@
1
+ // ─── AbortSignal helpers ──────────────────────────────────────────────────────
2
+ //
3
+ // Centralised utilities for composing AbortSignals. These exist because
4
+ // Node 18 lacks `AbortSignal.any` (added in Node 20) and we want to keep
5
+ // the `engines` floor at 18 for backwards compatibility with existing
6
+ // consumers.
7
+ /**
8
+ * Polyfill for `AbortSignal.any(signals)` (Node 20+).
9
+ *
10
+ * Returns a single signal that aborts when ANY of the input signals aborts,
11
+ * with the same reason. If any input is already aborted, the returned signal
12
+ * is aborted synchronously.
13
+ *
14
+ * Listener registration is `{ once: true }` — once any signal fires, we stop
15
+ * listening on the others. The composite signal cannot be "un-aborted".
16
+ */
17
+ export function anySignal(signals) {
18
+ // Fast path: native implementation (Node 20+, modern browsers, Bun).
19
+ // The cast is safe — the runtime check guards the call.
20
+ const native = AbortSignal.any;
21
+ if (typeof native === 'function')
22
+ return native.call(AbortSignal, signals);
23
+ // Polyfill for Node 18.
24
+ const controller = new AbortController();
25
+ for (const signal of signals) {
26
+ if (signal.aborted) {
27
+ controller.abort(signal.reason);
28
+ break;
29
+ }
30
+ signal.addEventListener('abort', () => controller.abort(signal.reason), { once: true });
31
+ }
32
+ return controller.signal;
33
+ }
34
+ /**
35
+ * Race a promise against an AbortSignal.
36
+ *
37
+ * - If the signal is already aborted, rejects immediately with `signal.reason`.
38
+ * - If the signal aborts while the promise is pending, rejects with `signal.reason`.
39
+ * - If the promise settles first, returns its value (or rejects with its error).
40
+ *
41
+ * The listener is registered with `{ once: true }` and never leaks: either
42
+ * the signal fires (listener auto-removed) or the promise settles (the
43
+ * signal will eventually be GC'd along with the listener).
44
+ *
45
+ * Used by `dedupePolicy` so joiners can cancel their own `await` even if the
46
+ * originator's `fn` is still running.
47
+ */
48
+ export function raceAbort(promise, signal) {
49
+ if (signal.aborted)
50
+ return Promise.reject(signal.reason);
51
+ return new Promise((resolve, reject) => {
52
+ let settled = false;
53
+ const onAbort = () => {
54
+ if (settled)
55
+ return;
56
+ settled = true;
57
+ reject(signal.reason);
58
+ };
59
+ signal.addEventListener('abort', onAbort, { once: true });
60
+ promise.then((value) => {
61
+ if (settled)
62
+ return;
63
+ settled = true;
64
+ resolve(value);
65
+ }, (error) => {
66
+ if (settled)
67
+ return;
68
+ settled = true;
69
+ reject(error);
70
+ });
71
+ });
72
+ }
73
+ /**
74
+ * Sleep for `ms` milliseconds, but abort early if `signal` fires.
75
+ *
76
+ * Resolves normally on timer expiry. Rejects with `signal.reason` if the
77
+ * signal aborts before the timer fires. If the signal is already aborted
78
+ * when called, rejects synchronously (in microtask).
79
+ *
80
+ * Used by `retryPolicy` to make backoff delays interruptible: when an outer
81
+ * `totalTimeout` fires mid-delay, the delay rejects immediately instead of
82
+ * blocking the retry loop until the timer would have elapsed.
83
+ */
84
+ export function sleep(ms, signal) {
85
+ if (ms <= 0) {
86
+ if (signal?.aborted)
87
+ return Promise.reject(signal.reason);
88
+ return Promise.resolve();
89
+ }
90
+ if (signal?.aborted)
91
+ return Promise.reject(signal.reason);
92
+ return new Promise((resolve, reject) => {
93
+ const timer = setTimeout(() => {
94
+ signal?.removeEventListener('abort', onAbort);
95
+ resolve();
96
+ }, ms);
97
+ const onAbort = () => {
98
+ clearTimeout(timer);
99
+ reject(signal.reason);
100
+ };
101
+ signal?.addEventListener('abort', onAbort, { once: true });
102
+ });
103
+ }
104
+ /**
105
+ * True if `err` is an `AbortError` (DOMException name or Error name).
106
+ *
107
+ * The default `shouldRetry` predicate uses this to skip retrying on
108
+ * cancellations — if the caller aborted, retrying would just abort again.
109
+ */
110
+ export function isAbortError(err) {
111
+ if (err == null || typeof err !== 'object')
112
+ return false;
113
+ const name = err.name;
114
+ return name === 'AbortError' || name === 'TimeoutError' &&
115
+ err instanceof Error &&
116
+ // DOMException with name 'TimeoutError' is what AbortSignal.timeout throws.
117
+ // Distinguish from our own TimeoutError class by checking for DOMException.
118
+ typeof DOMException !== 'undefined' &&
119
+ err instanceof DOMException;
120
+ }
121
+ /**
122
+ * Link a parent signal to a child controller: when the parent aborts, the
123
+ * child is aborted with the same reason. No-op if the parent is already
124
+ * aborted (the caller should check `parent.aborted` separately if it cares
125
+ * about synchronous abort).
126
+ *
127
+ * The listener is `{ once: true }` — no leak.
128
+ */
129
+ export function linkSignal(parent, child) {
130
+ if (parent.aborted) {
131
+ child.abort(parent.reason);
132
+ return;
133
+ }
134
+ parent.addEventListener('abort', () => child.abort(parent.reason), { once: true });
135
+ }
136
+ //# sourceMappingURL=abort.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"abort.js","sourceRoot":"","sources":["../../src/utils/abort.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,43 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.computeDelay = computeDelay;
4
+ /**
5
+ * Compute the delay before the next retry attempt, applying backoff, cap, and
6
+ * jitter in that order.
7
+ *
8
+ * Order matters:
9
+ * 1. `backoff` grows the base delay geometrically/linearly.
10
+ * 2. `maxDelay` caps the result (prevents exponential blowup).
11
+ * 3. `jitter` randomises within `[0, delay]` (prevents thundering herd).
12
+ *
13
+ * Returns 0 if `delayMs` is 0 or undefined — skipping the sleep entirely.
14
+ */
15
+ function computeDelay(attempt, opts) {
16
+ const base = opts.delayMs ?? 0;
17
+ if (base === 0)
18
+ return 0;
19
+ // Step 1: backoff
20
+ let delay;
21
+ switch (opts.backoff ?? 'none') {
22
+ case 'linear':
23
+ delay = base * attempt;
24
+ break;
25
+ case 'exponential':
26
+ delay = base * 2 ** (attempt - 1);
27
+ break;
28
+ default: delay = base;
29
+ }
30
+ // Step 2: cap (guard against Infinity and NaN before Math.min)
31
+ const max = opts.maxDelay ?? Number.POSITIVE_INFINITY;
32
+ if (!Number.isFinite(delay))
33
+ delay = max;
34
+ delay = Math.min(delay, max);
35
+ // Step 3: jitter
36
+ switch (opts.jitter ?? 'full') {
37
+ case 'none': return delay;
38
+ case 'full': return Math.random() * delay;
39
+ case 'equal': return delay / 2 + Math.random() * delay / 2;
40
+ case 'decorrelated': return base + Math.random() * (delay - base);
41
+ default: return delay;
42
+ }
43
+ }
@@ -0,0 +1,14 @@
1
+ import type { RetryOptions } from '../types/index.js';
2
+ /**
3
+ * Compute the delay before the next retry attempt, applying backoff, cap, and
4
+ * jitter in that order.
5
+ *
6
+ * Order matters:
7
+ * 1. `backoff` grows the base delay geometrically/linearly.
8
+ * 2. `maxDelay` caps the result (prevents exponential blowup).
9
+ * 3. `jitter` randomises within `[0, delay]` (prevents thundering herd).
10
+ *
11
+ * Returns 0 if `delayMs` is 0 or undefined — skipping the sleep entirely.
12
+ */
13
+ export declare function computeDelay(attempt: number, opts: RetryOptions): number;
14
+ //# sourceMappingURL=backoff.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"backoff.d.ts","sourceRoot":"","sources":["../../src/utils/backoff.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAA;AAErD;;;;;;;;;;GAUG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,YAAY,GAAG,MAAM,CAyBxE"}
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Compute the delay before the next retry attempt, applying backoff, cap, and
3
+ * jitter in that order.
4
+ *
5
+ * Order matters:
6
+ * 1. `backoff` grows the base delay geometrically/linearly.
7
+ * 2. `maxDelay` caps the result (prevents exponential blowup).
8
+ * 3. `jitter` randomises within `[0, delay]` (prevents thundering herd).
9
+ *
10
+ * Returns 0 if `delayMs` is 0 or undefined — skipping the sleep entirely.
11
+ */
12
+ export function computeDelay(attempt, opts) {
13
+ const base = opts.delayMs ?? 0;
14
+ if (base === 0)
15
+ return 0;
16
+ // Step 1: backoff
17
+ let delay;
18
+ switch (opts.backoff ?? 'none') {
19
+ case 'linear':
20
+ delay = base * attempt;
21
+ break;
22
+ case 'exponential':
23
+ delay = base * 2 ** (attempt - 1);
24
+ break;
25
+ default: delay = base;
26
+ }
27
+ // Step 2: cap (guard against Infinity and NaN before Math.min)
28
+ const max = opts.maxDelay ?? Number.POSITIVE_INFINITY;
29
+ if (!Number.isFinite(delay))
30
+ delay = max;
31
+ delay = Math.min(delay, max);
32
+ // Step 3: jitter
33
+ switch (opts.jitter ?? 'full') {
34
+ case 'none': return delay;
35
+ case 'full': return Math.random() * delay;
36
+ case 'equal': return delay / 2 + Math.random() * delay / 2;
37
+ case 'decorrelated': return base + Math.random() * (delay - base);
38
+ default: return delay;
39
+ }
40
+ }
41
+ //# sourceMappingURL=backoff.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"backoff.js","sourceRoot":"","sources":["../../src/utils/backoff.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;GAUG;AACH,MAAM,UAAU,YAAY,CAAC,OAAe,EAAE,IAAkB;IAC9D,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,IAAI,CAAC,CAAA;IAC9B,IAAI,IAAI,KAAK,CAAC;QAAE,OAAO,CAAC,CAAA;IAExB,kBAAkB;IAClB,IAAI,KAAa,CAAA;IACjB,QAAQ,IAAI,CAAC,OAAO,IAAI,MAAM,EAAE,CAAC;QAC/B,KAAK,QAAQ;YAAO,KAAK,GAAG,IAAI,GAAG,OAAO,CAAC;YAAC,MAAK;QACjD,KAAK,aAAa;YAAE,KAAK,GAAG,IAAI,GAAG,CAAC,IAAI,CAAC,OAAO,GAAG,CAAC,CAAC,CAAC;YAAC,MAAK;QAC5D,OAAO,CAAC,CAAY,KAAK,GAAG,IAAI,CAAA;IAClC,CAAC;IAED,+DAA+D;IAC/D,MAAM,GAAG,GAAG,IAAI,CAAC,QAAQ,IAAI,MAAM,CAAC,iBAAiB,CAAA;IACrD,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC;QAAE,KAAK,GAAG,GAAG,CAAA;IACxC,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,GAAG,CAAC,CAAA;IAE5B,iBAAiB;IACjB,QAAQ,IAAI,CAAC,MAAM,IAAI,MAAM,EAAE,CAAC;QAC9B,KAAK,MAAM,CAAC,CAAS,OAAO,KAAK,CAAA;QACjC,KAAK,MAAM,CAAC,CAAS,OAAO,IAAI,CAAC,MAAM,EAAE,GAAG,KAAK,CAAA;QACjD,KAAK,OAAO,CAAC,CAAQ,OAAO,KAAK,GAAG,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,GAAG,KAAK,GAAG,CAAC,CAAA;QACjE,KAAK,cAAc,CAAC,CAAC,OAAO,IAAI,GAAG,IAAI,CAAC,MAAM,EAAE,GAAG,CAAC,KAAK,GAAG,IAAI,CAAC,CAAA;QACjE,OAAO,CAAC,CAAa,OAAO,KAAK,CAAA;IACnC,CAAC;AACH,CAAC"}
@@ -0,0 +1,79 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.assertKey = assertKey;
4
+ exports.assertRetryOptions = assertRetryOptions;
5
+ exports.assertTimeoutOptions = assertTimeoutOptions;
6
+ exports.assertCacheOptions = assertCacheOptions;
7
+ exports.assertDedupeOptions = assertDedupeOptions;
8
+ exports.assertOptions = assertOptions;
9
+ /**
10
+ * Validate user-facing option shapes. Throws `RangeError` / `TypeError` on
11
+ * invalid input — these are programmer errors, not runtime failures, so
12
+ * throwing (rather than returning an `ActFailure`) is the right call.
13
+ *
14
+ * Called once at the top of `act()` so policies can assume well-formed input.
15
+ */
16
+ function assertKey(key) {
17
+ if (typeof key !== 'string') {
18
+ throw new TypeError(`Actly: key must be a string, got ${typeof key}`);
19
+ }
20
+ if (key.length === 0) {
21
+ throw new RangeError("Actly: key must be non-empty. An empty key collapses every caller " +
22
+ "onto the same dedupe/cache slot — almost certainly a bug.");
23
+ }
24
+ // Reject reserved internal prefixes so user keys cannot collide with
25
+ // dedupe/cache namespace prefixes.
26
+ if (key.startsWith('dedupe:') || key.startsWith('cache:') || key.startsWith('__inflight:')) {
27
+ throw new RangeError(`Actly: key must not start with reserved prefix "dedupe:", "cache:", or "__inflight:" (got ${JSON.stringify(key)}).`);
28
+ }
29
+ }
30
+ function assertRetryOptions(opts) {
31
+ if (!Number.isInteger(opts.attempts) || opts.attempts < 1) {
32
+ throw new RangeError(`Actly: retry.attempts must be a positive integer, got ${opts.attempts}`);
33
+ }
34
+ if (opts.delayMs !== undefined) {
35
+ assertNonNegativeFinite('retry.delayMs', opts.delayMs);
36
+ }
37
+ if (opts.maxDelay !== undefined) {
38
+ assertNonNegativeFinite('retry.maxDelay', opts.maxDelay);
39
+ }
40
+ if (opts.shouldRetry !== undefined && typeof opts.shouldRetry !== 'function') {
41
+ throw new TypeError(`Actly: retry.shouldRetry must be a function, got ${typeof opts.shouldRetry}`);
42
+ }
43
+ }
44
+ function assertTimeoutOptions(opts, field) {
45
+ if (typeof opts.ms !== 'number' || !Number.isFinite(opts.ms) || opts.ms <= 0) {
46
+ throw new RangeError(`Actly: ${field}.ms must be a positive finite number, got ${opts.ms}`);
47
+ }
48
+ }
49
+ function assertCacheOptions(opts) {
50
+ if (typeof opts.ttl !== 'number' || !Number.isFinite(opts.ttl) || opts.ttl <= 0) {
51
+ throw new RangeError(`Actly: cache.ttl must be a positive finite number, got ${opts.ttl}`);
52
+ }
53
+ }
54
+ function assertDedupeOptions(opts) {
55
+ if (opts.inflightTtl !== undefined) {
56
+ assertNonNegativeFinite('dedupe.inflightTtl', opts.inflightTtl);
57
+ }
58
+ }
59
+ function assertOptions(options) {
60
+ if (options.retry)
61
+ assertRetryOptions(options.retry);
62
+ if (options.timeout)
63
+ assertTimeoutOptions(options.timeout, 'timeout');
64
+ if (options.totalTimeout)
65
+ assertTimeoutOptions(options.totalTimeout, 'totalTimeout');
66
+ if (options.cache)
67
+ assertCacheOptions(options.cache);
68
+ if (options.dedupe && typeof options.dedupe !== 'boolean') {
69
+ assertDedupeOptions(options.dedupe);
70
+ }
71
+ if (options.signal !== undefined && !(options.signal instanceof AbortSignal)) {
72
+ throw new TypeError(`Actly: signal must be an AbortSignal, got ${options.signal === null ? 'null' : typeof options.signal}`);
73
+ }
74
+ }
75
+ function assertNonNegativeFinite(field, value) {
76
+ if (typeof value !== 'number' || !Number.isFinite(value) || value < 0) {
77
+ throw new RangeError(`Actly: ${field} must be a non-negative finite number, got ${value}`);
78
+ }
79
+ }
@@ -0,0 +1,15 @@
1
+ import type { ActOptions, CacheOptions, DedupeOptions, RetryOptions, TimeoutOptions } from '../types/index.js';
2
+ /**
3
+ * Validate user-facing option shapes. Throws `RangeError` / `TypeError` on
4
+ * invalid input — these are programmer errors, not runtime failures, so
5
+ * throwing (rather than returning an `ActFailure`) is the right call.
6
+ *
7
+ * Called once at the top of `act()` so policies can assume well-formed input.
8
+ */
9
+ export declare function assertKey(key: string): void;
10
+ export declare function assertRetryOptions(opts: RetryOptions): void;
11
+ export declare function assertTimeoutOptions(opts: TimeoutOptions, field: string): void;
12
+ export declare function assertCacheOptions(opts: CacheOptions): void;
13
+ export declare function assertDedupeOptions(opts: DedupeOptions): void;
14
+ export declare function assertOptions(options: ActOptions): void;
15
+ //# sourceMappingURL=validate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validate.d.ts","sourceRoot":"","sources":["../../src/utils/validate.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,UAAU,EACV,YAAY,EACZ,aAAa,EACb,YAAY,EACZ,cAAc,EACf,MAAM,mBAAmB,CAAA;AAE1B;;;;;;GAMG;AAEH,wBAAgB,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAiB3C;AAED,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,YAAY,GAAG,IAAI,CAiB3D;AAED,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,cAAc,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAM9E;AAED,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,YAAY,GAAG,IAAI,CAM3D;AAED,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,aAAa,GAAG,IAAI,CAI7D;AAED,wBAAgB,aAAa,CAAC,OAAO,EAAE,UAAU,GAAG,IAAI,CAavD"}
@@ -0,0 +1,72 @@
1
+ /**
2
+ * Validate user-facing option shapes. Throws `RangeError` / `TypeError` on
3
+ * invalid input — these are programmer errors, not runtime failures, so
4
+ * throwing (rather than returning an `ActFailure`) is the right call.
5
+ *
6
+ * Called once at the top of `act()` so policies can assume well-formed input.
7
+ */
8
+ export function assertKey(key) {
9
+ if (typeof key !== 'string') {
10
+ throw new TypeError(`Actly: key must be a string, got ${typeof key}`);
11
+ }
12
+ if (key.length === 0) {
13
+ throw new RangeError("Actly: key must be non-empty. An empty key collapses every caller " +
14
+ "onto the same dedupe/cache slot — almost certainly a bug.");
15
+ }
16
+ // Reject reserved internal prefixes so user keys cannot collide with
17
+ // dedupe/cache namespace prefixes.
18
+ if (key.startsWith('dedupe:') || key.startsWith('cache:') || key.startsWith('__inflight:')) {
19
+ throw new RangeError(`Actly: key must not start with reserved prefix "dedupe:", "cache:", or "__inflight:" (got ${JSON.stringify(key)}).`);
20
+ }
21
+ }
22
+ export function assertRetryOptions(opts) {
23
+ if (!Number.isInteger(opts.attempts) || opts.attempts < 1) {
24
+ throw new RangeError(`Actly: retry.attempts must be a positive integer, got ${opts.attempts}`);
25
+ }
26
+ if (opts.delayMs !== undefined) {
27
+ assertNonNegativeFinite('retry.delayMs', opts.delayMs);
28
+ }
29
+ if (opts.maxDelay !== undefined) {
30
+ assertNonNegativeFinite('retry.maxDelay', opts.maxDelay);
31
+ }
32
+ if (opts.shouldRetry !== undefined && typeof opts.shouldRetry !== 'function') {
33
+ throw new TypeError(`Actly: retry.shouldRetry must be a function, got ${typeof opts.shouldRetry}`);
34
+ }
35
+ }
36
+ export function assertTimeoutOptions(opts, field) {
37
+ if (typeof opts.ms !== 'number' || !Number.isFinite(opts.ms) || opts.ms <= 0) {
38
+ throw new RangeError(`Actly: ${field}.ms must be a positive finite number, got ${opts.ms}`);
39
+ }
40
+ }
41
+ export function assertCacheOptions(opts) {
42
+ if (typeof opts.ttl !== 'number' || !Number.isFinite(opts.ttl) || opts.ttl <= 0) {
43
+ throw new RangeError(`Actly: cache.ttl must be a positive finite number, got ${opts.ttl}`);
44
+ }
45
+ }
46
+ export function assertDedupeOptions(opts) {
47
+ if (opts.inflightTtl !== undefined) {
48
+ assertNonNegativeFinite('dedupe.inflightTtl', opts.inflightTtl);
49
+ }
50
+ }
51
+ export function assertOptions(options) {
52
+ if (options.retry)
53
+ assertRetryOptions(options.retry);
54
+ if (options.timeout)
55
+ assertTimeoutOptions(options.timeout, 'timeout');
56
+ if (options.totalTimeout)
57
+ assertTimeoutOptions(options.totalTimeout, 'totalTimeout');
58
+ if (options.cache)
59
+ assertCacheOptions(options.cache);
60
+ if (options.dedupe && typeof options.dedupe !== 'boolean') {
61
+ assertDedupeOptions(options.dedupe);
62
+ }
63
+ if (options.signal !== undefined && !(options.signal instanceof AbortSignal)) {
64
+ throw new TypeError(`Actly: signal must be an AbortSignal, got ${options.signal === null ? 'null' : typeof options.signal}`);
65
+ }
66
+ }
67
+ function assertNonNegativeFinite(field, value) {
68
+ if (typeof value !== 'number' || !Number.isFinite(value) || value < 0) {
69
+ throw new RangeError(`Actly: ${field} must be a non-negative finite number, got ${value}`);
70
+ }
71
+ }
72
+ //# sourceMappingURL=validate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validate.js","sourceRoot":"","sources":["../../src/utils/validate.ts"],"names":[],"mappings":"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"}