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.
- package/LICENSE +1 -1
- package/README.md +612 -0
- package/dist/core/act.cjs +187 -37
- package/dist/core/act.d.ts +81 -10
- package/dist/core/act.d.ts.map +1 -1
- package/dist/core/act.js +184 -36
- package/dist/core/act.js.map +1 -1
- package/dist/core/executor.cjs +30 -4
- package/dist/core/executor.d.ts +33 -11
- package/dist/core/executor.d.ts.map +1 -1
- package/dist/core/executor.js +29 -4
- package/dist/core/executor.js.map +1 -1
- package/dist/index.cjs +15 -5
- package/dist/index.d.ts +9 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +9 -5
- package/dist/index.js.map +1 -1
- package/dist/policies/cache.cjs +91 -7
- package/dist/policies/cache.d.ts +30 -3
- package/dist/policies/cache.d.ts.map +1 -1
- package/dist/policies/cache.js +91 -7
- package/dist/policies/cache.js.map +1 -1
- package/dist/policies/dedupe.cjs +79 -16
- package/dist/policies/dedupe.d.ts +39 -7
- package/dist/policies/dedupe.d.ts.map +1 -1
- package/dist/policies/dedupe.js +79 -16
- package/dist/policies/dedupe.js.map +1 -1
- package/dist/policies/retry.cjs +65 -25
- package/dist/policies/retry.d.ts +25 -2
- package/dist/policies/retry.d.ts.map +1 -1
- package/dist/policies/retry.js +65 -25
- package/dist/policies/retry.js.map +1 -1
- package/dist/policies/timeout.cjs +87 -17
- package/dist/policies/timeout.d.ts +28 -8
- package/dist/policies/timeout.d.ts.map +1 -1
- package/dist/policies/timeout.js +87 -17
- package/dist/policies/timeout.js.map +1 -1
- package/dist/state/store.cjs +11 -38
- package/dist/state/store.d.ts +10 -12
- package/dist/state/store.d.ts.map +1 -1
- package/dist/state/store.js +10 -37
- package/dist/state/store.js.map +1 -1
- package/dist/stores/base.cjs +20 -0
- package/dist/stores/base.d.ts +88 -0
- package/dist/stores/base.d.ts.map +1 -0
- package/dist/stores/base.js +17 -0
- package/dist/stores/base.js.map +1 -0
- package/dist/stores/memory.cjs +140 -0
- package/dist/stores/memory.d.ts +79 -0
- package/dist/stores/memory.d.ts.map +1 -0
- package/dist/stores/memory.js +137 -0
- package/dist/stores/memory.js.map +1 -0
- package/dist/types/index.d.ts +151 -34
- package/dist/types/index.d.ts.map +1 -1
- package/dist/utils/abort.cjs +142 -0
- package/dist/utils/abort.d.ts +55 -0
- package/dist/utils/abort.d.ts.map +1 -0
- package/dist/utils/abort.js +136 -0
- package/dist/utils/abort.js.map +1 -0
- package/dist/utils/backoff.cjs +43 -0
- package/dist/utils/backoff.d.ts +14 -0
- package/dist/utils/backoff.d.ts.map +1 -0
- package/dist/utils/backoff.js +41 -0
- package/dist/utils/backoff.js.map +1 -0
- package/dist/utils/validate.cjs +79 -0
- package/dist/utils/validate.d.ts +15 -0
- package/dist/utils/validate.d.ts.map +1 -0
- package/dist/utils/validate.js +72 -0
- package/dist/utils/validate.js.map +1 -0
- 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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"}
|