@zap-studio/retry 0.3.2 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/CHANGELOG.md +50 -18
  2. package/LICENSE +1 -1
  3. package/README.md +88 -154
  4. package/dist/base-policy.d.ts +61 -53
  5. package/dist/base-policy.d.ts.map +1 -1
  6. package/dist/base-policy.js +306 -2
  7. package/dist/base-policy.js.map +1 -0
  8. package/dist/{errors-BVZjP1Q5.d.ts → errors-CS5UPJWs.d.ts} +25 -1
  9. package/dist/errors-CS5UPJWs.d.ts.map +1 -0
  10. package/dist/errors.d.ts +1 -1
  11. package/dist/errors.js +18 -0
  12. package/dist/errors.js.map +1 -1
  13. package/dist/exponential-backoff.d.ts +13 -28
  14. package/dist/exponential-backoff.d.ts.map +1 -1
  15. package/dist/exponential-backoff.js +10 -35
  16. package/dist/exponential-backoff.js.map +1 -1
  17. package/dist/fixed-delay.d.ts +9 -24
  18. package/dist/fixed-delay.d.ts.map +1 -1
  19. package/dist/fixed-delay.js +10 -30
  20. package/dist/fixed-delay.js.map +1 -1
  21. package/dist/index.d.ts +6 -7
  22. package/dist/index.js +5 -6
  23. package/dist/linear-backoff.d.ts +46 -0
  24. package/dist/linear-backoff.d.ts.map +1 -0
  25. package/dist/linear-backoff.js +35 -0
  26. package/dist/linear-backoff.js.map +1 -0
  27. package/dist/types.d.ts +51 -7
  28. package/dist/types.d.ts.map +1 -1
  29. package/package.json +9 -9
  30. package/dist/abort.d.ts +0 -29
  31. package/dist/abort.d.ts.map +0 -1
  32. package/dist/abort.js +0 -61
  33. package/dist/abort.js.map +0 -1
  34. package/dist/base-policy-Dn3TOJd3.js +0 -248
  35. package/dist/base-policy-Dn3TOJd3.js.map +0 -1
  36. package/dist/errors-BVZjP1Q5.d.ts.map +0 -1
  37. package/dist/sleep.d.ts +0 -17
  38. package/dist/sleep.d.ts.map +0 -1
  39. package/dist/sleep.js +0 -23
  40. package/dist/sleep.js.map +0 -1
package/CHANGELOG.md CHANGED
@@ -1,28 +1,63 @@
1
- ## @zap-studio/retry@0.3.2
1
+ # Changelog
2
2
 
