@zap-studio/retry 0.1.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.
- package/CHANGELOG.md +18 -0
- package/LICENSE +21 -0
- package/README.md +162 -0
- package/dist/error-CWRADATN.d.mts +44 -0
- package/dist/error-CWRADATN.d.mts.map +1 -0
- package/dist/error.d.mts +2 -0
- package/dist/error.mjs +38 -0
- package/dist/error.mjs.map +1 -0
- package/dist/exponential-backoff.d.mts +38 -0
- package/dist/exponential-backoff.d.mts.map +1 -0
- package/dist/exponential-backoff.mjs +51 -0
- package/dist/exponential-backoff.mjs.map +1 -0
- package/dist/fixed-delay.d.mts +35 -0
- package/dist/fixed-delay.d.mts.map +1 -0
- package/dist/fixed-delay.mjs +47 -0
- package/dist/fixed-delay.mjs.map +1 -0
- package/dist/index.d.mts +44 -0
- package/dist/index.d.mts.map +1 -0
- package/dist/index.mjs +84 -0
- package/dist/index.mjs.map +1 -0
- package/dist/types.d.mts +77 -0
- package/dist/types.d.mts.map +1 -0
- package/dist/types.mjs +1 -0
- package/package.json +55 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# @zap-studio/retry
|
|
2
|
+
|
|
3
|
+
## 0.1.0
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
|
|
7
|
+
- Introduced a transport-agnostic `RetryPolicy` contract with `RetryDecisionInput` and `RetryDecision`.
|
|
8
|
+
- Added required `onExhausted` hook to `RetryPolicy` for policy-specific terminal error shaping.
|
|
9
|
+
- Added shared `BaseRetryPolicy` abstract class to centralize default `onExhausted` behavior.
|
|
10
|
+
- Added `BaseRetryPolicy.run(execute, options)` runner method to execute retry policies with minimal boilerplate.
|
|
11
|
+
- Added `throwOnExhausted` runner option with non-throw `RetryRunResult<T>` mode.
|
|
12
|
+
- Added `ExponentialBackoff` policy with bounded exponential delay via `baseDelayMs`, `maxDelayMs`, and `maxAttempts`.
|
|
13
|
+
- Added `FixedDelay` policy with constant delay and bounded attempts.
|
|
14
|
+
- Added `RetryError` for exhausted-retry failures with structured attempt/error/data context.
|
|
15
|
+
|
|
16
|
+
### Documentation
|
|
17
|
+
|
|
18
|
+
- Documented throwable behavior on `RetryPolicy`, `BaseRetryPolicy.run`, and related contracts with explicit `@throws` tags for policy, exhaustion, and custom `sleep` failures.
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Alexandre Trotel
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
# @zap-studio/retry
|
|
2
|
+
|
|
3
|
+
Composable retry policy primitives for HTTP clients and async workflows.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @zap-studio/retry
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Usage
|
|
12
|
+
|
|
13
|
+
```ts
|
|
14
|
+
import { ExponentialBackoff } from "@zap-studio/retry/exponential-backoff";
|
|
15
|
+
import { FixedDelay } from "@zap-studio/retry/fixed-delay";
|
|
16
|
+
import { $fetch } from "@zap-studio/fetch";
|
|
17
|
+
|
|
18
|
+
const exponential = new ExponentialBackoff({
|
|
19
|
+
maxAttempts: 5,
|
|
20
|
+
baseDelayMs: 100,
|
|
21
|
+
maxDelayMs: 2_000,
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
const data = await exponential.run(async () => {
|
|
25
|
+
const response = await $fetch("https://api.example.com/users", {
|
|
26
|
+
throwOnFetchError: true,
|
|
27
|
+
});
|
|
28
|
+
return await response.json();
|
|
29
|
+
});
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Handling Errors
|
|
33
|
+
|
|
34
|
+
`run(...)` throws when retries are exhausted.
|
|
35
|
+
|
|
36
|
+
By default, policies extending `BaseRetryPolicy` throw `RetryError`.
|
|
37
|
+
|
|
38
|
+
```ts
|
|
39
|
+
import { RetryError } from "@zap-studio/retry/error";
|
|
40
|
+
|
|
41
|
+
try {
|
|
42
|
+
const data = await exponential.run(async () => {
|
|
43
|
+
const response = await $fetch("https://api.example.com/users", {
|
|
44
|
+
throwOnFetchError: true,
|
|
45
|
+
});
|
|
46
|
+
return await response.json();
|
|
47
|
+
});
|
|
48
|
+
console.log(data);
|
|
49
|
+
} catch (error) {
|
|
50
|
+
if (error instanceof RetryError) {
|
|
51
|
+
console.error("Retries exhausted:", error.attempts);
|
|
52
|
+
console.error("Last error:", error.lastError);
|
|
53
|
+
} else {
|
|
54
|
+
throw error;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
To handle exhaustion without throwing, pass `throwOnExhausted: false`:
|
|
60
|
+
|
|
61
|
+
```ts
|
|
62
|
+
const result = await exponential.run(
|
|
63
|
+
async () => {
|
|
64
|
+
const response = await $fetch("https://api.example.com/users", {
|
|
65
|
+
throwOnFetchError: true,
|
|
66
|
+
});
|
|
67
|
+
return await response.json();
|
|
68
|
+
},
|
|
69
|
+
{ throwOnExhausted: false },
|
|
70
|
+
);
|
|
71
|
+
|
|
72
|
+
if (!result.ok) {
|
|
73
|
+
console.error("Retries exhausted:", result.attempts);
|
|
74
|
+
console.error("Last error:", result.error.lastError);
|
|
75
|
+
} else {
|
|
76
|
+
console.log(result.value);
|
|
77
|
+
}
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
## Choosing The Right Policy
|
|
81
|
+
|
|
82
|
+
Use `ExponentialBackoff` for transient network instability and shared upstream services.
|
|
83
|
+
|
|
84
|
+
```ts
|
|
85
|
+
const unstableNetworkPolicy = new ExponentialBackoff({
|
|
86
|
+
maxAttempts: 6,
|
|
87
|
+
baseDelayMs: 100,
|
|
88
|
+
maxDelayMs: 2_000,
|
|
89
|
+
});
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
Use `FixedDelay` for stable, predictable retry intervals in controlled environments.
|
|
93
|
+
|
|
94
|
+
```ts
|
|
95
|
+
const predictableIntervalPolicy = new FixedDelay({
|
|
96
|
+
maxAttempts: 4,
|
|
97
|
+
delayMs: 300,
|
|
98
|
+
});
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
## Custom Policies
|
|
102
|
+
|
|
103
|
+
Extend `BaseRetryPolicy` when the built-in policies do not match your retry rules.
|
|
104
|
+
|
|
105
|
+
You implement `next(...)` only; the base class supplies `onExhausted` with a default
|
|
106
|
+
`RetryError` and keeps the shared `run(...)` orchestration (override `onExhausted` when
|
|
107
|
+
you need a different terminal error).
|
|
108
|
+
|
|
109
|
+
```ts
|
|
110
|
+
import { BaseRetryPolicy } from "@zap-studio/retry";
|
|
111
|
+
import type { RetryDecision, RetryDecisionInput } from "@zap-studio/retry/types";
|
|
112
|
+
|
|
113
|
+
class LinearBackoff extends BaseRetryPolicy {
|
|
114
|
+
constructor(
|
|
115
|
+
private readonly maxAttempts: number,
|
|
116
|
+
private readonly stepMs: number,
|
|
117
|
+
) {
|
|
118
|
+
super();
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
public next(input: RetryDecisionInput): RetryDecision {
|
|
122
|
+
if (input.attempt >= this.maxAttempts) {
|
|
123
|
+
return {
|
|
124
|
+
shouldRetry: false,
|
|
125
|
+
delayMs: 0,
|
|
126
|
+
reason: "max-attempts-reached",
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
return {
|
|
131
|
+
shouldRetry: true,
|
|
132
|
+
delayMs: input.attempt * this.stepMs,
|
|
133
|
+
reason: "retry",
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
const policy = new LinearBackoff(5, 250);
|
|
139
|
+
const value = await policy.run(doWork);
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
## RetryError
|
|
143
|
+
|
|
144
|
+
Use `RetryError` when an orchestrator exhausts retries and needs to surface final context.
|
|
145
|
+
|
|
146
|
+
```ts
|
|
147
|
+
import { RetryError } from "@zap-studio/retry/error";
|
|
148
|
+
|
|
149
|
+
throw new RetryError("Retry policy exhausted all attempts.", {
|
|
150
|
+
attempts: attempt,
|
|
151
|
+
lastError: error,
|
|
152
|
+
lastData: data,
|
|
153
|
+
});
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
Policies implement `onExhausted(input)` to return the terminal error used by the built-in runner.
|
|
157
|
+
|
|
158
|
+
`ExponentialBackoff` and `FixedDelay` inherit the default implementation from `BaseRetryPolicy`.
|
|
159
|
+
|
|
160
|
+
## License
|
|
161
|
+
|
|
162
|
+
MIT
|
|
@@ -0,0 +1,44 @@
|
|
|
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
|
|
@@ -0,0 +1 @@
|
|
|
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
ADDED
package/dist/error.mjs
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
//#region src/error.ts
|
|
2
|
+
/**
|
|
3
|
+
* Error thrown when retries are exhausted.
|
|
4
|
+
*
|
|
5
|
+
* @example
|
|
6
|
+
* throw new RetryError("Retry exhausted", {
|
|
7
|
+
* attempts: 3,
|
|
8
|
+
* lastError: new Error("network"),
|
|
9
|
+
* });
|
|
10
|
+
*/
|
|
11
|
+
var RetryError = class extends Error {
|
|
12
|
+
/**
|
|
13
|
+
* Total attempts performed before exhaustion.
|
|
14
|
+
*/
|
|
15
|
+
attempts;
|
|
16
|
+
/**
|
|
17
|
+
* Last captured error from execution.
|
|
18
|
+
*/
|
|
19
|
+
lastError;
|
|
20
|
+
/**
|
|
21
|
+
* Last captured data value, when available.
|
|
22
|
+
*/
|
|
23
|
+
lastData;
|
|
24
|
+
/**
|
|
25
|
+
* Creates a RetryError with structured terminal context.
|
|
26
|
+
*/
|
|
27
|
+
constructor(message, context) {
|
|
28
|
+
super(message);
|
|
29
|
+
this.name = "RetryError";
|
|
30
|
+
this.attempts = context.attempts;
|
|
31
|
+
this.lastError = context.lastError;
|
|
32
|
+
this.lastData = context.lastData;
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
//#endregion
|
|
36
|
+
export { RetryError };
|
|
37
|
+
|
|
38
|
+
//# sourceMappingURL=error.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
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"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { RetryDecision, RetryDecisionInput } from "./types.mjs";
|
|
2
|
+
import { BaseRetryPolicy } from "./index.mjs";
|
|
3
|
+
|
|
4
|
+
//#region src/exponential-backoff.d.ts
|
|
5
|
+
/**
|
|
6
|
+
* Configuration for `ExponentialBackoff`.
|
|
7
|
+
*/
|
|
8
|
+
interface ExponentialBackoffOptions {
|
|
9
|
+
maxAttempts: number;
|
|
10
|
+
baseDelayMs: number;
|
|
11
|
+
maxDelayMs: number;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Retries with exponential delay growth up to a max cap.
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* const policy = new ExponentialBackoff({
|
|
18
|
+
* maxAttempts: 5,
|
|
19
|
+
* baseDelayMs: 100,
|
|
20
|
+
* maxDelayMs: 2_000,
|
|
21
|
+
* });
|
|
22
|
+
*/
|
|
23
|
+
declare class ExponentialBackoff extends BaseRetryPolicy {
|
|
24
|
+
private readonly maxAttempts;
|
|
25
|
+
private readonly baseDelayMs;
|
|
26
|
+
private readonly maxDelayMs;
|
|
27
|
+
/**
|
|
28
|
+
* Creates an exponential backoff retry policy.
|
|
29
|
+
*/
|
|
30
|
+
constructor(options: ExponentialBackoffOptions);
|
|
31
|
+
/**
|
|
32
|
+
* Computes retry decision for the current attempt.
|
|
33
|
+
*/
|
|
34
|
+
next(input: RetryDecisionInput): RetryDecision;
|
|
35
|
+
}
|
|
36
|
+
//#endregion
|
|
37
|
+
export { ExponentialBackoff, ExponentialBackoffOptions };
|
|
38
|
+
//# sourceMappingURL=exponential-backoff.d.mts.map
|
|
@@ -0,0 +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"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { BaseRetryPolicy } from "./index.mjs";
|
|
2
|
+
//#region src/exponential-backoff.ts
|
|
3
|
+
/**
|
|
4
|
+
* Exponential backoff retry strategy.
|
|
5
|
+
*
|
|
6
|
+
* @module
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* Retries with exponential delay growth up to a max cap.
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* const policy = new ExponentialBackoff({
|
|
13
|
+
* maxAttempts: 5,
|
|
14
|
+
* baseDelayMs: 100,
|
|
15
|
+
* maxDelayMs: 2_000,
|
|
16
|
+
* });
|
|
17
|
+
*/
|
|
18
|
+
var ExponentialBackoff = class extends BaseRetryPolicy {
|
|
19
|
+
maxAttempts;
|
|
20
|
+
baseDelayMs;
|
|
21
|
+
maxDelayMs;
|
|
22
|
+
/**
|
|
23
|
+
* Creates an exponential backoff retry policy.
|
|
24
|
+
*/
|
|
25
|
+
constructor(options) {
|
|
26
|
+
super();
|
|
27
|
+
this.maxAttempts = options.maxAttempts;
|
|
28
|
+
this.baseDelayMs = options.baseDelayMs;
|
|
29
|
+
this.maxDelayMs = options.maxDelayMs;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Computes retry decision for the current attempt.
|
|
33
|
+
*/
|
|
34
|
+
next(input) {
|
|
35
|
+
if (input.attempt >= this.maxAttempts) return {
|
|
36
|
+
shouldRetry: false,
|
|
37
|
+
delayMs: 0,
|
|
38
|
+
reason: "max-attempts-reached"
|
|
39
|
+
};
|
|
40
|
+
const exponent = Math.max(0, input.attempt - 1);
|
|
41
|
+
return {
|
|
42
|
+
shouldRetry: true,
|
|
43
|
+
delayMs: Math.min(this.maxDelayMs, this.baseDelayMs * 2 ** exponent),
|
|
44
|
+
reason: "retry"
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
//#endregion
|
|
49
|
+
export { ExponentialBackoff };
|
|
50
|
+
|
|
51
|
+
//# sourceMappingURL=exponential-backoff.mjs.map
|
|
@@ -0,0 +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"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { RetryDecision, RetryDecisionInput } from "./types.mjs";
|
|
2
|
+
import { BaseRetryPolicy } from "./index.mjs";
|
|
3
|
+
|
|
4
|
+
//#region src/fixed-delay.d.ts
|
|
5
|
+
/**
|
|
6
|
+
* Configuration for `FixedDelay`.
|
|
7
|
+
*/
|
|
8
|
+
interface FixedDelayOptions {
|
|
9
|
+
maxAttempts: number;
|
|
10
|
+
delayMs: number;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Retries with a constant delay between attempts.
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* const policy = new FixedDelay({
|
|
17
|
+
* maxAttempts: 3,
|
|
18
|
+
* delayMs: 250,
|
|
19
|
+
* });
|
|
20
|
+
*/
|
|
21
|
+
declare class FixedDelay extends BaseRetryPolicy {
|
|
22
|
+
private readonly maxAttempts;
|
|
23
|
+
private readonly delayMs;
|
|
24
|
+
/**
|
|
25
|
+
* Creates a fixed-delay retry policy.
|
|
26
|
+
*/
|
|
27
|
+
constructor(options: FixedDelayOptions);
|
|
28
|
+
/**
|
|
29
|
+
* Computes retry decision for the current attempt.
|
|
30
|
+
*/
|
|
31
|
+
next(input: RetryDecisionInput): RetryDecision;
|
|
32
|
+
}
|
|
33
|
+
//#endregion
|
|
34
|
+
export { FixedDelay, FixedDelayOptions };
|
|
35
|
+
//# sourceMappingURL=fixed-delay.d.mts.map
|
|
@@ -0,0 +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"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { BaseRetryPolicy } from "./index.mjs";
|
|
2
|
+
//#region src/fixed-delay.ts
|
|
3
|
+
/**
|
|
4
|
+
* Fixed-delay retry strategy.
|
|
5
|
+
*
|
|
6
|
+
* @module
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* Retries with a constant delay between attempts.
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* const policy = new FixedDelay({
|
|
13
|
+
* maxAttempts: 3,
|
|
14
|
+
* delayMs: 250,
|
|
15
|
+
* });
|
|
16
|
+
*/
|
|
17
|
+
var FixedDelay = class extends BaseRetryPolicy {
|
|
18
|
+
maxAttempts;
|
|
19
|
+
delayMs;
|
|
20
|
+
/**
|
|
21
|
+
* Creates a fixed-delay retry policy.
|
|
22
|
+
*/
|
|
23
|
+
constructor(options) {
|
|
24
|
+
super();
|
|
25
|
+
this.maxAttempts = options.maxAttempts;
|
|
26
|
+
this.delayMs = options.delayMs;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Computes retry decision for the current attempt.
|
|
30
|
+
*/
|
|
31
|
+
next(input) {
|
|
32
|
+
if (input.attempt >= this.maxAttempts) return {
|
|
33
|
+
shouldRetry: false,
|
|
34
|
+
delayMs: 0,
|
|
35
|
+
reason: "max-attempts-reached"
|
|
36
|
+
};
|
|
37
|
+
return {
|
|
38
|
+
shouldRetry: true,
|
|
39
|
+
delayMs: this.delayMs,
|
|
40
|
+
reason: "retry"
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
//#endregion
|
|
45
|
+
export { FixedDelay };
|
|
46
|
+
|
|
47
|
+
//# sourceMappingURL=fixed-delay.mjs.map
|
|
@@ -0,0 +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"}
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { t as RetryError } from "./error-CWRADATN.mjs";
|
|
2
|
+
import { RetryDecision, RetryDecisionInput, RetryExhaustedInput, RetryPolicy, RetryRunOptions, RetryRunResult } from "./types.mjs";
|
|
3
|
+
|
|
4
|
+
//#region src/index.d.ts
|
|
5
|
+
declare abstract class BaseRetryPolicy<TError = unknown, TData = unknown> implements RetryPolicy<TError, TData> {
|
|
6
|
+
/**
|
|
7
|
+
* Returns the retry decision for a failed attempt.
|
|
8
|
+
*
|
|
9
|
+
* @param input - Attempt context used to compute retry behavior.
|
|
10
|
+
* @throws Any error thrown by a concrete retry policy implementation.
|
|
11
|
+
*/
|
|
12
|
+
abstract next(input: RetryDecisionInput<TError, TData>): RetryDecision;
|
|
13
|
+
/**
|
|
14
|
+
* Builds the terminal error thrown or returned when retries are exhausted.
|
|
15
|
+
*
|
|
16
|
+
* Override this when you need custom terminal error types.
|
|
17
|
+
*
|
|
18
|
+
* @param input - Exhaustion context.
|
|
19
|
+
* @returns `RetryError` by default.
|
|
20
|
+
* @throws Any error thrown by an overriding policy implementation.
|
|
21
|
+
*/
|
|
22
|
+
onExhausted(input: RetryExhaustedInput<TError, TData>): RetryError;
|
|
23
|
+
run<T>(execute: (attempt: number) => Promise<T>, options: RetryRunOptions & {
|
|
24
|
+
throwOnExhausted: false;
|
|
25
|
+
}): Promise<RetryRunResult<T>>;
|
|
26
|
+
/**
|
|
27
|
+
* Runs retry orchestration and throws terminal error on exhaustion.
|
|
28
|
+
*
|
|
29
|
+
* @param execute - Async function to execute per attempt.
|
|
30
|
+
* @param options - Optional runner settings.
|
|
31
|
+
* @returns The successful execution value.
|
|
32
|
+
* @throws {RetryError} When retries are exhausted and `onExhausted` returns the
|
|
33
|
+
* terminal retry error. The default implementation returns `RetryError` with the last
|
|
34
|
+
* execution failure available on `RetryError.lastError`.
|
|
35
|
+
* @throws Any error thrown by `next`, by `onExhausted`, or by a custom `sleep`
|
|
36
|
+
* function.
|
|
37
|
+
*/
|
|
38
|
+
run<T>(execute: (attempt: number) => Promise<T>, options?: RetryRunOptions & {
|
|
39
|
+
throwOnExhausted?: true | undefined;
|
|
40
|
+
}): Promise<T>;
|
|
41
|
+
}
|
|
42
|
+
//#endregion
|
|
43
|
+
export { BaseRetryPolicy };
|
|
44
|
+
//# sourceMappingURL=index.d.mts.map
|
|
@@ -0,0 +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"}
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { RetryError } from "./error.mjs";
|
|
2
|
+
//#region src/index.ts
|
|
3
|
+
/**
|
|
4
|
+
* Retry runner base class and shared orchestration implementation.
|
|
5
|
+
*
|
|
6
|
+
* @module
|
|
7
|
+
*/
|
|
8
|
+
var BaseRetryPolicy = class {
|
|
9
|
+
/**
|
|
10
|
+
* Builds the terminal error thrown or returned when retries are exhausted.
|
|
11
|
+
*
|
|
12
|
+
* Override this when you need custom terminal error types.
|
|
13
|
+
*
|
|
14
|
+
* @param input - Exhaustion context.
|
|
15
|
+
* @returns `RetryError` by default.
|
|
16
|
+
* @throws Any error thrown by an overriding policy implementation.
|
|
17
|
+
*/
|
|
18
|
+
onExhausted(input) {
|
|
19
|
+
return new RetryError("Retry policy exhausted all attempts.", {
|
|
20
|
+
attempts: input.attempts,
|
|
21
|
+
lastError: input.error,
|
|
22
|
+
lastData: input.data
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Runs retry orchestration in non-throw mode.
|
|
27
|
+
*
|
|
28
|
+
* When `throwOnExhausted` is `false`, returns a discriminated result union.
|
|
29
|
+
*
|
|
30
|
+
* @param execute - Async function to execute per attempt.
|
|
31
|
+
* @param options - Runner settings.
|
|
32
|
+
* @returns Success value or terminal result object based on option mode.
|
|
33
|
+
* @throws Any error thrown by `next`, by `onExhausted`, or by a custom `sleep`
|
|
34
|
+
* function. When `throwOnExhausted` is `false`, exhaustion itself is returned
|
|
35
|
+
* as `{ ok: false }` instead of thrown.
|
|
36
|
+
*
|
|
37
|
+
* @example
|
|
38
|
+
* const result = await policy.run(doWork, { throwOnExhausted: false });
|
|
39
|
+
* if (!result.ok) console.error(result.error);
|
|
40
|
+
*/
|
|
41
|
+
async run(execute, options = {}) {
|
|
42
|
+
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
|
+
}
|
|
73
|
+
}
|
|
74
|
+
};
|
|
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
|
+
//#endregion
|
|
82
|
+
export { BaseRetryPolicy };
|
|
83
|
+
|
|
84
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +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 | undefined },\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"}
|
package/dist/types.d.mts
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { t as RetryError } from "./error-CWRADATN.mjs";
|
|
2
|
+
|
|
3
|
+
//#region src/types.d.ts
|
|
4
|
+
/**
|
|
5
|
+
* Retry policy contract used by `BaseRetryPolicy`.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* const policy: RetryPolicy = {
|
|
9
|
+
* next: ({ attempt }) => ({ shouldRetry: attempt < 3, delayMs: 100 }),
|
|
10
|
+
* onExhausted: ({ attempts }) => new RetryError("done", { attempts }),
|
|
11
|
+
* };
|
|
12
|
+
*/
|
|
13
|
+
interface RetryPolicy<TError = unknown, TData = unknown> {
|
|
14
|
+
/**
|
|
15
|
+
* Returns the retry decision for a failed attempt.
|
|
16
|
+
*
|
|
17
|
+
* @throws Any error thrown by the policy implementation.
|
|
18
|
+
*/
|
|
19
|
+
next(input: RetryDecisionInput<TError, TData>): RetryDecision;
|
|
20
|
+
/**
|
|
21
|
+
* Builds the terminal error used when retries are exhausted.
|
|
22
|
+
*
|
|
23
|
+
* @throws Any error thrown by the policy implementation.
|
|
24
|
+
*/
|
|
25
|
+
onExhausted(input: RetryExhaustedInput<TError, TData>): RetryError;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Decision returned by a retry policy for a specific attempt.
|
|
29
|
+
*/
|
|
30
|
+
interface RetryDecision {
|
|
31
|
+
readonly shouldRetry: boolean;
|
|
32
|
+
readonly delayMs: number;
|
|
33
|
+
readonly reason?: "retry" | "max-attempts-reached" | "policy-declined";
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Input passed to `RetryPolicy.next(...)` for each failed attempt.
|
|
37
|
+
*/
|
|
38
|
+
interface RetryDecisionInput<TError = unknown, TData = unknown> {
|
|
39
|
+
readonly attempt: number;
|
|
40
|
+
readonly maxAttempts?: number | undefined;
|
|
41
|
+
readonly error?: TError;
|
|
42
|
+
readonly data?: TData;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Input passed to `RetryPolicy.onExhausted(...)` when retries stop.
|
|
46
|
+
*/
|
|
47
|
+
interface RetryExhaustedInput<TError = unknown, TData = unknown> {
|
|
48
|
+
readonly attempts: number;
|
|
49
|
+
readonly error?: TError;
|
|
50
|
+
readonly data?: TData;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Options for `BaseRetryPolicy.run(...)`.
|
|
54
|
+
*/
|
|
55
|
+
interface RetryRunOptions {
|
|
56
|
+
/**
|
|
57
|
+
* Delay function used between retry attempts.
|
|
58
|
+
*
|
|
59
|
+
* @throws Any error thrown or rejected by the custom delay implementation.
|
|
60
|
+
*/
|
|
61
|
+
readonly sleep?: ((delayMs: number) => Promise<void>) | undefined;
|
|
62
|
+
readonly throwOnExhausted?: boolean | undefined;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Result union returned by non-throw runner mode.
|
|
66
|
+
*/
|
|
67
|
+
type RetryRunResult<T> = {
|
|
68
|
+
ok: true;
|
|
69
|
+
value: T;
|
|
70
|
+
} | {
|
|
71
|
+
ok: false;
|
|
72
|
+
error: RetryError;
|
|
73
|
+
attempts: number;
|
|
74
|
+
};
|
|
75
|
+
//#endregion
|
|
76
|
+
export { RetryDecision, RetryDecisionInput, RetryExhaustedInput, RetryPolicy, RetryRunOptions, RetryRunResult };
|
|
77
|
+
//# sourceMappingURL=types.d.mts.map
|
|
@@ -0,0 +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,KAAU,OAAA,aAAoB,OAAA;EAAA,SAC9B,gBAAA;AAAA;;;;KAMC,cAAA;EAEN,EAAA;EACA,KAAA,EAAO,CAAA;AAAA;EAGP,EAAA;EACA,KAAA,EAAO,UAAA;EACP,QAAA;AAAA"}
|
package/dist/types.mjs
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/package.json
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@zap-studio/retry",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"private": false,
|
|
5
|
+
"description": "Composable retry policies for resilient async operations.",
|
|
6
|
+
"keywords": [
|
|
7
|
+
"backoff",
|
|
8
|
+
"exponential backoff",
|
|
9
|
+
"fetch",
|
|
10
|
+
"http",
|
|
11
|
+
"resilience",
|
|
12
|
+
"retry",
|
|
13
|
+
"typescript"
|
|
14
|
+
],
|
|
15
|
+
"homepage": "https://www.zapstudio.dev/packages/retry",
|
|
16
|
+
"license": "MIT",
|
|
17
|
+
"repository": {
|
|
18
|
+
"type": "git",
|
|
19
|
+
"url": "git+https://github.com/zap-studio/monorepo.git",
|
|
20
|
+
"directory": "packages/retry"
|
|
21
|
+
},
|
|
22
|
+
"files": [
|
|
23
|
+
"dist",
|
|
24
|
+
"CHANGELOG.md",
|
|
25
|
+
"LICENSE",
|
|
26
|
+
"README.md"
|
|
27
|
+
],
|
|
28
|
+
"type": "module",
|
|
29
|
+
"sideEffects": false,
|
|
30
|
+
"types": "./dist/index.d.mts",
|
|
31
|
+
"exports": {
|
|
32
|
+
".": "./dist/index.mjs",
|
|
33
|
+
"./error": "./dist/error.mjs",
|
|
34
|
+
"./exponential-backoff": "./dist/exponential-backoff.mjs",
|
|
35
|
+
"./fixed-delay": "./dist/fixed-delay.mjs",
|
|
36
|
+
"./types": "./dist/types.mjs",
|
|
37
|
+
"./package.json": "./package.json"
|
|
38
|
+
},
|
|
39
|
+
"publishConfig": {
|
|
40
|
+
"access": "public"
|
|
41
|
+
},
|
|
42
|
+
"devDependencies": {
|
|
43
|
+
"typescript": "^6.0.3",
|
|
44
|
+
"vite-plus": "^0.1.19",
|
|
45
|
+
"@zap-studio/typescript": "0.0.0"
|
|
46
|
+
},
|
|
47
|
+
"engines": {
|
|
48
|
+
"node": ">=18.0.0"
|
|
49
|
+
},
|
|
50
|
+
"scripts": {
|
|
51
|
+
"build": "vp pack",
|
|
52
|
+
"test": "vp test run",
|
|
53
|
+
"test:watch": "vp test watch"
|
|
54
|
+
}
|
|
55
|
+
}
|