@zap-studio/retry 0.3.0 → 0.3.2
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 +15 -0
- package/LICENSE +1 -1
- package/README.md +28 -17
- package/dist/abort.d.ts +29 -0
- package/dist/abort.d.ts.map +1 -0
- package/dist/{abort.mjs → abort.js} +17 -17
- package/dist/abort.js.map +1 -0
- package/dist/base-policy-Dn3TOJd3.js +248 -0
- package/dist/base-policy-Dn3TOJd3.js.map +1 -0
- package/dist/base-policy.d.ts +59 -0
- package/dist/base-policy.d.ts.map +1 -0
- package/dist/base-policy.js +2 -0
- package/dist/errors-BVZjP1Q5.d.ts +76 -0
- package/dist/errors-BVZjP1Q5.d.ts.map +1 -0
- package/dist/{errors.d.mts → errors.d.ts} +1 -1
- package/dist/{errors.mjs → errors.js} +1 -1
- package/dist/errors.js.map +1 -0
- package/dist/exponential-backoff.d.ts +55 -0
- package/dist/exponential-backoff.d.ts.map +1 -0
- package/dist/{exponential-backoff.mjs → exponential-backoff.js} +6 -6
- package/dist/exponential-backoff.js.map +1 -0
- package/dist/fixed-delay.d.ts +46 -0
- package/dist/fixed-delay.d.ts.map +1 -0
- package/dist/{fixed-delay.mjs → fixed-delay.js} +6 -6
- package/dist/fixed-delay.js.map +1 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +7 -0
- package/dist/sleep.d.ts +17 -0
- package/dist/sleep.d.ts.map +1 -0
- package/dist/{sleep.mjs → sleep.js} +6 -4
- package/dist/sleep.js.map +1 -0
- package/dist/types.d.ts +143 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +0 -0
- package/package.json +15 -26
- package/dist/abort.d.mts +0 -30
- package/dist/abort.d.mts.map +0 -1
- package/dist/abort.mjs.map +0 -1
- package/dist/errors-fWo_KyVO.d.mts +0 -76
- package/dist/errors-fWo_KyVO.d.mts.map +0 -1
- package/dist/errors.mjs.map +0 -1
- package/dist/exponential-backoff.d.mts +0 -56
- package/dist/exponential-backoff.d.mts.map +0 -1
- package/dist/exponential-backoff.mjs.map +0 -1
- package/dist/fixed-delay.d.mts +0 -47
- package/dist/fixed-delay.d.mts.map +0 -1
- package/dist/fixed-delay.mjs.map +0 -1
- package/dist/index.d.mts +0 -60
- package/dist/index.d.mts.map +0 -1
- package/dist/index.mjs +0 -63
- package/dist/index.mjs.map +0 -1
- package/dist/result-mode.d.mts +0 -24
- package/dist/result-mode.d.mts.map +0 -1
- package/dist/result-mode.mjs +0 -145
- package/dist/result-mode.mjs.map +0 -1
- package/dist/sleep.d.mts +0 -17
- package/dist/sleep.d.mts.map +0 -1
- package/dist/sleep.mjs.map +0 -1
- package/dist/throw-mode.d.mts +0 -26
- package/dist/throw-mode.d.mts.map +0 -1
- package/dist/throw-mode.mjs +0 -50
- package/dist/throw-mode.mjs.map +0 -1
- package/dist/types.d.mts +0 -145
- package/dist/types.d.mts.map +0 -1
- package/dist/types.mjs +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,18 @@
|
|
|
1
|
+
## @zap-studio/retry@0.3.2
|
|
2
|
+
|
|
3
|
+
### Tree-shakeable root re-exports
|
|
4
|
+
|
|
5
|
+
The package root now re-exports the full public API, so everything can be imported from `@zap-studio/retry` directly (`BaseRetryPolicy`, `ExponentialBackoff`, `FixedDelay`, `RetryError`, `AbortError`, abort helpers, `defaultSleep`, and all public types). All exports are side-effect free and tree-shakeable; granular subpath imports keep working.
|
|
6
|
+
|
|
7
|
+
- `BaseRetryPolicy` moved from the entrypoint into its own module, available as the new `./base-policy` subpath.
|
|
8
|
+
- Removed the `./result-mode` and `./throw-mode` subpath exports. Both were orchestration internals (`runResultMode`, `runThrowMode`) and are no longer part of the public API.
|
|
9
|
+
|
|
10
|
+
## @zap-studio/retry@0.3.1
|
|
11
|
+
|
|
12
|
+
### Migrate to ultracite lint/format
|
|
13
|
+
|
|
14
|
+
Internal formatting and lint cleanup only. No public API or behavior change.
|
|
15
|
+
|
|
1
16
|
# @zap-studio/retry
|
|
2
17
|
|
|
3
18
|
## 0.3.0
|
package/LICENSE
CHANGED
package/README.md
CHANGED
|
@@ -5,14 +5,29 @@ Composable retry policy primitives for HTTP clients and async workflows.
|
|
|
5
5
|
## Installation
|
|
6
6
|
|
|
7
7
|
```bash
|
|
8
|
+
nub add @zap-studio/retry
|
|
9
|
+
# or
|
|
8
10
|
npm install @zap-studio/retry
|
|
11
|
+
# or
|
|
12
|
+
pnpm add @zap-studio/retry
|
|
9
13
|
```
|
|
10
14
|
|
|
15
|
+
## Runtime Support
|
|
16
|
+
|
|
17
|
+
| Runtime | Minimum version |
|
|
18
|
+
| ------------------ | --------------------------------------- |
|
|
19
|
+
| Node.js | 18.0.0 |
|
|
20
|
+
| Bun | 1.0.0 |
|
|
21
|
+
| Deno | 1.42 |
|
|
22
|
+
| Cloudflare Workers | Any current release |
|
|
23
|
+
| Browsers | Chrome/Edge 98, Firefox 97, Safari 15.4 |
|
|
24
|
+
|
|
25
|
+
Cancellation relies on `AbortSignal.reason`, which sets the browser minimums above. Deno 1.42 is the first release that can install packages from JSR (`deno add jsr:@zap-studio/retry`).
|
|
26
|
+
|
|
11
27
|
## Usage
|
|
12
28
|
|
|
13
29
|
```ts
|
|
14
|
-
import { ExponentialBackoff } from "@zap-studio/retry
|
|
15
|
-
import { FixedDelay } from "@zap-studio/retry/fixed-delay";
|
|
30
|
+
import { ExponentialBackoff, FixedDelay } from "@zap-studio/retry";
|
|
16
31
|
import { $fetch } from "@zap-studio/fetch";
|
|
17
32
|
|
|
18
33
|
const exponential = new ExponentialBackoff({
|
|
@@ -33,11 +48,10 @@ const data = await exponential.run(async () => {
|
|
|
33
48
|
|
|
34
49
|
`run(...)` throws when retries are exhausted.
|
|
35
50
|
|
|
36
|
-
By default, policies extending `BaseRetryPolicy` throw `RetryError` on exhaustion
|
|
37
|
-
and `AbortError` on cancellation.
|
|
51
|
+
By default, policies extending `BaseRetryPolicy` throw `RetryError` on exhaustion and `AbortError` on cancellation.
|
|
38
52
|
|
|
39
53
|
```ts
|
|
40
|
-
import { AbortError, RetryError } from "@zap-studio/retry
|
|
54
|
+
import { AbortError, RetryError } from "@zap-studio/retry";
|
|
41
55
|
|
|
42
56
|
try {
|
|
43
57
|
const data = await exponential.run(async () => {
|
|
@@ -69,7 +83,7 @@ const result = await exponential.run(
|
|
|
69
83
|
});
|
|
70
84
|
return await response.json();
|
|
71
85
|
},
|
|
72
|
-
{ throwOnExhausted: false }
|
|
86
|
+
{ throwOnExhausted: false }
|
|
73
87
|
);
|
|
74
88
|
|
|
75
89
|
if (!result.ok) {
|
|
@@ -84,7 +98,7 @@ if (!result.ok) {
|
|
|
84
98
|
|
|
85
99
|
`BaseRetryPolicy.run` automatically applies a delay between retry attempts when no custom `sleep` function is provided in the options.
|
|
86
100
|
|
|
87
|
-
That default is the `defaultSleep` helper, exported from `@zap-studio/retry
|
|
101
|
+
That default is the `defaultSleep` helper, exported from `@zap-studio/retry`.
|
|
88
102
|
|
|
89
103
|
By default, this delay mechanism relies on the native JavaScript `setTimeout`, meaning retries are scheduled using the standard event loop timing rather than any custom or blocking implementation.
|
|
90
104
|
|
|
@@ -102,7 +116,7 @@ const promise = exponential.run(
|
|
|
102
116
|
});
|
|
103
117
|
return await response.json();
|
|
104
118
|
},
|
|
105
|
-
{ signal: controller.signal }
|
|
119
|
+
{ signal: controller.signal }
|
|
106
120
|
);
|
|
107
121
|
|
|
108
122
|
controller.abort(new Error("Request canceled"));
|
|
@@ -110,8 +124,7 @@ controller.abort(new Error("Request canceled"));
|
|
|
110
124
|
await promise;
|
|
111
125
|
```
|
|
112
126
|
|
|
113
|
-
In non-throw mode, abort is returned as `{ ok: false }` with `AbortError` on
|
|
114
|
-
`result.error`:
|
|
127
|
+
In non-throw mode, abort is returned as `{ ok: false }` with `AbortError` on `result.error`:
|
|
115
128
|
|
|
116
129
|
```ts
|
|
117
130
|
const controller = new AbortController();
|
|
@@ -126,7 +139,7 @@ const result = await exponential.run(
|
|
|
126
139
|
{
|
|
127
140
|
signal: controller.signal,
|
|
128
141
|
throwOnExhausted: false,
|
|
129
|
-
}
|
|
142
|
+
}
|
|
130
143
|
);
|
|
131
144
|
|
|
132
145
|
if (!result.ok) {
|
|
@@ -159,18 +172,16 @@ const predictableIntervalPolicy = new FixedDelay({
|
|
|
159
172
|
|
|
160
173
|
Extend `BaseRetryPolicy` when the built-in policies do not match your retry rules.
|
|
161
174
|
|
|
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).
|
|
175
|
+
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
176
|
|
|
166
177
|
```ts
|
|
167
178
|
import { BaseRetryPolicy } from "@zap-studio/retry";
|
|
168
|
-
import type { RetryDecision, RetryDecisionInput } from "@zap-studio/retry
|
|
179
|
+
import type { RetryDecision, RetryDecisionInput } from "@zap-studio/retry";
|
|
169
180
|
|
|
170
181
|
class LinearBackoff extends BaseRetryPolicy {
|
|
171
182
|
constructor(
|
|
172
183
|
private readonly maxAttempts: number,
|
|
173
|
-
private readonly stepMs: number
|
|
184
|
+
private readonly stepMs: number
|
|
174
185
|
) {
|
|
175
186
|
super();
|
|
176
187
|
}
|
|
@@ -201,7 +212,7 @@ const value = await policy.run(doWork);
|
|
|
201
212
|
Use `RetryError` when an orchestrator exhausts retries and needs to surface final context.
|
|
202
213
|
|
|
203
214
|
```ts
|
|
204
|
-
import { RetryError } from "@zap-studio/retry
|
|
215
|
+
import { RetryError } from "@zap-studio/retry";
|
|
205
216
|
|
|
206
217
|
throw new RetryError("Retry policy exhausted all attempts.", {
|
|
207
218
|
attempts: attempt,
|
package/dist/abort.d.ts
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { t as AbortError } from "./errors-BVZjP1Q5.js";
|
|
2
|
+
//#region src/abort.d.ts
|
|
3
|
+
/**
|
|
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;
|
|
10
|
+
/**
|
|
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;
|
|
17
|
+
/**
|
|
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>;
|
|
27
|
+
//#endregion
|
|
28
|
+
export { sleepWithAbortSignal, throwIfAborted, toAbortError };
|
|
29
|
+
//# sourceMappingURL=abort.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"abort.d.ts","names":[],"sources":["../src/abort.ts"],"mappings":";;;;;;;;cAca,eAAgB,oBAAkB;;;;;;;cA8BlC,iBAAkB,SAAS;;;;;;;;;;cAiB3B,uBACX,QAAQ,oBAAoB,eAC5B,iBACA,QAAQ,gBACP"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { AbortError } from "./errors.
|
|
1
|
+
import { AbortError } from "./errors.js";
|
|
2
2
|
//#region src/abort.ts
|
|
3
3
|
/**
|
|
4
4
|
* Abort-signal helpers for retry orchestration internals.
|
|
@@ -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,8 +54,8 @@ 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
|
|
|
61
|
-
//# sourceMappingURL=abort.
|
|
61
|
+
//# sourceMappingURL=abort.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"abort.js","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,248 @@
|
|
|
1
|
+
import { RetryError } from "./errors.js";
|
|
2
|
+
import { sleepWithAbortSignal, throwIfAborted, toAbortError } from "./abort.js";
|
|
3
|
+
import { defaultSleep } from "./sleep.js";
|
|
4
|
+
//#region src/_result-mode.ts
|
|
5
|
+
/**
|
|
6
|
+
* Result-mode execution path for `BaseRetryPolicy.run` when
|
|
7
|
+
* `throwOnExhausted: false` is set.
|
|
8
|
+
*
|
|
9
|
+
* @module @zap-studio/retry/_result-mode (private)
|
|
10
|
+
*/
|
|
11
|
+
/**
|
|
12
|
+
* When `signal` is already aborted, builds the terminal `{ ok: false }` object
|
|
13
|
+
* with a normalized `AbortError` on `error`.
|
|
14
|
+
*
|
|
15
|
+
* @param signal - Optional abort signal; only acts when `aborted` is set.
|
|
16
|
+
* @param attempts - Number of finished attempts to report in the result.
|
|
17
|
+
* @returns Failure result or `undefined` if not aborted.
|
|
18
|
+
*/
|
|
19
|
+
const buildAbortResult = (signal, attempts) => {
|
|
20
|
+
if (signal?.aborted !== true) return;
|
|
21
|
+
return {
|
|
22
|
+
attempts,
|
|
23
|
+
error: toAbortError(signal.reason),
|
|
24
|
+
ok: false
|
|
25
|
+
};
|
|
26
|
+
};
|
|
27
|
+
/**
|
|
28
|
+
* Runs one `execute(attempt)` call and returns either a success value or a
|
|
29
|
+
* captured error without rethrowing.
|
|
30
|
+
*
|
|
31
|
+
* @param execute - User work callback.
|
|
32
|
+
* @param attempt - One-based attempt number passed to `execute`.
|
|
33
|
+
* @returns A tagged success with `value` or a tagged failure with `error`.
|
|
34
|
+
*/
|
|
35
|
+
const runAttempt = async (execute, attempt) => {
|
|
36
|
+
try {
|
|
37
|
+
return {
|
|
38
|
+
ok: true,
|
|
39
|
+
value: await execute(attempt)
|
|
40
|
+
};
|
|
41
|
+
} catch (error) {
|
|
42
|
+
return {
|
|
43
|
+
error,
|
|
44
|
+
ok: false
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
/**
|
|
49
|
+
* Awaits inter-attempt delay in result mode, mapping an abort during wait to
|
|
50
|
+
* a terminal result instead of throwing when `throwOnExhausted` is false.
|
|
51
|
+
*
|
|
52
|
+
* @param sleep - Custom or default sleep implementation.
|
|
53
|
+
* @param delayMs - Milliseconds to wait.
|
|
54
|
+
* @param signal - If set, `sleep` is raced with the abort signal.
|
|
55
|
+
* @param attempts - Attempt count to attach if the wait ends in abort.
|
|
56
|
+
* @returns A terminal result when canceled during the wait, otherwise
|
|
57
|
+
* `undefined`.
|
|
58
|
+
* @throws {Error} The underlying `sleep` rejection when it is not an abort.
|
|
59
|
+
*/
|
|
60
|
+
const waitForDelay = async (sleep, delayMs, signal, attempts) => {
|
|
61
|
+
if (signal === void 0) {
|
|
62
|
+
await sleep(delayMs);
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
try {
|
|
66
|
+
await sleepWithAbortSignal(sleep, delayMs, signal);
|
|
67
|
+
return;
|
|
68
|
+
} catch (error) {
|
|
69
|
+
const aborted = buildAbortResult(signal, attempts);
|
|
70
|
+
if (aborted !== void 0) return aborted;
|
|
71
|
+
throw error;
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
/**
|
|
75
|
+
* After a failed attempt, applies abort rules, `next`, optional delay, and
|
|
76
|
+
* either returns a terminal `RetryRunResult` or `undefined` to continue.
|
|
77
|
+
*
|
|
78
|
+
* @param policy - Retry policy hooks (`next`, `onExhausted`) matching
|
|
79
|
+
* `BaseRetryPolicy`.
|
|
80
|
+
* @param params - Failure context for the current attempt.
|
|
81
|
+
* @param params.attempt - Current attempt number.
|
|
82
|
+
* @param params.error - Error thrown by the attempt.
|
|
83
|
+
* @param params.sleep - Delay function between retries.
|
|
84
|
+
* @param params.signal - Optional abort signal.
|
|
85
|
+
* @returns Terminal non-throw result if the loop should stop, otherwise
|
|
86
|
+
* `undefined` to schedule another attempt.
|
|
87
|
+
* @throws {Error} Any error thrown by `next`, `onExhausted`, or a custom `sleep` when
|
|
88
|
+
* the error is not an abort.
|
|
89
|
+
*/
|
|
90
|
+
const handleFailure = async (policy, params) => {
|
|
91
|
+
const { attempt, error, sleep, signal } = params;
|
|
92
|
+
const abortResult = buildAbortResult(signal, attempt);
|
|
93
|
+
if (abortResult !== void 0) return abortResult;
|
|
94
|
+
const decision = policy.next({
|
|
95
|
+
attempt,
|
|
96
|
+
error
|
|
97
|
+
});
|
|
98
|
+
if (!decision.shouldRetry) return {
|
|
99
|
+
attempts: attempt,
|
|
100
|
+
error: policy.onExhausted({
|
|
101
|
+
attempts: attempt,
|
|
102
|
+
error
|
|
103
|
+
}),
|
|
104
|
+
ok: false
|
|
105
|
+
};
|
|
106
|
+
if (decision.delayMs > 0) {
|
|
107
|
+
const delayAbortResult = await waitForDelay(sleep, decision.delayMs, signal, attempt);
|
|
108
|
+
if (delayAbortResult !== void 0) return delayAbortResult;
|
|
109
|
+
}
|
|
110
|
+
};
|
|
111
|
+
/**
|
|
112
|
+
* Runs the non-throw retry loop, returning
|
|
113
|
+
* `RetryRunResult`.
|
|
114
|
+
*
|
|
115
|
+
* @param policy - Object providing `next` and `onExhausted` (same contract as
|
|
116
|
+
* `BaseRetryPolicy`).
|
|
117
|
+
* @param execute - Async work callback per attempt.
|
|
118
|
+
* @param sleep - Delay function between retries.
|
|
119
|
+
* @param signal - Optional cancel signal.
|
|
120
|
+
* @returns Terminal success or failure object.
|
|
121
|
+
* @throws {Error} Any error thrown by `next`, `onExhausted`, or a non-abort `sleep`
|
|
122
|
+
* failure.
|
|
123
|
+
*/
|
|
124
|
+
const runResultMode = async (policy, execute, sleep, signal) => {
|
|
125
|
+
let attempt = 1;
|
|
126
|
+
while (true) {
|
|
127
|
+
const abortResult = buildAbortResult(signal, Math.max(0, attempt - 1));
|
|
128
|
+
if (abortResult !== void 0) return abortResult;
|
|
129
|
+
const execution = await runAttempt(execute, attempt);
|
|
130
|
+
if (execution.ok) return {
|
|
131
|
+
ok: true,
|
|
132
|
+
value: execution.value
|
|
133
|
+
};
|
|
134
|
+
const failure = await handleFailure(policy, {
|
|
135
|
+
attempt,
|
|
136
|
+
error: execution.error,
|
|
137
|
+
signal,
|
|
138
|
+
sleep
|
|
139
|
+
});
|
|
140
|
+
if (failure !== void 0) return failure;
|
|
141
|
+
attempt += 1;
|
|
142
|
+
}
|
|
143
|
+
};
|
|
144
|
+
//#endregion
|
|
145
|
+
//#region src/_throw-mode.ts
|
|
146
|
+
/**
|
|
147
|
+
* Throw-mode execution path for `BaseRetryPolicy.run` (default when
|
|
148
|
+
* `throwOnExhausted` is not `false`).
|
|
149
|
+
*
|
|
150
|
+
* @module @zap-studio/retry/_throw-mode (private)
|
|
151
|
+
*/
|
|
152
|
+
/**
|
|
153
|
+
* Runs the throw-mode retry loop: throws `RetryError` on exhaustion and
|
|
154
|
+
* `AbortError` when `signal` aborts.
|
|
155
|
+
*
|
|
156
|
+
* @param policy - Object providing `next` and `onExhausted` (same contract as
|
|
157
|
+
* `BaseRetryPolicy`).
|
|
158
|
+
* @param execute - Async work callback per attempt.
|
|
159
|
+
* @param sleep - Delay function between retries.
|
|
160
|
+
* @param signal - Optional cancel signal.
|
|
161
|
+
* @returns Resolves to the first successful return value.
|
|
162
|
+
* @throws {RetryError} When retries are exhausted and `onExhausted` returns
|
|
163
|
+
* the terminal error.
|
|
164
|
+
* @throws {AbortError} When `signal` is already aborted or aborts while waiting.
|
|
165
|
+
* @throws {Error} Any error thrown by `next`, `onExhausted`, or `sleep`.
|
|
166
|
+
*/
|
|
167
|
+
const runThrowMode = async (policy, execute, sleep, signal) => {
|
|
168
|
+
let attempt = 1;
|
|
169
|
+
while (true) {
|
|
170
|
+
throwIfAborted(signal);
|
|
171
|
+
try {
|
|
172
|
+
return await execute(attempt);
|
|
173
|
+
} catch (error) {
|
|
174
|
+
throwIfAborted(signal);
|
|
175
|
+
const typedError = error;
|
|
176
|
+
const decision = policy.next({
|
|
177
|
+
attempt,
|
|
178
|
+
error: typedError
|
|
179
|
+
});
|
|
180
|
+
if (!decision.shouldRetry) throw policy.onExhausted({
|
|
181
|
+
attempts: attempt,
|
|
182
|
+
error: typedError
|
|
183
|
+
});
|
|
184
|
+
if (decision.delayMs > 0) await (signal === void 0 ? sleep(decision.delayMs) : sleepWithAbortSignal(sleep, decision.delayMs, signal));
|
|
185
|
+
attempt += 1;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
};
|
|
189
|
+
//#endregion
|
|
190
|
+
//#region src/base-policy.ts
|
|
191
|
+
/**
|
|
192
|
+
* Retry runner base class and shared orchestration implementation.
|
|
193
|
+
*
|
|
194
|
+
* @module @zap-studio/retry/base-policy
|
|
195
|
+
*/
|
|
196
|
+
/**
|
|
197
|
+
* Base class for implementing retry policies and running retry orchestration.
|
|
198
|
+
*
|
|
199
|
+
* Extend this class and implement {@link BaseRetryPolicy.next} to define retry
|
|
200
|
+
* behavior, then call {@link BaseRetryPolicy.run} to execute operations with that
|
|
201
|
+
* policy.
|
|
202
|
+
*/
|
|
203
|
+
var BaseRetryPolicy = class {
|
|
204
|
+
/**
|
|
205
|
+
* Builds the terminal error thrown or returned when retries are exhausted.
|
|
206
|
+
*
|
|
207
|
+
* Override this when you need custom terminal error types.
|
|
208
|
+
*
|
|
209
|
+
* @param input - Exhaustion context.
|
|
210
|
+
* @returns `RetryError` by default.
|
|
211
|
+
* @throws {Error} Any error thrown by an overriding policy implementation.
|
|
212
|
+
*/
|
|
213
|
+
onExhausted(input) {
|
|
214
|
+
return new RetryError("Retry policy exhausted all attempts.", {
|
|
215
|
+
attempts: input.attempts,
|
|
216
|
+
lastData: input.data,
|
|
217
|
+
lastError: input.error
|
|
218
|
+
});
|
|
219
|
+
}
|
|
220
|
+
/**
|
|
221
|
+
* Runs retry orchestration in non-throw mode.
|
|
222
|
+
*
|
|
223
|
+
* When `throwOnExhausted` is `false`, returns a discriminated result union.
|
|
224
|
+
*
|
|
225
|
+
* @param execute - Async function to execute per attempt.
|
|
226
|
+
* @param options - Runner settings.
|
|
227
|
+
* @returns Success value or terminal result object based on option mode.
|
|
228
|
+
* @throws {Error} Any error thrown by `next`, by `onExhausted`, or by a custom `sleep`
|
|
229
|
+
* function. When `throwOnExhausted` is `false`, exhaustion itself is returned
|
|
230
|
+
* as `{ ok: false }` instead of thrown.
|
|
231
|
+
* Cancellation is returned as `{ ok: false, error: AbortError }` in non-throw
|
|
232
|
+
* mode.
|
|
233
|
+
*
|
|
234
|
+
* @example
|
|
235
|
+
* const result = await policy.run(doWork, { throwOnExhausted: false });
|
|
236
|
+
* if (!result.ok) console.error(result.error);
|
|
237
|
+
*/
|
|
238
|
+
async run(execute, options = {}) {
|
|
239
|
+
const sleep = options.sleep ?? defaultSleep;
|
|
240
|
+
const { signal } = options;
|
|
241
|
+
if (options.throwOnExhausted === false) return await runResultMode(this, execute, sleep, signal);
|
|
242
|
+
return await runThrowMode(this, execute, sleep, signal);
|
|
243
|
+
}
|
|
244
|
+
};
|
|
245
|
+
//#endregion
|
|
246
|
+
export { BaseRetryPolicy as t };
|
|
247
|
+
|
|
248
|
+
//# sourceMappingURL=base-policy-Dn3TOJd3.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"base-policy-Dn3TOJd3.js","names":[],"sources":["../src/_result-mode.ts","../src/_throw-mode.ts","../src/base-policy.ts"],"sourcesContent":["/**\n * Result-mode execution path for `BaseRetryPolicy.run` when\n * `throwOnExhausted: false` is set.\n *\n * @module @zap-studio/retry/_result-mode (private)\n */\n\nimport { sleepWithAbortSignal, toAbortError } from \"./abort.js\";\nimport type { RetryPolicy, RetryRunResult } from \"./types.js\";\n\n/**\n * When `signal` is already aborted, builds the terminal `{ ok: false }` object\n * with a normalized `AbortError` on `error`.\n *\n * @param signal - Optional abort signal; only acts when `aborted` is set.\n * @param attempts - Number of finished attempts to report in the result.\n * @returns Failure result or `undefined` if not aborted.\n */\nconst buildAbortResult = (\n signal: AbortSignal | undefined,\n attempts: number\n): RetryRunResult<never> | undefined => {\n if (signal?.aborted !== true) {\n return undefined;\n }\n\n return {\n attempts,\n error: toAbortError(signal.reason),\n ok: false,\n };\n};\n\n/**\n * Runs one `execute(attempt)` call and returns either a success value or a\n * captured error without rethrowing.\n *\n * @param execute - User work callback.\n * @param attempt - One-based attempt number passed to `execute`.\n * @returns A tagged success with `value` or a tagged failure with `error`.\n */\nconst runAttempt = async <T>(\n execute: (attempt: number) => Promise<T>,\n attempt: number\n): Promise<{ ok: true; value: T } | { ok: false; error: unknown }> => {\n try {\n return {\n ok: true,\n value: await execute(attempt),\n };\n } catch (error) {\n return {\n error,\n ok: false,\n };\n }\n};\n\n/**\n * Awaits inter-attempt delay in result mode, mapping an abort during wait to\n * a terminal result instead of throwing when `throwOnExhausted` is false.\n *\n * @param sleep - Custom or default sleep implementation.\n * @param delayMs - Milliseconds to wait.\n * @param signal - If set, `sleep` is raced with the abort signal.\n * @param attempts - Attempt count to attach if the wait ends in abort.\n * @returns A terminal result when canceled during the wait, otherwise\n * `undefined`.\n * @throws {Error} The underlying `sleep` rejection when it is not an abort.\n */\nconst waitForDelay = async (\n sleep: (delayMs: number) => Promise<void>,\n delayMs: number,\n signal: AbortSignal | undefined,\n attempts: number\n): Promise<RetryRunResult<never> | undefined> => {\n if (signal === undefined) {\n await sleep(delayMs);\n return undefined;\n }\n\n try {\n await sleepWithAbortSignal(sleep, delayMs, signal);\n return undefined;\n } catch (error) {\n const aborted = buildAbortResult(signal, attempts);\n if (aborted !== undefined) {\n return aborted;\n }\n throw error;\n }\n};\n\n/**\n * After a failed attempt, applies abort rules, `next`, optional delay, and\n * either returns a terminal `RetryRunResult` or `undefined` to continue.\n *\n * @param policy - Retry policy hooks (`next`, `onExhausted`) matching\n * `BaseRetryPolicy`.\n * @param params - Failure context for the current attempt.\n * @param params.attempt - Current attempt number.\n * @param params.error - Error thrown by the attempt.\n * @param params.sleep - Delay function between retries.\n * @param params.signal - Optional abort signal.\n * @returns Terminal non-throw result if the loop should stop, otherwise\n * `undefined` to schedule another attempt.\n * @throws {Error} Any error thrown by `next`, `onExhausted`, or a custom `sleep` when\n * the error is not an abort.\n */\nconst handleFailure = async <TError, TData>(\n policy: RetryPolicy<TError, TData>,\n params: {\n attempt: number;\n error: TError;\n sleep: (delayMs: number) => Promise<void>;\n signal: AbortSignal | undefined;\n }\n): Promise<RetryRunResult<never> | undefined> => {\n const { attempt, error, sleep, signal } = params;\n const abortResult = buildAbortResult(signal, attempt);\n if (abortResult !== undefined) {\n return abortResult;\n }\n\n const decision = policy.next({\n attempt,\n error,\n });\n\n if (!decision.shouldRetry) {\n const terminalError = policy.onExhausted({\n attempts: attempt,\n error,\n });\n\n return {\n attempts: attempt,\n error: terminalError,\n ok: false,\n };\n }\n\n if (decision.delayMs > 0) {\n const delayAbortResult = await waitForDelay(\n sleep,\n decision.delayMs,\n signal,\n attempt\n );\n if (delayAbortResult !== undefined) {\n return delayAbortResult;\n }\n }\n\n return undefined;\n};\n\n/**\n * Runs the non-throw retry loop, returning\n * `RetryRunResult`.\n *\n * @param policy - Object providing `next` and `onExhausted` (same contract as\n * `BaseRetryPolicy`).\n * @param execute - Async work callback per attempt.\n * @param sleep - Delay function between retries.\n * @param signal - Optional cancel signal.\n * @returns Terminal success or failure object.\n * @throws {Error} Any error thrown by `next`, `onExhausted`, or a non-abort `sleep`\n * failure.\n */\nexport const runResultMode = async <T, TError, TData>(\n policy: RetryPolicy<TError, TData>,\n execute: (attempt: number) => Promise<T>,\n sleep: (delayMs: number) => Promise<void>,\n signal?: AbortSignal\n): Promise<RetryRunResult<T>> => {\n let attempt = 1;\n\n while (true) {\n const abortResult = buildAbortResult(signal, Math.max(0, attempt - 1));\n if (abortResult !== undefined) {\n return abortResult;\n }\n\n // oxlint-disable-next-line no-await-in-loop -- Retry attempts must run sequentially.\n const execution = await runAttempt(execute, attempt);\n if (execution.ok) {\n return { ok: true, value: execution.value };\n }\n\n // oxlint-disable-next-line no-await-in-loop -- Failure handling belongs to the current sequential attempt.\n const failure = await handleFailure(policy, {\n attempt,\n // oxlint-disable-next-line typescript/no-unsafe-type-assertion -- Policy error generic represents the caller's thrown error domain.\n error: execution.error as TError,\n signal,\n sleep,\n });\n if (failure !== undefined) {\n return failure;\n }\n\n attempt += 1;\n }\n};\n","/**\n * Throw-mode execution path for `BaseRetryPolicy.run` (default when\n * `throwOnExhausted` is not `false`).\n *\n * @module @zap-studio/retry/_throw-mode (private)\n */\n\nimport { sleepWithAbortSignal, throwIfAborted } from \"./abort.js\";\nimport type { RetryPolicy } from \"./types.js\";\n\n/**\n * Runs the throw-mode retry loop: throws `RetryError` on exhaustion and\n * `AbortError` when `signal` aborts.\n *\n * @param policy - Object providing `next` and `onExhausted` (same contract as\n * `BaseRetryPolicy`).\n * @param execute - Async work callback per attempt.\n * @param sleep - Delay function between retries.\n * @param signal - Optional cancel signal.\n * @returns Resolves to the first successful return value.\n * @throws {RetryError} When retries are exhausted and `onExhausted` returns\n * the terminal error.\n * @throws {AbortError} When `signal` is already aborted or aborts while waiting.\n * @throws {Error} Any error thrown by `next`, `onExhausted`, or `sleep`.\n */\nexport const runThrowMode = async <T, TError, TData>(\n policy: RetryPolicy<TError, TData>,\n execute: (attempt: number) => Promise<T>,\n sleep: (delayMs: number) => Promise<void>,\n signal?: AbortSignal\n): Promise<T> => {\n let attempt = 1;\n\n while (true) {\n throwIfAborted(signal);\n\n try {\n // oxlint-disable-next-line no-await-in-loop -- Retry attempts must run sequentially.\n return await execute(attempt);\n } catch (error) {\n throwIfAborted(signal);\n\n // oxlint-disable-next-line typescript/no-unsafe-type-assertion -- Policy error generic represents the caller's thrown error domain.\n const typedError = error as TError;\n const decision = policy.next({\n attempt,\n error: typedError,\n });\n\n if (!decision.shouldRetry) {\n throw policy.onExhausted({\n attempts: attempt,\n error: typedError,\n });\n }\n\n if (decision.delayMs > 0) {\n // oxlint-disable-next-line no-await-in-loop -- Delay belongs between sequential retry attempts.\n await (signal === undefined\n ? sleep(decision.delayMs)\n : sleepWithAbortSignal(sleep, decision.delayMs, signal));\n }\n\n attempt += 1;\n }\n }\n};\n","/**\n * Retry runner base class and shared orchestration implementation.\n *\n * @module @zap-studio/retry/base-policy\n */\n\nimport { runResultMode } from \"./_result-mode.js\";\nimport { runThrowMode } from \"./_throw-mode.js\";\nimport { RetryError } from \"./errors.js\";\nimport { defaultSleep } from \"./sleep.js\";\nimport type {\n RetryDecision,\n RetryDecisionInput,\n RetryExhaustedInput,\n RetryPolicy,\n RetryRunOptions,\n RetryRunResult,\n} from \"./types.js\";\n\n/**\n * Base class for implementing retry policies and running retry orchestration.\n *\n * Extend this class and implement {@link BaseRetryPolicy.next} to define retry\n * behavior, then call {@link BaseRetryPolicy.run} to execute operations with that\n * policy.\n */\nexport abstract class BaseRetryPolicy<\n TError = unknown,\n TData = unknown,\n> implements RetryPolicy<TError, TData> {\n /**\n * Returns the retry decision for a failed attempt.\n *\n * @param input - Attempt context used to compute retry behavior.\n * @throws {Error} Any error thrown by a concrete retry policy implementation.\n */\n public abstract next(input: RetryDecisionInput<TError, TData>): RetryDecision;\n\n /**\n * Builds the terminal error thrown or returned when retries are exhausted.\n *\n * Override this when you need custom terminal error types.\n *\n * @param input - Exhaustion context.\n * @returns `RetryError` by default.\n * @throws {Error} Any error thrown by an overriding policy implementation.\n */\n // oxlint-disable-next-line class-methods-use-this -- RetryPolicy requires an instance hook that subclasses may override.\n public onExhausted(input: RetryExhaustedInput<TError, TData>): RetryError {\n return new RetryError(\"Retry policy exhausted all attempts.\", {\n attempts: input.attempts,\n lastData: input.data,\n lastError: input.error,\n });\n }\n\n /**\n * Runs retry orchestration in non-throw mode.\n *\n * @param execute - Async function to execute per attempt.\n * @param options - Runner settings with `throwOnExhausted: false`.\n * @returns A discriminated result union containing success value or terminal error.\n * @throws {Error} Any error thrown by `next`, `onExhausted`, or a custom `sleep`.\n */\n public async run<T>(\n execute: (attempt: number) => Promise<T>,\n options: RetryRunOptions & { throwOnExhausted: false }\n ): Promise<RetryRunResult<T>>;\n\n /**\n * Runs retry orchestration and throws terminal error on exhaustion.\n *\n * @param execute - Async function to execute per attempt.\n * @param options - Optional runner settings.\n * @returns The successful execution value.\n * @throws {RetryError} When retries are exhausted and `onExhausted` returns the\n * terminal retry error. The default implementation returns `RetryError` with the last\n * execution failure available on `RetryError.lastError`.\n * @throws {AbortError} When `options.signal` is already aborted or aborts while retrying.\n * @throws {Error} Any error thrown by `next`, by `onExhausted`, or by a custom `sleep`\n * function.\n */\n public async run<T>(\n execute: (attempt: number) => Promise<T>,\n options?: RetryRunOptions & { throwOnExhausted?: true }\n ): Promise<T>;\n\n /**\n * Runs retry orchestration in non-throw mode.\n *\n * When `throwOnExhausted` is `false`, returns a discriminated result union.\n *\n * @param execute - Async function to execute per attempt.\n * @param options - Runner settings.\n * @returns Success value or terminal result object based on option mode.\n * @throws {Error} Any error thrown by `next`, by `onExhausted`, or by a custom `sleep`\n * function. When `throwOnExhausted` is `false`, exhaustion itself is returned\n * as `{ ok: false }` instead of thrown.\n * Cancellation is returned as `{ ok: false, error: AbortError }` in non-throw\n * mode.\n *\n * @example\n * const result = await policy.run(doWork, { throwOnExhausted: false });\n * if (!result.ok) console.error(result.error);\n */\n public async run<T>(\n execute: (attempt: number) => Promise<T>,\n options: RetryRunOptions = {}\n ): Promise<T | RetryRunResult<T>> {\n const sleep = options.sleep ?? defaultSleep;\n const { signal } = options;\n if (options.throwOnExhausted === false) {\n return await runResultMode(this, execute, sleep, signal);\n }\n\n return await runThrowMode(this, execute, sleep, signal);\n }\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { r as RetryError } from "./errors-BVZjP1Q5.js";
|
|
2
|
+
import { RetryDecision, RetryDecisionInput, RetryExhaustedInput, RetryPolicy, RetryRunOptions, RetryRunResult } from "./types.js";
|
|
3
|
+
//#region src/base-policy.d.ts
|
|
4
|
+
/**
|
|
5
|
+
* Base class for implementing retry policies and running retry orchestration.
|
|
6
|
+
*
|
|
7
|
+
* Extend this class and implement {@link BaseRetryPolicy.next} to define retry
|
|
8
|
+
* behavior, then call {@link BaseRetryPolicy.run} to execute operations with that
|
|
9
|
+
* policy.
|
|
10
|
+
*/
|
|
11
|
+
declare abstract class BaseRetryPolicy<TError = unknown, TData = unknown> implements RetryPolicy<TError, TData> {
|
|
12
|
+
/**
|
|
13
|
+
* Returns the retry decision for a failed attempt.
|
|
14
|
+
*
|
|
15
|
+
* @param input - Attempt context used to compute retry behavior.
|
|
16
|
+
* @throws {Error} Any error thrown by a concrete retry policy implementation.
|
|
17
|
+
*/
|
|
18
|
+
abstract next(input: RetryDecisionInput<TError, TData>): RetryDecision;
|
|
19
|
+
/**
|
|
20
|
+
* Builds the terminal error thrown or returned when retries are exhausted.
|
|
21
|
+
*
|
|
22
|
+
* Override this when you need custom terminal error types.
|
|
23
|
+
*
|
|
24
|
+
* @param input - Exhaustion context.
|
|
25
|
+
* @returns `RetryError` by default.
|
|
26
|
+
* @throws {Error} Any error thrown by an overriding policy implementation.
|
|
27
|
+
*/
|
|
28
|
+
onExhausted(input: RetryExhaustedInput<TError, TData>): RetryError;
|
|
29
|
+
/**
|
|
30
|
+
* Runs retry orchestration in non-throw mode.
|
|
31
|
+
*
|
|
32
|
+
* @param execute - Async function to execute per attempt.
|
|
33
|
+
* @param options - Runner settings with `throwOnExhausted: false`.
|
|
34
|
+
* @returns A discriminated result union containing success value or terminal error.
|
|
35
|
+
* @throws {Error} Any error thrown by `next`, `onExhausted`, or a custom `sleep`.
|
|
36
|
+
*/
|
|
37
|
+
run<T>(execute: (attempt: number) => Promise<T>, options: RetryRunOptions & {
|
|
38
|
+
throwOnExhausted: false;
|
|
39
|
+
}): Promise<RetryRunResult<T>>;
|
|
40
|
+
/**
|
|
41
|
+
* Runs retry orchestration and throws terminal error on exhaustion.
|
|
42
|
+
*
|
|
43
|
+
* @param execute - Async function to execute per attempt.
|
|
44
|
+
* @param options - Optional runner settings.
|
|
45
|
+
* @returns The successful execution value.
|
|
46
|
+
* @throws {RetryError} When retries are exhausted and `onExhausted` returns the
|
|
47
|
+
* terminal retry error. The default implementation returns `RetryError` with the last
|
|
48
|
+
* execution failure available on `RetryError.lastError`.
|
|
49
|
+
* @throws {AbortError} When `options.signal` is already aborted or aborts while retrying.
|
|
50
|
+
* @throws {Error} Any error thrown by `next`, by `onExhausted`, or by a custom `sleep`
|
|
51
|
+
* function.
|
|
52
|
+
*/
|
|
53
|
+
run<T>(execute: (attempt: number) => Promise<T>, options?: RetryRunOptions & {
|
|
54
|
+
throwOnExhausted?: true;
|
|
55
|
+
}): Promise<T>;
|
|
56
|
+
}
|
|
57
|
+
//#endregion
|
|
58
|
+
export { BaseRetryPolicy };
|
|
59
|
+
//# sourceMappingURL=base-policy.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"base-policy.d.ts","names":[],"sources":["../src/base-policy.ts"],"mappings":";;;;;;;;;;uBA0BsB,gBACpB,kBACA,4BACW,YAAY,QAAQ;;;;;;;WAOf,KAAK,OAAO,mBAAmB,QAAQ,SAAS;;;;;;;;;;EAYhE,YAAmB,OAAO,oBAAoB,QAAQ,SAAS;;;;;;;;;EAgB/D,IAAiB,GACf,UAAU,oBAAoB,QAAQ,IACtC,SAAS;IAAoB;MAC5B,QAAQ,eAAe;;;;;;;;;;;;;;EAe1B,IAAiB,GACf,UAAU,oBAAoB,QAAQ,IACtC,UAAU;IAAoB;MAC7B,QAAQ"}
|