3
- ### Tree-shakeable root re-exports
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
+
7
+ ## [1.0.0]
8
+
9
+ ### Added
10
+
11
+ `RetryPolicy` gains an optional `isKnownError?: (error: unknown) => error is TError` hook that `BaseRetryPolicy.run(...)` now calls before handing a caught value to `next(...)`/`onExhausted(...)`. `BaseRetryPolicy.isKnownError` default checks `error instanceof Error`.
12
+
13
+ - Override `isKnownError` when `TError` is a narrower subclass (an HTTP error, a domain-specific error) to get real narrowing instead of an `instanceof Error` assumption, and to stop unrelated `Error` types from being retried as if they belonged to your domain.
14
+
15
+ See [Narrow the Error Domain](https://www.zapstudio.dev/retry/custom-policies#narrow-the-error-domain) for the override pattern.
16
+
17
+ New built-in policy: `linearBackoff(options)` adds a fixed `incrementMs` to the delay after each failed attempt, capped at `maxDelayMs` — steadier growth than `exponentialBackoff`, more spacing than `fixedDelay`. See [linearBackoff](https://www.zapstudio.dev/retry/linear-backoff).
18
+
19
+ ### Changed
20
+
21
+ - **Breaking:** `TError` is now constrained to `TError extends Error` and defaults to `Error` (was `TError = unknown`) on `RetryPolicy`, `RetryDecisionInput`, `RetryExhaustedInput`, and `BaseRetryPolicy`.
22
+ - **Breaking behavior change:** a rejection with a non-`Error` value (a thrown string, plain object, `undefined`, ...) now bypasses retry entirely on the attempt that produced it — it no longer reaches `next(...)`, and no delay/backoff is applied. Previously any thrown value was passed through to the policy unchanged. In throw mode, `run(...)` rethrows the value as-is; with `throwOnExhausted: false`, it's wrapped in a `RetryError` and returned on `result.error` — `run(...)` never throws in that mode.
23
+ - **Breaking:** Policies are plain objects instead of classes, for tree-shaking — bundlers can drop an unused policy factory and its defaults entirely, which isn't possible across a shared class hierarchy. `ExponentialBackoff`/`FixedDelay` classes are replaced by `exponentialBackoff(options)`/`fixedDelay(options)` factory functions that return a `RetryPolicy`. Migrate `new ExponentialBackoff(opts)` to `exponentialBackoff(opts)`, and `new FixedDelay(opts)` to `fixedDelay(opts)`.
24
+ - **Breaking:** `RetryPolicy.onExhausted` is now optional (previously required); omit it to use the same default `RetryError` that `BaseRetryPolicy.onExhausted` used to build.
25
+
26
+ ### Removed
27
+
28
+ - **Breaking:** `BaseRetryPolicy` is removed. Retry orchestration is now the standalone function `runRetryPolicy(policy, execute, options?)`, which accepts any object satisfying `RetryPolicy` — no subclassing required. Migrate `policy.run(execute, options)` to `runRetryPolicy(policy, execute, options)`. A custom policy that previously extended `BaseRetryPolicy` and overrode `next`/`onExhausted`/`isKnownError` becomes an object literal implementing the same members; see [Custom Policies](https://www.zapstudio.dev/retry/custom-policies).
29
+
30
+ ### Removed
31
+
32
+ Collapsed abort/sleep orchestration internals out of the public API.
33
+
34
+ - Removed the `./abort` and `./sleep` subpath exports.
35
+ - Removed the public `sleepWithAbortSignal`, `throwIfAborted`, and `toAbortError` exports — they were orchestration internals with no consumer outside the retry loop, not standalone utilities.
36
+ - `defaultSleep` is unaffected and still exported from `@zap-studio/retry` (no dedicated subpath).
37
+
38
+ ## [0.3.2]
39
+
40
+ ### Added
4
41
 
5
42
  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
43
 
7
44
  - `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
45
 
12
- ### Migrate to ultracite lint/format
46
+ ### Removed
13
47
 
14
- Internal formatting and lint cleanup only. No public API or behavior change.
48
+ - Removed the `./result-mode` and `./throw-mode` subpath exports. Both were orchestration internals (`runResultMode`, `runThrowMode`) and are no longer part of the public API.
15
49
 
16
- # @zap-studio/retry
50
+ ## [0.3.1]
17
51
 
18
- ## 0.3.0
52
+ ### Changed
19
53
 
20
- ### Breaking
54
+ Internal formatting and lint cleanup only. No public API or behavior change.
21
55
 
22
- - **Subpath for error types:** use `@zap-studio/retry/errors` (plural) for `RetryError`, `AbortError`, and related types. A prior JSR `error` subpath that pointed at a non-existent `error.ts` entry is removed; update deep imports from `@zap-studio/retry/error` to `@zap-studio/retry/errors`.
56
+ ## [0.3.0]
23
57
 
24
58
  ### Changed
25
59
 
60
+ - **Breaking:** Subpath for error types: use `@zap-studio/retry/errors` (plural) for `RetryError`, `AbortError`, and related types. A prior JSR `error` subpath that pointed at a non-existent `error.ts` entry is removed; update deep imports from `@zap-studio/retry/error` to `@zap-studio/retry/errors`.
26
61
  - Add dedicated `AbortError` and normalize cancellation paths so retry internals throw/return `RetryError` or `AbortError` instead of plain `Error`.
27
62
  - Expose `defaultSleep` from the `@zap-studio/retry/sleep` subpath only (the main entry does not re-export it; `run` still uses it internally when `sleep` is omitted).
28
63
  - Align non-throw exhaustion metadata so `result.attempts` and `result.error.attempts` stay consistent for `RetryError` outcomes.
@@ -33,7 +68,7 @@ Internal formatting and lint cleanup only. No public API or behavior change.
33
68
  - Add exhaustive TSDoc for `result-mode` and other `src` modules, including private helpers, policy option and state fields, and `RetryRunResult` union members.
34
69
  - Rework test layout into `sleep`, `throw-mode`, `result-mode`, and `index` test files with a shared `sequence-policy` fixture, replacing the prior combined `index` and `abort` test files.
35
70
 
36
- ## 0.2.0
71
+ ## [0.2.0]
37
72
 
38
73
  ### Changed
39
74
 
@@ -43,13 +78,13 @@ Internal formatting and lint cleanup only. No public API or behavior change.
43
78
  - Add abort-focused ecosystem benchmarks comparing signal overhead and immediate cancellation behavior.
44
79
  - Expand TSDoc coverage for new runner internals added in this release.
45
80
 
46
- ## 0.1.2
81
+ ## [0.1.2]
47
82
 
48
83
  ### Changed
49
84
 
50
85
  - Expand TSDoc coverage across retry modules and exported contracts for stronger JSR documentation completeness.
51
86
 
52
- ## 0.1.1
87
+ ## [0.1.1]
53
88
 
54
89
  ### Fixed
55
90
 
@@ -59,7 +94,7 @@ Internal formatting and lint cleanup only. No public API or behavior change.
59
94
 
60
95
  - e9903c5: Removed redundant `| undefined` unions from public retry option and decision types.
61
96
 
62
- ## 0.1.0
97
+ ## [0.1.0]
63
98
 
64
99
  ### Added
65
100
 
@@ -71,7 +106,4 @@ Internal formatting and lint cleanup only. No public API or behavior change.
71
106
  - Added `ExponentialBackoff` policy with bounded exponential delay via `baseDelayMs`, `maxDelayMs`, and `maxAttempts`.
72
107
  - Added `FixedDelay` policy with constant delay and bounded attempts.
73
108
  - Added `RetryError` for exhausted-retry failures with structured attempt/error/data context.
74
-
75
- ### Documentation
76
-
77
109
  - Documented throwable behavior on `RetryPolicy`, `BaseRetryPolicy.run`, and related contracts with explicit `@throws` tags for policy, exhaustion, and custom `sleep` failures.
package/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2026 alexandretrotel
3
+ Copyright (c) 2026 Alexandre Trotel
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
package/README.md CHANGED
@@ -2,41 +2,37 @@
2
2
 
3
3
  Composable retry policy primitives for HTTP clients and async workflows.
4
4
 
5
+ Full documentation: [zapstudio.dev/retry](https://www.zapstudio.dev/retry)
6
+
5
7
  ## Installation
6
8
 
7
9
  ```bash
8
- nub add @zap-studio/retry
9
- # or
10
10
  npm install @zap-studio/retry
11
- # or
12
- pnpm add @zap-studio/retry
13
11
  ```
14
12
 
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 |
13
+ ## Features
24
14
 
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`).
15
+ - **Built-in policies**: `fixedDelay(...)`, `linearBackoff(...)`, and `exponentialBackoff(...)`.
16
+ - **A shared runner** via `runRetryPolicy(policy, execute, options?)` with attempt-aware callbacks and custom sleep injection.
17
+ - **Structured terminal errors**: `RetryError` on exhaustion, `AbortError` on cancellation.
18
+ - **Non-throw mode** (`throwOnExhausted: false`) returns a `RetryRunResult` instead of throwing.
19
+ - **Cancellation** through `AbortSignal`, checked before, between, and during retries.
20
+ - **Custom policies** as plain objects implementing `RetryPolicy` — just a `next(...)` function, no subclassing.
21
+ - **Tree-shakeable** — policies are functions returning plain objects, not classes; unused policies are dropped by any modern bundler.
26
22
 
27
- ## Usage
23
+ ## Quick Start
28
24
 
29
25
  ```ts
30
- import { ExponentialBackoff, FixedDelay } from "@zap-studio/retry";
26
+ import { exponentialBackoff, runRetryPolicy } from "@zap-studio/retry";
31
27
  import { $fetch } from "@zap-studio/fetch";
32
28
 
33
- const exponential = new ExponentialBackoff({
29
+ const policy = exponentialBackoff({
34
30
  maxAttempts: 5,
35
31
  baseDelayMs: 100,
36
32
  maxDelayMs: 2_000,
37
33
  });
38
34
 
39
- const data = await exponential.run(async () => {
35
+ const data = await runRetryPolicy(policy, async () => {
40
36
  const response = await $fetch("https://api.example.com/users", {
41
37
  throwOnFetchError: true,
42
38
  });
@@ -44,186 +40,124 @@ const data = await exponential.run(async () => {
44
40
  });
45
41
  ```
46
42
 
47
- ## Handling Errors
48
-
49
- `run(...)` throws when retries are exhausted.
43
+ ## Built-in Policies
50
44
 
51
- By default, policies extending `BaseRetryPolicy` throw `RetryError` on exhaustion and `AbortError` on cancellation.
45
+ `fixedDelay(...)`, `linearBackoff(...)`, and `exponentialBackoff(...)`.
52
46
 
53
47
  ```ts
54
- import { AbortError, RetryError } from "@zap-studio/retry";
48
+ import {
49
+ exponentialBackoff,
50
+ fixedDelay,
51
+ linearBackoff,
52
+ } from "@zap-studio/retry";
55
53
 
56
- try {
57
- const data = await exponential.run(async () => {
58
- const response = await $fetch("https://api.example.com/users", {
59
- throwOnFetchError: true,
60
- });
61
- return await response.json();
62
- });
63
- console.log(data);
64
- } catch (error) {
65
- if (error instanceof RetryError) {
66
- console.error("Retries exhausted:", error.attempts);
67
- console.error("Last error:", error.lastError);
68
- } else if (error instanceof AbortError) {
69
- console.error("Retry aborted:", error.message);
70
- } else {
71
- throw error;
72
- }
73
- }
54
+ const exponential = exponentialBackoff({
55
+ maxAttempts: 5,
56
+ baseDelayMs: 100,
57
+ maxDelayMs: 2_000,
58
+ });
59
+ const linear = linearBackoff({
60
+ maxAttempts: 5,
61
+ baseDelayMs: 100,
62
+ incrementMs: 100,
63
+ maxDelayMs: 2_000,
64
+ });
65
+ const fixed = fixedDelay({ maxAttempts: 4, delayMs: 300 });
74
66
  ```
75
67
 
76
- To handle exhaustion without throwing, pass `throwOnExhausted: false`:
68
+ ## Shared Runner
77
69
 
78
- ```ts
79
- const result = await exponential.run(
80
- async () => {
81
- const response = await $fetch("https://api.example.com/users", {
82
- throwOnFetchError: true,
83
- });
84
- return await response.json();
85
- },
86
- { throwOnExhausted: false }
87
- );
70
+ Via `runRetryPolicy(policy, execute, options?)` with attempt-aware callbacks and custom sleep injection.
88
71
 
89
- if (!result.ok) {
90
- console.error("Retries exhausted:", result.attempts);
91
- console.error("Last error:", result.error.lastError);
92
- } else {
93
- console.log(result.value);
94
- }
72
+ ```ts
73
+ await runRetryPolicy(policy, execute, {
74
+ sleep: (delayMs) => customSleep(delayMs),
75
+ });
95
76
  ```
96
77
 
97
- ## Default sleep
98
-
99
- `BaseRetryPolicy.run` automatically applies a delay between retry attempts when no custom `sleep` function is provided in the options.
100
-
101
- That default is the `defaultSleep` helper, exported from `@zap-studio/retry`.
102
-
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.
78
+ ## Structured Terminal Errors
104
79
 
105
- ## Cancellation With AbortSignal
106
-
107
- Use `signal` in `run(...)` options to stop retrying early.
80
+ `RetryError` on exhaustion, `AbortError` on cancellation.
108
81
 
109
82
  ```ts
110
- const controller = new AbortController();
83
+ import { AbortError, RetryError, runRetryPolicy } from "@zap-studio/retry";
111
84
 
112
- const promise = exponential.run(
113
- async () => {
114
- const response = await $fetch("https://api.example.com/users", {
115
- throwOnFetchError: true,
116
- });
117
- return await response.json();
118
- },
119
- { signal: controller.signal }
120
- );
121
-
122
- controller.abort(new Error("Request canceled"));
123
-
124
- await promise;
85
+ try {
86
+ await runRetryPolicy(policy, execute);
87
+ } catch (error) {
88
+ if (error instanceof RetryError)
89
+ console.error(error.attempts, error.lastError);
90
+ if (error instanceof AbortError) console.error(error.message);
91
+ }
125
92
  ```
126
93
 
127
- In non-throw mode, abort is returned as `{ ok: false }` with `AbortError` on `result.error`:
94
+ ## Non-throw Mode
128
95
 
129
- ```ts
130
- const controller = new AbortController();
96
+ `throwOnExhausted: false` returns a `RetryRunResult` instead of throwing.
131
97
 
132
- const result = await exponential.run(
133
- async () => {
134
- const response = await $fetch("https://api.example.com/users", {
135
- throwOnFetchError: true,
136
- });
137
- return await response.json();
138
- },
139
- {
140
- signal: controller.signal,
141
- throwOnExhausted: false,
142
- }
143
- );
98
+ ```ts
99
+ const result = await runRetryPolicy(policy, execute, {
100
+ throwOnExhausted: false,
101
+ });
144
102
 
145
103
  if (!result.ok) {
146
- console.error("Retry stopped:", result.error);
104
+ console.error(result.error);
105
+ } else {
106
+ console.log(result.value);
147
107
  }
148
108
  ```
149
109
 
150
- ## Choosing The Right Policy
110
+ ## Cancellation
151
111
 
152
- Use `ExponentialBackoff` for transient network instability and shared upstream services.
112
+ Through `AbortSignal`, checked before, between, and during retries.
153
113
 
154
114
  ```ts
155
- const unstableNetworkPolicy = new ExponentialBackoff({
156
- maxAttempts: 6,
157
- baseDelayMs: 100,
158
- maxDelayMs: 2_000,
159
- });
160
- ```
115
+ const controller = new AbortController();
161
116
 
162
- Use `FixedDelay` for stable, predictable retry intervals in controlled environments.
117
+ const promise = runRetryPolicy(policy, execute, { signal: controller.signal });
163
118
 
164
- ```ts
165
- const predictableIntervalPolicy = new FixedDelay({
166
- maxAttempts: 4,
167
- delayMs: 300,
168
- });
119
+ controller.abort(new Error("Request canceled"));
169
120
  ```
170
121
 
171
122
  ## Custom Policies
172
123
 
173
- Extend `BaseRetryPolicy` when the built-in policies do not match your retry rules.
174
-
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).
124
+ As plain objects implementing `RetryPolicy` just a `next(...)` function, no subclassing.
176
125
 
177
126
  ```ts
178
- import { BaseRetryPolicy } from "@zap-studio/retry";
179
- import type { RetryDecision, RetryDecisionInput } from "@zap-studio/retry";
180
-
181
- class LinearBackoff extends BaseRetryPolicy {
182
- constructor(
183
- private readonly maxAttempts: number,
184
- private readonly stepMs: number
185
- ) {
186
- super();
187
- }
188
-
189
- public next(input: RetryDecisionInput): RetryDecision {
190
- if (input.attempt >= this.maxAttempts) {
191
- return {
192
- shouldRetry: false,
193
- delayMs: 0,
194
- reason: "max-attempts-reached",
195
- };
127
+ import { runRetryPolicy } from "@zap-studio/retry";
128
+ import type {
129
+ RetryDecision,
130
+ RetryDecisionInput,
131
+ RetryPolicy,
132
+ } from "@zap-studio/retry";
133
+
134
+ const stepDelay = (maxAttempts: number, stepMs: number): RetryPolicy => ({
135
+ next(input: RetryDecisionInput): RetryDecision {
136
+ if (input.attempt >= maxAttempts) {
137
+ return { shouldRetry: false, delayMs: 0, reason: "max-attempts-reached" };
196
138
  }
197
-
198
139
  return {
199
140
  shouldRetry: true,
200
- delayMs: input.attempt * this.stepMs,
141
+ delayMs: input.attempt * stepMs,
201
142
  reason: "retry",
202
143
  };
203
- }
204
- }
144
+ },
145
+ });
205
146
 
206
- const policy = new LinearBackoff(5, 250);
207
- const value = await policy.run(doWork);
147
+ const data = await runRetryPolicy(stepDelay(5, 100), execute);
208
148
  ```
209
149
 
210
- ## RetryError
211
-
212
- Use `RetryError` when an orchestrator exhausts retries and needs to surface final context.
213
-
214
- ```ts
215
- import { RetryError } from "@zap-studio/retry";
216
-
217
- throw new RetryError("Retry policy exhausted all attempts.", {
218
- attempts: attempt,
219
- lastError: error,
220
- lastData: data,
221
- });
222
- ```
150
+ ## Runtime Support
223
151
 
224
- Policies implement `onExhausted(input)` to return the terminal error used by the built-in runner.
152
+ | Runtime | Minimum version |
153
+ | ------------------ | --------------------------------------- |
154
+ | Node.js | 18.0.0 |
155
+ | Bun | 1.0.0 |
156
+ | Deno | 1.42 |
157
+ | Cloudflare Workers | Any current release |
158
+ | Browsers | Chrome/Edge 98, Firefox 97, Safari 15.4 |
225
159
 
226
- `ExponentialBackoff` and `FixedDelay` inherit the default implementation from `BaseRetryPolicy`.
160
+ 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`).
227
161
 
228
162
  ## License
229
163
 
@@ -1,59 +1,67 @@
1
- import { r as RetryError } from "./errors-BVZjP1Q5.js";
2
- import { RetryDecision, RetryDecisionInput, RetryExhaustedInput, RetryPolicy, RetryRunOptions, RetryRunResult } from "./types.js";
1
+ import { RetryPolicy, RetryRunOptions, RetryRunResult } from "./types.js";
3
2
  //#region src/base-policy.d.ts
4
3
  /**
5
- * Base class for implementing retry policies and running retry orchestration.
4
+ * Awaits a timer-based delay, unless `delayMs` is non-positive.
6
5
  *
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.
6
+ * @param delayMs - Milliseconds to wait before resolving.
7
+ * @returns Promise that resolves when the delay completes.
8
+ *
9
+ * @example
10
+ * ```ts
11
+ * import { defaultSleep } from "@zap-studio/retry";
12
+ *
13
+ * await defaultSleep(250); // waits 250ms
14
+ * ```
15
+ */
16
+ declare const defaultSleep: (delayMs: number) => Promise<void>;
17
+ /**
18
+ * Runs retry orchestration in non-throw mode.
19
+ *
20
+ * @param policy - Retry policy: `next` is required, `onExhausted` and
21
+ * `isKnownError` fall back to their defaults when omitted.
22
+ * @param execute - Async function to execute per attempt.
23
+ * @param options - Runner settings with `throwOnExhausted: false`.
24
+ * @returns A discriminated result union containing success value or terminal error.
25
+ * When `policy.isKnownError` rejects a caught value, it is wrapped in a
26
+ * `RetryError` and returned as the terminal failure instead of thrown.
27
+ * @throws {Error} Any error thrown by `next`, `onExhausted`, or a custom `sleep`.
28
+ */
29
+ declare function runRetryPolicy<T, TError extends Error = Error, TData = unknown>(policy: RetryPolicy<TError, TData>, execute: (attempt: number) => Promise<T>, options: RetryRunOptions & {
30
+ throwOnExhausted: false;
31
+ }): Promise<RetryRunResult<T>>;
32
+ /**
33
+ * Runs retry orchestration and throws terminal error on exhaustion.
34
+ *
35
+ * @param policy - Retry policy: `next` is required, `onExhausted` and
36
+ * `isKnownError` fall back to their defaults when omitted.
37
+ * @param execute - Async function to execute per attempt.
38
+ * @param options - Optional runner settings.
39
+ * @returns The successful execution value.
40
+ * @throws {RetryError} When retries are exhausted and `onExhausted` returns the
41
+ * terminal retry error. The default implementation returns `RetryError` with the last
42
+ * execution failure available on `RetryError.lastError`.
43
+ * @throws {AbortError} When `options.signal` is already aborted or aborts while retrying.
44
+ * @throws {Error} Any error thrown by `next`, by `onExhausted`, or by a custom `sleep`
45
+ * function.
46
+ *
47
+ * @example
48
+ * ```ts
49
+ * import { runRetryPolicy } from "@zap-studio/retry";
50
+ * import type { RetryPolicy } from "@zap-studio/retry";
51
+ *
52
+ * const linearBackoff: RetryPolicy = {
53
+ * next: ({ attempt }) =>
54
+ * attempt < 3
55
+ * ? { shouldRetry: true, delayMs: attempt * 100, reason: "retry" }
56
+ * : { shouldRetry: false, delayMs: 0, reason: "max-attempts-reached" },
57
+ * };
58
+ *
59
+ * const data = await runRetryPolicy(linearBackoff, async () => fetchFlakyResource());
60
+ * ```
10
61
  */
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
- }
62
+ declare function runRetryPolicy<T, TError extends Error = Error, TData = unknown>(policy: RetryPolicy<TError, TData>, execute: (attempt: number) => Promise<T>, options?: RetryRunOptions & {
63
+ throwOnExhausted?: true;
64
+ }): Promise<T>;
57
65
  //#endregion
58
- export { BaseRetryPolicy };
66
+ export { defaultSleep, runRetryPolicy };
59
67
  //# sourceMappingURL=base-policy.d.ts.map
@@ -1 +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"}
1
+ {"version":3,"file":"base-policy.d.ts","names":[],"sources":["../src/base-policy.ts"],"mappings":";;;;;;;;;;;;;;;cA4Ba,eAAsB,oBAAkB;;;;;;;;;;;;;iBA8YrC,eACd,GACA,eAAe,QAAQ,OACvB,iBAEA,QAAQ,YAAY,QAAQ,QAC5B,UAAU,oBAAoB,QAAQ,IACtC,SAAS;EAAoB;IAC5B,QAAQ,eAAe;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAgCV,eACd,GACA,eAAe,QAAQ,OACvB,iBAEA,QAAQ,YAAY,QAAQ,QAC5B,UAAU,oBAAoB,QAAQ,IACtC,UAAU;EAAoB;IAC7B,QAAQ"}