@zap-studio/retry 0.3.0 → 0.3.1
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/CHANGELOG.md +6 -0
- package/README.md +11 -12
- package/dist/abort.d.mts +22 -23
- package/dist/abort.d.mts.map +1 -1
- package/dist/abort.mjs +15 -15
- package/dist/abort.mjs.map +1 -1
- package/dist/errors-BVZjP1Q5.d.mts +76 -0
- package/dist/errors-BVZjP1Q5.d.mts.map +1 -0
- package/dist/errors.d.mts +1 -1
- package/dist/errors.mjs.map +1 -1
- package/dist/exponential-backoff.d.mts +27 -28
- package/dist/exponential-backoff.d.mts.map +1 -1
- package/dist/exponential-backoff.mjs +4 -4
- package/dist/exponential-backoff.mjs.map +1 -1
- package/dist/fixed-delay.d.mts +22 -23
- package/dist/fixed-delay.d.mts.map +1 -1
- package/dist/fixed-delay.mjs +4 -4
- package/dist/fixed-delay.mjs.map +1 -1
- package/dist/index.d.mts +39 -40
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +8 -8
- package/dist/index.mjs.map +1 -1
- package/dist/result-mode.d.mts +14 -19
- package/dist/result-mode.d.mts.map +1 -1
- package/dist/result-mode.mjs +81 -81
- package/dist/result-mode.mjs.map +1 -1
- package/dist/sleep.d.mts +11 -11
- package/dist/sleep.d.mts.map +1 -1
- package/dist/sleep.mjs +5 -3
- package/dist/sleep.mjs.map +1 -1
- package/dist/throw-mode.d.mts +16 -21
- package/dist/throw-mode.d.mts.map +1 -1
- package/dist/throw-mode.mjs +4 -5
- package/dist/throw-mode.mjs.map +1 -1
- package/dist/types.d.mts +83 -85
- package/dist/types.d.mts.map +1 -1
- package/package.json +5 -6
- package/dist/errors-fWo_KyVO.d.mts +0 -76
- package/dist/errors-fWo_KyVO.d.mts.map +0 -1
package/CHANGELOG.md
CHANGED
package/README.md
CHANGED
|
@@ -33,8 +33,7 @@ const data = await exponential.run(async () => {
|
|
|
33
33
|
|
|
34
34
|
`run(...)` throws when retries are exhausted.
|
|
35
35
|
|
|
36
|
-
By default, policies extending `BaseRetryPolicy` throw `RetryError` on exhaustion
|
|
37
|
-
and `AbortError` on cancellation.
|
|
36
|
+
By default, policies extending `BaseRetryPolicy` throw `RetryError` on exhaustion and `AbortError` on cancellation.
|
|
38
37
|
|
|
39
38
|
```ts
|
|
40
39
|
import { AbortError, RetryError } from "@zap-studio/retry/errors";
|
|
@@ -69,7 +68,7 @@ const result = await exponential.run(
|
|
|
69
68
|
});
|
|
70
69
|
return await response.json();
|
|
71
70
|
},
|
|
72
|
-
{ throwOnExhausted: false }
|
|
71
|
+
{ throwOnExhausted: false }
|
|
73
72
|
);
|
|
74
73
|
|
|
75
74
|
if (!result.ok) {
|
|
@@ -102,7 +101,7 @@ const promise = exponential.run(
|
|
|
102
101
|
});
|
|
103
102
|
return await response.json();
|
|
104
103
|
},
|
|
105
|
-
{ signal: controller.signal }
|
|
104
|
+
{ signal: controller.signal }
|
|
106
105
|
);
|
|
107
106
|
|
|
108
107
|
controller.abort(new Error("Request canceled"));
|
|
@@ -110,8 +109,7 @@ controller.abort(new Error("Request canceled"));
|
|
|
110
109
|
await promise;
|
|
111
110
|
```
|
|
112
111
|
|
|
113
|
-
In non-throw mode, abort is returned as `{ ok: false }` with `AbortError` on
|
|
114
|
-
`result.error`:
|
|
112
|
+
In non-throw mode, abort is returned as `{ ok: false }` with `AbortError` on `result.error`:
|
|
115
113
|
|
|
116
114
|
```ts
|
|
117
115
|
const controller = new AbortController();
|
|
@@ -126,7 +124,7 @@ const result = await exponential.run(
|
|
|
126
124
|
{
|
|
127
125
|
signal: controller.signal,
|
|
128
126
|
throwOnExhausted: false,
|
|
129
|
-
}
|
|
127
|
+
}
|
|
130
128
|
);
|
|
131
129
|
|
|
132
130
|
if (!result.ok) {
|
|
@@ -159,18 +157,19 @@ const predictableIntervalPolicy = new FixedDelay({
|
|
|
159
157
|
|
|
160
158
|
Extend `BaseRetryPolicy` when the built-in policies do not match your retry rules.
|
|
161
159
|
|
|
162
|
-
You implement `next(...)` only; the base class supplies `onExhausted` with a default
|
|
163
|
-
`RetryError` and keeps the shared `run(...)` orchestration (override `onExhausted` when
|
|
164
|
-
you need a different terminal error).
|
|
160
|
+
You implement `next(...)` only; the base class supplies `onExhausted` with a default `RetryError` and keeps the shared `run(...)` orchestration (override `onExhausted` when you need a different terminal error).
|
|
165
161
|
|
|
166
162
|
```ts
|
|
167
163
|
import { BaseRetryPolicy } from "@zap-studio/retry";
|
|
168
|
-
import type {
|
|
164
|
+
import type {
|
|
165
|
+
RetryDecision,
|
|
166
|
+
RetryDecisionInput,
|
|
167
|
+
} from "@zap-studio/retry/types";
|
|
169
168
|
|
|
170
169
|
class LinearBackoff extends BaseRetryPolicy {
|
|
171
170
|
constructor(
|
|
172
171
|
private readonly maxAttempts: number,
|
|
173
|
-
private readonly stepMs: number
|
|
172
|
+
private readonly stepMs: number
|
|
174
173
|
) {
|
|
175
174
|
super();
|
|
176
175
|
}
|
package/dist/abort.d.mts
CHANGED
|
@@ -1,30 +1,29 @@
|
|
|
1
|
-
import { t as AbortError } from "./errors-
|
|
2
|
-
|
|
1
|
+
import { t as AbortError } from "./errors-BVZjP1Q5.mjs";
|
|
3
2
|
//#region src/abort.d.ts
|
|
4
3
|
/**
|
|
5
|
-
* Throws when the provided abort signal is already aborted.
|
|
6
|
-
*
|
|
7
|
-
* @param signal - Optional abort signal to inspect.
|
|
8
|
-
* @throws {AbortError} When the signal is aborted.
|
|
9
|
-
*/
|
|
10
|
-
declare
|
|
4
|
+
* Throws when the provided abort signal is already aborted.
|
|
5
|
+
*
|
|
6
|
+
* @param signal - Optional abort signal to inspect.
|
|
7
|
+
* @throws {AbortError} When the signal is aborted.
|
|
8
|
+
*/
|
|
9
|
+
declare const toAbortError: (reason: unknown) => AbortError;
|
|
11
10
|
/**
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
* @param
|
|
15
|
-
* @
|
|
16
|
-
*/
|
|
17
|
-
declare
|
|
11
|
+
* Throws when the provided abort signal is already aborted.
|
|
12
|
+
*
|
|
13
|
+
* @param signal - Optional abort signal to inspect.
|
|
14
|
+
* @throws {AbortError} When the signal is aborted.
|
|
15
|
+
*/
|
|
16
|
+
declare const throwIfAborted: (signal?: AbortSignal) => void;
|
|
18
17
|
/**
|
|
19
|
-
* Waits for delay sleep while observing cancellation through an abort signal.
|
|
20
|
-
*
|
|
21
|
-
* @param sleep - Sleep function used to await `delayMs`.
|
|
22
|
-
* @param delayMs - Delay duration in milliseconds.
|
|
23
|
-
* @param signal - Abort signal to observe while waiting.
|
|
24
|
-
* @returns Promise that resolves when delay finishes.
|
|
25
|
-
* @throws {AbortError} When the signal aborts before or during wait.
|
|
26
|
-
*/
|
|
27
|
-
declare
|
|
18
|
+
* Waits for delay sleep while observing cancellation through an abort signal.
|
|
19
|
+
*
|
|
20
|
+
* @param sleep - Sleep function used to await `delayMs`.
|
|
21
|
+
* @param delayMs - Delay duration in milliseconds.
|
|
22
|
+
* @param signal - Abort signal to observe while waiting.
|
|
23
|
+
* @returns Promise that resolves when delay finishes.
|
|
24
|
+
* @throws {AbortError} When the signal aborts before or during wait.
|
|
25
|
+
*/
|
|
26
|
+
declare const sleepWithAbortSignal: (sleep: (delayMs: number) => Promise<void>, delayMs: number, signal: AbortSignal) => Promise<void>;
|
|
28
27
|
//#endregion
|
|
29
28
|
export { sleepWithAbortSignal, throwIfAborted, toAbortError };
|
|
30
29
|
//# sourceMappingURL=abort.d.mts.map
|
package/dist/abort.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"abort.d.mts","names":[],"sources":["../src/abort.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"abort.d.mts","names":[],"sources":["../src/abort.ts"],"mappings":";;;;;;;;cAca,eAAgB,oBAAkB;;;;;;;cA8BlC,iBAAkB,SAAS;;;;;;;;;;cAiB3B,uBACX,QAAQ,oBAAoB,eAC5B,iBACA,QAAQ,gBACP"}
|
package/dist/abort.mjs
CHANGED
|
@@ -11,17 +11,7 @@ import { AbortError } from "./errors.mjs";
|
|
|
11
11
|
* @param signal - Optional abort signal to inspect.
|
|
12
12
|
* @throws {AbortError} When the signal is aborted.
|
|
13
13
|
*/
|
|
14
|
-
|
|
15
|
-
if (!signal?.aborted) return;
|
|
16
|
-
throw toAbortError(signal.reason);
|
|
17
|
-
}
|
|
18
|
-
/**
|
|
19
|
-
* Converts an abort reason into a normalized `AbortError`.
|
|
20
|
-
*
|
|
21
|
-
* @param reason - Arbitrary abort reason value.
|
|
22
|
-
* @returns Normalized abort error instance.
|
|
23
|
-
*/
|
|
24
|
-
function toAbortError(reason) {
|
|
14
|
+
const toAbortError = (reason) => {
|
|
25
15
|
if (reason instanceof AbortError) return reason;
|
|
26
16
|
if (reason instanceof Error) return new AbortError(reason.message, { cause: reason });
|
|
27
17
|
if (typeof reason === "string" && reason.length > 0) return new AbortError(reason);
|
|
@@ -31,7 +21,17 @@ function toAbortError(reason) {
|
|
|
31
21
|
} catch {
|
|
32
22
|
return new AbortError("Retry aborted.");
|
|
33
23
|
}
|
|
34
|
-
}
|
|
24
|
+
};
|
|
25
|
+
/**
|
|
26
|
+
* Throws when the provided abort signal is already aborted.
|
|
27
|
+
*
|
|
28
|
+
* @param signal - Optional abort signal to inspect.
|
|
29
|
+
* @throws {AbortError} When the signal is aborted.
|
|
30
|
+
*/
|
|
31
|
+
const throwIfAborted = (signal) => {
|
|
32
|
+
if (signal?.aborted !== true) return;
|
|
33
|
+
throw toAbortError(signal.reason);
|
|
34
|
+
};
|
|
35
35
|
/**
|
|
36
36
|
* Waits for delay sleep while observing cancellation through an abort signal.
|
|
37
37
|
*
|
|
@@ -41,11 +41,11 @@ function toAbortError(reason) {
|
|
|
41
41
|
* @returns Promise that resolves when delay finishes.
|
|
42
42
|
* @throws {AbortError} When the signal aborts before or during wait.
|
|
43
43
|
*/
|
|
44
|
-
async
|
|
44
|
+
const sleepWithAbortSignal = async (sleep, delayMs, signal) => {
|
|
45
45
|
if (signal.aborted) throw toAbortError(signal.reason);
|
|
46
46
|
let onAbort;
|
|
47
47
|
try {
|
|
48
|
-
await Promise.race([sleep(delayMs), new Promise((
|
|
48
|
+
await Promise.race([sleep(delayMs), new Promise((_resolve, reject) => {
|
|
49
49
|
onAbort = () => {
|
|
50
50
|
reject(toAbortError(signal.reason));
|
|
51
51
|
};
|
|
@@ -54,7 +54,7 @@ async function sleepWithAbortSignal(sleep, delayMs, signal) {
|
|
|
54
54
|
} finally {
|
|
55
55
|
if (onAbort) signal.removeEventListener("abort", onAbort);
|
|
56
56
|
}
|
|
57
|
-
}
|
|
57
|
+
};
|
|
58
58
|
//#endregion
|
|
59
59
|
export { sleepWithAbortSignal, throwIfAborted, toAbortError };
|
|
60
60
|
|
package/dist/abort.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"abort.mjs","names":[],"sources":["../src/abort.ts"],"sourcesContent":["/**\n * Abort-signal helpers for retry orchestration internals.\n *\n * @module @zap-studio/retry/abort\n */\n\nimport { AbortError } from \"./errors.js\";\n\n/**\n * Throws when the provided abort signal is already aborted.\n *\n * @param signal - Optional abort signal to inspect.\n * @throws {AbortError} When the signal is aborted.\n */\nexport
|
|
1
|
+
{"version":3,"file":"abort.mjs","names":[],"sources":["../src/abort.ts"],"sourcesContent":["/**\n * Abort-signal helpers for retry orchestration internals.\n *\n * @module @zap-studio/retry/abort\n */\n\nimport { AbortError } from \"./errors.js\";\n\n/**\n * Throws when the provided abort signal is already aborted.\n *\n * @param signal - Optional abort signal to inspect.\n * @throws {AbortError} When the signal is aborted.\n */\nexport const toAbortError = (reason: unknown): AbortError => {\n if (reason instanceof AbortError) {\n return reason;\n }\n\n if (reason instanceof Error) {\n return new AbortError(reason.message, { cause: reason });\n }\n\n if (typeof reason === \"string\" && reason.length > 0) {\n return new AbortError(reason);\n }\n\n if (reason === undefined) {\n return new AbortError(\"Retry aborted.\");\n }\n\n try {\n return new AbortError(`Retry aborted: ${JSON.stringify(reason)}`);\n } catch {\n return new AbortError(\"Retry aborted.\");\n }\n};\n\n/**\n * Throws when the provided abort signal is already aborted.\n *\n * @param signal - Optional abort signal to inspect.\n * @throws {AbortError} When the signal is aborted.\n */\nexport const throwIfAborted = (signal?: AbortSignal): void => {\n if (signal?.aborted !== true) {\n return;\n }\n\n throw toAbortError(signal.reason);\n};\n\n/**\n * Waits for delay sleep while observing cancellation through an abort signal.\n *\n * @param sleep - Sleep function used to await `delayMs`.\n * @param delayMs - Delay duration in milliseconds.\n * @param signal - Abort signal to observe while waiting.\n * @returns Promise that resolves when delay finishes.\n * @throws {AbortError} When the signal aborts before or during wait.\n */\nexport const sleepWithAbortSignal = async (\n sleep: (delayMs: number) => Promise<void>,\n delayMs: number,\n signal: AbortSignal\n): Promise<void> => {\n if (signal.aborted) {\n throw toAbortError(signal.reason);\n }\n\n let onAbort: (() => void) | undefined;\n\n try {\n await Promise.race([\n sleep(delayMs),\n // oxlint-disable-next-line promise/avoid-new -- AbortSignal callback is adapted into the race promise.\n new Promise<never>((_resolve, reject) => {\n onAbort = (): void => {\n reject(toAbortError(signal.reason));\n };\n\n signal.addEventListener(\"abort\", onAbort, { once: true });\n }),\n ]);\n } finally {\n if (onAbort) {\n signal.removeEventListener(\"abort\", onAbort);\n }\n }\n};\n"],"mappings":";;;;;;;;;;;;;AAcA,MAAa,gBAAgB,WAAgC;CAC3D,IAAI,kBAAkB,YACpB,OAAO;CAGT,IAAI,kBAAkB,OACpB,OAAO,IAAI,WAAW,OAAO,SAAS,EAAE,OAAO,OAAO,CAAC;CAGzD,IAAI,OAAO,WAAW,YAAY,OAAO,SAAS,GAChD,OAAO,IAAI,WAAW,MAAM;CAG9B,IAAI,WAAW,KAAA,GACb,OAAO,IAAI,WAAW,gBAAgB;CAGxC,IAAI;EACF,OAAO,IAAI,WAAW,kBAAkB,KAAK,UAAU,MAAM,GAAG;CAClE,QAAQ;EACN,OAAO,IAAI,WAAW,gBAAgB;CACxC;AACF;;;;;;;AAQA,MAAa,kBAAkB,WAA+B;CAC5D,IAAI,QAAQ,YAAY,MACtB;CAGF,MAAM,aAAa,OAAO,MAAM;AAClC;;;;;;;;;;AAWA,MAAa,uBAAuB,OAClC,OACA,SACA,WACkB;CAClB,IAAI,OAAO,SACT,MAAM,aAAa,OAAO,MAAM;CAGlC,IAAI;CAEJ,IAAI;EACF,MAAM,QAAQ,KAAK,CACjB,MAAM,OAAO,GAEb,IAAI,SAAgB,UAAU,WAAW;GACvC,gBAAsB;IACpB,OAAO,aAAa,OAAO,MAAM,CAAC;GACpC;GAEA,OAAO,iBAAiB,SAAS,SAAS,EAAE,MAAM,KAAK,CAAC;EAC1D,CAAC,CACH,CAAC;CACH,UAAU;EACR,IAAI,SACF,OAAO,oBAAoB,SAAS,OAAO;CAE/C;AACF"}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
//#region src/errors.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* Terminal error types used by retry policies and runners.
|
|
4
|
+
*
|
|
5
|
+
* @module @zap-studio/retry/errors
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Context payload attached to `RetryError`.
|
|
9
|
+
*/
|
|
10
|
+
interface RetryErrorContext {
|
|
11
|
+
/**
|
|
12
|
+
* Count of completed attempts at exhaustion.
|
|
13
|
+
*/
|
|
14
|
+
readonly attempts: number;
|
|
15
|
+
/**
|
|
16
|
+
* The last error object raised by a failed `execute` attempt.
|
|
17
|
+
*/
|
|
18
|
+
readonly lastError?: unknown;
|
|
19
|
+
/**
|
|
20
|
+
* Optional data captured from the last attempt when provided by a policy.
|
|
21
|
+
*/
|
|
22
|
+
readonly lastData?: unknown;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Context payload attached to `AbortError`.
|
|
26
|
+
*/
|
|
27
|
+
interface AbortErrorContext {
|
|
28
|
+
/**
|
|
29
|
+
* When the abort `reason` was an `Error`, the optional wrapped cause.
|
|
30
|
+
*/
|
|
31
|
+
readonly cause?: unknown;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Error thrown when retries are exhausted.
|
|
35
|
+
*
|
|
36
|
+
* @example
|
|
37
|
+
* throw new RetryError("Retry exhausted", {
|
|
38
|
+
* attempts: 3,
|
|
39
|
+
* lastError: new Error("network"),
|
|
40
|
+
* });
|
|
41
|
+
*/
|
|
42
|
+
declare class RetryError extends Error {
|
|
43
|
+
/**
|
|
44
|
+
* Total attempts performed before exhaustion.
|
|
45
|
+
*/
|
|
46
|
+
readonly attempts: number;
|
|
47
|
+
/**
|
|
48
|
+
* Last captured error from execution.
|
|
49
|
+
*/
|
|
50
|
+
readonly lastError?: unknown;
|
|
51
|
+
/**
|
|
52
|
+
* Last captured data value, when available.
|
|
53
|
+
*/
|
|
54
|
+
readonly lastData?: unknown;
|
|
55
|
+
/**
|
|
56
|
+
* Creates a RetryError with structured terminal context.
|
|
57
|
+
*/
|
|
58
|
+
constructor(message: string, context: RetryErrorContext);
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Error thrown when retry orchestration is canceled through `AbortSignal`.
|
|
62
|
+
*/
|
|
63
|
+
declare class AbortError extends Error {
|
|
64
|
+
/**
|
|
65
|
+
* Optional wrapped cause when the native abort `reason` was an `Error`.
|
|
66
|
+
*/
|
|
67
|
+
override readonly cause?: unknown;
|
|
68
|
+
/**
|
|
69
|
+
* @param message - Human-readable abort description.
|
|
70
|
+
* @param context - Optional `cause` link for diagnostic chaining.
|
|
71
|
+
*/
|
|
72
|
+
constructor(message: string, context?: AbortErrorContext);
|
|
73
|
+
}
|
|
74
|
+
//#endregion
|
|
75
|
+
export { RetryErrorContext as i, AbortErrorContext as n, RetryError as r, AbortError as t };
|
|
76
|
+
//# sourceMappingURL=errors-BVZjP1Q5.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors-BVZjP1Q5.d.mts","names":[],"sources":["../src/errors.ts"],"mappings":";;;;;;;;;UAWiB;;;;WAIN;;;;WAIA;;;;WAIA;;;;;UAMM;;;;WAIN;;;;;;;;;;;cAYE,mBAAmB;;;;WAId;;;;WAIA;;;;WAIA;;;;EAKhB,YAAY,iBAAiB,SAAS;;;;;cAY3B,mBAAmB;;;;oBAIL;;;;;EAMzB,YAAY,iBAAiB,UAAS"}
|
package/dist/errors.d.mts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { i as RetryErrorContext, n as AbortErrorContext, r as RetryError, t as AbortError } from "./errors-
|
|
1
|
+
import { i as RetryErrorContext, n as AbortErrorContext, r as RetryError, t as AbortError } from "./errors-BVZjP1Q5.mjs";
|
|
2
2
|
export { AbortError, AbortErrorContext, RetryError, RetryErrorContext };
|
package/dist/errors.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errors.mjs","names":[],"sources":["../src/errors.ts"],"sourcesContent":["/**\n * Terminal error types used by retry policies and runners.\n *\n * @module @zap-studio/retry/errors\n */\n\n/**\n * Context payload attached to `RetryError`.\n */\nexport interface RetryErrorContext {\n /**\n * Count of completed attempts at exhaustion.\n */\n readonly attempts: number;\n /**\n * The last error object raised by a failed `execute` attempt.\n */\n readonly lastError?: unknown;\n /**\n * Optional data captured from the last attempt when provided by a policy.\n */\n readonly lastData?: unknown;\n}\n\n/**\n * Context payload attached to `AbortError`.\n */\nexport interface AbortErrorContext {\n /**\n * When the abort `reason` was an `Error`, the optional wrapped cause.\n */\n readonly cause?: unknown;\n}\n\n/**\n * Error thrown when retries are exhausted.\n *\n * @example\n * throw new RetryError(\"Retry exhausted\", {\n * attempts: 3,\n * lastError: new Error(\"network\"),\n * });\n */\nexport class RetryError extends Error {\n /**\n * Total attempts performed before exhaustion.\n */\n public readonly attempts: number;\n /**\n * Last captured error from execution.\n */\n public readonly lastError?: unknown;\n /**\n * Last captured data value, when available.\n */\n public readonly lastData?: unknown;\n\n /**\n * Creates a RetryError with structured terminal context.\n */\n constructor(message: string, context: RetryErrorContext) {\n super(message);\n this.name = \"RetryError\";\n this.attempts = context.attempts;\n this.lastError = context.lastError;\n this.lastData = context.lastData;\n }\n}\n\n/**\n * Error thrown when retry orchestration is canceled through `AbortSignal`.\n */\nexport class AbortError extends Error {\n /**\n * Optional wrapped cause when the native abort `reason` was an `Error`.\n */\n public override readonly cause?: unknown;\n\n /**\n * @param message - Human-readable abort description.\n * @param context - Optional `cause` link for diagnostic chaining.\n */\n constructor(message: string, context: AbortErrorContext = {}) {\n super(message);\n this.name = \"AbortError\";\n this.cause = context.cause;\n }\n}\n"],"mappings":";;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"errors.mjs","names":[],"sources":["../src/errors.ts"],"sourcesContent":["// oxlint-disable max-classes-per-file -- Public retry error types are intentionally colocated.\n\n/**\n * Terminal error types used by retry policies and runners.\n *\n * @module @zap-studio/retry/errors\n */\n\n/**\n * Context payload attached to `RetryError`.\n */\nexport interface RetryErrorContext {\n /**\n * Count of completed attempts at exhaustion.\n */\n readonly attempts: number;\n /**\n * The last error object raised by a failed `execute` attempt.\n */\n readonly lastError?: unknown;\n /**\n * Optional data captured from the last attempt when provided by a policy.\n */\n readonly lastData?: unknown;\n}\n\n/**\n * Context payload attached to `AbortError`.\n */\nexport interface AbortErrorContext {\n /**\n * When the abort `reason` was an `Error`, the optional wrapped cause.\n */\n readonly cause?: unknown;\n}\n\n/**\n * Error thrown when retries are exhausted.\n *\n * @example\n * throw new RetryError(\"Retry exhausted\", {\n * attempts: 3,\n * lastError: new Error(\"network\"),\n * });\n */\nexport class RetryError extends Error {\n /**\n * Total attempts performed before exhaustion.\n */\n public readonly attempts: number;\n /**\n * Last captured error from execution.\n */\n public readonly lastError?: unknown;\n /**\n * Last captured data value, when available.\n */\n public readonly lastData?: unknown;\n\n /**\n * Creates a RetryError with structured terminal context.\n */\n constructor(message: string, context: RetryErrorContext) {\n super(message);\n this.name = \"RetryError\";\n this.attempts = context.attempts;\n this.lastError = context.lastError;\n this.lastData = context.lastData;\n }\n}\n\n/**\n * Error thrown when retry orchestration is canceled through `AbortSignal`.\n */\nexport class AbortError extends Error {\n /**\n * Optional wrapped cause when the native abort `reason` was an `Error`.\n */\n public override readonly cause?: unknown;\n\n /**\n * @param message - Human-readable abort description.\n * @param context - Optional `cause` link for diagnostic chaining.\n */\n constructor(message: string, context: AbortErrorContext = {}) {\n super(message);\n this.name = \"AbortError\";\n this.cause = context.cause;\n }\n}\n"],"mappings":";;;;;;;;;;AA6CA,IAAa,aAAb,cAAgC,MAAM;;;;CAIpC;;;;CAIA;;;;CAIA;;;;CAKA,YAAY,SAAiB,SAA4B;EACvD,MAAM,OAAO;EACb,KAAK,OAAO;EACZ,KAAK,WAAW,QAAQ;EACxB,KAAK,YAAY,QAAQ;EACzB,KAAK,WAAW,QAAQ;CAC1B;AACF;;;;AAKA,IAAa,aAAb,cAAgC,MAAM;;;;CAIpC;;;;;CAMA,YAAY,SAAiB,UAA6B,CAAC,GAAG;EAC5D,MAAM,OAAO;EACb,KAAK,OAAO;EACZ,KAAK,QAAQ,QAAQ;CACvB;AACF"}
|
|
@@ -1,54 +1,53 @@
|
|
|
1
1
|
import { RetryDecision, RetryDecisionInput } from "./types.mjs";
|
|
2
2
|
import { BaseRetryPolicy } from "./index.mjs";
|
|
3
|
-
|
|
4
3
|
//#region src/exponential-backoff.d.ts
|
|
5
4
|
/**
|
|
6
|
-
* Configuration for `ExponentialBackoff`.
|
|
7
|
-
*/
|
|
5
|
+
* Configuration for `ExponentialBackoff`.
|
|
6
|
+
*/
|
|
8
7
|
interface ExponentialBackoffOptions {
|
|
9
8
|
/**
|
|
10
|
-
|
|
11
|
-
|
|
9
|
+
* Maximum number of attempts (including the first) before giving up.
|
|
10
|
+
*/
|
|
12
11
|
maxAttempts: number;
|
|
13
12
|
/**
|
|
14
|
-
|
|
15
|
-
|
|
13
|
+
* Initial delay in milliseconds, doubled each retry until capped.
|
|
14
|
+
*/
|
|
16
15
|
baseDelayMs: number;
|
|
17
16
|
/**
|
|
18
|
-
|
|
19
|
-
|
|
17
|
+
* Hard upper bound in milliseconds for computed exponential delay.
|
|
18
|
+
*/
|
|
20
19
|
maxDelayMs: number;
|
|
21
20
|
}
|
|
22
21
|
/**
|
|
23
|
-
* Retries with exponential delay growth up to a max cap.
|
|
24
|
-
*
|
|
25
|
-
* @example
|
|
26
|
-
* const policy = new ExponentialBackoff({
|
|
27
|
-
* maxAttempts: 5,
|
|
28
|
-
* baseDelayMs: 100,
|
|
29
|
-
* maxDelayMs: 2_000,
|
|
30
|
-
* });
|
|
31
|
-
*/
|
|
22
|
+
* Retries with exponential delay growth up to a max cap.
|
|
23
|
+
*
|
|
24
|
+
* @example
|
|
25
|
+
* const policy = new ExponentialBackoff({
|
|
26
|
+
* maxAttempts: 5,
|
|
27
|
+
* baseDelayMs: 100,
|
|
28
|
+
* maxDelayMs: 2_000,
|
|
29
|
+
* });
|
|
30
|
+
*/
|
|
32
31
|
declare class ExponentialBackoff extends BaseRetryPolicy {
|
|
33
32
|
/**
|
|
34
|
-
|
|
35
|
-
|
|
33
|
+
* Maximum number of attempts before the policy returns `max-attempts-reached`.
|
|
34
|
+
*/
|
|
36
35
|
private readonly maxAttempts;
|
|
37
36
|
/**
|
|
38
|
-
|
|
39
|
-
|
|
37
|
+
* Base delay in milliseconds used in `baseDelayMs * 2 ** (attempt - 1)`.
|
|
38
|
+
*/
|
|
40
39
|
private readonly baseDelayMs;
|
|
41
40
|
/**
|
|
42
|
-
|
|
43
|
-
|
|
41
|
+
* Upper cap for computed delay, applied with `Math.min`.
|
|
42
|
+
*/
|
|
44
43
|
private readonly maxDelayMs;
|
|
45
44
|
/**
|
|
46
|
-
|
|
47
|
-
|
|
45
|
+
* Creates an exponential backoff retry policy.
|
|
46
|
+
*/
|
|
48
47
|
constructor(options: ExponentialBackoffOptions);
|
|
49
48
|
/**
|
|
50
|
-
|
|
51
|
-
|
|
49
|
+
* Computes retry decision for the current attempt.
|
|
50
|
+
*/
|
|
52
51
|
next(input: RetryDecisionInput): RetryDecision;
|
|
53
52
|
}
|
|
54
53
|
//#endregion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"exponential-backoff.d.mts","names":[],"sources":["../src/exponential-backoff.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"exponential-backoff.d.mts","names":[],"sources":["../src/exponential-backoff.ts"],"mappings":";;;;;;UAYiB;;;;EAIf;;;;EAIA;;;;EAIA;;;;;;;;;;;;cAaW,2BAA2B;;;;mBAIrB;;;;mBAIA;;;;mBAIA;;;;EAKjB,YAAY,SAAS;;;;EAUrB,KAAY,OAAO,qBAAqB"}
|
|
@@ -42,15 +42,15 @@ var ExponentialBackoff = class extends BaseRetryPolicy {
|
|
|
42
42
|
*/
|
|
43
43
|
next(input) {
|
|
44
44
|
if (input.attempt >= this.maxAttempts) return {
|
|
45
|
-
shouldRetry: false,
|
|
46
45
|
delayMs: 0,
|
|
47
|
-
reason: "max-attempts-reached"
|
|
46
|
+
reason: "max-attempts-reached",
|
|
47
|
+
shouldRetry: false
|
|
48
48
|
};
|
|
49
49
|
const exponent = Math.max(0, input.attempt - 1);
|
|
50
50
|
return {
|
|
51
|
-
shouldRetry: true,
|
|
52
51
|
delayMs: Math.min(this.maxDelayMs, this.baseDelayMs * 2 ** exponent),
|
|
53
|
-
reason: "retry"
|
|
52
|
+
reason: "retry",
|
|
53
|
+
shouldRetry: true
|
|
54
54
|
};
|
|
55
55
|
}
|
|
56
56
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"exponential-backoff.mjs","names":[],"sources":["../src/exponential-backoff.ts"],"sourcesContent":["/**\n * Exponential backoff retry strategy.\n *\n * @module @zap-studio/retry/exponential-backoff\n */\n\nimport { BaseRetryPolicy } from \"./index.js\";\nimport type { RetryDecision, RetryDecisionInput } from \"./types.js\";\n\n/**\n * Configuration for `ExponentialBackoff`.\n */\nexport interface ExponentialBackoffOptions {\n /**\n * Maximum number of attempts (including the first) before giving up.\n */\n maxAttempts: number;\n /**\n * Initial delay in milliseconds, doubled each retry until capped.\n */\n baseDelayMs: number;\n /**\n * Hard upper bound in milliseconds for computed exponential delay.\n */\n maxDelayMs: number;\n}\n\n/**\n * Retries with exponential delay growth up to a max cap.\n *\n * @example\n * const policy = new ExponentialBackoff({\n * maxAttempts: 5,\n * baseDelayMs: 100,\n * maxDelayMs: 2_000,\n * });\n */\nexport class ExponentialBackoff extends BaseRetryPolicy {\n /**\n * Maximum number of attempts before the policy returns `max-attempts-reached`.\n */\n private readonly maxAttempts: number;\n /**\n * Base delay in milliseconds used in `baseDelayMs * 2 ** (attempt - 1)`.\n */\n private readonly baseDelayMs: number;\n /**\n * Upper cap for computed delay, applied with `Math.min`.\n */\n private readonly maxDelayMs: number;\n\n /**\n * Creates an exponential backoff retry policy.\n */\n constructor(options: ExponentialBackoffOptions) {\n super();\n this.maxAttempts = options.maxAttempts;\n this.baseDelayMs = options.baseDelayMs;\n this.maxDelayMs = options.maxDelayMs;\n }\n\n /**\n * Computes retry decision for the current attempt.\n */\n public next(input: RetryDecisionInput): RetryDecision {\n if (input.attempt >= this.maxAttempts) {\n return {
|
|
1
|
+
{"version":3,"file":"exponential-backoff.mjs","names":[],"sources":["../src/exponential-backoff.ts"],"sourcesContent":["/**\n * Exponential backoff retry strategy.\n *\n * @module @zap-studio/retry/exponential-backoff\n */\n\nimport { BaseRetryPolicy } from \"./index.js\";\nimport type { RetryDecision, RetryDecisionInput } from \"./types.js\";\n\n/**\n * Configuration for `ExponentialBackoff`.\n */\nexport interface ExponentialBackoffOptions {\n /**\n * Maximum number of attempts (including the first) before giving up.\n */\n maxAttempts: number;\n /**\n * Initial delay in milliseconds, doubled each retry until capped.\n */\n baseDelayMs: number;\n /**\n * Hard upper bound in milliseconds for computed exponential delay.\n */\n maxDelayMs: number;\n}\n\n/**\n * Retries with exponential delay growth up to a max cap.\n *\n * @example\n * const policy = new ExponentialBackoff({\n * maxAttempts: 5,\n * baseDelayMs: 100,\n * maxDelayMs: 2_000,\n * });\n */\nexport class ExponentialBackoff extends BaseRetryPolicy {\n /**\n * Maximum number of attempts before the policy returns `max-attempts-reached`.\n */\n private readonly maxAttempts: number;\n /**\n * Base delay in milliseconds used in `baseDelayMs * 2 ** (attempt - 1)`.\n */\n private readonly baseDelayMs: number;\n /**\n * Upper cap for computed delay, applied with `Math.min`.\n */\n private readonly maxDelayMs: number;\n\n /**\n * Creates an exponential backoff retry policy.\n */\n constructor(options: ExponentialBackoffOptions) {\n super();\n this.maxAttempts = options.maxAttempts;\n this.baseDelayMs = options.baseDelayMs;\n this.maxDelayMs = options.maxDelayMs;\n }\n\n /**\n * Computes retry decision for the current attempt.\n */\n public next(input: RetryDecisionInput): RetryDecision {\n if (input.attempt >= this.maxAttempts) {\n return { delayMs: 0, reason: \"max-attempts-reached\", shouldRetry: false };\n }\n\n const exponent = Math.max(0, input.attempt - 1);\n const delayMs = Math.min(this.maxDelayMs, this.baseDelayMs * 2 ** exponent);\n\n return { delayMs, reason: \"retry\", shouldRetry: true };\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;AAqCA,IAAa,qBAAb,cAAwC,gBAAgB;;;;CAItD;;;;CAIA;;;;CAIA;;;;CAKA,YAAY,SAAoC;EAC9C,MAAM;EACN,KAAK,cAAc,QAAQ;EAC3B,KAAK,cAAc,QAAQ;EAC3B,KAAK,aAAa,QAAQ;CAC5B;;;;CAKA,KAAY,OAA0C;EACpD,IAAI,MAAM,WAAW,KAAK,aACxB,OAAO;GAAE,SAAS;GAAG,QAAQ;GAAwB,aAAa;EAAM;EAG1E,MAAM,WAAW,KAAK,IAAI,GAAG,MAAM,UAAU,CAAC;EAG9C,OAAO;GAAE,SAFO,KAAK,IAAI,KAAK,YAAY,KAAK,cAAc,KAAK,QAEnD;GAAG,QAAQ;GAAS,aAAa;EAAK;CACvD;AACF"}
|
package/dist/fixed-delay.d.mts
CHANGED
|
@@ -1,45 +1,44 @@
|
|
|
1
1
|
import { RetryDecision, RetryDecisionInput } from "./types.mjs";
|
|
2
2
|
import { BaseRetryPolicy } from "./index.mjs";
|
|
3
|
-
|
|
4
3
|
//#region src/fixed-delay.d.ts
|
|
5
4
|
/**
|
|
6
|
-
* Configuration for `FixedDelay`.
|
|
7
|
-
*/
|
|
5
|
+
* Configuration for `FixedDelay`.
|
|
6
|
+
*/
|
|
8
7
|
interface FixedDelayOptions {
|
|
9
8
|
/**
|
|
10
|
-
|
|
11
|
-
|
|
9
|
+
* Maximum number of attempts (including the first) before giving up.
|
|
10
|
+
*/
|
|
12
11
|
maxAttempts: number;
|
|
13
12
|
/**
|
|
14
|
-
|
|
15
|
-
|
|
13
|
+
* Constant delay in milliseconds before each retry after a failure.
|
|
14
|
+
*/
|
|
16
15
|
delayMs: number;
|
|
17
16
|
}
|
|
18
17
|
/**
|
|
19
|
-
* Retries with a constant delay between attempts.
|
|
20
|
-
*
|
|
21
|
-
* @example
|
|
22
|
-
* const policy = new FixedDelay({
|
|
23
|
-
* maxAttempts: 3,
|
|
24
|
-
* delayMs: 250,
|
|
25
|
-
* });
|
|
26
|
-
*/
|
|
18
|
+
* Retries with a constant delay between attempts.
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* const policy = new FixedDelay({
|
|
22
|
+
* maxAttempts: 3,
|
|
23
|
+
* delayMs: 250,
|
|
24
|
+
* });
|
|
25
|
+
*/
|
|
27
26
|
declare class FixedDelay extends BaseRetryPolicy {
|
|
28
27
|
/**
|
|
29
|
-
|
|
30
|
-
|
|
28
|
+
* Maximum number of attempts before the policy returns `max-attempts-reached`.
|
|
29
|
+
*/
|
|
31
30
|
private readonly maxAttempts;
|
|
32
31
|
/**
|
|
33
|
-
|
|
34
|
-
|
|
32
|
+
* Constant delay in milliseconds before each subsequent attempt.
|
|
33
|
+
*/
|
|
35
34
|
private readonly delayMs;
|
|
36
35
|
/**
|
|
37
|
-
|
|
38
|
-
|
|
36
|
+
* Creates a fixed-delay retry policy.
|
|
37
|
+
*/
|
|
39
38
|
constructor(options: FixedDelayOptions);
|
|
40
39
|
/**
|
|
41
|
-
|
|
42
|
-
|
|
40
|
+
* Computes retry decision for the current attempt.
|
|
41
|
+
*/
|
|
43
42
|
next(input: RetryDecisionInput): RetryDecision;
|
|
44
43
|
}
|
|
45
44
|
//#endregion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fixed-delay.d.mts","names":[],"sources":["../src/fixed-delay.ts"],"mappings":";;;;;;
|
|
1
|
+
{"version":3,"file":"fixed-delay.d.mts","names":[],"sources":["../src/fixed-delay.ts"],"mappings":";;;;;;UAYiB;;;;EAIf;;;;EAIA;;;;;;;;;;;cAYW,mBAAmB;;;;mBAIb;;;;mBAIA;;;;EAKjB,YAAY,SAAS;;;;EASrB,KAAY,OAAO,qBAAqB"}
|
package/dist/fixed-delay.mjs
CHANGED
|
@@ -36,14 +36,14 @@ var FixedDelay = class extends BaseRetryPolicy {
|
|
|
36
36
|
*/
|
|
37
37
|
next(input) {
|
|
38
38
|
if (input.attempt >= this.maxAttempts) return {
|
|
39
|
-
shouldRetry: false,
|
|
40
39
|
delayMs: 0,
|
|
41
|
-
reason: "max-attempts-reached"
|
|
40
|
+
reason: "max-attempts-reached",
|
|
41
|
+
shouldRetry: false
|
|
42
42
|
};
|
|
43
43
|
return {
|
|
44
|
-
shouldRetry: true,
|
|
45
44
|
delayMs: this.delayMs,
|
|
46
|
-
reason: "retry"
|
|
45
|
+
reason: "retry",
|
|
46
|
+
shouldRetry: true
|
|
47
47
|
};
|
|
48
48
|
}
|
|
49
49
|
};
|
package/dist/fixed-delay.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fixed-delay.mjs","names":[],"sources":["../src/fixed-delay.ts"],"sourcesContent":["/**\n * Fixed-delay retry strategy.\n *\n * @module @zap-studio/retry/fixed-delay\n */\n\nimport { BaseRetryPolicy } from \"./index.js\";\nimport type { RetryDecision, RetryDecisionInput } from \"./types.js\";\n\n/**\n * Configuration for `FixedDelay`.\n */\nexport interface FixedDelayOptions {\n /**\n * Maximum number of attempts (including the first) before giving up.\n */\n maxAttempts: number;\n /**\n * Constant delay in milliseconds before each retry after a failure.\n */\n delayMs: number;\n}\n\n/**\n * Retries with a constant delay between attempts.\n *\n * @example\n * const policy = new FixedDelay({\n * maxAttempts: 3,\n * delayMs: 250,\n * });\n */\nexport class FixedDelay extends BaseRetryPolicy {\n /**\n * Maximum number of attempts before the policy returns `max-attempts-reached`.\n */\n private readonly maxAttempts: number;\n /**\n * Constant delay in milliseconds before each subsequent attempt.\n */\n private readonly delayMs: number;\n\n /**\n * Creates a fixed-delay retry policy.\n */\n constructor(options: FixedDelayOptions) {\n super();\n this.maxAttempts = options.maxAttempts;\n this.delayMs = options.delayMs;\n }\n\n /**\n * Computes retry decision for the current attempt.\n */\n public next(input: RetryDecisionInput): RetryDecision {\n if (input.attempt >= this.maxAttempts) {\n return {
|
|
1
|
+
{"version":3,"file":"fixed-delay.mjs","names":[],"sources":["../src/fixed-delay.ts"],"sourcesContent":["/**\n * Fixed-delay retry strategy.\n *\n * @module @zap-studio/retry/fixed-delay\n */\n\nimport { BaseRetryPolicy } from \"./index.js\";\nimport type { RetryDecision, RetryDecisionInput } from \"./types.js\";\n\n/**\n * Configuration for `FixedDelay`.\n */\nexport interface FixedDelayOptions {\n /**\n * Maximum number of attempts (including the first) before giving up.\n */\n maxAttempts: number;\n /**\n * Constant delay in milliseconds before each retry after a failure.\n */\n delayMs: number;\n}\n\n/**\n * Retries with a constant delay between attempts.\n *\n * @example\n * const policy = new FixedDelay({\n * maxAttempts: 3,\n * delayMs: 250,\n * });\n */\nexport class FixedDelay extends BaseRetryPolicy {\n /**\n * Maximum number of attempts before the policy returns `max-attempts-reached`.\n */\n private readonly maxAttempts: number;\n /**\n * Constant delay in milliseconds before each subsequent attempt.\n */\n private readonly delayMs: number;\n\n /**\n * Creates a fixed-delay retry policy.\n */\n constructor(options: FixedDelayOptions) {\n super();\n this.maxAttempts = options.maxAttempts;\n this.delayMs = options.delayMs;\n }\n\n /**\n * Computes retry decision for the current attempt.\n */\n public next(input: RetryDecisionInput): RetryDecision {\n if (input.attempt >= this.maxAttempts) {\n return { delayMs: 0, reason: \"max-attempts-reached\", shouldRetry: false };\n }\n\n return { delayMs: this.delayMs, reason: \"retry\", shouldRetry: true };\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;AAgCA,IAAa,aAAb,cAAgC,gBAAgB;;;;CAI9C;;;;CAIA;;;;CAKA,YAAY,SAA4B;EACtC,MAAM;EACN,KAAK,cAAc,QAAQ;EAC3B,KAAK,UAAU,QAAQ;CACzB;;;;CAKA,KAAY,OAA0C;EACpD,IAAI,MAAM,WAAW,KAAK,aACxB,OAAO;GAAE,SAAS;GAAG,QAAQ;GAAwB,aAAa;EAAM;EAG1E,OAAO;GAAE,SAAS,KAAK;GAAS,QAAQ;GAAS,aAAa;EAAK;CACrE;AACF"}
|