@zap-studio/retry 0.1.2 → 0.3.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 (42) hide show
  1. package/CHANGELOG.md +28 -0
  2. package/README.md +60 -3
  3. package/dist/abort.d.mts +30 -0
  4. package/dist/abort.d.mts.map +1 -0
  5. package/dist/abort.mjs +61 -0
  6. package/dist/abort.mjs.map +1 -0
  7. package/dist/errors-fWo_KyVO.d.mts +76 -0
  8. package/dist/errors-fWo_KyVO.d.mts.map +1 -0
  9. package/dist/errors.d.mts +2 -0
  10. package/dist/{error.mjs → errors.mjs} +21 -3
  11. package/dist/errors.mjs.map +1 -0
  12. package/dist/exponential-backoff.d.mts +18 -0
  13. package/dist/exponential-backoff.d.mts.map +1 -1
  14. package/dist/exponential-backoff.mjs +10 -1
  15. package/dist/exponential-backoff.mjs.map +1 -1
  16. package/dist/fixed-delay.d.mts +12 -0
  17. package/dist/fixed-delay.d.mts.map +1 -1
  18. package/dist/fixed-delay.mjs +7 -1
  19. package/dist/fixed-delay.mjs.map +1 -1
  20. package/dist/index.d.mts +17 -1
  21. package/dist/index.d.mts.map +1 -1
  22. package/dist/index.mjs +17 -38
  23. package/dist/index.mjs.map +1 -1
  24. package/dist/result-mode.d.mts +24 -0
  25. package/dist/result-mode.d.mts.map +1 -0
  26. package/dist/result-mode.mjs +145 -0
  27. package/dist/result-mode.mjs.map +1 -0
  28. package/dist/sleep.d.mts +17 -0
  29. package/dist/sleep.d.mts.map +1 -0
  30. package/dist/sleep.mjs +21 -0
  31. package/dist/sleep.mjs.map +1 -0
  32. package/dist/throw-mode.d.mts +26 -0
  33. package/dist/throw-mode.d.mts.map +1 -0
  34. package/dist/throw-mode.mjs +50 -0
  35. package/dist/throw-mode.mjs.map +1 -0
  36. package/dist/types.d.mts +70 -2
  37. package/dist/types.d.mts.map +1 -1
  38. package/package.json +12 -2
  39. package/dist/error-CWRADATN.d.mts +0 -44
  40. package/dist/error-CWRADATN.d.mts.map +0 -1
  41. package/dist/error.d.mts +0 -2
  42. package/dist/error.mjs.map +0 -1
package/CHANGELOG.md CHANGED
@@ -1,5 +1,33 @@
1
1
  # @zap-studio/retry
2
2
 
3
+ ## 0.3.0
4
+
5
+ ### Breaking
6
+
7
+ - **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`.
8
+
9
+ ### Changed
10
+
11
+ - Add dedicated `AbortError` and normalize cancellation paths so retry internals throw/return `RetryError` or `AbortError` instead of plain `Error`.
12
+ - 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).
13
+ - Align non-throw exhaustion metadata so `result.attempts` and `result.error.attempts` stay consistent for `RetryError` outcomes.
14
+ - In non-throw mode, return a normalized `AbortError` on `result.error` for cancellation; `result.attempts` still reports completed attempts.
15
+ - Refactor result-mode internals into smaller helpers for lower complexity and cleaner maintainability.
16
+ - Expand docs across README and package docs pages to explain `AbortError` behavior in throw and non-throw modes.
17
+ - Split the retry runner into dedicated modules: `throw-mode` (throwing execution path), `result-mode` (non-throw `RetryRunResult` path), and `sleep` (the default `defaultSleep` implementation). `BaseRetryPolicy` in `index` now delegates to these modules without changing public behavior.
18
+ - Add exhaustive TSDoc for `result-mode` and other `src` modules, including private helpers, policy option and state fields, and `RetryRunResult` union members.
19
+ - 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.
20
+
21
+ ## 0.2.0
22
+
23
+ ### Changed
24
+
25
+ - Optimize retry runner hot paths by splitting throw/non-throw execution flows and skipping sleep calls when delay is non-positive.
26
+ - Add `AbortSignal` support to `run(...)` so retry orchestration can be canceled before or between attempts.
27
+ - Add retry benchmarking coverage with core and ecosystem scenarios, including real-world and fair-mode comparisons.
28
+ - Add abort-focused ecosystem benchmarks comparing signal overhead and immediate cancellation behavior.
29
+ - Expand TSDoc coverage for new runner internals added in this release.
30
+
3
31
  ## 0.1.2
4
32
 
5
33
  ### Changed
package/README.md CHANGED
@@ -33,10 +33,11 @@ const data = await exponential.run(async () => {
33
33
 
34
34
  `run(...)` throws when retries are exhausted.
35
35
 
36
- By default, policies extending `BaseRetryPolicy` throw `RetryError`.
36
+ By default, policies extending `BaseRetryPolicy` throw `RetryError` on exhaustion
37
+ and `AbortError` on cancellation.
37
38
 
38
39
  ```ts
39
- import { RetryError } from "@zap-studio/retry/error";
40
+ import { AbortError, RetryError } from "@zap-studio/retry/errors";
40
41
 
