@zap-studio/retry 0.3.1 → 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.
Files changed (57) hide show
  1. package/CHANGELOG.md +9 -0
  2. package/LICENSE +1 -1
  3. package/README.md +21 -9
  4. package/dist/{abort.d.mts → abort.d.ts} +2 -2
  5. package/dist/abort.d.ts.map +1 -0
  6. package/dist/{abort.mjs → abort.js} +2 -2
  7. package/dist/abort.js.map +1 -0
  8. package/dist/{result-mode.mjs → base-policy-Dn3TOJd3.js} +108 -5
  9. package/dist/base-policy-Dn3TOJd3.js.map +1 -0
  10. package/dist/{index.d.mts → base-policy.d.ts} +4 -4
  11. package/dist/base-policy.d.ts.map +1 -0
  12. package/dist/base-policy.js +2 -0
  13. package/dist/{errors-BVZjP1Q5.d.mts → errors-BVZjP1Q5.d.ts} +1 -1
  14. package/dist/errors-BVZjP1Q5.d.ts.map +1 -0
  15. package/dist/{errors.d.mts → errors.d.ts} +1 -1
  16. package/dist/{errors.mjs → errors.js} +1 -1
  17. package/dist/errors.js.map +1 -0
  18. package/dist/{exponential-backoff.d.mts → exponential-backoff.d.ts} +3 -3
  19. package/dist/exponential-backoff.d.ts.map +1 -0
  20. package/dist/{exponential-backoff.mjs → exponential-backoff.js} +2 -2
  21. package/dist/exponential-backoff.js.map +1 -0
  22. package/dist/{fixed-delay.d.mts → fixed-delay.d.ts} +3 -3
  23. package/dist/fixed-delay.d.ts.map +1 -0
  24. package/dist/{fixed-delay.mjs → fixed-delay.js} +2 -2
  25. package/dist/fixed-delay.js.map +1 -0
  26. package/dist/index.d.ts +8 -0
  27. package/dist/index.js +7 -0
  28. package/dist/{sleep.d.mts → sleep.d.ts} +1 -1
  29. package/dist/sleep.d.ts.map +1 -0
  30. package/dist/{sleep.mjs → sleep.js} +1 -1
  31. package/dist/sleep.js.map +1 -0
  32. package/dist/{types.d.mts → types.d.ts} +2 -2
  33. package/dist/types.d.ts.map +1 -0
  34. package/dist/types.js +0 -0
  35. package/package.json +15 -25
  36. package/dist/abort.d.mts.map +0 -1
  37. package/dist/abort.mjs.map +0 -1
  38. package/dist/errors-BVZjP1Q5.d.mts.map +0 -1
  39. package/dist/errors.mjs.map +0 -1
  40. package/dist/exponential-backoff.d.mts.map +0 -1
  41. package/dist/exponential-backoff.mjs.map +0 -1
  42. package/dist/fixed-delay.d.mts.map +0 -1
  43. package/dist/fixed-delay.mjs.map +0 -1
  44. package/dist/index.d.mts.map +0 -1
  45. package/dist/index.mjs +0 -63
  46. package/dist/index.mjs.map +0 -1
  47. package/dist/result-mode.d.mts +0 -19
  48. package/dist/result-mode.d.mts.map +0 -1
  49. package/dist/result-mode.mjs.map +0 -1
  50. package/dist/sleep.d.mts.map +0 -1
  51. package/dist/sleep.mjs.map +0 -1
  52. package/dist/throw-mode.d.mts +0 -21
  53. package/dist/throw-mode.d.mts.map +0 -1
  54. package/dist/throw-mode.mjs +0 -49
  55. package/dist/throw-mode.mjs.map +0 -1
  56. package/dist/types.d.mts.map +0 -1
  57. package/dist/types.mjs +0 -1
package/CHANGELOG.md CHANGED
@@ -1,3 +1,12 @@
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
+
1
10
  ## @zap-studio/retry@0.3.1
2
11
 
3
12
  ### Migrate to ultracite lint/format
