@zap-studio/retry 1.0.0 → 1.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 +8 -0
- package/README.md +14 -0
- package/dist/exponential-backoff.d.ts +7 -0
- package/dist/exponential-backoff.d.ts.map +1 -1
- package/dist/exponential-backoff.js +9 -2
- package/dist/exponential-backoff.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +2 -1
- package/dist/jitter.d.ts +48 -0
- package/dist/jitter.d.ts.map +1 -0
- package/dist/jitter.js +24 -0
- package/dist/jitter.js.map +1 -0
- package/dist/linear-backoff.d.ts +7 -0
- package/dist/linear-backoff.d.ts.map +1 -1
- package/dist/linear-backoff.js +9 -2
- package/dist/linear-backoff.js.map +1 -1
- package/package.json +2 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,14 @@ All notable changes to this project will be documented in this file.
|
|
|
4
4
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
6
6
|
|
|
7
|
+
## [1.1.0]
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
`exponentialBackoff(...)` and `linearBackoff(...)` gain an optional `jitter?: "full" | "equal" | JitterOptions` option, applied to the computed delay after it's capped at `maxDelayMs`. `"full"` randomizes across `[0, delayMs]`; `"equal"` keeps a floor at half the delay, randomizing across `[delayMs/2, delayMs]`. Pass `{ mode, random }` to override the random source, useful for deterministic tests.
|
|
12
|
+
|
|
13
|
+
New export: `applyJitter(delayMs, jitter?)`, also available from the `./jitter` subpath. See [Jitter](https://www.zapstudio.dev/retry/jitter).
|
|
14
|
+
|
|
7
15
|
## [1.0.0]
|
|
8
16
|
|
|
9
17
|
### Added
|
package/README.md
CHANGED
|
@@ -13,6 +13,7 @@ npm install @zap-studio/retry
|
|
|
13
13
|
## Features
|
|
14
14
|
|
|
15
15
|
- **Built-in policies**: `fixedDelay(...)`, `linearBackoff(...)`, and `exponentialBackoff(...)`.
|
|
16
|
+
- **Jitter**: `"full"` or `"equal"` jitter on `exponentialBackoff`/`linearBackoff`, to avoid synchronized retries against a shared upstream.
|
|
16
17
|
- **A shared runner** via `runRetryPolicy(policy, execute, options?)` with attempt-aware callbacks and custom sleep injection.
|
|
17
18
|
- **Structured terminal errors**: `RetryError` on exhaustion, `AbortError` on cancellation.
|
|
18
19
|
- **Non-throw mode** (`throwOnExhausted: false`) returns a `RetryRunResult` instead of throwing.
|
|
@@ -65,6 +66,19 @@ const linear = linearBackoff({
|
|
|
65
66
|
const fixed = fixedDelay({ maxAttempts: 4, delayMs: 300 });
|
|
66
67
|
```
|
|
67
68
|
|
|
69
|
+
## Jitter
|
|
70
|
+
|
|
71
|
+
`"full"` or `"equal"` jitter on `exponentialBackoff`/`linearBackoff`, applied to the delay after it's capped, to avoid synchronized retries against a shared upstream.
|
|
72
|
+
|
|
73
|
+
```ts
|
|
74
|
+
const policy = exponentialBackoff({
|
|
75
|
+
maxAttempts: 5,
|
|
76
|
+
baseDelayMs: 100,
|
|
77
|
+
maxDelayMs: 2_000,
|
|
78
|
+
jitter: "full",
|
|
79
|
+
});
|
|
80
|
+
```
|
|
81
|
+
|
|
68
82
|
## Shared Runner
|
|
69
83
|
|
|
70
84
|
Via `runRetryPolicy(policy, execute, options?)` with attempt-aware callbacks and custom sleep injection.
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { RetryPolicy } from "./types.js";
|
|
2
|
+
import { JitterMode, JitterOptions } from "./jitter.js";
|
|
2
3
|
//#region src/exponential-backoff.d.ts
|
|
3
4
|
/**
|
|
4
5
|
* Configuration for `exponentialBackoff(...)`.
|
|
@@ -8,6 +9,7 @@ import { RetryPolicy } from "./types.js";
|
|
|
8
9
|
* maxAttempts: 5,
|
|
9
10
|
* baseDelayMs: 100,
|
|
10
11
|
* maxDelayMs: 2_000,
|
|
12
|
+
* jitter: "full",
|
|
11
13
|
* };
|
|
12
14
|
*/
|
|
13
15
|
interface ExponentialBackoffOptions {
|
|
@@ -23,6 +25,11 @@ interface ExponentialBackoffOptions {
|
|
|
23
25
|
* Hard upper bound in milliseconds for computed exponential delay.
|
|
24
26
|
*/
|
|
25
27
|
maxDelayMs: number;
|
|
28
|
+
/**
|
|
29
|
+
* Optional jitter applied to the computed delay, after capping at
|
|
30
|
+
* `maxDelayMs`.
|
|
31
|
+
*/
|
|
32
|
+
jitter?: JitterMode | JitterOptions;
|
|
26
33
|
}
|
|
27
34
|
/**
|
|
28
35
|
* Creates a retry policy with exponential delay growth up to a max cap.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"exponential-backoff.d.ts","names":[],"sources":["../src/exponential-backoff.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"exponential-backoff.d.ts","names":[],"sources":["../src/exponential-backoff.ts"],"mappings":";;;;;;;;;;;;;;UAyBiB;;;;EAIf;;;;EAIA;;;;EAIA;;;;;EAKA,SAAS,aAAa;;;;;;;;;;;;cAaX,qBACX,SAAS,8BACR"}
|
|
@@ -1,5 +1,11 @@
|
|
|
1
|
+
import { applyJitter } from "./jitter.js";
|
|
1
2
|
//#region src/exponential-backoff.ts
|
|
2
3
|
/**
|
|
4
|
+
* Exponential backoff retry strategy.
|
|
5
|
+
*
|
|
6
|
+
* @module @zap-studio/retry/exponential-backoff
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
3
9
|
* Creates a retry policy with exponential delay growth up to a max cap.
|
|
4
10
|
*
|
|
5
11
|
* @example
|
|
@@ -10,7 +16,7 @@
|
|
|
10
16
|
* });
|
|
11
17
|
*/
|
|
12
18
|
const exponentialBackoff = (options) => {
|
|
13
|
-
const { maxAttempts, baseDelayMs, maxDelayMs } = options;
|
|
19
|
+
const { maxAttempts, baseDelayMs, maxDelayMs, jitter } = options;
|
|
14
20
|
return {
|
|
15
21
|
/**
|
|
16
22
|
* Computes retry decision for the current attempt.
|
|
@@ -22,8 +28,9 @@ next(input) {
|
|
|
22
28
|
shouldRetry: false
|
|
23
29
|
};
|
|
24
30
|
const exponent = Math.max(0, input.attempt - 1);
|
|
31
|
+
const cappedDelayMs = Math.min(maxDelayMs, baseDelayMs * 2 ** exponent);
|
|
25
32
|
return {
|
|
26
|
-
delayMs:
|
|
33
|
+
delayMs: applyJitter(cappedDelayMs, jitter),
|
|
27
34
|
reason: "retry",
|
|
28
35
|
shouldRetry: true
|
|
29
36
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"exponential-backoff.js","names":[],"sources":["../src/exponential-backoff.ts"],"sourcesContent":["/**\n * Exponential backoff retry strategy.\n *\n * @module @zap-studio/retry/exponential-backoff\n */\n\nimport type {\n RetryDecision,\n RetryDecisionInput,\n RetryPolicy,\n} from \"./types.js\";\n\n/**\n * Configuration for `exponentialBackoff(...)`.\n *\n * @example\n * const options: ExponentialBackoffOptions = {\n * maxAttempts: 5,\n * baseDelayMs: 100,\n * maxDelayMs: 2_000,\n * };\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 * Creates a retry policy with exponential delay growth up to a max cap.\n *\n * @example\n * const policy = exponentialBackoff({\n * maxAttempts: 5,\n * baseDelayMs: 100,\n * maxDelayMs: 2_000,\n * });\n */\nexport const exponentialBackoff = (\n options: ExponentialBackoffOptions\n): RetryPolicy => {\n const { maxAttempts, baseDelayMs, maxDelayMs } = options;\n\n return {\n /**\n * Computes retry decision for the current attempt.\n */\n next(input: RetryDecisionInput): RetryDecision {\n if (input.attempt >= maxAttempts) {\n return {\n delayMs: 0,\n reason: \"max-attempts-reached\",\n shouldRetry: false,\n };\n }\n\n const exponent = Math.max(0, input.attempt - 1);\n const
|
|
1
|
+
{"version":3,"file":"exponential-backoff.js","names":[],"sources":["../src/exponential-backoff.ts"],"sourcesContent":["/**\n * Exponential backoff retry strategy.\n *\n * @module @zap-studio/retry/exponential-backoff\n */\n\nimport { applyJitter } from \"./jitter.js\";\nimport type { JitterMode, JitterOptions } from \"./jitter.js\";\nimport type {\n RetryDecision,\n RetryDecisionInput,\n RetryPolicy,\n} from \"./types.js\";\n\n/**\n * Configuration for `exponentialBackoff(...)`.\n *\n * @example\n * const options: ExponentialBackoffOptions = {\n * maxAttempts: 5,\n * baseDelayMs: 100,\n * maxDelayMs: 2_000,\n * jitter: \"full\",\n * };\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 * Optional jitter applied to the computed delay, after capping at\n * `maxDelayMs`.\n */\n jitter?: JitterMode | JitterOptions;\n}\n\n/**\n * Creates a retry policy with exponential delay growth up to a max cap.\n *\n * @example\n * const policy = exponentialBackoff({\n * maxAttempts: 5,\n * baseDelayMs: 100,\n * maxDelayMs: 2_000,\n * });\n */\nexport const exponentialBackoff = (\n options: ExponentialBackoffOptions\n): RetryPolicy => {\n const { maxAttempts, baseDelayMs, maxDelayMs, jitter } = options;\n\n return {\n /**\n * Computes retry decision for the current attempt.\n */\n next(input: RetryDecisionInput): RetryDecision {\n if (input.attempt >= maxAttempts) {\n return {\n delayMs: 0,\n reason: \"max-attempts-reached\",\n shouldRetry: false,\n };\n }\n\n const exponent = Math.max(0, input.attempt - 1);\n const cappedDelayMs = Math.min(maxDelayMs, baseDelayMs * 2 ** exponent);\n const delayMs = applyJitter(cappedDelayMs, jitter);\n\n return { delayMs, reason: \"retry\", shouldRetry: true };\n },\n };\n};\n"],"mappings":";;;;;;;;;;;;;;;;;AAuDA,MAAa,sBACX,YACgB;CAChB,MAAM,EAAE,aAAa,aAAa,YAAY,WAAW;CAEzD,OAAO;;;;AAIL,KAAK,OAA0C;EAC7C,IAAI,MAAM,WAAW,aACnB,OAAO;GACL,SAAS;GACT,QAAQ;GACR,aAAa;EACf;EAGF,MAAM,WAAW,KAAK,IAAI,GAAG,MAAM,UAAU,CAAC;EAC9C,MAAM,gBAAgB,KAAK,IAAI,YAAY,cAAc,KAAK,QAAQ;EAGtE,OAAO;GAAE,SAFO,YAAY,eAAe,MAE5B;GAAG,QAAQ;GAAS,aAAa;EAAK;CACvD,EACF;AACF"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { i as RetryErrorContext, n as AbortErrorContext, r as RetryError, t as AbortError } from "./errors-CS5UPJWs.js";
|
|
2
2
|
import { RetryDecision, RetryDecisionInput, RetryExhaustedInput, RetryPolicy, RetryRunOptions, RetryRunResult } from "./types.js";
|
|
3
3
|
import { defaultSleep, runRetryPolicy } from "./base-policy.js";
|
|
4
|
+
import { JitterMode, JitterOptions, applyJitter } from "./jitter.js";
|
|
4
5
|
import { ExponentialBackoffOptions, exponentialBackoff } from "./exponential-backoff.js";
|
|
5
6
|
import { FixedDelayOptions, fixedDelay } from "./fixed-delay.js";
|
|
6
7
|
import { LinearBackoffOptions, linearBackoff } from "./linear-backoff.js";
|
|
7
|
-
export { AbortError, type AbortErrorContext, type ExponentialBackoffOptions, type FixedDelayOptions, type LinearBackoffOptions, type RetryDecision, type RetryDecisionInput, RetryError, type RetryErrorContext, type RetryExhaustedInput, type RetryPolicy, type RetryRunOptions, type RetryRunResult, defaultSleep, exponentialBackoff, fixedDelay, linearBackoff, runRetryPolicy };
|
|
8
|
+
export { AbortError, type AbortErrorContext, type ExponentialBackoffOptions, type FixedDelayOptions, type JitterMode, type JitterOptions, type LinearBackoffOptions, type RetryDecision, type RetryDecisionInput, RetryError, type RetryErrorContext, type RetryExhaustedInput, type RetryPolicy, type RetryRunOptions, type RetryRunResult, applyJitter, defaultSleep, exponentialBackoff, fixedDelay, linearBackoff, runRetryPolicy };
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { AbortError, RetryError } from "./errors.js";
|
|
2
2
|
import { defaultSleep, runRetryPolicy } from "./base-policy.js";
|
|
3
|
+
import { applyJitter } from "./jitter.js";
|
|
3
4
|
import { exponentialBackoff } from "./exponential-backoff.js";
|
|
4
5
|
import { fixedDelay } from "./fixed-delay.js";
|
|
5
6
|
import { linearBackoff } from "./linear-backoff.js";
|
|
6
|
-
export { AbortError, RetryError, defaultSleep, exponentialBackoff, fixedDelay, linearBackoff, runRetryPolicy };
|
|
7
|
+
export { AbortError, RetryError, applyJitter, defaultSleep, exponentialBackoff, fixedDelay, linearBackoff, runRetryPolicy };
|
package/dist/jitter.d.ts
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
//#region src/jitter.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* Jitter strategies applied to a computed backoff delay.
|
|
4
|
+
*
|
|
5
|
+
* @module @zap-studio/retry/jitter
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Supported jitter strategies.
|
|
9
|
+
*
|
|
10
|
+
* - `"full"`: `random(0, delayMs)` — max spread, best thundering-herd
|
|
11
|
+
* protection.
|
|
12
|
+
* - `"equal"`: `delayMs/2 + random(0, delayMs/2)` — keeps a floor at half
|
|
13
|
+
* the computed delay, less spread than full jitter.
|
|
14
|
+
*/
|
|
15
|
+
type JitterMode = "equal" | "full";
|
|
16
|
+
/**
|
|
17
|
+
* Configuration for jitter application.
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* const jitter: JitterOptions = { mode: "full" };
|
|
21
|
+
*/
|
|
22
|
+
interface JitterOptions {
|
|
23
|
+
/**
|
|
24
|
+
* Jitter strategy to apply.
|
|
25
|
+
*/
|
|
26
|
+
mode: JitterMode;
|
|
27
|
+
/**
|
|
28
|
+
* Random source in `[0, 1)`, overridable for deterministic tests.
|
|
29
|
+
*
|
|
30
|
+
* @default Math.random
|
|
31
|
+
*/
|
|
32
|
+
random?: () => number;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Applies a jitter strategy to a computed delay.
|
|
36
|
+
*
|
|
37
|
+
* @param delayMs - Delay in milliseconds before jitter.
|
|
38
|
+
* @param jitter - Jitter mode shorthand, full `JitterOptions`, or `undefined`
|
|
39
|
+
* to leave `delayMs` untouched.
|
|
40
|
+
* @returns Jittered delay in milliseconds, rounded to the nearest integer.
|
|
41
|
+
*
|
|
42
|
+
* @example
|
|
43
|
+
* const delayMs = applyJitter(1000, "full"); // 0-1000
|
|
44
|
+
*/
|
|
45
|
+
declare const applyJitter: (delayMs: number, jitter?: JitterMode | JitterOptions) => number;
|
|
46
|
+
//#endregion
|
|
47
|
+
export { JitterMode, JitterOptions, applyJitter };
|
|
48
|
+
//# sourceMappingURL=jitter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"jitter.d.ts","names":[],"sources":["../src/jitter.ts"],"mappings":";;;;;;;;;;;;;;KAcY;;;;;;;UAQK;;;;EAIf,MAAM;;;;;;EAMN;;;;;;;;;;;;;cAcW,cACX,iBACA,SAAS,aAAa"}
|
package/dist/jitter.js
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
//#region src/jitter.ts
|
|
2
|
+
/**
|
|
3
|
+
* Applies a jitter strategy to a computed delay.
|
|
4
|
+
*
|
|
5
|
+
* @param delayMs - Delay in milliseconds before jitter.
|
|
6
|
+
* @param jitter - Jitter mode shorthand, full `JitterOptions`, or `undefined`
|
|
7
|
+
* to leave `delayMs` untouched.
|
|
8
|
+
* @returns Jittered delay in milliseconds, rounded to the nearest integer.
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* const delayMs = applyJitter(1000, "full"); // 0-1000
|
|
12
|
+
*/
|
|
13
|
+
const applyJitter = (delayMs, jitter) => {
|
|
14
|
+
if (jitter === void 0) return delayMs;
|
|
15
|
+
const mode = typeof jitter === "string" ? jitter : jitter.mode;
|
|
16
|
+
const random = (typeof jitter === "string" ? void 0 : jitter.random) ?? Math.random;
|
|
17
|
+
if (mode === "full") return Math.round(random() * delayMs);
|
|
18
|
+
const half = delayMs / 2;
|
|
19
|
+
return Math.round(half + random() * half);
|
|
20
|
+
};
|
|
21
|
+
//#endregion
|
|
22
|
+
export { applyJitter };
|
|
23
|
+
|
|
24
|
+
//# sourceMappingURL=jitter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"jitter.js","names":[],"sources":["../src/jitter.ts"],"sourcesContent":["/**\n * Jitter strategies applied to a computed backoff delay.\n *\n * @module @zap-studio/retry/jitter\n */\n\n/**\n * Supported jitter strategies.\n *\n * - `\"full\"`: `random(0, delayMs)` — max spread, best thundering-herd\n * protection.\n * - `\"equal\"`: `delayMs/2 + random(0, delayMs/2)` — keeps a floor at half\n * the computed delay, less spread than full jitter.\n */\nexport type JitterMode = \"equal\" | \"full\";\n\n/**\n * Configuration for jitter application.\n *\n * @example\n * const jitter: JitterOptions = { mode: \"full\" };\n */\nexport interface JitterOptions {\n /**\n * Jitter strategy to apply.\n */\n mode: JitterMode;\n /**\n * Random source in `[0, 1)`, overridable for deterministic tests.\n *\n * @default Math.random\n */\n random?: () => number;\n}\n\n/**\n * Applies a jitter strategy to a computed delay.\n *\n * @param delayMs - Delay in milliseconds before jitter.\n * @param jitter - Jitter mode shorthand, full `JitterOptions`, or `undefined`\n * to leave `delayMs` untouched.\n * @returns Jittered delay in milliseconds, rounded to the nearest integer.\n *\n * @example\n * const delayMs = applyJitter(1000, \"full\"); // 0-1000\n */\nexport const applyJitter = (\n delayMs: number,\n jitter?: JitterMode | JitterOptions\n): number => {\n if (jitter === undefined) {\n return delayMs;\n }\n\n const mode = typeof jitter === \"string\" ? jitter : jitter.mode;\n const random =\n (typeof jitter === \"string\" ? undefined : jitter.random) ?? Math.random;\n\n if (mode === \"full\") {\n return Math.round(random() * delayMs);\n }\n\n const half = delayMs / 2;\n return Math.round(half + random() * half);\n};\n"],"mappings":";;;;;;;;;;;;AA8CA,MAAa,eACX,SACA,WACW;CACX,IAAI,WAAW,KAAA,GACb,OAAO;CAGT,MAAM,OAAO,OAAO,WAAW,WAAW,SAAS,OAAO;CAC1D,MAAM,UACH,OAAO,WAAW,WAAW,KAAA,IAAY,OAAO,WAAW,KAAK;CAEnE,IAAI,SAAS,QACX,OAAO,KAAK,MAAM,OAAO,IAAI,OAAO;CAGtC,MAAM,OAAO,UAAU;CACvB,OAAO,KAAK,MAAM,OAAO,OAAO,IAAI,IAAI;AAC1C"}
|
package/dist/linear-backoff.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { RetryPolicy } from "./types.js";
|
|
2
|
+
import { JitterMode, JitterOptions } from "./jitter.js";
|
|
2
3
|
//#region src/linear-backoff.d.ts
|
|
3
4
|
/**
|
|
4
5
|
* Configuration for `linearBackoff(...)`.
|
|
@@ -9,6 +10,7 @@ import { RetryPolicy } from "./types.js";
|
|
|
9
10
|
* baseDelayMs: 100,
|
|
10
11
|
* incrementMs: 100,
|
|
11
12
|
* maxDelayMs: 2_000,
|
|
13
|
+
* jitter: "equal",
|
|
12
14
|
* };
|
|
13
15
|
*/
|
|
14
16
|
interface LinearBackoffOptions {
|
|
@@ -28,6 +30,11 @@ interface LinearBackoffOptions {
|
|
|
28
30
|
* Hard upper bound in milliseconds for computed linear delay.
|
|
29
31
|
*/
|
|
30
32
|
maxDelayMs: number;
|
|
33
|
+
/**
|
|
34
|
+
* Optional jitter applied to the computed delay, after capping at
|
|
35
|
+
* `maxDelayMs`.
|
|
36
|
+
*/
|
|
37
|
+
jitter?: JitterMode | JitterOptions;
|
|
31
38
|
}
|
|
32
39
|
/**
|
|
33
40
|
* Creates a retry policy with linear delay growth up to a max cap.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"linear-backoff.d.ts","names":[],"sources":["../src/linear-backoff.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"linear-backoff.d.ts","names":[],"sources":["../src/linear-backoff.ts"],"mappings":";;;;;;;;;;;;;;;UA0BiB;;;;EAIf;;;;EAIA;;;;EAIA;;;;EAIA;;;;;EAKA,SAAS,aAAa;;;;;;;;;;;;;cAcX,gBAAiB,SAAS,yBAAuB"}
|
package/dist/linear-backoff.js
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
|
+
import { applyJitter } from "./jitter.js";
|
|
1
2
|
//#region src/linear-backoff.ts
|
|
2
3
|
/**
|
|
4
|
+
* Linear backoff retry strategy.
|
|
5
|
+
*
|
|
6
|
+
* @module @zap-studio/retry/linear-backoff
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
3
9
|
* Creates a retry policy with linear delay growth up to a max cap.
|
|
4
10
|
*
|
|
5
11
|
* @example
|
|
@@ -11,7 +17,7 @@
|
|
|
11
17
|
* });
|
|
12
18
|
*/
|
|
13
19
|
const linearBackoff = (options) => {
|
|
14
|
-
const { maxAttempts, baseDelayMs, incrementMs, maxDelayMs } = options;
|
|
20
|
+
const { maxAttempts, baseDelayMs, incrementMs, maxDelayMs, jitter } = options;
|
|
15
21
|
return {
|
|
16
22
|
/**
|
|
17
23
|
* Computes retry decision for the current attempt.
|
|
@@ -22,8 +28,9 @@ next(input) {
|
|
|
22
28
|
reason: "max-attempts-reached",
|
|
23
29
|
shouldRetry: false
|
|
24
30
|
};
|
|
31
|
+
const cappedDelayMs = Math.min(maxDelayMs, baseDelayMs + incrementMs * (input.attempt - 1));
|
|
25
32
|
return {
|
|
26
|
-
delayMs:
|
|
33
|
+
delayMs: applyJitter(cappedDelayMs, jitter),
|
|
27
34
|
reason: "retry",
|
|
28
35
|
shouldRetry: true
|
|
29
36
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"linear-backoff.js","names":[],"sources":["../src/linear-backoff.ts"],"sourcesContent":["/**\n * Linear backoff retry strategy.\n *\n * @module @zap-studio/retry/linear-backoff\n */\n\nimport type {\n RetryDecision,\n RetryDecisionInput,\n RetryPolicy,\n} from \"./types.js\";\n\n/**\n * Configuration for `linearBackoff(...)`.\n *\n * @example\n * const options: LinearBackoffOptions = {\n * maxAttempts: 5,\n * baseDelayMs: 100,\n * incrementMs: 100,\n * maxDelayMs: 2_000,\n * };\n */\nexport interface LinearBackoffOptions {\n /**\n * Maximum number of attempts (including the first) before giving up.\n */\n maxAttempts: number;\n /**\n * Delay in milliseconds after the first failed attempt.\n */\n baseDelayMs: number;\n /**\n * Amount added to the delay for each subsequent retry.\n */\n incrementMs: number;\n /**\n * Hard upper bound in milliseconds for computed linear delay.\n */\n maxDelayMs: number;\n}\n\n/**\n * Creates a retry policy with linear delay growth up to a max cap.\n *\n * @example\n * const policy = linearBackoff({\n * maxAttempts: 5,\n * baseDelayMs: 100,\n * incrementMs: 100,\n * maxDelayMs: 2_000,\n * });\n */\nexport const linearBackoff = (options: LinearBackoffOptions): RetryPolicy => {\n const { maxAttempts, baseDelayMs, incrementMs, maxDelayMs } = options;\n\n return {\n /**\n * Computes retry decision for the current attempt.\n */\n next(input: RetryDecisionInput): RetryDecision {\n if (input.attempt >= maxAttempts) {\n return {\n delayMs: 0,\n reason: \"max-attempts-reached\",\n shouldRetry: false,\n };\n }\n\n const
|
|
1
|
+
{"version":3,"file":"linear-backoff.js","names":[],"sources":["../src/linear-backoff.ts"],"sourcesContent":["/**\n * Linear backoff retry strategy.\n *\n * @module @zap-studio/retry/linear-backoff\n */\n\nimport { applyJitter } from \"./jitter.js\";\nimport type { JitterMode, JitterOptions } from \"./jitter.js\";\nimport type {\n RetryDecision,\n RetryDecisionInput,\n RetryPolicy,\n} from \"./types.js\";\n\n/**\n * Configuration for `linearBackoff(...)`.\n *\n * @example\n * const options: LinearBackoffOptions = {\n * maxAttempts: 5,\n * baseDelayMs: 100,\n * incrementMs: 100,\n * maxDelayMs: 2_000,\n * jitter: \"equal\",\n * };\n */\nexport interface LinearBackoffOptions {\n /**\n * Maximum number of attempts (including the first) before giving up.\n */\n maxAttempts: number;\n /**\n * Delay in milliseconds after the first failed attempt.\n */\n baseDelayMs: number;\n /**\n * Amount added to the delay for each subsequent retry.\n */\n incrementMs: number;\n /**\n * Hard upper bound in milliseconds for computed linear delay.\n */\n maxDelayMs: number;\n /**\n * Optional jitter applied to the computed delay, after capping at\n * `maxDelayMs`.\n */\n jitter?: JitterMode | JitterOptions;\n}\n\n/**\n * Creates a retry policy with linear delay growth up to a max cap.\n *\n * @example\n * const policy = linearBackoff({\n * maxAttempts: 5,\n * baseDelayMs: 100,\n * incrementMs: 100,\n * maxDelayMs: 2_000,\n * });\n */\nexport const linearBackoff = (options: LinearBackoffOptions): RetryPolicy => {\n const { maxAttempts, baseDelayMs, incrementMs, maxDelayMs, jitter } = options;\n\n return {\n /**\n * Computes retry decision for the current attempt.\n */\n next(input: RetryDecisionInput): RetryDecision {\n if (input.attempt >= maxAttempts) {\n return {\n delayMs: 0,\n reason: \"max-attempts-reached\",\n shouldRetry: false,\n };\n }\n\n const cappedDelayMs = Math.min(\n maxDelayMs,\n baseDelayMs + incrementMs * (input.attempt - 1)\n );\n const delayMs = applyJitter(cappedDelayMs, jitter);\n\n return { delayMs, reason: \"retry\", shouldRetry: true };\n },\n };\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;AA6DA,MAAa,iBAAiB,YAA+C;CAC3E,MAAM,EAAE,aAAa,aAAa,aAAa,YAAY,WAAW;CAEtE,OAAO;;;;AAIL,KAAK,OAA0C;EAC7C,IAAI,MAAM,WAAW,aACnB,OAAO;GACL,SAAS;GACT,QAAQ;GACR,aAAa;EACf;EAGF,MAAM,gBAAgB,KAAK,IACzB,YACA,cAAc,eAAe,MAAM,UAAU,EAC/C;EAGA,OAAO;GAAE,SAFO,YAAY,eAAe,MAE5B;GAAG,QAAQ;GAAS,aAAa;EAAK;CACvD,EACF;AACF"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zap-studio/retry",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Composable, tree-shakeable retry policies for resilient async operations.",
|
|
6
6
|
"keywords": [
|
|
@@ -35,6 +35,7 @@
|
|
|
35
35
|
"./errors": "./dist/errors.js",
|
|
36
36
|
"./exponential-backoff": "./dist/exponential-backoff.js",
|
|
37
37
|
"./fixed-delay": "./dist/fixed-delay.js",
|
|
38
|
+
"./jitter": "./dist/jitter.js",
|
|
38
39
|
"./linear-backoff": "./dist/linear-backoff.js",
|
|
39
40
|
"./types": "./dist/types.js",
|
|
40
41
|
"./package.json": "./package.json"
|