41
42
  try {
42
43
  const data = await exponential.run(async () => {
@@ -50,6 +51,8 @@ try {
50
51
  if (error instanceof RetryError) {
51
52
  console.error("Retries exhausted:", error.attempts);
52
53
  console.error("Last error:", error.lastError);
54
+ } else if (error instanceof AbortError) {
55
+ console.error("Retry aborted:", error.message);
53
56
  } else {
54
57
  throw error;
55
58
  }
@@ -77,6 +80,60 @@ if (!result.ok) {
77
80
  }
78
81
  ```
79
82
 
83
+ ## Default sleep
84
+
85
+ `BaseRetryPolicy.run` automatically applies a delay between retry attempts when no custom `sleep` function is provided in the options.
86
+
87
+ That default is the `defaultSleep` helper, exported from `@zap-studio/retry/sleep`.
88
+
89
+ By default, this delay mechanism relies on the native JavaScript `setTimeout`, meaning retries are scheduled using the standard event loop timing rather than any custom or blocking implementation.
90
+
91
+ ## Cancellation With AbortSignal
92
+
93
+ Use `signal` in `run(...)` options to stop retrying early.
94
+
95
+ ```ts
96
+ const controller = new AbortController();
97
+
98
+ const promise = exponential.run(
99
+ async () => {
100
+ const response = await $fetch("https://api.example.com/users", {
101
+ throwOnFetchError: true,
102
+ });
103
+ return await response.json();
104
+ },
105
+ { signal: controller.signal },
106
+ );
107
+
108
+ controller.abort(new Error("Request canceled"));
109
+
110
+ await promise;
111
+ ```
112
+
113
+ In non-throw mode, abort is returned as `{ ok: false }` with `AbortError` on
114
+ `result.error`:
115
+
116
+ ```ts
117
+ const controller = new AbortController();
118
+
119
+ const result = await exponential.run(
120
+ async () => {
121
+ const response = await $fetch("https://api.example.com/users", {
122
+ throwOnFetchError: true,
123
+ });
124
+ return await response.json();
125
+ },
126
+ {
127
+ signal: controller.signal,
128
+ throwOnExhausted: false,
129
+ },
130
+ );
131
+
132
+ if (!result.ok) {
133
+ console.error("Retry stopped:", result.error);
134
+ }
135
+ ```
136
+
80
137
  ## Choosing The Right Policy
81
138
 
82
139
  Use `ExponentialBackoff` for transient network instability and shared upstream services.
@@ -144,7 +201,7 @@ const value = await policy.run(doWork);
144
201
  Use `RetryError` when an orchestrator exhausts retries and needs to surface final context.
145
202
 
146
203
  ```ts
147
- import { RetryError } from "@zap-studio/retry/error";
204
+ import { RetryError } from "@zap-studio/retry/errors";
148
205
 
149
206
  throw new RetryError("Retry policy exhausted all attempts.", {
150
207
  attempts: attempt,
@@ -0,0 +1,30 @@
1
+ import { t as AbortError } from "./errors-fWo_KyVO.mjs";
2
+
3
+ //#region src/abort.d.ts
4
+ /**
5
+ * Throws when the provided abort signal is already aborted.
6
+ *
7
+ * @param signal - Optional abort signal to inspect.
8
+ * @throws {AbortError} When the signal is aborted.
9
+ */
10
+ declare function throwIfAborted(signal?: AbortSignal): void;
11
+ /**
12
+ * Converts an abort reason into a normalized `AbortError`.
13
+ *
14
+ * @param reason - Arbitrary abort reason value.
15
+ * @returns Normalized abort error instance.
16
+ */
17
+ declare function toAbortError(reason: unknown): AbortError;
18
+ /**
19
+ * Waits for delay sleep while observing cancellation through an abort signal.
20
+ *
21
+ * @param sleep - Sleep function used to await `delayMs`.
22
+ * @param delayMs - Delay duration in milliseconds.
23
+ * @param signal - Abort signal to observe while waiting.
24
+ * @returns Promise that resolves when delay finishes.
25
+ * @throws {AbortError} When the signal aborts before or during wait.
26
+ */
27
+ declare function sleepWithAbortSignal(sleep: (delayMs: number) => Promise<void>, delayMs: number, signal: AbortSignal): Promise<void>;
28
+ //#endregion
29
+ export { sleepWithAbortSignal, throwIfAborted, toAbortError };
30
+ //# sourceMappingURL=abort.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"abort.d.mts","names":[],"sources":["../src/abort.ts"],"mappings":";;;;;AA4BA;;;;iBAdgB,cAAA,CAAe,MAAA,GAAS,WAAA;AA+CxC;;;;;;AAAA,iBAjCgB,YAAA,CAAa,MAAA,YAAkB,UAAA;;;;;;;;;;iBAiCzB,oBAAA,CACpB,KAAA,GAAQ,OAAA,aAAoB,OAAA,QAC5B,OAAA,UACA,MAAA,EAAQ,WAAA,GACP,OAAA"}
package/dist/abort.mjs ADDED
@@ -0,0 +1,61 @@
1
+ import { AbortError } from "./errors.mjs";
2
+ //#region src/abort.ts
3
+ /**
4
+ * Abort-signal helpers for retry orchestration internals.
5
+ *
6
+ * @module @zap-studio/retry/abort
7
+ */
8
+ /**
9
+ * Throws when the provided abort signal is already aborted.
10
+ *
11
+ * @param signal - Optional abort signal to inspect.
12
+ * @throws {AbortError} When the signal is aborted.
13
+ */
14
+ function throwIfAborted(signal) {
15
+ if (!signal?.aborted) return;
16
+ throw toAbortError(signal.reason);
17
+ }
18
+ /**
19
+ * Converts an abort reason into a normalized `AbortError`.
20
+ *
21
+ * @param reason - Arbitrary abort reason value.
22
+ * @returns Normalized abort error instance.
23
+ */
24
+ function toAbortError(reason) {
25
+ if (reason instanceof AbortError) return reason;
26
+ if (reason instanceof Error) return new AbortError(reason.message, { cause: reason });
27
+ if (typeof reason === "string" && reason.length > 0) return new AbortError(reason);
28
+ if (reason === void 0) return new AbortError("Retry aborted.");
29
+ try {
30
+ return new AbortError(`Retry aborted: ${JSON.stringify(reason)}`);
31
+ } catch {
32
+ return new AbortError("Retry aborted.");
33
+ }
34
+ }
35
+ /**
36
+ * Waits for delay sleep while observing cancellation through an abort signal.
37
+ *
38
+ * @param sleep - Sleep function used to await `delayMs`.
39
+ * @param delayMs - Delay duration in milliseconds.
40
+ * @param signal - Abort signal to observe while waiting.
41
+ * @returns Promise that resolves when delay finishes.
42
+ * @throws {AbortError} When the signal aborts before or during wait.
43
+ */
44
+ async function sleepWithAbortSignal(sleep, delayMs, signal) {
45
+ if (signal.aborted) throw toAbortError(signal.reason);
46
+ let onAbort;
47
+ try {
48
+ await Promise.race([sleep(delayMs), new Promise((_, reject) => {
49
+ onAbort = () => {
50
+ reject(toAbortError(signal.reason));
51
+ };
52
+ signal.addEventListener("abort", onAbort, { once: true });
53
+ })]);
54
+ } finally {
55
+ if (onAbort) signal.removeEventListener("abort", onAbort);
56
+ }
57
+ }
58
+ //#endregion
59
+ export { sleepWithAbortSignal, throwIfAborted, toAbortError };
60
+
61
+ //# sourceMappingURL=abort.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"abort.mjs","names":[],"sources":["../src/abort.ts"],"sourcesContent":["/**\n * Abort-signal helpers for retry orchestration internals.\n *\n * @module @zap-studio/retry/abort\n */\n\nimport { AbortError } from \"./errors.js\";\n\n/**\n * Throws when the provided abort signal is already aborted.\n *\n * @param signal - Optional abort signal to inspect.\n * @throws {AbortError} When the signal is aborted.\n */\nexport function throwIfAborted(signal?: AbortSignal): void {\n if (!signal?.aborted) {\n return;\n }\n\n throw toAbortError(signal.reason);\n}\n\n/**\n * Converts an abort reason into a normalized `AbortError`.\n *\n * @param reason - Arbitrary abort reason value.\n * @returns Normalized abort error instance.\n */\nexport function 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 * 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 async function sleepWithAbortSignal(\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 new Promise<never>((_, 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,SAAgB,eAAe,QAA4B;AACzD,KAAI,CAAC,QAAQ,QACX;AAGF,OAAM,aAAa,OAAO,OAAO;;;;;;;;AASnC,SAAgB,aAAa,QAA6B;AACxD,KAAI,kBAAkB,WACpB,QAAO;AAGT,KAAI,kBAAkB,MACpB,QAAO,IAAI,WAAW,OAAO,SAAS,EAAE,OAAO,QAAQ,CAAC;AAG1D,KAAI,OAAO,WAAW,YAAY,OAAO,SAAS,EAChD,QAAO,IAAI,WAAW,OAAO;AAG/B,KAAI,WAAW,KAAA,EACb,QAAO,IAAI,WAAW,iBAAiB;AAGzC,KAAI;AACF,SAAO,IAAI,WAAW,kBAAkB,KAAK,UAAU,OAAO,GAAG;SAC3D;AACN,SAAO,IAAI,WAAW,iBAAiB;;;;;;;;;;;;AAa3C,eAAsB,qBACpB,OACA,SACA,QACe;AACf,KAAI,OAAO,QACT,OAAM,aAAa,OAAO,OAAO;CAGnC,IAAI;AAEJ,KAAI;AACF,QAAM,QAAQ,KAAK,CACjB,MAAM,QAAQ,EACd,IAAI,SAAgB,GAAG,WAAW;AAChC,mBAAsB;AACpB,WAAO,aAAa,OAAO,OAAO,CAAC;;AAGrC,UAAO,iBAAiB,SAAS,SAAS,EAAE,MAAM,MAAM,CAAC;IACzD,CACH,CAAC;WACM;AACR,MAAI,QACF,QAAO,oBAAoB,SAAS,QAAQ"}
@@ -0,0 +1,76 @@
1
+ //#region src/errors.d.ts
2
+ /**
3
+ * Terminal error types used by retry policies and runners.
4
+ *
5
+ * @module @zap-studio/retry/errors
6
+ */
7
+ /**
8
+ * Context payload attached to `RetryError`.
9
+ */
10
+ interface RetryErrorContext {
11
+ /**
12
+ * Count of completed attempts at exhaustion.
13
+ */
14
+ readonly attempts: number;
15
+ /**
16
+ * The last error object raised by a failed `execute` attempt.
17
+ */
18
+ readonly lastError?: unknown;
19
+ /**
20
+ * Optional data captured from the last attempt when provided by a policy.
21
+ */
22
+ readonly lastData?: unknown;
23
+ }
24
+ /**
25
+ * Context payload attached to `AbortError`.
26
+ */
27
+ interface AbortErrorContext {
28
+ /**
29
+ * When the abort `reason` was an `Error`, the optional wrapped cause.
30
+ */
31
+ readonly cause?: unknown;
32
+ }
33
+ /**
34
+ * Error thrown when retries are exhausted.
35
+ *
36
+ * @example
37
+ * throw new RetryError("Retry exhausted", {
38
+ * attempts: 3,
39
+ * lastError: new Error("network"),
40
+ * });
41
+ */
42
+ declare class RetryError extends Error {
43
+ /**
44
+ * Total attempts performed before exhaustion.
45
+ */
46
+ readonly attempts: number;
47
+ /**
48
+ * Last captured error from execution.
49
+ */
50
+ readonly lastError?: unknown;
51
+ /**
52
+ * Last captured data value, when available.
53
+ */
54
+ readonly lastData?: unknown;
55
+ /**
56
+ * Creates a RetryError with structured terminal context.
57
+ */
58
+ constructor(message: string, context: RetryErrorContext);
59
+ }
60
+ /**
61
+ * Error thrown when retry orchestration is canceled through `AbortSignal`.
62
+ */
63
+ declare class AbortError extends Error {
64
+ /**
65
+ * Optional wrapped cause when the native abort `reason` was an `Error`.
66
+ */
67
+ override readonly cause?: unknown;
68
+ /**
69
+ * @param message - Human-readable abort description.
70
+ * @param context - Optional `cause` link for diagnostic chaining.
71
+ */
72
+ constructor(message: string, context?: AbortErrorContext);
73
+ }
74
+ //#endregion
75
+ export { RetryErrorContext as i, AbortErrorContext as n, RetryError as r, AbortError as t };
76
+ //# sourceMappingURL=errors-fWo_KyVO.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors-fWo_KyVO.d.mts","names":[],"sources":["../src/errors.ts"],"mappings":";;AASA;;;;;;;UAAiB,iBAAA;EAYN;AAMX;;EANW,SARA,QAAA;EAcM;;AAgBjB;EAhBiB,SAVN,SAAA;;;;WAIA,QAAA;AAAA;;;;UAMM,iBAAA;;;;WAIN,KAAA;AAAA;;;;;;;;;;cAYE,UAAA,SAAmB,KAAA;;;;WAId,QAAA;;;;WAIA,SAAA;;;;WAIA,QAAA;;;;EAKhB,WAAA,CAAY,OAAA,UAAiB,OAAA,EAAS,iBAAA;AAAA;;;;cAY3B,UAAA,SAAmB,KAAA;;;;oBAIL,KAAA;;;;;EAMzB,WAAA,CAAY,OAAA,UAAiB,OAAA,GAAS,iBAAA;AAAA"}
@@ -0,0 +1,2 @@
1
+ import { i as RetryErrorContext, n as AbortErrorContext, r as RetryError, t as AbortError } from "./errors-fWo_KyVO.mjs";
2
+ export { AbortError, AbortErrorContext, RetryError, RetryErrorContext };
@@ -1,4 +1,4 @@
1
- //#region src/error.ts
1
+ //#region src/errors.ts
2
2
  /**
3
3
  * Error thrown when retries are exhausted.
4
4
  *
@@ -32,7 +32,25 @@ var RetryError = class extends Error {
32
32
  this.lastData = context.lastData;
33
33
  }
34
34
  };
35
+ /**
36
+ * Error thrown when retry orchestration is canceled through `AbortSignal`.
37
+ */
38
+ var AbortError = class extends Error {
39
+ /**
40
+ * Optional wrapped cause when the native abort `reason` was an `Error`.
41
+ */
42
+ cause;
43
+ /**
44
+ * @param message - Human-readable abort description.
45
+ * @param context - Optional `cause` link for diagnostic chaining.
46
+ */
47
+ constructor(message, context = {}) {
48
+ super(message);
49
+ this.name = "AbortError";
50
+ this.cause = context.cause;
51
+ }
52
+ };
35
53
  //#endregion
36
- export { RetryError };
54
+ export { AbortError, RetryError };
37
55
 
38
- //# sourceMappingURL=error.mjs.map
56
+ //# sourceMappingURL=errors.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.mjs","names":[],"sources":["../src/errors.ts"],"sourcesContent":["/**\n * Terminal error types used by retry policies and runners.\n *\n * @module @zap-studio/retry/errors\n */\n\n/**\n * Context payload attached to `RetryError`.\n */\nexport interface RetryErrorContext {\n /**\n * Count of completed attempts at exhaustion.\n */\n readonly attempts: number;\n /**\n * The last error object raised by a failed `execute` attempt.\n */\n readonly lastError?: unknown;\n /**\n * Optional data captured from the last attempt when provided by a policy.\n */\n readonly lastData?: unknown;\n}\n\n/**\n * Context payload attached to `AbortError`.\n */\nexport interface AbortErrorContext {\n /**\n * When the abort `reason` was an `Error`, the optional wrapped cause.\n */\n readonly cause?: unknown;\n}\n\n/**\n * Error thrown when retries are exhausted.\n *\n * @example\n * throw new RetryError(\"Retry exhausted\", {\n * attempts: 3,\n * lastError: new Error(\"network\"),\n * });\n */\nexport class RetryError extends Error {\n /**\n * Total attempts performed before exhaustion.\n */\n public readonly attempts: number;\n /**\n * Last captured error from execution.\n */\n public readonly lastError?: unknown;\n /**\n * Last captured data value, when available.\n */\n public readonly lastData?: unknown;\n\n /**\n * Creates a RetryError with structured terminal context.\n */\n constructor(message: string, context: RetryErrorContext) {\n super(message);\n this.name = \"RetryError\";\n this.attempts = context.attempts;\n this.lastError = context.lastError;\n this.lastData = context.lastData;\n }\n}\n\n/**\n * Error thrown when retry orchestration is canceled through `AbortSignal`.\n */\nexport class AbortError extends Error {\n /**\n * Optional wrapped cause when the native abort `reason` was an `Error`.\n */\n public override readonly cause?: unknown;\n\n /**\n * @param message - Human-readable abort description.\n * @param context - Optional `cause` link for diagnostic chaining.\n */\n constructor(message: string, context: AbortErrorContext = {}) {\n super(message);\n this.name = \"AbortError\";\n this.cause = context.cause;\n }\n}\n"],"mappings":";;;;;;;;;;AA2CA,IAAa,aAAb,cAAgC,MAAM;;;;CAIpC;;;;CAIA;;;;CAIA;;;;CAKA,YAAY,SAAiB,SAA4B;AACvD,QAAM,QAAQ;AACd,OAAK,OAAO;AACZ,OAAK,WAAW,QAAQ;AACxB,OAAK,YAAY,QAAQ;AACzB,OAAK,WAAW,QAAQ;;;;;;AAO5B,IAAa,aAAb,cAAgC,MAAM;;;;CAIpC;;;;;CAMA,YAAY,SAAiB,UAA6B,EAAE,EAAE;AAC5D,QAAM,QAAQ;AACd,OAAK,OAAO;AACZ,OAAK,QAAQ,QAAQ"}
@@ -6,8 +6,17 @@ import { BaseRetryPolicy } from "./index.mjs";
6
6
  * Configuration for `ExponentialBackoff`.
7
7
  */
8
8
  interface ExponentialBackoffOptions {
9
+ /**
10
+ * Maximum number of attempts (including the first) before giving up.
11
+ */
9
12
  maxAttempts: number;
13
+ /**
14
+ * Initial delay in milliseconds, doubled each retry until capped.
15
+ */
10
16
  baseDelayMs: number;
17
+ /**
18
+ * Hard upper bound in milliseconds for computed exponential delay.
19
+ */
11
20
  maxDelayMs: number;
12
21
  }
13
22
  /**
@@ -21,8 +30,17 @@ interface ExponentialBackoffOptions {
21
30
  * });
22
31
  */
23
32
  declare class ExponentialBackoff extends BaseRetryPolicy {
33
+ /**
34
+ * Maximum number of attempts before the policy returns `max-attempts-reached`.
35
+ */
24
36
  private readonly maxAttempts;
37
+ /**
38
+ * Base delay in milliseconds used in `baseDelayMs * 2 ** (attempt - 1)`.
39
+ */
25
40
  private readonly baseDelayMs;
41
+ /**
42
+ * Upper cap for computed delay, applied with `Math.min`.
43
+ */
26
44
  private readonly maxDelayMs;
27
45
  /**
28
46
  * Creates an exponential backoff retry policy.
@@ -1 +1 @@
1
- {"version":3,"file":"exponential-backoff.d.mts","names":[],"sources":["../src/exponential-backoff.ts"],"mappings":";;;;;;;UAYiB,yBAAA;EACf,WAAA;EACA,WAAA;EACA,UAAA;AAAA;;;;;;;;;;;cAaW,kBAAA,SAA2B,eAAA;EAAA,iBACrB,WAAA;EAAA,iBACA,WAAA;EAAA,iBACA,UAAA;;;;EAKjB,WAAA,CAAY,OAAA,EAAS,yBAAA;;;;EAUrB,IAAA,CAAY,KAAA,EAAO,kBAAA,GAAqB,aAAA;AAAA"}
1
+ {"version":3,"file":"exponential-backoff.d.mts","names":[],"sources":["../src/exponential-backoff.ts"],"mappings":";;;;;;;UAYiB,yBAAA;;;AAyBjB;EArBE,WAAA;;;;EAIA,WAAA;;;;EAIA,UAAA;AAAA;;;;;;;;;;;cAaW,kBAAA,SAA2B,eAAA;;;;mBAIrB,WAAA;;;;mBAIA,WAAA;;;;mBAIA,UAAA;;;;EAKjB,WAAA,CAAY,OAAA,EAAS,yBAAA;;;;EAUrB,IAAA,CAAY,KAAA,EAAO,kBAAA,GAAqB,aAAA;AAAA"}
@@ -3,7 +3,7 @@ import { BaseRetryPolicy } from "./index.mjs";
3
3
  /**
4
4
  * Exponential backoff retry strategy.
5
5
  *
6
- * @module
6
+ * @module @zap-studio/retry/exponential-backoff
7
7
  */
8
8
  /**
9
9
  * Retries with exponential delay growth up to a max cap.
@@ -16,8 +16,17 @@ import { BaseRetryPolicy } from "./index.mjs";
16
16
  * });
17
17
  */
18
18
  var ExponentialBackoff = class extends BaseRetryPolicy {
19
+ /**
20
+ * Maximum number of attempts before the policy returns `max-attempts-reached`.
21
+ */
19
22
  maxAttempts;
23
+ /**
24
+ * Base delay in milliseconds used in `baseDelayMs * 2 ** (attempt - 1)`.
25
+ */
20
26
  baseDelayMs;
27
+ /**
28
+ * Upper cap for computed delay, applied with `Math.min`.
29
+ */
21
30
  maxDelayMs;
22
31
  /**
23
32
  * Creates an exponential backoff retry policy.
@@ -1 +1 @@
1
- {"version":3,"file":"exponential-backoff.mjs","names":[],"sources":["../src/exponential-backoff.ts"],"sourcesContent":["/**\n * Exponential backoff retry strategy.\n *\n * @module\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 maxAttempts: number;\n baseDelayMs: number;\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 private readonly maxAttempts: number;\n private readonly baseDelayMs: number;\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 { shouldRetry: false, delayMs: 0, reason: \"max-attempts-reached\" };\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 { shouldRetry: true, delayMs, reason: \"retry\" };\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;AA4BA,IAAa,qBAAb,cAAwC,gBAAgB;CACtD;CACA;CACA;;;;CAKA,YAAY,SAAoC;AAC9C,SAAO;AACP,OAAK,cAAc,QAAQ;AAC3B,OAAK,cAAc,QAAQ;AAC3B,OAAK,aAAa,QAAQ;;;;;CAM5B,KAAY,OAA0C;AACpD,MAAI,MAAM,WAAW,KAAK,YACxB,QAAO;GAAE,aAAa;GAAO,SAAS;GAAG,QAAQ;GAAwB;EAG3E,MAAM,WAAW,KAAK,IAAI,GAAG,MAAM,UAAU,EAAE;AAG/C,SAAO;GAAE,aAAa;GAAM,SAFZ,KAAK,IAAI,KAAK,YAAY,KAAK,cAAc,KAAK,SAAS;GAEtC,QAAQ;GAAS"}
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 { shouldRetry: false, delayMs: 0, reason: \"max-attempts-reached\" };\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 { shouldRetry: true, delayMs, reason: \"retry\" };\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;AAqCA,IAAa,qBAAb,cAAwC,gBAAgB;;;;CAItD;;;;CAIA;;;;CAIA;;;;CAKA,YAAY,SAAoC;AAC9C,SAAO;AACP,OAAK,cAAc,QAAQ;AAC3B,OAAK,cAAc,QAAQ;AAC3B,OAAK,aAAa,QAAQ;;;;;CAM5B,KAAY,OAA0C;AACpD,MAAI,MAAM,WAAW,KAAK,YACxB,QAAO;GAAE,aAAa;GAAO,SAAS;GAAG,QAAQ;GAAwB;EAG3E,MAAM,WAAW,KAAK,IAAI,GAAG,MAAM,UAAU,EAAE;AAG/C,SAAO;GAAE,aAAa;GAAM,SAFZ,KAAK,IAAI,KAAK,YAAY,KAAK,cAAc,KAAK,SAAS;GAEtC,QAAQ;GAAS"}
@@ -6,7 +6,13 @@ import { BaseRetryPolicy } from "./index.mjs";
6
6
  * Configuration for `FixedDelay`.
7
7
  */
8
8
  interface FixedDelayOptions {
9
+ /**
10
+ * Maximum number of attempts (including the first) before giving up.
11
+ */
9
12
  maxAttempts: number;
13
+ /**
14
+ * Constant delay in milliseconds before each retry after a failure.
15
+ */
10
16
  delayMs: number;
11
17
  }
12
18
  /**
@@ -19,7 +25,13 @@ interface FixedDelayOptions {
19
25
  * });
20
26
  */
21
27
  declare class FixedDelay extends BaseRetryPolicy {
28
+ /**
29
+ * Maximum number of attempts before the policy returns `max-attempts-reached`.
30
+ */
22
31
  private readonly maxAttempts;
32
+ /**
33
+ * Constant delay in milliseconds before each subsequent attempt.
34
+ */
23
35
  private readonly delayMs;
24
36
  /**
25
37
  * Creates a fixed-delay retry policy.
@@ -1 +1 @@
1
- {"version":3,"file":"fixed-delay.d.mts","names":[],"sources":["../src/fixed-delay.ts"],"mappings":";;;;;;AA0BA;UAdiB,iBAAA;EACf,WAAA;EACA,OAAA;AAAA;;;;;;;;;;cAYW,UAAA,SAAmB,eAAA;EAAA,iBACb,WAAA;EAAA,iBACA,OAAA;;;;EAKjB,WAAA,CAAY,OAAA,EAAS,iBAAA;;;;EASrB,IAAA,CAAY,KAAA,EAAO,kBAAA,GAAqB,aAAA;AAAA"}
1
+ {"version":3,"file":"fixed-delay.d.mts","names":[],"sources":["../src/fixed-delay.ts"],"mappings":";;;;;;AAgCA;UApBiB,iBAAA;;;;EAIf,WAAA;;;;EAIA,OAAA;AAAA;;;;;;;;;;cAYW,UAAA,SAAmB,eAAA;;;;mBAIb,WAAA;;;;mBAIA,OAAA;;;;EAKjB,WAAA,CAAY,OAAA,EAAS,iBAAA;;;;EASrB,IAAA,CAAY,KAAA,EAAO,kBAAA,GAAqB,aAAA;AAAA"}
@@ -3,7 +3,7 @@ import { BaseRetryPolicy } from "./index.mjs";
3
3
  /**
4
4
  * Fixed-delay retry strategy.
5
5
  *
6
- * @module
6
+ * @module @zap-studio/retry/fixed-delay
7
7
  */
8
8
  /**
9
9
  * Retries with a constant delay between attempts.
@@ -15,7 +15,13 @@ import { BaseRetryPolicy } from "./index.mjs";
15
15
  * });
16
16
  */
17
17
  var FixedDelay = class extends BaseRetryPolicy {
18
+ /**
19
+ * Maximum number of attempts before the policy returns `max-attempts-reached`.
20
+ */
18
21
  maxAttempts;
22
+ /**
23
+ * Constant delay in milliseconds before each subsequent attempt.
24
+ */
19
25
  delayMs;
20
26
  /**
21
27
  * Creates a fixed-delay retry policy.
@@ -1 +1 @@
1
- {"version":3,"file":"fixed-delay.mjs","names":[],"sources":["../src/fixed-delay.ts"],"sourcesContent":["/**\n * Fixed-delay retry strategy.\n *\n * @module\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 maxAttempts: number;\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 private readonly maxAttempts: number;\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 { shouldRetry: false, delayMs: 0, reason: \"max-attempts-reached\" };\n }\n\n return { shouldRetry: true, delayMs: this.delayMs, reason: \"retry\" };\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;AA0BA,IAAa,aAAb,cAAgC,gBAAgB;CAC9C;CACA;;;;CAKA,YAAY,SAA4B;AACtC,SAAO;AACP,OAAK,cAAc,QAAQ;AAC3B,OAAK,UAAU,QAAQ;;;;;CAMzB,KAAY,OAA0C;AACpD,MAAI,MAAM,WAAW,KAAK,YACxB,QAAO;GAAE,aAAa;GAAO,SAAS;GAAG,QAAQ;GAAwB;AAG3E,SAAO;GAAE,aAAa;GAAM,SAAS,KAAK;GAAS,QAAQ;GAAS"}
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 { shouldRetry: false, delayMs: 0, reason: \"max-attempts-reached\" };\n }\n\n return { shouldRetry: true, delayMs: this.delayMs, reason: \"retry\" };\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;AAgCA,IAAa,aAAb,cAAgC,gBAAgB;;;;CAI9C;;;;CAIA;;;;CAKA,YAAY,SAA4B;AACtC,SAAO;AACP,OAAK,cAAc,QAAQ;AAC3B,OAAK,UAAU,QAAQ;;;;;CAMzB,KAAY,OAA0C;AACpD,MAAI,MAAM,WAAW,KAAK,YACxB,QAAO;GAAE,aAAa;GAAO,SAAS;GAAG,QAAQ;GAAwB;AAG3E,SAAO;GAAE,aAAa;GAAM,SAAS,KAAK;GAAS,QAAQ;GAAS"}
package/dist/index.d.mts CHANGED
@@ -1,7 +1,14 @@
1
- import { t as RetryError } from "./error-CWRADATN.mjs";
1
+ import { r as RetryError } from "./errors-fWo_KyVO.mjs";
2
2
  import { RetryDecision, RetryDecisionInput, RetryExhaustedInput, RetryPolicy, RetryRunOptions, RetryRunResult } from "./types.mjs";
3
3
 
4
4
  //#region src/index.d.ts
5
+ /**
6
+ * Base class for implementing retry policies and running retry orchestration.
7
+ *
8
+ * Extend this class and implement {@link BaseRetryPolicy.next} to define retry
9
+ * behavior, then call {@link BaseRetryPolicy.run} to execute operations with that
10
+ * policy.
11
+ */
5
12
  declare abstract class BaseRetryPolicy<TError = unknown, TData = unknown> implements RetryPolicy<TError, TData> {
6
13
  /**
7
14
  * Returns the retry decision for a failed attempt.
@@ -20,6 +27,14 @@ declare abstract class BaseRetryPolicy<TError = unknown, TData = unknown> implem
20
27
  * @throws Any error thrown by an overriding policy implementation.
21
28
  */
22
29
  onExhausted(input: RetryExhaustedInput<TError, TData>): RetryError;
30
+ /**
31
+ * Runs retry orchestration in non-throw mode.
32
+ *
33
+ * @param execute - Async function to execute per attempt.
34
+ * @param options - Runner settings with `throwOnExhausted: false`.
35
+ * @returns A discriminated result union containing success value or terminal error.
36
+ * @throws Any error thrown by `next`, `onExhausted`, or a custom `sleep`.
37
+ */
23
38
  run<T>(execute: (attempt: number) => Promise<T>, options: RetryRunOptions & {
24
39
  throwOnExhausted: false;
25
40
  }): Promise<RetryRunResult<T>>;
@@ -32,6 +47,7 @@ declare abstract class BaseRetryPolicy<TError = unknown, TData = unknown> implem
32
47
  * @throws {RetryError} When retries are exhausted and `onExhausted` returns the
33
48
  * terminal retry error. The default implementation returns `RetryError` with the last
34
49
  * execution failure available on `RetryError.lastError`.
50
+ * @throws {AbortError} When `options.signal` is already aborted or aborts while retrying.
35
51
  * @throws Any error thrown by `next`, by `onExhausted`, or by a custom `sleep`
36
52
  * function.
37
53
  */
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.mts","names":[],"sources":["../src/index.ts"],"mappings":";;;;uBAgBsB,eAAA,+CAA8D,WAAA,CAClF,MAAA,EACA,KAAA;;;;;;;WAQgB,IAAA,CAAK,KAAA,EAAO,kBAAA,CAAmB,MAAA,EAAQ,KAAA,IAAS,aAAA;;;;;;;;;;EAWhE,WAAA,CAAmB,KAAA,EAAO,mBAAA,CAAoB,MAAA,EAAQ,KAAA,IAAS,UAAA;EAQ/D,GAAA,GAAA,CACE,OAAA,GAAU,OAAA,aAAoB,OAAA,CAAQ,CAAA,GACtC,OAAA,EAAS,eAAA;IAAoB,gBAAA;EAAA,IAC5B,OAAA,CAAQ,cAAA,CAAe,CAAA;;;;;;;;;;;;;EAc1B,GAAA,GAAA,CACE,OAAA,GAAU,OAAA,aAAoB,OAAA,CAAQ,CAAA,GACtC,OAAA,GAAU,eAAA;IAAoB,gBAAA;EAAA,IAC7B,OAAA,CAAQ,CAAA;AAAA"}
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../src/index.ts"],"mappings":";;;;;;;;;;;uBA0BsB,eAAA,+CAA8D,WAAA,CAClF,MAAA,EACA,KAAA;;;;;;;WAQgB,IAAA,CAAK,KAAA,EAAO,kBAAA,CAAmB,MAAA,EAAQ,KAAA,IAAS,aAAA;;;;;;;;;;EAWhE,WAAA,CAAmB,KAAA,EAAO,mBAAA,CAAoB,MAAA,EAAQ,KAAA,IAAS,UAAA;;;;;;;;;EAgB/D,GAAA,GAAA,CACE,OAAA,GAAU,OAAA,aAAoB,OAAA,CAAQ,CAAA,GACtC,OAAA,EAAS,eAAA;IAAoB,gBAAA;EAAA,IAC5B,OAAA,CAAQ,cAAA,CAAe,CAAA;;;;;;;;;;;;;;EAe1B,GAAA,GAAA,CACE,OAAA,GAAU,OAAA,aAAoB,OAAA,CAAQ,CAAA,GACtC,OAAA,GAAU,eAAA;IAAoB,gBAAA;EAAA,IAC7B,OAAA,CAAQ,CAAA;AAAA"}
package/dist/index.mjs CHANGED
@@ -1,9 +1,19 @@
1
- import { RetryError } from "./error.mjs";
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";
2
5
  //#region src/index.ts
3
6
  /**
4
7
  * Retry runner base class and shared orchestration implementation.
5
8
  *
6
- * @module
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.
7
17
  */
8
18
  var BaseRetryPolicy = class {
9
19
  /**
@@ -33,6 +43,8 @@ var BaseRetryPolicy = class {
33
43
  * @throws Any error thrown by `next`, by `onExhausted`, or by a custom `sleep`
34
44
  * function. When `throwOnExhausted` is `false`, exhaustion itself is returned
35
45
  * as `{ ok: false }` instead of thrown.
46
+ * Cancellation is returned as `{ ok: false, error: AbortError }` in non-throw
47
+ * mode (not wrapped in `RetryError`).
36
48
  *
37
49
  * @example
38
50
  * const result = await policy.run(doWork, { throwOnExhausted: false });
@@ -40,44 +52,11 @@ var BaseRetryPolicy = class {
40
52
  */
41
53
  async run(execute, options = {}) {
42
54
  const sleep = options.sleep ?? defaultSleep;
43
- let attempt = 1;
44
- let lastError;
45
- while (true) try {
46
- const value = await execute(attempt);
47
- if (options.throwOnExhausted === false) return {
48
- ok: true,
49
- value
50
- };
51
- return value;
52
- } catch (error) {
53
- lastError = error;
54
- const decision = this.next({
55
- attempt,
56
- error
57
- });
58
- if (!decision.shouldRetry) {
59
- const terminalError = this.onExhausted({
60
- attempts: attempt,
61
- error: lastError
62
- });
63
- if (options.throwOnExhausted === false) return {
64
- ok: false,
65
- error: terminalError,
66
- attempts: attempt
67
- };
68
- throw terminalError;
69
- }
70
- await sleep(decision.delayMs);
71
- attempt += 1;
72
- }
55
+ const signal = options.signal;
56
+ if (options.throwOnExhausted === false) return runResultMode(this, execute, sleep, signal);
57
+ return runThrowMode(this, execute, sleep, signal);
73
58
  }
74
59
  };
75
- /**
76
- * Default delay implementation used by `run(...)` when no custom sleep function is provided.
77
- */
78
- async function defaultSleep(delayMs) {
79
- await new Promise((resolve) => setTimeout(resolve, delayMs));
80
- }
81
60
  //#endregion
82
61
  export { BaseRetryPolicy };
83
62
 
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":[],"sources":["../src/index.ts"],"sourcesContent":["/**\n * Retry runner base class and shared orchestration implementation.\n *\n * @module\n */\n\nimport { RetryError } from \"./error.js\";\nimport type {\n RetryDecision,\n RetryDecisionInput,\n RetryExhaustedInput,\n RetryPolicy,\n RetryRunOptions,\n RetryRunResult,\n} from \"./types.js\";\n\nexport abstract class BaseRetryPolicy<TError = unknown, TData = unknown> implements RetryPolicy<\n TError,\n TData\n> {\n /**\n * Returns the retry decision for a failed attempt.\n *\n * @param input - Attempt context used to compute retry behavior.\n * @throws 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 Any error thrown by an overriding policy implementation.\n */\n public onExhausted(input: RetryExhaustedInput<TError, TData>): RetryError {\n return new RetryError(\"Retry policy exhausted all attempts.\", {\n attempts: input.attempts,\n lastError: input.error,\n lastData: input.data,\n });\n }\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 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 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 *\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 let attempt = 1;\n let lastError: TError | undefined;\n\n while (true) {\n try {\n const value = await execute(attempt);\n if (options.throwOnExhausted === false) {\n return { ok: true, value };\n }\n return value;\n } catch (error) {\n lastError = error as TError;\n const decision = this.next({\n attempt,\n error: error as TError,\n });\n\n if (!decision.shouldRetry) {\n const terminalError = this.onExhausted({\n attempts: attempt,\n error: lastError,\n });\n if (options.throwOnExhausted === false) {\n return {\n ok: false,\n error: terminalError,\n attempts: attempt,\n };\n }\n throw terminalError;\n }\n\n await sleep(decision.delayMs);\n attempt += 1;\n }\n }\n }\n}\n\n/**\n * Default delay implementation used by `run(...)` when no custom sleep function is provided.\n */\nasync function defaultSleep(delayMs: number): Promise<void> {\n await new Promise((resolve) => setTimeout(resolve, delayMs));\n}\n"],"mappings":";;;;;;;AAgBA,IAAsB,kBAAtB,MAGE;;;;;;;;;;CAkBA,YAAmB,OAAuD;AACxE,SAAO,IAAI,WAAW,wCAAwC;GAC5D,UAAU,MAAM;GAChB,WAAW,MAAM;GACjB,UAAU,MAAM;GACjB,CAAC;;;;;;;;;;;;;;;;;;CAyCJ,MAAa,IACX,SACA,UAA2B,EAAE,EACG;EAChC,MAAM,QAAQ,QAAQ,SAAS;EAC/B,IAAI,UAAU;EACd,IAAI;AAEJ,SAAO,KACL,KAAI;GACF,MAAM,QAAQ,MAAM,QAAQ,QAAQ;AACpC,OAAI,QAAQ,qBAAqB,MAC/B,QAAO;IAAE,IAAI;IAAM;IAAO;AAE5B,UAAO;WACA,OAAO;AACd,eAAY;GACZ,MAAM,WAAW,KAAK,KAAK;IACzB;IACO;IACR,CAAC;AAEF,OAAI,CAAC,SAAS,aAAa;IACzB,MAAM,gBAAgB,KAAK,YAAY;KACrC,UAAU;KACV,OAAO;KACR,CAAC;AACF,QAAI,QAAQ,qBAAqB,MAC/B,QAAO;KACL,IAAI;KACJ,OAAO;KACP,UAAU;KACX;AAEH,UAAM;;AAGR,SAAM,MAAM,SAAS,QAAQ;AAC7B,cAAW;;;;;;;AASnB,eAAe,aAAa,SAAgC;AAC1D,OAAM,IAAI,SAAS,YAAY,WAAW,SAAS,QAAQ,CAAC"}
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<TError = unknown, TData = unknown> implements RetryPolicy<\n TError,\n TData\n> {\n /**\n * Returns the retry decision for a failed attempt.\n *\n * @param input - Attempt context used to compute retry behavior.\n * @throws 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 Any error thrown by an overriding policy implementation.\n */\n public onExhausted(input: RetryExhaustedInput<TError, TData>): RetryError {\n return new RetryError(\"Retry policy exhausted all attempts.\", {\n attempts: input.attempts,\n lastError: input.error,\n lastData: input.data,\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 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 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 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 (not wrapped in `RetryError`).\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.signal;\n if (options.throwOnExhausted === false) {\n return runResultMode(this, execute, sleep, signal);\n }\n\n return runThrowMode(this, execute, sleep, signal);\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;AA0BA,IAAsB,kBAAtB,MAGE;;;;;;;;;;CAkBA,YAAmB,OAAuD;AACxE,SAAO,IAAI,WAAW,wCAAwC;GAC5D,UAAU,MAAM;GAChB,WAAW,MAAM;GACjB,UAAU,MAAM;GACjB,CAAC;;;;;;;;;;;;;;;;;;;;CAoDJ,MAAa,IACX,SACA,UAA2B,EAAE,EACG;EAChC,MAAM,QAAQ,QAAQ,SAAS;EAC/B,MAAM,SAAS,QAAQ;AACvB,MAAI,QAAQ,qBAAqB,MAC/B,QAAO,cAAc,MAAM,SAAS,OAAO,OAAO;AAGpD,SAAO,aAAa,MAAM,SAAS,OAAO,OAAO"}
@@ -0,0 +1,24 @@
1
+ import { r as RetryError } from "./errors-fWo_KyVO.mjs";
2
+ import { RetryDecision, RetryDecisionInput, RetryExhaustedInput, RetryRunResult } from "./types.mjs";
3
+
4
+ //#region src/result-mode.d.ts
5
+ /**
6
+ * Runs the non-throw retry loop, returning
7
+ * `RetryRunResult`.
8
+ *
9
+ * @param policy - Object providing `next` and `onExhausted` (same contract as
10
+ * `BaseRetryPolicy`).
11
+ * @param execute - Async work callback per attempt.
12
+ * @param sleep - Delay function between retries.
13
+ * @param signal - Optional cancel signal.
14
+ * @returns Terminal success or failure object.
15
+ * @throws Any error thrown by `next`, `onExhausted`, or a non-abort `sleep`
16
+ * failure.
17
+ */
18
+ declare function runResultMode<T, TError, TData>(policy: {
19
+ next: (input: RetryDecisionInput<TError, TData>) => RetryDecision;
20
+ onExhausted: (input: RetryExhaustedInput<TError, TData>) => RetryError;
21
+ }, execute: (attempt: number) => Promise<T>, sleep: (delayMs: number) => Promise<void>, signal?: AbortSignal): Promise<RetryRunResult<T>>;
22
+ //#endregion
23
+ export { runResultMode };
24
+ //# sourceMappingURL=result-mode.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"result-mode.d.mts","names":[],"sources":["../src/result-mode.ts"],"mappings":";;;;;;AA6BA;;;;;;;;;;;iBAAsB,aAAA,kBAAA,CACpB,MAAA;EACE,IAAA,GAAO,KAAA,EAAO,kBAAA,CAAmB,MAAA,EAAQ,KAAA,MAAW,aAAA;EACpD,WAAA,GAAc,KAAA,EAAO,mBAAA,CAAoB,MAAA,EAAQ,KAAA,MAAW,UAAA;AAAA,GAE9D,OAAA,GAAU,OAAA,aAAoB,OAAA,CAAQ,CAAA,GACtC,KAAA,GAAQ,OAAA,aAAoB,OAAA,QAC5B,MAAA,GAAS,WAAA,GACR,OAAA,CAAQ,cAAA,CAAe,CAAA"}
@@ -0,0 +1,145 @@
1
+ import { sleepWithAbortSignal, toAbortError } from "./abort.mjs";
2
+ //#region src/result-mode.ts
3
+ /**
4
+ * Result-mode execution path for `BaseRetryPolicy.run` when
5
+ * `throwOnExhausted: false` is set.
6
+ *
7
+ * @module @zap-studio/retry/result-mode
8
+ */
9
+ /**
10
+ * Runs the non-throw retry loop, returning
11
+ * `RetryRunResult`.
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 Terminal success or failure object.
19
+ * @throws Any error thrown by `next`, `onExhausted`, or a non-abort `sleep`
20
+ * failure.
21
+ */
22
+ async function runResultMode(policy, execute, sleep, signal) {
23
+ let attempt = 1;
24
+ while (true) {
25
+ const earlyAbortResult = abortResult(signal, Math.max(0, attempt - 1));
26
+ if (earlyAbortResult) return earlyAbortResult;
27
+ const execution = await runAttempt(execute, attempt);
28
+ if (execution.ok) return {
29
+ ok: true,
30
+ value: execution.value
31
+ };
32
+ const failure = await handleFailure(policy, {
33
+ attempt,
34
+ error: execution.error,
35
+ sleep,
36
+ signal
37
+ });
38
+ if (failure) return failure;
39
+ attempt += 1;
40
+ }
41
+ }
42
+ /**
43
+ * Runs one `execute(attempt)` call and returns either a success value or a
44
+ * captured error without rethrowing.
45
+ *
46
+ * @param execute - User work callback.
47
+ * @param attempt - One-based attempt number passed to `execute`.
48
+ * @returns A tagged success with `value` or a tagged failure with `error`.
49
+ */
50
+ async function runAttempt(execute, attempt) {
51
+ try {
52
+ return {
53
+ ok: true,
54
+ value: await execute(attempt)
55
+ };
56
+ } catch (error) {
57
+ return {
58
+ ok: false,
59
+ error
60
+ };
61
+ }
62
+ }
63
+ /**
64
+ * After a failed attempt, applies abort rules, `next`, optional delay, and
65
+ * either returns a terminal `RetryRunResult` or `undefined` to continue.
66
+ *
67
+ * @param policy - Retry policy hooks (`next`, `onExhausted`) matching
68
+ * `BaseRetryPolicy`.
69
+ * @param params - Failure context for the current attempt.
70
+ * @param params.attempt - Current attempt number.
71
+ * @param params.error - Error thrown by the attempt.
72
+ * @param params.sleep - Delay function between retries.
73
+ * @param params.signal - Optional abort signal.
74
+ * @returns Terminal non-throw result if the loop should stop, otherwise
75
+ * `undefined` to schedule another attempt.
76
+ * @throws Any error thrown by `next`, `onExhausted`, or a custom `sleep` when
77
+ * the error is not an abort.
78
+ */
79
+ async function handleFailure(policy, params) {
80
+ const { attempt, error, sleep, signal } = params;
81
+ const postExecuteAbortResult = abortResult(signal, attempt);
82
+ if (postExecuteAbortResult) return postExecuteAbortResult;
83
+ const decision = policy.next({
84
+ attempt,
85
+ error
86
+ });
87
+ if (!decision.shouldRetry) return {
88
+ ok: false,
89
+ error: policy.onExhausted({
90
+ attempts: attempt,
91
+ error
92
+ }),
93
+ attempts: attempt
94
+ };
95
+ if (decision.delayMs > 0) {
96
+ const sleepAbortResult = await waitForDelay(sleep, decision.delayMs, signal, attempt);
97
+ if (sleepAbortResult) return sleepAbortResult;
98
+ }
99
+ }
100
+ /**
101
+ * When `signal` is already aborted, builds the terminal `{ ok: false }` object
102
+ * with a normalized `AbortError` on `error` (not wrapped in `RetryError`).
103
+ *
104
+ * @param signal - Optional abort signal; only acts when `aborted` is set.
105
+ * @param attempts - Number of finished attempts to report in the result.
106
+ * @returns Failure result or `undefined` if not aborted.
107
+ */
108
+ function abortResult(signal, attempts) {
109
+ if (!signal?.aborted) return;
110
+ return {
111
+ ok: false,
112
+ error: toAbortError(signal.reason),
113
+ attempts
114
+ };
115
+ }
116
+ /**
117
+ * Awaits inter-attempt delay in result mode, mapping an abort during wait to
118
+ * a terminal result instead of throwing when `throwOnExhausted` is false.
119
+ *
120
+ * @param sleep - Custom or default sleep implementation.
121
+ * @param delayMs - Milliseconds to wait.
122
+ * @param signal - If set, `sleep` is raced with the abort signal.
123
+ * @param attempts - Attempt count to attach if the wait ends in abort.
124
+ * @returns A terminal result when canceled during the wait, otherwise
125
+ * `undefined`.
126
+ * @throws The underlying `sleep` rejection when it is not an abort.
127
+ */
128
+ async function waitForDelay(sleep, delayMs, signal, attempts) {
129
+ if (!signal) {
130
+ await sleep(delayMs);
131
+ return;
132
+ }
133
+ try {
134
+ await sleepWithAbortSignal(sleep, delayMs, signal);
135
+ return;
136
+ } catch (error) {
137
+ const aborted = abortResult(signal, attempts);
138
+ if (aborted) return aborted;
139
+ throw error;
140
+ }
141
+ }
142
+ //#endregion
143
+ export { runResultMode };
144
+
145
+ //# sourceMappingURL=result-mode.mjs.map
@@ -0,0 +1 @@
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 { RetryError } from \"./errors.js\";\nimport type {\n RetryDecision,\n RetryDecisionInput,\n RetryExhaustedInput,\n RetryRunResult,\n} from \"./types.js\";\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 Any error thrown by `next`, `onExhausted`, or a non-abort `sleep`\n * failure.\n */\nexport async function runResultMode<T, TError, TData>(\n policy: {\n next: (input: RetryDecisionInput<TError, TData>) => RetryDecision;\n onExhausted: (input: RetryExhaustedInput<TError, TData>) => RetryError;\n },\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 earlyAbortResult = abortResult(signal, Math.max(0, attempt - 1));\n if (earlyAbortResult) return earlyAbortResult;\n\n const execution = await runAttempt(execute, attempt);\n if (execution.ok) {\n return { ok: true, value: execution.value };\n }\n\n const failure = await handleFailure(policy, {\n attempt,\n error: execution.error as TError,\n sleep,\n signal,\n });\n if (failure) return failure;\n\n attempt += 1;\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 */\nasync function runAttempt<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 ok: false,\n error,\n };\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 Any error thrown by `next`, `onExhausted`, or a custom `sleep` when\n * the error is not an abort.\n */\nasync function handleFailure<TError, TData>(\n policy: {\n next: (input: RetryDecisionInput<TError, TData>) => RetryDecision;\n onExhausted: (input: RetryExhaustedInput<TError, TData>) => RetryError;\n },\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 postExecuteAbortResult = abortResult(signal, attempt);\n if (postExecuteAbortResult) return postExecuteAbortResult;\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 ok: false,\n error: terminalError,\n attempts: attempt,\n };\n }\n\n if (decision.delayMs > 0) {\n const sleepAbortResult = await waitForDelay(sleep, decision.delayMs, signal, attempt);\n if (sleepAbortResult) return sleepAbortResult;\n }\n\n return;\n}\n\n/**\n * When `signal` is already aborted, builds the terminal `{ ok: false }` object\n * with a normalized `AbortError` on `error` (not wrapped in `RetryError`).\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 */\nfunction abortResult(\n signal: AbortSignal | undefined,\n attempts: number,\n): RetryRunResult<never> | undefined {\n if (!signal?.aborted) {\n return;\n }\n\n return {\n ok: false,\n error: toAbortError(signal.reason),\n attempts,\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 The underlying `sleep` rejection when it is not an abort.\n */\nasync function waitForDelay(\n sleep: (delayMs: number) => Promise<void>,\n delayMs: number,\n signal: AbortSignal | undefined,\n attempts: number,\n): Promise<RetryRunResult<never> | undefined> {\n if (!signal) {\n await sleep(delayMs);\n return;\n }\n\n try {\n await sleepWithAbortSignal(sleep, delayMs, signal);\n return;\n } catch (error) {\n const aborted = abortResult(signal, attempts);\n if (aborted) {\n return aborted;\n }\n throw error;\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;AA6BA,eAAsB,cACpB,QAIA,SACA,OACA,QAC4B;CAC5B,IAAI,UAAU;AAEd,QAAO,MAAM;EACX,MAAM,mBAAmB,YAAY,QAAQ,KAAK,IAAI,GAAG,UAAU,EAAE,CAAC;AACtE,MAAI,iBAAkB,QAAO;EAE7B,MAAM,YAAY,MAAM,WAAW,SAAS,QAAQ;AACpD,MAAI,UAAU,GACZ,QAAO;GAAE,IAAI;GAAM,OAAO,UAAU;GAAO;EAG7C,MAAM,UAAU,MAAM,cAAc,QAAQ;GAC1C;GACA,OAAO,UAAU;GACjB;GACA;GACD,CAAC;AACF,MAAI,QAAS,QAAO;AAEpB,aAAW;;;;;;;;;;;AAYf,eAAe,WACb,SACA,SACiE;AACjE,KAAI;AACF,SAAO;GACL,IAAI;GACJ,OAAO,MAAM,QAAQ,QAAQ;GAC9B;UACM,OAAO;AACd,SAAO;GACL,IAAI;GACJ;GACD;;;;;;;;;;;;;;;;;;;AAoBL,eAAe,cACb,QAIA,QAM4C;CAC5C,MAAM,EAAE,SAAS,OAAO,OAAO,WAAW;CAC1C,MAAM,yBAAyB,YAAY,QAAQ,QAAQ;AAC3D,KAAI,uBAAwB,QAAO;CAEnC,MAAM,WAAW,OAAO,KAAK;EAC3B;EACA;EACD,CAAC;AAEF,KAAI,CAAC,SAAS,YAMZ,QAAO;EACL,IAAI;EACJ,OAPoB,OAAO,YAAY;GACvC,UAAU;GACV;GACD,CAAC;EAKA,UAAU;EACX;AAGH,KAAI,SAAS,UAAU,GAAG;EACxB,MAAM,mBAAmB,MAAM,aAAa,OAAO,SAAS,SAAS,QAAQ,QAAQ;AACrF,MAAI,iBAAkB,QAAO;;;;;;;;;;;AAcjC,SAAS,YACP,QACA,UACmC;AACnC,KAAI,CAAC,QAAQ,QACX;AAGF,QAAO;EACL,IAAI;EACJ,OAAO,aAAa,OAAO,OAAO;EAClC;EACD;;;;;;;;;;;;;;AAeH,eAAe,aACb,OACA,SACA,QACA,UAC4C;AAC5C,KAAI,CAAC,QAAQ;AACX,QAAM,MAAM,QAAQ;AACpB;;AAGF,KAAI;AACF,QAAM,qBAAqB,OAAO,SAAS,OAAO;AAClD;UACO,OAAO;EACd,MAAM,UAAU,YAAY,QAAQ,SAAS;AAC7C,MAAI,QACF,QAAO;AAET,QAAM"}
@@ -0,0 +1,17 @@
1
+ //#region src/sleep.d.ts
2
+ /**
3
+ * Default delay implementation used by `BaseRetryPolicy.run` when no custom
4
+ * `sleep` is provided.
5
+ *
6
+ * @module @zap-studio/retry/sleep
7
+ */
8
+ /**
9
+ * Awaits a timer-based delay, unless `delayMs` is non-positive.
10
+ *
11
+ * @param delayMs - Milliseconds to wait before resolving.
12
+ * @returns Promise that resolves when the delay completes.
13
+ */
14
+ declare function defaultSleep(delayMs: number): Promise<void>;
15
+ //#endregion
16
+ export { defaultSleep };
17
+ //# sourceMappingURL=sleep.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sleep.d.mts","names":[],"sources":["../src/sleep.ts"],"mappings":";;AAaA;;;;;;;;;;;iBAAsB,YAAA,CAAa,OAAA,WAAkB,OAAA"}
package/dist/sleep.mjs ADDED
@@ -0,0 +1,21 @@
1
+ //#region src/sleep.ts
2
+ /**
3
+ * Default delay implementation used by `BaseRetryPolicy.run` when no custom
4
+ * `sleep` is provided.
5
+ *
6
+ * @module @zap-studio/retry/sleep
7
+ */
8
+ /**
9
+ * Awaits a timer-based delay, unless `delayMs` is non-positive.
10
+ *
11
+ * @param delayMs - Milliseconds to wait before resolving.
12
+ * @returns Promise that resolves when the delay completes.
13
+ */
14
+ async function defaultSleep(delayMs) {
15
+ if (delayMs <= 0) return;
16
+ await new Promise((resolve) => setTimeout(resolve, delayMs));
17
+ }
18
+ //#endregion
19
+ export { defaultSleep };
20
+
21
+ //# sourceMappingURL=sleep.mjs.map
@@ -0,0 +1 @@
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 async function defaultSleep(delayMs: number): Promise<void> {\n if (delayMs <= 0) {\n return;\n }\n\n await new Promise((resolve) => setTimeout(resolve, delayMs));\n}\n"],"mappings":";;;;;;;;;;;;;AAaA,eAAsB,aAAa,SAAgC;AACjE,KAAI,WAAW,EACb;AAGF,OAAM,IAAI,SAAS,YAAY,WAAW,SAAS,QAAQ,CAAC"}
@@ -0,0 +1,26 @@
1
+ import { r as RetryError } from "./errors-fWo_KyVO.mjs";
2
+ import { RetryDecision, RetryDecisionInput, RetryExhaustedInput } from "./types.mjs";
3
+
4
+ //#region src/throw-mode.d.ts
5
+ /**
6
+ * Runs the throw-mode retry loop: throws `RetryError` on exhaustion and
7
+ * `AbortError` when `signal` aborts.
8
+ *
9
+ * @param policy - Object providing `next` and `onExhausted` (same contract as
10
+ * `BaseRetryPolicy`).
11
+ * @param execute - Async work callback per attempt.
12
+ * @param sleep - Delay function between retries.
13
+ * @param signal - Optional cancel signal.
14
+ * @returns Resolves to the first successful return value.
15
+ * @throws {RetryError} When retries are exhausted and `onExhausted` returns
16
+ * the terminal error.
17
+ * @throws {AbortError} When `signal` is already aborted or aborts while waiting.
18
+ * @throws Any error thrown by `next`, `onExhausted`, or `sleep`.
19
+ */
20
+ declare function runThrowMode<T, TError, TData>(policy: {
21
+ next: (input: RetryDecisionInput<TError, TData>) => RetryDecision;
22
+ onExhausted: (input: RetryExhaustedInput<TError, TData>) => RetryError;
23
+ }, execute: (attempt: number) => Promise<T>, sleep: (delayMs: number) => Promise<void>, signal?: AbortSignal): Promise<T>;
24
+ //#endregion
25
+ export { runThrowMode };
26
+ //# sourceMappingURL=throw-mode.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"throw-mode.d.mts","names":[],"sources":["../src/throw-mode.ts"],"mappings":";;;;;;AA0BA;;;;;;;;;;;;;iBAAsB,YAAA,kBAAA,CACpB,MAAA;EACE,IAAA,GAAO,KAAA,EAAO,kBAAA,CAAmB,MAAA,EAAQ,KAAA,MAAW,aAAA;EACpD,WAAA,GAAc,KAAA,EAAO,mBAAA,CAAoB,MAAA,EAAQ,KAAA,MAAW,UAAA;AAAA,GAE9D,OAAA,GAAU,OAAA,aAAoB,OAAA,CAAQ,CAAA,GACtC,KAAA,GAAQ,OAAA,aAAoB,OAAA,QAC5B,MAAA,GAAS,WAAA,GACR,OAAA,CAAQ,CAAA"}
@@ -0,0 +1,50 @@
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 Any error thrown by `next`, `onExhausted`, or `sleep`.
23
+ */
24
+ async function runThrowMode(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) if (signal) await sleepWithAbortSignal(sleep, decision.delayMs, signal);
42
+ else await sleep(decision.delayMs);
43
+ attempt += 1;
44
+ }
45
+ }
46
+ }
47
+ //#endregion
48
+ export { runThrowMode };
49
+
50
+ //# sourceMappingURL=throw-mode.mjs.map
@@ -0,0 +1 @@
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 { RetryError } from \"./errors.js\";\nimport type { RetryDecision, RetryDecisionInput, RetryExhaustedInput } 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 Any error thrown by `next`, `onExhausted`, or `sleep`.\n */\nexport async function runThrowMode<T, TError, TData>(\n policy: {\n next: (input: RetryDecisionInput<TError, TData>) => RetryDecision;\n onExhausted: (input: RetryExhaustedInput<TError, TData>) => RetryError;\n },\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 return await execute(attempt);\n } catch (error) {\n throwIfAborted(signal);\n\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 if (signal) {\n await sleepWithAbortSignal(sleep, decision.delayMs, signal);\n } else {\n await sleep(decision.delayMs);\n }\n }\n\n attempt += 1;\n }\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;AA0BA,eAAsB,aACpB,QAIA,SACA,OACA,QACY;CACZ,IAAI,UAAU;AAEd,QAAO,MAAM;AACX,iBAAe,OAAO;AAEtB,MAAI;AACF,UAAO,MAAM,QAAQ,QAAQ;WACtB,OAAO;AACd,kBAAe,OAAO;GAEtB,MAAM,aAAa;GACnB,MAAM,WAAW,OAAO,KAAK;IAC3B;IACA,OAAO;IACR,CAAC;AAEF,OAAI,CAAC,SAAS,YACZ,OAAM,OAAO,YAAY;IACvB,UAAU;IACV,OAAO;IACR,CAAC;AAGJ,OAAI,SAAS,UAAU,EACrB,KAAI,OACF,OAAM,qBAAqB,OAAO,SAAS,SAAS,OAAO;OAE3D,OAAM,MAAM,SAAS,QAAQ;AAIjC,cAAW"}
package/dist/types.d.mts CHANGED
@@ -1,4 +1,4 @@
1
- import { t as RetryError } from "./error-CWRADATN.mjs";
1
+ import { r as RetryError, t as AbortError } from "./errors-fWo_KyVO.mjs";
2
2
 
3
3
  //#region src/types.d.ts
4
4
  /**
@@ -28,25 +28,59 @@ interface RetryPolicy<TError = unknown, TData = unknown> {
28
28
  * Decision returned by a retry policy for a specific attempt.
29
29
  */
30
30
  interface RetryDecision {
31
+ /**
32
+ * When `true`, the runner may schedule another attempt (subject to
33
+ * `delayMs` and the runner's abort rules).
34
+ */
31
35
  readonly shouldRetry: boolean;
36
+ /**
37
+ * Milliseconds to wait before the next attempt when `shouldRetry` is `true`.
38
+ */
32
39
  readonly delayMs: number;
40
+ /**
41
+ * Optional machine-readable reason for the decision.
42
+ */
33
43
  readonly reason?: "retry" | "max-attempts-reached" | "policy-declined";
34
44
  }
35
45
  /**
36
46
  * Input passed to `RetryPolicy.next(...)` for each failed attempt.
37
47
  */
38
48
  interface RetryDecisionInput<TError = unknown, TData = unknown> {
49
+ /**
50
+ * One-based attempt number for the current failure.
51
+ */
39
52
  readonly attempt: number;
53
+ /**
54
+ * Optional policy-level maximum attempts, when a policy wants to pass it
55
+ * through to `next`.
56
+ */
40
57
  readonly maxAttempts?: number;
58
+ /**
59
+ * Error raised by the most recent `execute(attempt)` call, when a failure
60
+ * occurred.
61
+ */
41
62
  readonly error?: TError;
63
+ /**
64
+ * Optional data captured alongside the failure, when a policy populates
65
+ * it.
66
+ */
42
67
  readonly data?: TData;
43
68
  }
44
69
  /**
45
70
  * Input passed to `RetryPolicy.onExhausted(...)` when retries stop.
46
71
  */
47
72
  interface RetryExhaustedInput<TError = unknown, TData = unknown> {
73
+ /**
74
+ * Count of completed attempts that led to stopping retries.
75
+ */
48
76
  readonly attempts: number;
77
+ /**
78
+ * Last execution error, when available.
79
+ */
49
80
  readonly error?: TError;
81
+ /**
82
+ * Last captured data, when a policy or runner supplies it.
83
+ */
50
84
  readonly data?: TData;
51
85
  }
52
86
  /**
@@ -59,17 +93,51 @@ interface RetryRunOptions {
59
93
  * @throws Any error thrown or rejected by the custom delay implementation.
60
94
  */
61
95
  readonly sleep?: (delayMs: number) => Promise<void>;
96
+ /**
97
+ * Abort signal used to cancel retry orchestration.
98
+ *
99
+ * When aborted, the runner stops retrying and terminates immediately.
100
+ */
101
+ readonly signal?: AbortSignal;
102
+ /**
103
+ * When `true`, the runner throws a `RetryError` when retries are exhausted.
104
+ *
105
+ * When `false`, the runner returns a `RetryRunResult` discriminated union.
106
+ *
107
+ * @default true
108
+ */
62
109
  readonly throwOnExhausted?: boolean;
63
110
  }
64
111
  /**
65
112
  * Result union returned by non-throw runner mode.
113
+ *
114
+ * - Success: `ok: true` with the resolved `value`.
115
+ * - Failure: `ok: false` with terminal `error` and completed `attempts` count
116
+ * (exhaustion or abort).
66
117
  */
67
118
  type RetryRunResult<T> = {
119
+ /**
120
+ * Discriminator for a successful run.
121
+ */
68
122
  ok: true;
123
+ /**
124
+ * Successful return value from the final attempt.
125
+ */
69
126
  value: T;
70
127
  } | {
128
+ /**
129
+ * Discriminator for a failed or aborted run.
130
+ */
71
131
  ok: false;
72
- error: RetryError;
132
+ /**
133
+ * Terminal error: `RetryError` when retries are exhausted, or
134
+ * `AbortError` when the run is canceled (non-throw path uses the same
135
+ * instances as throw mode, not wrapped).
136
+ */
137
+ error: RetryError | AbortError;
138
+ /**
139
+ * Number of attempts that completed before the terminal outcome.
140
+ */
73
141
  attempts: number;
74
142
  };
75
143
  //#endregion
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.mts","names":[],"sources":["../src/types.ts"],"mappings":";;;;;;;;;;;;UAiBiB,WAAA;;;;;;EAMf,IAAA,CAAK,KAAA,EAAO,kBAAA,CAAmB,MAAA,EAAQ,KAAA,IAAS,aAAA;;;;;;EAMhD,WAAA,CAAY,KAAA,EAAO,mBAAA,CAAoB,MAAA,EAAQ,KAAA,IAAS,UAAA;AAAA;AAM1D;;;AAAA,UAAiB,aAAA;EAAA,SACN,WAAA;EAAA,SACA,OAAA;EAAA,SACA,MAAA;AAAA;;AAMX;;UAAiB,kBAAA;EAAA,SACN,OAAA;EAAA,SACA,WAAA;EAAA,SACA,KAAA,GAAQ,MAAA;EAAA,SACR,IAAA,GAAO,KAAA;AAAA;;;;UAMD,mBAAA;EAAA,SACN,QAAA;EAAA,SACA,KAAA,GAAQ,MAAA;EAAA,SACR,IAAA,GAAO,KAAA;AAAA;;;;UAMD,eAAA;EANC;AAMlB;;;;EANkB,SAYP,KAAA,IAAS,OAAA,aAAoB,OAAA;EAAA,SAC7B,gBAAA;AAAA;;;;KAMC,cAAA;EAEN,EAAA;EACA,KAAA,EAAO,CAAA;AAAA;EAGP,EAAA;EACA,KAAA,EAAO,UAAA;EACP,QAAA;AAAA"}
1
+ {"version":3,"file":"types.d.mts","names":[],"sources":["../src/types.ts"],"mappings":";;;;;;;;;;;;UAiBiB,WAAA;;;;;;EAMf,IAAA,CAAK,KAAA,EAAO,kBAAA,CAAmB,MAAA,EAAQ,KAAA,IAAS,aAAA;;;;;;EAMhD,WAAA,CAAY,KAAA,EAAO,mBAAA,CAAoB,MAAA,EAAQ,KAAA,IAAS,UAAA;AAAA;AAM1D;;;AAAA,UAAiB,aAAA;;;;;WAKN,WAAA;EAcX;;;EAAA,SAVW,OAAA;;;;WAIA,MAAA;AAAA;;;;UAMM,kBAAA;;;;WAIN,OAAA;;;;;WAKA,WAAA;;;AAkCX;;WA7BW,KAAA,GAAQ,MAAA;EAyCC;;;;EAAA,SApCT,IAAA,GAAO,KAAA;AAAA;;;;UAMD,mBAAA;;;;WAIN,QAAA;;;;WAIA,KAAA,GAAQ,MAAA;;;;WAIR,IAAA,GAAO,KAAA;AAAA;;;;UAMD,eAAA;;;;;;WAMN,KAAA,IAAS,OAAA,aAAoB,OAAA;;;;;;WAM7B,MAAA,GAAS,WAAA;;;;;;;;WAQT,gBAAA;AAAA;;;;;;;;KAUC,cAAA;;;;EAKN,EAAA;;;;EAIA,KAAA,EAAO,CAAA;AAAA;;;;EAMP,EAAA;;;;;;EAMA,KAAA,EAAO,UAAA,GAAa,UAAA;;;;EAIpB,QAAA;AAAA"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zap-studio/retry",
3
- "version": "0.1.2",
3
+ "version": "0.3.0",
4
4
  "private": false,
5
5
  "description": "Composable retry policies for resilient async operations.",
6
6
  "keywords": [
@@ -30,9 +30,13 @@
30
30
  "types": "./dist/index.d.mts",
31
31
  "exports": {
32
32
  ".": "./dist/index.mjs",
33
- "./error": "./dist/error.mjs",
33
+ "./abort": "./dist/abort.mjs",
34
+ "./errors": "./dist/errors.mjs",
34
35
  "./exponential-backoff": "./dist/exponential-backoff.mjs",
35
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",
36
40
  "./types": "./dist/types.mjs",
37
41
  "./package.json": "./package.json"
38
42
  },
@@ -40,6 +44,12 @@
40
44
  "access": "public"
41
45
  },
42
46
  "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",
43
53
  "typescript": "^6.0.3",
44
54
  "vite-plus": "^0.1.19",
45
55
  "@zap-studio/typescript": "0.0.0"
@@ -1,44 +0,0 @@
1
- //#region src/error.d.ts
2
- /**
3
- * Terminal error types used by retry policies and runners.
4
- *
5
- * @module
6
- */
7
- /**
8
- * Context payload attached to `RetryError`.
9
- */
10
- interface RetryErrorContext {
11
- readonly attempts: number;
12
- readonly lastError?: unknown;
13
- readonly lastData?: unknown;
14
- }
15
- /**
16
- * Error thrown when retries are exhausted.
17
- *
18
- * @example
19
- * throw new RetryError("Retry exhausted", {
20
- * attempts: 3,
21
- * lastError: new Error("network"),
22
- * });
23
- */
24
- declare class RetryError extends Error {
25
- /**
26
- * Total attempts performed before exhaustion.
27
- */
28
- readonly attempts: number;
29
- /**
30
- * Last captured error from execution.
31
- */
32
- readonly lastError?: unknown;
33
- /**
34
- * Last captured data value, when available.
35
- */
36
- readonly lastData?: unknown;
37
- /**
38
- * Creates a RetryError with structured terminal context.
39
- */
40
- constructor(message: string, context: RetryErrorContext);
41
- }
42
- //#endregion
43
- export { RetryErrorContext as n, RetryError as t };
44
- //# sourceMappingURL=error-CWRADATN.d.mts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"error-CWRADATN.d.mts","names":[],"sources":["../src/error.ts"],"mappings":";;AASA;;;;;;;UAAiB,iBAAA;EAAA,SACN,QAAA;EAAA,SACA,SAAA;EAAA,SACA,QAAA;AAAA;;;;;;;;;;cAYE,UAAA,SAAmB,KAAA;EAiBQ;;;EAAA,SAbtB,QAAA;;;;WAIA,SAAA;;;;WAIA,QAAA;;;;EAKhB,WAAA,CAAY,OAAA,UAAiB,OAAA,EAAS,iBAAA;AAAA"}
package/dist/error.d.mts DELETED
@@ -1,2 +0,0 @@
1
- import { n as RetryErrorContext, t as RetryError } from "./error-CWRADATN.mjs";
2
- export { RetryError, RetryErrorContext };
@@ -1 +0,0 @@
1
- {"version":3,"file":"error.mjs","names":[],"sources":["../src/error.ts"],"sourcesContent":["/**\n * Terminal error types used by retry policies and runners.\n *\n * @module\n */\n\n/**\n * Context payload attached to `RetryError`.\n */\nexport interface RetryErrorContext {\n readonly attempts: number;\n readonly lastError?: unknown;\n readonly lastData?: 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"],"mappings":";;;;;;;;;;AAwBA,IAAa,aAAb,cAAgC,MAAM;;;;CAIpC;;;;CAIA;;;;CAIA;;;;CAKA,YAAY,SAAiB,SAA4B;AACvD,QAAM,QAAQ;AACd,OAAK,OAAO;AACZ,OAAK,WAAW,QAAQ;AACxB,OAAK,YAAY,QAAQ;AACzB,OAAK,WAAW,QAAQ"}