package/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2026 Alexandre Trotel
3
+ Copyright (c) 2026 alexandretrotel
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
@@ -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/exponential-backoff";
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({
@@ -36,7 +51,7 @@ const data = await exponential.run(async () => {
36
51
  By default, policies extending `BaseRetryPolicy` throw `RetryError` on exhaustion and `AbortError` on cancellation.
37
52
 
38
53
  ```ts
39
- import { AbortError, RetryError } from "@zap-studio/retry/errors";
54
+ import { AbortError, RetryError } from "@zap-studio/retry";
40
55
 
41
56
  try {
42
57
  const data = await exponential.run(async () => {
@@ -83,7 +98,7 @@ if (!result.ok) {
83
98
 
84
99
  `BaseRetryPolicy.run` automatically applies a delay between retry attempts when no custom `sleep` function is provided in the options.
85
100
 
86
- That default is the `defaultSleep` helper, exported from `@zap-studio/retry/sleep`.
101
+ That default is the `defaultSleep` helper, exported from `@zap-studio/retry`.
87
102
 
88
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.
89
104
 
@@ -161,10 +176,7 @@ You implement `next(...)` only; the base class supplies `onExhausted` with a def
161
176
 
162
177
  ```ts
163
178
  import { BaseRetryPolicy } from "@zap-studio/retry";
164
- import type {
165
- RetryDecision,
166
- RetryDecisionInput,
167
- } from "@zap-studio/retry/types";
179
+ import type { RetryDecision, RetryDecisionInput } from "@zap-studio/retry";
168
180
 
169
181
  class LinearBackoff extends BaseRetryPolicy {
170
182
  constructor(
@@ -200,7 +212,7 @@ const value = await policy.run(doWork);
200
212
  Use `RetryError` when an orchestrator exhausts retries and needs to surface final context.
201
213
 
202
214
  ```ts
203
- import { RetryError } from "@zap-studio/retry/errors";
215
+ import { RetryError } from "@zap-studio/retry";
204
216
 
205
217
  throw new RetryError("Retry policy exhausted all attempts.", {
206
218
  attempts: attempt,
@@ -1,4 +1,4 @@
1
- import { t as AbortError } from "./errors-BVZjP1Q5.mjs";
1
+ import { t as AbortError } from "./errors-BVZjP1Q5.js";
2
2
  //#region src/abort.d.ts
3
3
  /**
4
4
  * Throws when the provided abort signal is already aborted.
@@ -26,4 +26,4 @@ declare const throwIfAborted: (signal?: AbortSignal) => void;
26
26
  declare const sleepWithAbortSignal: (sleep: (delayMs: number) => Promise<void>, delayMs: number, signal: AbortSignal) => Promise<void>;
27
27
  //#endregion
28
28
  export { sleepWithAbortSignal, throwIfAborted, toAbortError };
29
- //# sourceMappingURL=abort.d.mts.map
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.mjs";
1
+ import { AbortError } from "./errors.js";
2
2
  //#region src/abort.ts
3
3
  /**
4
4
  * Abort-signal helpers for retry orchestration internals.
@@ -58,4 +58,4 @@ const sleepWithAbortSignal = async (sleep, delayMs, signal) => {
58
58
  //#endregion
59
59
  export { sleepWithAbortSignal, throwIfAborted, toAbortError };
60
60
 
61
- //# sourceMappingURL=abort.mjs.map
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"}
@@ -1,10 +1,12 @@
1
- import { sleepWithAbortSignal, toAbortError } from "./abort.mjs";
2
- //#region src/result-mode.ts
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
3
5
  /**
4
6
  * Result-mode execution path for `BaseRetryPolicy.run` when
5
7
  * `throwOnExhausted: false` is set.
6
8
  *
7
- * @module @zap-studio/retry/result-mode
9
+ * @module @zap-studio/retry/_result-mode (private)
8
10
  */
9
11
  /**
10
12
  * When `signal` is already aborted, builds the terminal `{ ok: false }` object
@@ -140,6 +142,107 @@ const runResultMode = async (policy, execute, sleep, signal) => {
140
142
  }
141
143
  };
142
144
  //#endregion
143
- export { runResultMode };
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 };
144
247
 
145
- //# sourceMappingURL=result-mode.mjs.map
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":";;;;;;;;;;;;;;;;;;AAkBA,MAAM,oBACJ,QACA,aACsC;CACtC,IAAI,QAAQ,YAAY,MACtB;CAGF,OAAO;EACL;EACA,OAAO,aAAa,OAAO,MAAM;EACjC,IAAI;CACN;AACF;;;;;;;;;AAUA,MAAM,aAAa,OACjB,SACA,YACoE;CACpE,IAAI;EACF,OAAO;GACL,IAAI;GACJ,OAAO,MAAM,QAAQ,OAAO;EAC9B;CACF,SAAS,OAAO;EACd,OAAO;GACL;GACA,IAAI;EACN;CACF;AACF;;;;;;;;;;;;;AAcA,MAAM,eAAe,OACnB,OACA,SACA,QACA,aAC+C;CAC/C,IAAI,WAAW,KAAA,GAAW;EACxB,MAAM,MAAM,OAAO;EACnB;CACF;CAEA,IAAI;EACF,MAAM,qBAAqB,OAAO,SAAS,MAAM;EACjD;CACF,SAAS,OAAO;EACd,MAAM,UAAU,iBAAiB,QAAQ,QAAQ;EACjD,IAAI,YAAY,KAAA,GACd,OAAO;EAET,MAAM;CACR;AACF;;;;;;;;;;;;;;;;;AAkBA,MAAM,gBAAgB,OACpB,QACA,WAM+C;CAC/C,MAAM,EAAE,SAAS,OAAO,OAAO,WAAW;CAC1C,MAAM,cAAc,iBAAiB,QAAQ,OAAO;CACpD,IAAI,gBAAgB,KAAA,GAClB,OAAO;CAGT,MAAM,WAAW,OAAO,KAAK;EAC3B;EACA;CACF,CAAC;CAED,IAAI,CAAC,SAAS,aAMZ,OAAO;EACL,UAAU;EACV,OAPoB,OAAO,YAAY;GACvC,UAAU;GACV;EACF,CAIqB;EACnB,IAAI;CACN;CAGF,IAAI,SAAS,UAAU,GAAG;EACxB,MAAM,mBAAmB,MAAM,aAC7B,OACA,SAAS,SACT,QACA,OACF;EACA,IAAI,qBAAqB,KAAA,GACvB,OAAO;CAEX;AAGF;;;;;;;;;;;;;;AAeA,MAAa,gBAAgB,OAC3B,QACA,SACA,OACA,WAC+B;CAC/B,IAAI,UAAU;CAEd,OAAO,MAAM;EACX,MAAM,cAAc,iBAAiB,QAAQ,KAAK,IAAI,GAAG,UAAU,CAAC,CAAC;EACrE,IAAI,gBAAgB,KAAA,GAClB,OAAO;EAIT,MAAM,YAAY,MAAM,WAAW,SAAS,OAAO;EACnD,IAAI,UAAU,IACZ,OAAO;GAAE,IAAI;GAAM,OAAO,UAAU;EAAM;EAI5C,MAAM,UAAU,MAAM,cAAc,QAAQ;GAC1C;GAEA,OAAO,UAAU;GACjB;GACA;EACF,CAAC;EACD,IAAI,YAAY,KAAA,GACd,OAAO;EAGT,WAAW;CACb;AACF;;;;;;;;;;;;;;;;;;;;;;;;ACnLA,MAAa,eAAe,OAC1B,QACA,SACA,OACA,WACe;CACf,IAAI,UAAU;CAEd,OAAO,MAAM;EACX,eAAe,MAAM;EAErB,IAAI;GAEF,OAAO,MAAM,QAAQ,OAAO;EAC9B,SAAS,OAAO;GACd,eAAe,MAAM;GAGrB,MAAM,aAAa;GACnB,MAAM,WAAW,OAAO,KAAK;IAC3B;IACA,OAAO;GACT,CAAC;GAED,IAAI,CAAC,SAAS,aACZ,MAAM,OAAO,YAAY;IACvB,UAAU;IACV,OAAO;GACT,CAAC;GAGH,IAAI,SAAS,UAAU,GAErB,OAAO,WAAW,KAAA,IACd,MAAM,SAAS,OAAO,IACtB,qBAAqB,OAAO,SAAS,SAAS,MAAM;GAG1D,WAAW;EACb;CACF;AACF;;;;;;;;;;;;;;;ACxCA,IAAsB,kBAAtB,MAGwC;;;;;;;;;;CAmBtC,YAAmB,OAAuD;EACxE,OAAO,IAAI,WAAW,wCAAwC;GAC5D,UAAU,MAAM;GAChB,UAAU,MAAM;GAChB,WAAW,MAAM;EACnB,CAAC;CACH;;;;;;;;;;;;;;;;;;;CAmDA,MAAa,IACX,SACA,UAA2B,CAAC,GACI;EAChC,MAAM,QAAQ,QAAQ,SAAS;EAC/B,MAAM,EAAE,WAAW;EACnB,IAAI,QAAQ,qBAAqB,OAC/B,OAAO,MAAM,cAAc,MAAM,SAAS,OAAO,MAAM;EAGzD,OAAO,MAAM,aAAa,MAAM,SAAS,OAAO,MAAM;CACxD;AACF"}
@@ -1,6 +1,6 @@
1
- import { r as RetryError } from "./errors-BVZjP1Q5.mjs";
2
- import { RetryDecision, RetryDecisionInput, RetryExhaustedInput, RetryPolicy, RetryRunOptions, RetryRunResult } from "./types.mjs";
3
- //#region src/index.d.ts
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
4
  /**
5
5
  * Base class for implementing retry policies and running retry orchestration.
6
6
  *
@@ -56,4 +56,4 @@ declare abstract class BaseRetryPolicy<TError = unknown, TData = unknown> implem
56
56
  }
57
57
  //#endregion
58
58
  export { BaseRetryPolicy };
59
- //# sourceMappingURL=index.d.mts.map
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"}
@@ -0,0 +1,2 @@
1
+ import { t as BaseRetryPolicy } from "./base-policy-Dn3TOJd3.js";
2
+ export { BaseRetryPolicy };
@@ -73,4 +73,4 @@ declare class AbortError extends Error {
73
73
  }
74
74
  //#endregion
75
75
  export { RetryErrorContext as i, AbortErrorContext as n, RetryError as r, AbortError as t };
76
- //# sourceMappingURL=errors-BVZjP1Q5.d.mts.map
76
+ //# sourceMappingURL=errors-BVZjP1Q5.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors-BVZjP1Q5.d.ts","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"}
@@ -1,2 +1,2 @@
1
- import { i as RetryErrorContext, n as AbortErrorContext, r as RetryError, t as AbortError } from "./errors-BVZjP1Q5.mjs";
1
+ import { i as RetryErrorContext, n as AbortErrorContext, r as RetryError, t as AbortError } from "./errors-BVZjP1Q5.js";
2
2
  export { AbortError, AbortErrorContext, RetryError, RetryErrorContext };
@@ -53,4 +53,4 @@ var AbortError = class extends Error {
53
53
  //#endregion
54
54
  export { AbortError, RetryError };
55
55
 
56
- //# sourceMappingURL=errors.mjs.map
56
+ //# sourceMappingURL=errors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.js","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,5 +1,5 @@
1
- import { RetryDecision, RetryDecisionInput } from "./types.mjs";
2
- import { BaseRetryPolicy } from "./index.mjs";
1
+ import { RetryDecision, RetryDecisionInput } from "./types.js";
2
+ import { BaseRetryPolicy } from "./base-policy.js";
3
3
  //#region src/exponential-backoff.d.ts
4
4
  /**
5
5
  * Configuration for `ExponentialBackoff`.
@@ -52,4 +52,4 @@ declare class ExponentialBackoff extends BaseRetryPolicy {
52
52
  }
53
53
  //#endregion
54
54
  export { ExponentialBackoff, ExponentialBackoffOptions };
55
- //# sourceMappingURL=exponential-backoff.d.mts.map
55
+ //# sourceMappingURL=exponential-backoff.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"exponential-backoff.d.ts","names":[],"sources":["../src/exponential-backoff.ts"],"mappings":";;;;;;UAYiB;;;;EAIf;;;;EAIA;;;;EAIA;;;;;;;;;;;;cAaW,2BAA2B;;;;mBAIrB;;;;mBAIA;;;;mBAIA;;;;EAKjB,YAAY,SAAS;;;;EAUrB,KAAY,OAAO,qBAAqB"}
@@ -1,4 +1,4 @@
1
- import { BaseRetryPolicy } from "./index.mjs";
1
+ import { t as BaseRetryPolicy } from "./base-policy-Dn3TOJd3.js";
2
2
  //#region src/exponential-backoff.ts
3
3
  /**
4
4
  * Exponential backoff retry strategy.
@@ -57,4 +57,4 @@ var ExponentialBackoff = class extends BaseRetryPolicy {
57
57
  //#endregion
58
58
  export { ExponentialBackoff };
59
59
 
60
- //# sourceMappingURL=exponential-backoff.mjs.map
60
+ //# sourceMappingURL=exponential-backoff.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"exponential-backoff.js","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 \"./base-policy.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"}
@@ -1,5 +1,5 @@
1
- import { RetryDecision, RetryDecisionInput } from "./types.mjs";
2
- import { BaseRetryPolicy } from "./index.mjs";
1
+ import { RetryDecision, RetryDecisionInput } from "./types.js";
2
+ import { BaseRetryPolicy } from "./base-policy.js";
3
3
  //#region src/fixed-delay.d.ts
4
4
  /**
5
5
  * Configuration for `FixedDelay`.
@@ -43,4 +43,4 @@ declare class FixedDelay extends BaseRetryPolicy {
43
43
  }
44
44
  //#endregion
45
45
  export { FixedDelay, FixedDelayOptions };
46
- //# sourceMappingURL=fixed-delay.d.mts.map
46
+ //# sourceMappingURL=fixed-delay.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fixed-delay.d.ts","names":[],"sources":["../src/fixed-delay.ts"],"mappings":";;;;;;UAYiB;;;;EAIf;;;;EAIA;;;;;;;;;;;cAYW,mBAAmB;;;;mBAIb;;;;mBAIA;;;;EAKjB,YAAY,SAAS;;;;EASrB,KAAY,OAAO,qBAAqB"}
@@ -1,4 +1,4 @@
1
- import { BaseRetryPolicy } from "./index.mjs";
1
+ import { t as BaseRetryPolicy } from "./base-policy-Dn3TOJd3.js";
2
2
  //#region src/fixed-delay.ts
3
3
  /**
4
4
  * Fixed-delay retry strategy.
@@ -50,4 +50,4 @@ var FixedDelay = class extends BaseRetryPolicy {
50
50
  //#endregion
51
51
  export { FixedDelay };
52
52
 
53
- //# sourceMappingURL=fixed-delay.mjs.map
53
+ //# sourceMappingURL=fixed-delay.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fixed-delay.js","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 \"./base-policy.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"}
@@ -0,0 +1,8 @@
1
+ import { i as RetryErrorContext, n as AbortErrorContext, r as RetryError, t as AbortError } from "./errors-BVZjP1Q5.js";
2
+ import { sleepWithAbortSignal, throwIfAborted, toAbortError } from "./abort.js";
3
+ import { RetryDecision, RetryDecisionInput, RetryExhaustedInput, RetryPolicy, RetryRunOptions, RetryRunResult } from "./types.js";
4
+ import { BaseRetryPolicy } from "./base-policy.js";
5
+ import { ExponentialBackoff, ExponentialBackoffOptions } from "./exponential-backoff.js";
6
+ import { FixedDelay, FixedDelayOptions } from "./fixed-delay.js";
7
+ import { defaultSleep } from "./sleep.js";
8
+ export { AbortError, type AbortErrorContext, BaseRetryPolicy, ExponentialBackoff, type ExponentialBackoffOptions, FixedDelay, type FixedDelayOptions, type RetryDecision, type RetryDecisionInput, RetryError, type RetryErrorContext, type RetryExhaustedInput, type RetryPolicy, type RetryRunOptions, type RetryRunResult, defaultSleep, sleepWithAbortSignal, throwIfAborted, toAbortError };
package/dist/index.js ADDED
@@ -0,0 +1,7 @@
1
+ import { AbortError, RetryError } from "./errors.js";
2
+ import { sleepWithAbortSignal, throwIfAborted, toAbortError } from "./abort.js";
3
+ import { t as BaseRetryPolicy } from "./base-policy-Dn3TOJd3.js";
4
+ import { defaultSleep } from "./sleep.js";
5
+ import { ExponentialBackoff } from "./exponential-backoff.js";
6
+ import { FixedDelay } from "./fixed-delay.js";
7
+ export { AbortError, BaseRetryPolicy, ExponentialBackoff, FixedDelay, RetryError, defaultSleep, sleepWithAbortSignal, throwIfAborted, toAbortError };
@@ -14,4 +14,4 @@
14
14
  declare const defaultSleep: (delayMs: number) => Promise<void>;
15
15
  //#endregion
16
16
  export { defaultSleep };
17
- //# sourceMappingURL=sleep.d.mts.map
17
+ //# sourceMappingURL=sleep.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sleep.d.ts","names":[],"sources":["../src/sleep.ts"],"mappings":";;;;;;;;;;;;;cAaa,eAAsB,oBAAkB"}
@@ -20,4 +20,4 @@ const defaultSleep = async (delayMs) => {
20
20
  //#endregion
21
21
  export { defaultSleep };
22
22
 
23
- //# sourceMappingURL=sleep.mjs.map
23
+ //# sourceMappingURL=sleep.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sleep.js","names":[],"sources":["../src/sleep.ts"],"sourcesContent":["/**\n * Default delay implementation used by `BaseRetryPolicy.run` when no custom\n * `sleep` is provided.\n *\n * @module @zap-studio/retry/sleep\n */\n\n/**\n * Awaits a timer-based delay, unless `delayMs` is non-positive.\n *\n * @param delayMs - Milliseconds to wait before resolving.\n * @returns Promise that resolves when the delay completes.\n */\nexport const defaultSleep = async (delayMs: number): Promise<void> => {\n if (delayMs <= 0) {\n return;\n }\n\n // oxlint-disable-next-line promise/avoid-new -- Timer sleep requires adapting callback API to a promise.\n await new Promise<void>((resolve) => {\n setTimeout(resolve, delayMs);\n });\n};\n"],"mappings":";;;;;;;;;;;;;AAaA,MAAa,eAAe,OAAO,YAAmC;CACpE,IAAI,WAAW,GACb;CAIF,MAAM,IAAI,SAAe,YAAY;EACnC,WAAW,SAAS,OAAO;CAC7B,CAAC;AACH"}
@@ -1,4 +1,4 @@
1
- import { r as RetryError, t as AbortError } from "./errors-BVZjP1Q5.mjs";
1
+ import { r as RetryError, t as AbortError } from "./errors-BVZjP1Q5.js";
2
2
  //#region src/types.d.ts
3
3
  /**
4
4
  * Retry policy contract used by `BaseRetryPolicy`.
@@ -140,4 +140,4 @@ type RetryRunResult<T> = {
140
140
  };
141
141
  //#endregion
142
142
  export { RetryDecision, RetryDecisionInput, RetryExhaustedInput, RetryPolicy, RetryRunOptions, RetryRunResult };
143
- //# sourceMappingURL=types.d.mts.map
143
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","names":[],"sources":["../src/types.ts"],"mappings":";;;;;;;;;;;UAiBiB,YAAY,kBAAkB;;;;;;EAM7C,OAAO,OAAO,mBAAmB,QAAQ,WAAW;;;;;;EAMpD,cAAc,OAAO,oBAAoB,QAAQ,WAAW;;;;;UAM7C;;;;;WAKN;;;;WAIA;;;;WAIA;;;;;UAMM,mBAAmB,kBAAkB;;;;WAI3C;;;;;WAKA;;;;;WAKA,QAAQ;;;;;WAKR,OAAO;;;;;UAMD,oBAAoB,kBAAkB;;;;WAI5C;;;;WAIA,QAAQ;;;;WAIR,OAAO;;;;;UAMD;;;;;;WAMN,SAAS,oBAAoB;;;;;;WAM7B,SAAS;;;;;;;;WAQT;;;;;;;;;KAUC,eAAe;;;;EAKrB;;;;EAIA,OAAO;;;;;EAMP;;;;;EAKA,OAAO,aAAa;;;;EAIpB"}
package/dist/types.js ADDED
File without changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zap-studio/retry",
3
- "version": "0.3.1",
3
+ "version": "0.3.2",
4
4
  "private": false,
5
5
  "description": "Composable retry policies for resilient async operations.",
6
6
  "keywords": [
@@ -27,38 +27,28 @@
27
27
  ],
28
28
  "type": "module",
29
29
  "sideEffects": false,
30
- "types": "./dist/index.d.mts",
30
+ "types": "./dist/index.d.ts",
31
31
  "exports": {
32
- ".": "./dist/index.mjs",
33
- "./abort": "./dist/abort.mjs",
34
- "./errors": "./dist/errors.mjs",
35
- "./exponential-backoff": "./dist/exponential-backoff.mjs",
36
- "./fixed-delay": "./dist/fixed-delay.mjs",
37
- "./result-mode": "./dist/result-mode.mjs",
38
- "./sleep": "./dist/sleep.mjs",
39
- "./throw-mode": "./dist/throw-mode.mjs",
40
- "./types": "./dist/types.mjs",
32
+ ".": "./dist/index.js",
33
+ "./abort": "./dist/abort.js",
34
+ "./base-policy": "./dist/base-policy.js",
35
+ "./errors": "./dist/errors.js",
36
+ "./exponential-backoff": "./dist/exponential-backoff.js",
37
+ "./fixed-delay": "./dist/fixed-delay.js",
38
+ "./sleep": "./dist/sleep.js",
39
+ "./types": "./dist/types.js",
41
40
  "./package.json": "./package.json"
42
41
  },
43
42
  "publishConfig": {
44
43
  "access": "public"
45
44
  },
46
45
  "devDependencies": {
47
- "@types/async-retry": "^1.4.9",
48
- "@types/promise-retry": "^1.1.6",
49
- "async-retry": "^1.3.3",
50
- "exponential-backoff": "^3.1.3",
51
- "p-retry": "^8.0.0",
52
- "promise-retry": "^2.0.1",
53
- "tsdown": "^0.22.4",
54
- "typescript": "^7.0.2",
55
- "vitest": "^4.1.10",
56
- "@zap-studio/typescript": "0.0.0"
46
+ "@zap-studio/typescript": "workspace:*",
47
+ "tsdown": "catalog:",
48
+ "typescript": "catalog:",
49
+ "vitest": "catalog:"
57
50
  },
58
51
  "engines": {
59
52
  "node": ">=18.0.0"
60
- },
61
- "scripts": {
62
- "build": "tsdown --config ./tsdown.config.ts"
63
53
  }
64
- }
54
+ }
@@ -1 +0,0 @@
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"}
@@ -1 +0,0 @@
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"}
@@ -1 +0,0 @@
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"}
@@ -1 +0,0 @@
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 +0,0 @@
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"}
@@ -1 +0,0 @@
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"}
@@ -1 +0,0 @@
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"}
@@ -1 +0,0 @@
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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.d.mts","names":[],"sources":["../src/index.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"}
package/dist/index.mjs DELETED
@@ -1,63 +0,0 @@
1
- import { RetryError } from "./errors.mjs";
2
- import { runResultMode } from "./result-mode.mjs";
3
- import { defaultSleep } from "./sleep.mjs";
4
- import { runThrowMode } from "./throw-mode.mjs";
5
- //#region src/index.ts
6
- /**
7
- * Retry runner base class and shared orchestration implementation.
8
- *
9
- * @module @zap-studio/retry
10
- */
11
- /**
12
- * Base class for implementing retry policies and running retry orchestration.
13
- *
14
- * Extend this class and implement {@link BaseRetryPolicy.next} to define retry
15
- * behavior, then call {@link BaseRetryPolicy.run} to execute operations with that
16
- * policy.
17
- */
18
- var BaseRetryPolicy = class {
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) {
29
- return new RetryError("Retry policy exhausted all attempts.", {
30
- attempts: input.attempts,
31
- lastData: input.data,
32
- lastError: input.error
33
- });
34
- }
35
- /**
36
- * Runs retry orchestration in non-throw mode.
37
- *
38
- * When `throwOnExhausted` is `false`, returns a discriminated result union.
39
- *
40
- * @param execute - Async function to execute per attempt.
41
- * @param options - Runner settings.
42
- * @returns Success value or terminal result object based on option mode.
43
- * @throws {Error} Any error thrown by `next`, by `onExhausted`, or by a custom `sleep`
44
- * function. When `throwOnExhausted` is `false`, exhaustion itself is returned
45
- * as `{ ok: false }` instead of thrown.
46
- * Cancellation is returned as `{ ok: false, error: AbortError }` in non-throw
47
- * mode.
48
- *
49
- * @example
50
- * const result = await policy.run(doWork, { throwOnExhausted: false });
51
- * if (!result.ok) console.error(result.error);
52
- */
53
- async run(execute, options = {}) {
54
- const sleep = options.sleep ?? defaultSleep;
55
- const { signal } = options;
56
- if (options.throwOnExhausted === false) return await runResultMode(this, execute, sleep, signal);
57
- return await runThrowMode(this, execute, sleep, signal);
58
- }
59
- };
60
- //#endregion
61
- export { BaseRetryPolicy };
62
-
63
- //# sourceMappingURL=index.mjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.mjs","names":[],"sources":["../src/index.ts"],"sourcesContent":["/**\n * Retry runner base class and shared orchestration implementation.\n *\n * @module @zap-studio/retry\n */\n\nimport { RetryError } from \"./errors.js\";\nimport { runResultMode } from \"./result-mode.js\";\nimport { defaultSleep } from \"./sleep.js\";\nimport { runThrowMode } from \"./throw-mode.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":";;;;;;;;;;;;;;;;;AA0BA,IAAsB,kBAAtB,MAGwC;;;;;;;;;;CAmBtC,YAAmB,OAAuD;EACxE,OAAO,IAAI,WAAW,wCAAwC;GAC5D,UAAU,MAAM;GAChB,UAAU,MAAM;GAChB,WAAW,MAAM;EACnB,CAAC;CACH;;;;;;;;;;;;;;;;;;;CAmDA,MAAa,IACX,SACA,UAA2B,CAAC,GACI;EAChC,MAAM,QAAQ,QAAQ,SAAS;EAC/B,MAAM,EAAE,WAAW;EACnB,IAAI,QAAQ,qBAAqB,OAC/B,OAAO,MAAM,cAAc,MAAM,SAAS,OAAO,MAAM;EAGzD,OAAO,MAAM,aAAa,MAAM,SAAS,OAAO,MAAM;CACxD;AACF"}
@@ -1,19 +0,0 @@
1
- import { RetryPolicy, RetryRunResult } from "./types.mjs";
2
- //#region src/result-mode.d.ts
3
- /**
4
- * Runs the non-throw retry loop, returning
5
- * `RetryRunResult`.
6
- *
7
- * @param policy - Object providing `next` and `onExhausted` (same contract as
8
- * `BaseRetryPolicy`).
9
- * @param execute - Async work callback per attempt.
10
- * @param sleep - Delay function between retries.
11
- * @param signal - Optional cancel signal.
12
- * @returns Terminal success or failure object.
13
- * @throws {Error} Any error thrown by `next`, `onExhausted`, or a non-abort `sleep`
14
- * failure.
15
- */
16
- declare const runResultMode: <T, TError, TData>(policy: RetryPolicy<TError, TData>, execute: (attempt: number) => Promise<T>, sleep: (delayMs: number) => Promise<void>, signal?: AbortSignal) => Promise<RetryRunResult<T>>;
17
- //#endregion
18
- export { runResultMode };
19
- //# sourceMappingURL=result-mode.d.mts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"result-mode.d.mts","names":[],"sources":["../src/result-mode.ts"],"mappings":";;;;;;;;;;;;;;;cA0Ka,gBAAuB,GAAG,QAAQ,OAC7C,QAAQ,YAAY,QAAQ,QAC5B,UAAU,oBAAoB,QAAQ,IACtC,QAAQ,oBAAoB,eAC5B,SAAS,gBACR,QAAQ,eAAe"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"result-mode.mjs","names":[],"sources":["../src/result-mode.ts"],"sourcesContent":["/**\n * Result-mode execution path for `BaseRetryPolicy.run` when\n * `throwOnExhausted: false` is set.\n *\n * @module @zap-studio/retry/result-mode\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"],"mappings":";;;;;;;;;;;;;;;;AAkBA,MAAM,oBACJ,QACA,aACsC;CACtC,IAAI,QAAQ,YAAY,MACtB;CAGF,OAAO;EACL;EACA,OAAO,aAAa,OAAO,MAAM;EACjC,IAAI;CACN;AACF;;;;;;;;;AAUA,MAAM,aAAa,OACjB,SACA,YACoE;CACpE,IAAI;EACF,OAAO;GACL,IAAI;GACJ,OAAO,MAAM,QAAQ,OAAO;EAC9B;CACF,SAAS,OAAO;EACd,OAAO;GACL;GACA,IAAI;EACN;CACF;AACF;;;;;;;;;;;;;AAcA,MAAM,eAAe,OACnB,OACA,SACA,QACA,aAC+C;CAC/C,IAAI,WAAW,KAAA,GAAW;EACxB,MAAM,MAAM,OAAO;EACnB;CACF;CAEA,IAAI;EACF,MAAM,qBAAqB,OAAO,SAAS,MAAM;EACjD;CACF,SAAS,OAAO;EACd,MAAM,UAAU,iBAAiB,QAAQ,QAAQ;EACjD,IAAI,YAAY,KAAA,GACd,OAAO;EAET,MAAM;CACR;AACF;;;;;;;;;;;;;;;;;AAkBA,MAAM,gBAAgB,OACpB,QACA,WAM+C;CAC/C,MAAM,EAAE,SAAS,OAAO,OAAO,WAAW;CAC1C,MAAM,cAAc,iBAAiB,QAAQ,OAAO;CACpD,IAAI,gBAAgB,KAAA,GAClB,OAAO;CAGT,MAAM,WAAW,OAAO,KAAK;EAC3B;EACA;CACF,CAAC;CAED,IAAI,CAAC,SAAS,aAMZ,OAAO;EACL,UAAU;EACV,OAPoB,OAAO,YAAY;GACvC,UAAU;GACV;EACF,CAIqB;EACnB,IAAI;CACN;CAGF,IAAI,SAAS,UAAU,GAAG;EACxB,MAAM,mBAAmB,MAAM,aAC7B,OACA,SAAS,SACT,QACA,OACF;EACA,IAAI,qBAAqB,KAAA,GACvB,OAAO;CAEX;AAGF;;;;;;;;;;;;;;AAeA,MAAa,gBAAgB,OAC3B,QACA,SACA,OACA,WAC+B;CAC/B,IAAI,UAAU;CAEd,OAAO,MAAM;EACX,MAAM,cAAc,iBAAiB,QAAQ,KAAK,IAAI,GAAG,UAAU,CAAC,CAAC;EACrE,IAAI,gBAAgB,KAAA,GAClB,OAAO;EAIT,MAAM,YAAY,MAAM,WAAW,SAAS,OAAO;EACnD,IAAI,UAAU,IACZ,OAAO;GAAE,IAAI;GAAM,OAAO,UAAU;EAAM;EAI5C,MAAM,UAAU,MAAM,cAAc,QAAQ;GAC1C;GAEA,OAAO,UAAU;GACjB;GACA;EACF,CAAC;EACD,IAAI,YAAY,KAAA,GACd,OAAO;EAGT,WAAW;CACb;AACF"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"sleep.d.mts","names":[],"sources":["../src/sleep.ts"],"mappings":";;;;;;;;;;;;;cAaa,eAAsB,oBAAkB"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"sleep.mjs","names":[],"sources":["../src/sleep.ts"],"sourcesContent":["/**\n * Default delay implementation used by `BaseRetryPolicy.run` when no custom\n * `sleep` is provided.\n *\n * @module @zap-studio/retry/sleep\n */\n\n/**\n * Awaits a timer-based delay, unless `delayMs` is non-positive.\n *\n * @param delayMs - Milliseconds to wait before resolving.\n * @returns Promise that resolves when the delay completes.\n */\nexport const defaultSleep = async (delayMs: number): Promise<void> => {\n if (delayMs <= 0) {\n return;\n }\n\n // oxlint-disable-next-line promise/avoid-new -- Timer sleep requires adapting callback API to a promise.\n await new Promise<void>((resolve) => {\n setTimeout(resolve, delayMs);\n });\n};\n"],"mappings":";;;;;;;;;;;;;AAaA,MAAa,eAAe,OAAO,YAAmC;CACpE,IAAI,WAAW,GACb;CAIF,MAAM,IAAI,SAAe,YAAY;EACnC,WAAW,SAAS,OAAO;CAC7B,CAAC;AACH"}
@@ -1,21 +0,0 @@
1
- import { RetryPolicy } from "./types.mjs";
2
- //#region src/throw-mode.d.ts
3
- /**
4
- * Runs the throw-mode retry loop: throws `RetryError` on exhaustion and
5
- * `AbortError` when `signal` aborts.
6
- *
7
- * @param policy - Object providing `next` and `onExhausted` (same contract as
8
- * `BaseRetryPolicy`).
9
- * @param execute - Async work callback per attempt.
10
- * @param sleep - Delay function between retries.
11
- * @param signal - Optional cancel signal.
12
- * @returns Resolves to the first successful return value.
13
- * @throws {RetryError} When retries are exhausted and `onExhausted` returns
14
- * the terminal error.
15
- * @throws {AbortError} When `signal` is already aborted or aborts while waiting.
16
- * @throws {Error} Any error thrown by `next`, `onExhausted`, or `sleep`.
17
- */
18
- declare const runThrowMode: <T, TError, TData>(policy: RetryPolicy<TError, TData>, execute: (attempt: number) => Promise<T>, sleep: (delayMs: number) => Promise<void>, signal?: AbortSignal) => Promise<T>;
19
- //#endregion
20
- export { runThrowMode };
21
- //# sourceMappingURL=throw-mode.d.mts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"throw-mode.d.mts","names":[],"sources":["../src/throw-mode.ts"],"mappings":";;;;;;;;;;;;;;;;;cAyBa,eAAsB,GAAG,QAAQ,OAC5C,QAAQ,YAAY,QAAQ,QAC5B,UAAU,oBAAoB,QAAQ,IACtC,QAAQ,oBAAoB,eAC5B,SAAS,gBACR,QAAQ"}
@@ -1,49 +0,0 @@
1
- import { sleepWithAbortSignal, throwIfAborted } from "./abort.mjs";
2
- //#region src/throw-mode.ts
3
- /**
4
- * Throw-mode execution path for `BaseRetryPolicy.run` (default when
5
- * `throwOnExhausted` is not `false`).
6
- *
7
- * @module @zap-studio/retry/throw-mode
8
- */
9
- /**
10
- * Runs the throw-mode retry loop: throws `RetryError` on exhaustion and
11
- * `AbortError` when `signal` aborts.
12
- *
13
- * @param policy - Object providing `next` and `onExhausted` (same contract as
14
- * `BaseRetryPolicy`).
15
- * @param execute - Async work callback per attempt.
16
- * @param sleep - Delay function between retries.
17
- * @param signal - Optional cancel signal.
18
- * @returns Resolves to the first successful return value.
19
- * @throws {RetryError} When retries are exhausted and `onExhausted` returns
20
- * the terminal error.
21
- * @throws {AbortError} When `signal` is already aborted or aborts while waiting.
22
- * @throws {Error} Any error thrown by `next`, `onExhausted`, or `sleep`.
23
- */
24
- const runThrowMode = async (policy, execute, sleep, signal) => {
25
- let attempt = 1;
26
- while (true) {
27
- throwIfAborted(signal);
28
- try {
29
- return await execute(attempt);
30
- } catch (error) {
31
- throwIfAborted(signal);
32
- const typedError = error;
33
- const decision = policy.next({
34
- attempt,
35
- error: typedError
36
- });
37
- if (!decision.shouldRetry) throw policy.onExhausted({
38
- attempts: attempt,
39
- error: typedError
40
- });
41
- if (decision.delayMs > 0) await (signal === void 0 ? sleep(decision.delayMs) : sleepWithAbortSignal(sleep, decision.delayMs, signal));
42
- attempt += 1;
43
- }
44
- }
45
- };
46
- //#endregion
47
- export { runThrowMode };
48
-
49
- //# sourceMappingURL=throw-mode.mjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"throw-mode.mjs","names":[],"sources":["../src/throw-mode.ts"],"sourcesContent":["/**\n * Throw-mode execution path for `BaseRetryPolicy.run` (default when\n * `throwOnExhausted` is not `false`).\n *\n * @module @zap-studio/retry/throw-mode\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"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;AAyBA,MAAa,eAAe,OAC1B,QACA,SACA,OACA,WACe;CACf,IAAI,UAAU;CAEd,OAAO,MAAM;EACX,eAAe,MAAM;EAErB,IAAI;GAEF,OAAO,MAAM,QAAQ,OAAO;EAC9B,SAAS,OAAO;GACd,eAAe,MAAM;GAGrB,MAAM,aAAa;GACnB,MAAM,WAAW,OAAO,KAAK;IAC3B;IACA,OAAO;GACT,CAAC;GAED,IAAI,CAAC,SAAS,aACZ,MAAM,OAAO,YAAY;IACvB,UAAU;IACV,OAAO;GACT,CAAC;GAGH,IAAI,SAAS,UAAU,GAErB,OAAO,WAAW,KAAA,IACd,MAAM,SAAS,OAAO,IACtB,qBAAqB,OAAO,SAAS,SAAS,MAAM;GAG1D,WAAW;EACb;CACF;AACF"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"types.d.mts","names":[],"sources":["../src/types.ts"],"mappings":";;;;;;;;;;;UAiBiB,YAAY,kBAAkB;;;;;;EAM7C,OAAO,OAAO,mBAAmB,QAAQ,WAAW;;;;;;EAMpD,cAAc,OAAO,oBAAoB,QAAQ,WAAW;;;;;UAM7C;;;;;WAKN;;;;WAIA;;;;WAIA;;;;;UAMM,mBAAmB,kBAAkB;;;;WAI3C;;;;;WAKA;;;;;WAKA,QAAQ;;;;;WAKR,OAAO;;;;;UAMD,oBAAoB,kBAAkB;;;;WAI5C;;;;WAIA,QAAQ;;;;WAIR,OAAO;;;;;UAMD;;;;;;WAMN,SAAS,oBAAoB;;;;;;WAM7B,SAAS;;;;;;;;WAQT;;;;;;;;;KAUC,eAAe;;;;EAKrB;;;;EAIA,OAAO;;;;;EAMP;;;;;EAKA,OAAO,aAAa;;;;EAIpB"}
package/dist/types.mjs DELETED
@@ -1 +0,0 @@
1
- export {};