fetch-retrier 0.5.6 → 0.6.1
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/README.md +76 -29
- package/lib/core/backoff/jitter.d.ts +13 -0
- package/lib/core/backoff/jitter.js +25 -0
- package/lib/core/backoff/retry-after.d.ts +27 -0
- package/lib/core/backoff/retry-after.js +61 -0
- package/lib/core/errors.d.ts +100 -0
- package/lib/core/errors.js +132 -0
- package/lib/core/fetch-retrier.d.ts +28 -0
- package/lib/core/fetch-retrier.js +86 -0
- package/lib/core/http/timed-fetch.d.ts +32 -0
- package/lib/core/http/timed-fetch.js +41 -0
- package/lib/core/options.d.ts +74 -0
- package/lib/core/options.js +6 -0
- package/lib/core/policy/default-should-retry.d.ts +18 -0
- package/lib/core/policy/default-should-retry.js +25 -0
- package/lib/core/policy/validate-options.d.ts +12 -0
- package/lib/core/policy/validate-options.js +31 -0
- package/lib/core/retry-predicates.d.ts +11 -0
- package/lib/core/retry-predicates.js +18 -0
- package/lib/core/time/wait.d.ts +7 -0
- package/lib/core/time/wait.js +14 -0
- package/lib/index.d.ts +5 -192
- package/lib/index.js +17 -304
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
# Fetch Retrier
|
|
2
2
|
|
|
3
|
-
[](https://www.npmjs.com/package/fetch-retrier)
|
|
4
|
+
[](https://www.npmjs.com/package/fetch-retrier)
|
|
5
|
+
[](https://www.npmjs.com/package/fetch-retrier)
|
|
6
|
+
[](https://github.com/gammarers-labs/fetch-retrier/actions/workflows/build.yml)
|
|
7
7
|
|
|
8
8
|
A lightweight wrapper around `fetch` that adds **retries**, **per-attempt timeout**, **Retry-After** support, **full jitter** backoff, and **option validation**. Pass standard `RequestInit` options (`method`, `body`, `credentials`, and more) for POST/PUT APIs and other HTTP calls that may be rate-limited or temporarily unavailable.
|
|
9
9
|
|
|
@@ -12,30 +12,50 @@ A lightweight wrapper around `fetch` that adds **retries**, **per-attempt timeou
|
|
|
12
12
|
- **Configurable retries** – Set the maximum number of attempts per request (`retries >= 1`).
|
|
13
13
|
- **Per-attempt timeout** – Abort each attempt when it exceeds a given duration (`timeoutMs > 0`).
|
|
14
14
|
- **Retry-After support** – On HTTP retries, prefers a valid `Retry-After` header (delta-seconds or HTTP-date); falls back to full jitter when absent or invalid.
|
|
15
|
-
- **Full jitter backoff** – Exponential backoff with random jitter (AWS-style) for abort/network retries and as the HTTP fallback (`baseBackoffMs >= 0`).
|
|
15
|
+
- **Full jitter backoff** – Exponential backoff with random jitter (AWS-style) for abort/network retries and as the HTTP fallback (`baseBackoffMs >= 0`). Optional `maxBackoffMs` clips the jitter result (`>= 0`); it does not clip a valid `Retry-After`.
|
|
16
16
|
- **Option validation** – Invalid numeric options throw `FetchRetrierInvalidOptionsError` at call time.
|
|
17
17
|
- **RequestInit forwarding** – Pass `method`, `body`, `credentials`, `redirect`, and other `fetch` options via `init` on every attempt.
|
|
18
18
|
- **Header shorthand** – Optional top-level `headers` override `init.headers` when both are set.
|
|
19
19
|
- **Default retry policy** – Retries transient HTTP statuses (408, 425, 429, 500, 502, 503, 504) via `defaultShouldRetry` and `DEFAULT_RETRYABLE_HTTP_STATUSES`.
|
|
20
20
|
- **Extensible retry predicate** – Compose `defaultShouldRetry` with custom `shouldRetry` logic (receives response body text).
|
|
21
21
|
- **External cancellation** – Pass an `AbortSignal` to cancel in-flight requests.
|
|
22
|
-
- **Typed errors** – `FetchRetrierHttpError` (with `status` and `body`), `FetchRetrierNetworkError`, `FetchRetrierAbortError`, `FetchRetrierInvalidOptionsError
|
|
22
|
+
- **Typed errors** – All failures extend `FetchRetrierError`. Subclasses include `FetchRetrierHttpError` (with `status` and `body`), `FetchRetrierNetworkError`, `FetchRetrierAbortError`, and `FetchRetrierInvalidOptionsError`.
|
|
23
23
|
- **TypeScript** – Exported types including `RequestOptions` and `FetchInitOptions`.
|
|
24
24
|
|
|
25
|
+
## How it works
|
|
26
|
+
|
|
27
|
+
Options are validated, then each attempt sends the same URL and `init`/`headers` with an internal timeout. If `response.ok` is true, that response is returned. Otherwise `shouldRetry` decides whether to wait and try again. HTTP retries prefer a valid `Retry-After` header; abort and network retries use full jitter. After the last attempt, a subclass of `FetchRetrierError` is thrown.
|
|
28
|
+
|
|
29
|
+
- **Success** – If `response.ok` is true, the response is returned immediately.
|
|
30
|
+
- **Package errors** – Failures from this package extend `FetchRetrierError`. Catch the base, or a subclass for a specific case.
|
|
31
|
+
- **Invalid options** – If `retries < 1`, `timeoutMs <= 0`, `baseBackoffMs < 0`, or `maxBackoffMs` is set and `< 0`, `FetchRetrierInvalidOptionsError` is thrown before any request is made. This is not a `TypeError`.
|
|
32
|
+
- **Retriable failure** – If the response is not OK and `shouldRetry(response, body)` returns true, the client waits and retries until `retries` is exhausted. Wait prefers a valid `Retry-After` header (delta-seconds or HTTP-date); otherwise uses full jitter. On the last attempt, `FetchRetrierHttpError` is thrown (includes `status` and `body`).
|
|
33
|
+
- **Non-retriable failure** – If `shouldRetry` returns false, `FetchRetrierHttpError` is thrown immediately with `status` and `body` (e.g. `Non-retriable HTTP error: 404`).
|
|
34
|
+
- **Timeout** – If a request exceeds `timeoutMs`, that attempt is aborted and retried with full jitter until `retries` is exhausted. Timeout is per-attempt and does not cancel later attempts. The final failure is `FetchRetrierAbortError`.
|
|
35
|
+
- **External abort (in-flight)** – If `signal` is aborted during an attempt, the in-flight request is aborted. On the last attempt, the failure is `FetchRetrierAbortError`. If retries remain, the next attempt sees the still-aborted signal and throws `FetchRetrierAlreadyAbortedError` (no further request is made).
|
|
36
|
+
- **Network / TypeError** – Network errors are retried with full jitter; after the last attempt, `FetchRetrierNetworkError` is thrown with the original error as `cause`.
|
|
37
|
+
- **Already aborted signal** – If `signal` is already aborted before an attempt starts, `FetchRetrierAlreadyAbortedError` is thrown (no attempt is made).
|
|
38
|
+
|
|
25
39
|
## Installation
|
|
26
40
|
|
|
27
|
-
|
|
41
|
+
### npm
|
|
28
42
|
|
|
29
43
|
```bash
|
|
30
44
|
npm install fetch-retrier
|
|
31
45
|
```
|
|
32
46
|
|
|
33
|
-
|
|
47
|
+
### yarn
|
|
34
48
|
|
|
35
49
|
```bash
|
|
36
50
|
yarn add fetch-retrier
|
|
37
51
|
```
|
|
38
52
|
|
|
53
|
+
### pnpm
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
pnpm add fetch-retrier
|
|
57
|
+
```
|
|
58
|
+
|
|
39
59
|
## Usage
|
|
40
60
|
|
|
41
61
|
### GET request
|
|
@@ -104,12 +124,21 @@ const response = await fetchRetrier('https://api.example.com/data', {
|
|
|
104
124
|
// DEFAULT_RETRYABLE_HTTP_STATUSES is [408, 425, 429, 500, 502, 503, 504]
|
|
105
125
|
```
|
|
106
126
|
|
|
107
|
-
### Handling
|
|
127
|
+
### Handling errors
|
|
128
|
+
|
|
129
|
+
All failures thrown by `fetchRetrier` extend `FetchRetrierError`. Catch the base for any library
|
|
130
|
+
failure, or a subclass for a specific case. `FetchRetrierInvalidOptionsError` does not extend
|
|
131
|
+
`TypeError`.
|
|
108
132
|
|
|
109
|
-
On a non-OK response that is not retried (or after retries are exhausted), `FetchRetrierHttpError`
|
|
133
|
+
On a non-OK response that is not retried (or after retries are exhausted), `FetchRetrierHttpError`
|
|
134
|
+
includes both `status` and the already-read `body`:
|
|
110
135
|
|
|
111
136
|
```typescript
|
|
112
|
-
import {
|
|
137
|
+
import {
|
|
138
|
+
fetchRetrier,
|
|
139
|
+
FetchRetrierError,
|
|
140
|
+
FetchRetrierHttpError,
|
|
141
|
+
} from 'fetch-retrier';
|
|
113
142
|
|
|
114
143
|
try {
|
|
115
144
|
await fetchRetrier('https://api.example.com/data', {
|
|
@@ -121,45 +150,63 @@ try {
|
|
|
121
150
|
if (err instanceof FetchRetrierHttpError) {
|
|
122
151
|
console.error(err.status, err.body);
|
|
123
152
|
}
|
|
153
|
+
if (err instanceof FetchRetrierError) {
|
|
154
|
+
// Any failure from this package.
|
|
155
|
+
}
|
|
124
156
|
throw err;
|
|
125
157
|
}
|
|
126
158
|
```
|
|
127
159
|
|
|
128
160
|
### Cancellation with `AbortController`
|
|
129
161
|
|
|
162
|
+
Timeout abort and external abort are different. Per-attempt timeout retries remaining attempts and
|
|
163
|
+
then throws `FetchRetrierAbortError`. An in-flight external abort cancels the current request;
|
|
164
|
+
the last attempt throws `FetchRetrierAbortError`, while remaining attempts throw
|
|
165
|
+
`FetchRetrierAlreadyAbortedError` because the signal stays aborted.
|
|
166
|
+
|
|
167
|
+
`FetchRetrierAlreadyAbortedError` extends `FetchRetrierAbortError`, so check the subclass first.
|
|
168
|
+
|
|
130
169
|
```typescript
|
|
170
|
+
import {
|
|
171
|
+
fetchRetrier,
|
|
172
|
+
FetchRetrierAbortError,
|
|
173
|
+
FetchRetrierAlreadyAbortedError,
|
|
174
|
+
} from 'fetch-retrier';
|
|
175
|
+
|
|
131
176
|
const controller = new AbortController();
|
|
132
177
|
|
|
133
178
|
setTimeout(() => controller.abort(), 250);
|
|
134
179
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
180
|
+
try {
|
|
181
|
+
await fetchRetrier('https://api.example.com/data', {
|
|
182
|
+
retries: 3,
|
|
183
|
+
timeoutMs: 5000,
|
|
184
|
+
baseBackoffMs: 250,
|
|
185
|
+
signal: controller.signal,
|
|
186
|
+
});
|
|
187
|
+
} catch (err) {
|
|
188
|
+
if (err instanceof FetchRetrierAlreadyAbortedError) {
|
|
189
|
+
// Signal stayed aborted, so a later attempt was not started.
|
|
190
|
+
throw err;
|
|
191
|
+
}
|
|
192
|
+
if (err instanceof FetchRetrierAbortError) {
|
|
193
|
+
// Last attempt was cancelled by timeout or an in-flight external abort.
|
|
194
|
+
}
|
|
195
|
+
throw err;
|
|
196
|
+
}
|
|
141
197
|
```
|
|
142
198
|
|
|
143
|
-
### Retry and error behavior
|
|
144
|
-
|
|
145
|
-
- **Success** – If `response.ok` is true, the response is returned immediately.
|
|
146
|
-
- **Invalid options** – If `retries < 1`, `timeoutMs <= 0`, or `baseBackoffMs < 0`, `FetchRetrierInvalidOptionsError` is thrown before any request is made.
|
|
147
|
-
- **Retriable failure** – If the response is not OK and `shouldRetry(response, body)` returns true, the client waits and retries until `retries` is exhausted. Wait prefers a valid `Retry-After` header (delta-seconds or HTTP-date); otherwise uses full jitter. On the last attempt, `FetchRetrierHttpError` is thrown (includes `status` and `body`).
|
|
148
|
-
- **Non-retriable failure** – If `shouldRetry` returns false, `FetchRetrierHttpError` is thrown immediately with `status` and `body` (e.g. `Non-retriable HTTP error: 404`).
|
|
149
|
-
- **Timeout** – If a request exceeds `timeoutMs`, it is aborted and retried with full jitter until `retries` is exhausted; the final failure is `FetchRetrierAbortError`.
|
|
150
|
-
- **Network / TypeError** – Network errors are retried with full jitter; after the last attempt, `FetchRetrierNetworkError` is thrown with the original error as `cause`.
|
|
151
|
-
- **Already aborted signal** – If `signal` is already aborted before an attempt starts, `FetchRetrierAlreadyAbortedError` is thrown (no attempt is made).
|
|
152
|
-
|
|
153
199
|
## Options
|
|
154
200
|
|
|
155
201
|
| Option | Type | Required | Description |
|
|
156
202
|
|--------|------|----------|-------------|
|
|
157
203
|
| `retries` | `number` | Yes | Maximum number of attempts (including the first request). Must be `>= 1`. |
|
|
158
204
|
| `timeoutMs` | `number` | Yes | Timeout in milliseconds for each attempt. Exceeded attempts are aborted and retried. Must be `> 0`. |
|
|
159
|
-
| `baseBackoffMs` | `number` | Yes | Base delay in milliseconds for full jitter when `Retry-After` is absent or invalid (also used for abort/network retries).
|
|
205
|
+
| `baseBackoffMs` | `number` | Yes | Base delay in milliseconds for full jitter when `Retry-After` is absent or invalid (also used for abort/network retries). Exponential span is `baseBackoffMs * 2^attempt`, randomized. Must be `>= 0` (`0` skips backoff delay when falling back). |
|
|
206
|
+
| `maxBackoffMs` | `number` | No | Optional ceiling in milliseconds applied to the full-jitter result (`Math.min(jitter, maxBackoffMs)`). Does not clip a valid `Retry-After`. Must be `>= 0` when set (`0` skips jitter wait). Omitted means no extra clip. |
|
|
160
207
|
| `init` | `FetchInitOptions` | No | `fetch` options forwarded to every attempt: `method`, `body`, `credentials`, `redirect`, `mode`, `cache`, etc. `signal` is reserved for internal timeout and cancellation. |
|
|
161
208
|
| `headers` | `Record<string, string>` | No | Headers sent on every attempt. Overrides `init.headers` when both are set. |
|
|
162
|
-
| `signal` | `AbortSignal` | No | External abort signal. If already aborted, `FetchRetrierAlreadyAbortedError` is thrown. If aborted during an attempt, the request is aborted
|
|
209
|
+
| `signal` | `AbortSignal` | No | External abort signal. If already aborted before an attempt, `FetchRetrierAlreadyAbortedError` is thrown. If aborted during an attempt, the in-flight request is aborted: the last attempt fails with `FetchRetrierAbortError`; remaining attempts fail with `FetchRetrierAlreadyAbortedError`. Distinct from `timeoutMs`, which retries remaining attempts and then throws `FetchRetrierAbortError`. |
|
|
163
210
|
| `shouldRetry` | `(response: Response, body: string) => boolean` | No | Called after `response.text()` when `response.ok` is false. Return `true` to retry. Default: `defaultShouldRetry` (statuses in `DEFAULT_RETRYABLE_HTTP_STATUSES`: 408, 425, 429, 500, 502, 503, 504). |
|
|
164
211
|
|
|
165
212
|
### Exported helpers
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Full jitter backoff: random delay in `[0, base * 2^attempt)` ms (AWS-recommended pattern).
|
|
3
|
+
*
|
|
4
|
+
* Used between abort/network retries, and as the fallback when HTTP retries lack a usable
|
|
5
|
+
* `Retry-After` header. When `maxBackoffMs` is set, the computed delay is clipped with
|
|
6
|
+
* `Math.min(delay, maxBackoffMs)`.
|
|
7
|
+
*
|
|
8
|
+
* @param base - Base backoff in milliseconds
|
|
9
|
+
* @param attempt - 1-based attempt index (first retry uses `attempt === 1`)
|
|
10
|
+
* @param maxBackoffMs - Optional ceiling applied to the jitter result
|
|
11
|
+
* @returns Wait duration in milliseconds before the next attempt
|
|
12
|
+
*/
|
|
13
|
+
export declare const fullJitter: (base: number, attempt: number, maxBackoffMs?: number) => number;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.fullJitter = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Full jitter backoff: random delay in `[0, base * 2^attempt)` ms (AWS-recommended pattern).
|
|
6
|
+
*
|
|
7
|
+
* Used between abort/network retries, and as the fallback when HTTP retries lack a usable
|
|
8
|
+
* `Retry-After` header. When `maxBackoffMs` is set, the computed delay is clipped with
|
|
9
|
+
* `Math.min(delay, maxBackoffMs)`.
|
|
10
|
+
*
|
|
11
|
+
* @param base - Base backoff in milliseconds
|
|
12
|
+
* @param attempt - 1-based attempt index (first retry uses `attempt === 1`)
|
|
13
|
+
* @param maxBackoffMs - Optional ceiling applied to the jitter result
|
|
14
|
+
* @returns Wait duration in milliseconds before the next attempt
|
|
15
|
+
*/
|
|
16
|
+
const fullJitter = (base, attempt, maxBackoffMs) => {
|
|
17
|
+
const cap = base * Math.pow(2, attempt);
|
|
18
|
+
const delay = Math.floor(Math.random() * cap);
|
|
19
|
+
if (maxBackoffMs === undefined) {
|
|
20
|
+
return delay;
|
|
21
|
+
}
|
|
22
|
+
return Math.min(delay, maxBackoffMs);
|
|
23
|
+
};
|
|
24
|
+
exports.fullJitter = fullJitter;
|
|
25
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaml0dGVyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vc3JjL2NvcmUvYmFja29mZi9qaXR0ZXIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7O0FBQUE7Ozs7Ozs7Ozs7O0dBV0c7QUFDSSxNQUFNLFVBQVUsR0FBRyxDQUFDLElBQVksRUFBRSxPQUFlLEVBQUUsWUFBcUIsRUFBVSxFQUFFO0lBQ3pGLE1BQU0sR0FBRyxHQUFHLElBQUksR0FBRyxJQUFJLENBQUMsR0FBRyxDQUFDLENBQUMsRUFBRSxPQUFPLENBQUMsQ0FBQztJQUN4QyxNQUFNLEtBQUssR0FBRyxJQUFJLENBQUMsS0FBSyxDQUFDLElBQUksQ0FBQyxNQUFNLEVBQUUsR0FBRyxHQUFHLENBQUMsQ0FBQztJQUM5QyxJQUFJLFlBQVksS0FBSyxTQUFTLEVBQUUsQ0FBQztRQUMvQixPQUFPLEtBQUssQ0FBQztJQUNmLENBQUM7SUFDRCxPQUFPLElBQUksQ0FBQyxHQUFHLENBQUMsS0FBSyxFQUFFLFlBQVksQ0FBQyxDQUFDO0FBQ3ZDLENBQUMsQ0FBQztBQVBXLFFBQUEsVUFBVSxjQU9yQiIsInNvdXJjZXNDb250ZW50IjpbIi8qKlxuICogRnVsbCBqaXR0ZXIgYmFja29mZjogcmFuZG9tIGRlbGF5IGluIGBbMCwgYmFzZSAqIDJeYXR0ZW1wdClgIG1zIChBV1MtcmVjb21tZW5kZWQgcGF0dGVybikuXG4gKlxuICogVXNlZCBiZXR3ZWVuIGFib3J0L25ldHdvcmsgcmV0cmllcywgYW5kIGFzIHRoZSBmYWxsYmFjayB3aGVuIEhUVFAgcmV0cmllcyBsYWNrIGEgdXNhYmxlXG4gKiBgUmV0cnktQWZ0ZXJgIGhlYWRlci4gV2hlbiBgbWF4QmFja29mZk1zYCBpcyBzZXQsIHRoZSBjb21wdXRlZCBkZWxheSBpcyBjbGlwcGVkIHdpdGhcbiAqIGBNYXRoLm1pbihkZWxheSwgbWF4QmFja29mZk1zKWAuXG4gKlxuICogQHBhcmFtIGJhc2UgLSBCYXNlIGJhY2tvZmYgaW4gbWlsbGlzZWNvbmRzXG4gKiBAcGFyYW0gYXR0ZW1wdCAtIDEtYmFzZWQgYXR0ZW1wdCBpbmRleCAoZmlyc3QgcmV0cnkgdXNlcyBgYXR0ZW1wdCA9PT0gMWApXG4gKiBAcGFyYW0gbWF4QmFja29mZk1zIC0gT3B0aW9uYWwgY2VpbGluZyBhcHBsaWVkIHRvIHRoZSBqaXR0ZXIgcmVzdWx0XG4gKiBAcmV0dXJucyBXYWl0IGR1cmF0aW9uIGluIG1pbGxpc2Vjb25kcyBiZWZvcmUgdGhlIG5leHQgYXR0ZW1wdFxuICovXG5leHBvcnQgY29uc3QgZnVsbEppdHRlciA9IChiYXNlOiBudW1iZXIsIGF0dGVtcHQ6IG51bWJlciwgbWF4QmFja29mZk1zPzogbnVtYmVyKTogbnVtYmVyID0+IHtcbiAgY29uc3QgY2FwID0gYmFzZSAqIE1hdGgucG93KDIsIGF0dGVtcHQpO1xuICBjb25zdCBkZWxheSA9IE1hdGguZmxvb3IoTWF0aC5yYW5kb20oKSAqIGNhcCk7XG4gIGlmIChtYXhCYWNrb2ZmTXMgPT09IHVuZGVmaW5lZCkge1xuICAgIHJldHVybiBkZWxheTtcbiAgfVxuICByZXR1cm4gTWF0aC5taW4oZGVsYXksIG1heEJhY2tvZmZNcyk7XG59O1xuIl19
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parses a `Retry-After` header value into a delay in milliseconds.
|
|
3
|
+
*
|
|
4
|
+
* Supports RFC 7231 forms: non-negative integer delta-seconds, or an HTTP-date. Empty values,
|
|
5
|
+
* non-integer numerics (e.g. floats or negatives), and unparsable dates yield `undefined` so
|
|
6
|
+
* callers can fall back to full jitter. An HTTP-date in the past yields `0`.
|
|
7
|
+
*
|
|
8
|
+
* @param value - Raw `Retry-After` header value
|
|
9
|
+
* @param nowMs - Current time in milliseconds (injectable for tests)
|
|
10
|
+
* @returns Delay in milliseconds, or `undefined` when the value cannot be parsed
|
|
11
|
+
*/
|
|
12
|
+
export declare const parseRetryAfterMs: (value: string, nowMs?: number) => number | undefined;
|
|
13
|
+
/**
|
|
14
|
+
* Chooses the delay before the next HTTP retry: prefer a valid `Retry-After`, else full jitter.
|
|
15
|
+
*
|
|
16
|
+
* Reads `response.headers.get('Retry-After')` and parses it with {@link parseRetryAfterMs}.
|
|
17
|
+
* Missing headers, or values that parse to `undefined`, fall back to {@link fullJitter}.
|
|
18
|
+
* Abort and network retries do not use this helper.
|
|
19
|
+
*
|
|
20
|
+
* @param response - Non-OK response from the current attempt
|
|
21
|
+
* @param baseBackoffMs - Base backoff passed to {@link fullJitter} when falling back
|
|
22
|
+
* @param attempt - 1-based attempt index
|
|
23
|
+
* @param maxBackoffMs - Optional ceiling applied only to the jitter fallback
|
|
24
|
+
* @param nowMs - Current time in milliseconds (injectable for tests)
|
|
25
|
+
* @returns Wait duration in milliseconds before the next attempt
|
|
26
|
+
*/
|
|
27
|
+
export declare const resolveRetryDelayMs: (response: Response, baseBackoffMs: number, attempt: number, maxBackoffMs?: number, nowMs?: number) => number;
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.resolveRetryDelayMs = exports.parseRetryAfterMs = void 0;
|
|
4
|
+
const jitter_1 = require("./jitter");
|
|
5
|
+
/**
|
|
6
|
+
* Parses a `Retry-After` header value into a delay in milliseconds.
|
|
7
|
+
*
|
|
8
|
+
* Supports RFC 7231 forms: non-negative integer delta-seconds, or an HTTP-date. Empty values,
|
|
9
|
+
* non-integer numerics (e.g. floats or negatives), and unparsable dates yield `undefined` so
|
|
10
|
+
* callers can fall back to full jitter. An HTTP-date in the past yields `0`.
|
|
11
|
+
*
|
|
12
|
+
* @param value - Raw `Retry-After` header value
|
|
13
|
+
* @param nowMs - Current time in milliseconds (injectable for tests)
|
|
14
|
+
* @returns Delay in milliseconds, or `undefined` when the value cannot be parsed
|
|
15
|
+
*/
|
|
16
|
+
const parseRetryAfterMs = (value, nowMs = Date.now()) => {
|
|
17
|
+
const trimmed = value.trim();
|
|
18
|
+
if (trimmed === '') {
|
|
19
|
+
return undefined;
|
|
20
|
+
}
|
|
21
|
+
if (/^\d+$/.test(trimmed)) {
|
|
22
|
+
return Number.parseInt(trimmed, 10) * 1000;
|
|
23
|
+
}
|
|
24
|
+
// Reject other numeric forms (floats, negatives); not valid delta-seconds or HTTP-date.
|
|
25
|
+
if (/^-?\d+(\.\d+)?$/.test(trimmed)) {
|
|
26
|
+
return undefined;
|
|
27
|
+
}
|
|
28
|
+
const dateMs = Date.parse(trimmed);
|
|
29
|
+
if (Number.isNaN(dateMs)) {
|
|
30
|
+
return undefined;
|
|
31
|
+
}
|
|
32
|
+
return Math.max(0, dateMs - nowMs);
|
|
33
|
+
};
|
|
34
|
+
exports.parseRetryAfterMs = parseRetryAfterMs;
|
|
35
|
+
/**
|
|
36
|
+
* Chooses the delay before the next HTTP retry: prefer a valid `Retry-After`, else full jitter.
|
|
37
|
+
*
|
|
38
|
+
* Reads `response.headers.get('Retry-After')` and parses it with {@link parseRetryAfterMs}.
|
|
39
|
+
* Missing headers, or values that parse to `undefined`, fall back to {@link fullJitter}.
|
|
40
|
+
* Abort and network retries do not use this helper.
|
|
41
|
+
*
|
|
42
|
+
* @param response - Non-OK response from the current attempt
|
|
43
|
+
* @param baseBackoffMs - Base backoff passed to {@link fullJitter} when falling back
|
|
44
|
+
* @param attempt - 1-based attempt index
|
|
45
|
+
* @param maxBackoffMs - Optional ceiling applied only to the jitter fallback
|
|
46
|
+
* @param nowMs - Current time in milliseconds (injectable for tests)
|
|
47
|
+
* @returns Wait duration in milliseconds before the next attempt
|
|
48
|
+
*/
|
|
49
|
+
const resolveRetryDelayMs = (response, baseBackoffMs, attempt, maxBackoffMs, nowMs = Date.now()) => {
|
|
50
|
+
const header = response.headers?.get('Retry-After');
|
|
51
|
+
if (header == null) {
|
|
52
|
+
return (0, jitter_1.fullJitter)(baseBackoffMs, attempt, maxBackoffMs);
|
|
53
|
+
}
|
|
54
|
+
const fromHeader = (0, exports.parseRetryAfterMs)(header, nowMs);
|
|
55
|
+
if (fromHeader === undefined) {
|
|
56
|
+
return (0, jitter_1.fullJitter)(baseBackoffMs, attempt, maxBackoffMs);
|
|
57
|
+
}
|
|
58
|
+
return fromHeader;
|
|
59
|
+
};
|
|
60
|
+
exports.resolveRetryDelayMs = resolveRetryDelayMs;
|
|
61
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicmV0cnktYWZ0ZXIuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi9zcmMvY29yZS9iYWNrb2ZmL3JldHJ5LWFmdGVyLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7OztBQUFBLHFDQUFzQztBQUV0Qzs7Ozs7Ozs7OztHQVVHO0FBQ0ksTUFBTSxpQkFBaUIsR0FBRyxDQUFDLEtBQWEsRUFBRSxRQUFnQixJQUFJLENBQUMsR0FBRyxFQUFFLEVBQXNCLEVBQUU7SUFDakcsTUFBTSxPQUFPLEdBQUcsS0FBSyxDQUFDLElBQUksRUFBRSxDQUFDO0lBQzdCLElBQUksT0FBTyxLQUFLLEVBQUUsRUFBRSxDQUFDO1FBQ25CLE9BQU8sU0FBUyxDQUFDO0lBQ25CLENBQUM7SUFFRCxJQUFJLE9BQU8sQ0FBQyxJQUFJLENBQUMsT0FBTyxDQUFDLEVBQUUsQ0FBQztRQUMxQixPQUFPLE1BQU0sQ0FBQyxRQUFRLENBQUMsT0FBTyxFQUFFLEVBQUUsQ0FBQyxHQUFHLElBQUksQ0FBQztJQUM3QyxDQUFDO0lBRUQsd0ZBQXdGO0lBQ3hGLElBQUksaUJBQWlCLENBQUMsSUFBSSxDQUFDLE9BQU8sQ0FBQyxFQUFFLENBQUM7UUFDcEMsT0FBTyxTQUFTLENBQUM7SUFDbkIsQ0FBQztJQUVELE1BQU0sTUFBTSxHQUFHLElBQUksQ0FBQyxLQUFLLENBQUMsT0FBTyxDQUFDLENBQUM7SUFDbkMsSUFBSSxNQUFNLENBQUMsS0FBSyxDQUFDLE1BQU0sQ0FBQyxFQUFFLENBQUM7UUFDekIsT0FBTyxTQUFTLENBQUM7SUFDbkIsQ0FBQztJQUVELE9BQU8sSUFBSSxDQUFDLEdBQUcsQ0FBQyxDQUFDLEVBQUUsTUFBTSxHQUFHLEtBQUssQ0FBQyxDQUFDO0FBQ3JDLENBQUMsQ0FBQztBQXJCVyxRQUFBLGlCQUFpQixxQkFxQjVCO0FBRUY7Ozs7Ozs7Ozs7Ozs7R0FhRztBQUNJLE1BQU0sbUJBQW1CLEdBQUcsQ0FDakMsUUFBa0IsRUFDbEIsYUFBcUIsRUFDckIsT0FBZSxFQUNmLFlBQXFCLEVBQ3JCLFFBQWdCLElBQUksQ0FBQyxHQUFHLEVBQUUsRUFDbEIsRUFBRTtJQUNWLE1BQU0sTUFBTSxHQUFHLFFBQVEsQ0FBQyxPQUFPLEVBQUUsR0FBRyxDQUFDLGFBQWEsQ0FBQyxDQUFDO0lBQ3BELElBQUksTUFBTSxJQUFJLElBQUksRUFBRSxDQUFDO1FBQ25CLE9BQU8sSUFBQSxtQkFBVSxFQUFDLGFBQWEsRUFBRSxPQUFPLEVBQUUsWUFBWSxDQUFDLENBQUM7SUFDMUQsQ0FBQztJQUVELE1BQU0sVUFBVSxHQUFHLElBQUEseUJBQWlCLEVBQUMsTUFBTSxFQUFFLEtBQUssQ0FBQyxDQUFDO0lBQ3BELElBQUksVUFBVSxLQUFLLFNBQVMsRUFBRSxDQUFDO1FBQzdCLE9BQU8sSUFBQSxtQkFBVSxFQUFDLGFBQWEsRUFBRSxPQUFPLEVBQUUsWUFBWSxDQUFDLENBQUM7SUFDMUQsQ0FBQztJQUVELE9BQU8sVUFBVSxDQUFDO0FBQ3BCLENBQUMsQ0FBQztBQWxCVyxRQUFBLG1CQUFtQix1QkFrQjlCIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgZnVsbEppdHRlciB9IGZyb20gJy4vaml0dGVyJztcblxuLyoqXG4gKiBQYXJzZXMgYSBgUmV0cnktQWZ0ZXJgIGhlYWRlciB2YWx1ZSBpbnRvIGEgZGVsYXkgaW4gbWlsbGlzZWNvbmRzLlxuICpcbiAqIFN1cHBvcnRzIFJGQyA3MjMxIGZvcm1zOiBub24tbmVnYXRpdmUgaW50ZWdlciBkZWx0YS1zZWNvbmRzLCBvciBhbiBIVFRQLWRhdGUuIEVtcHR5IHZhbHVlcyxcbiAqIG5vbi1pbnRlZ2VyIG51bWVyaWNzIChlLmcuIGZsb2F0cyBvciBuZWdhdGl2ZXMpLCBhbmQgdW5wYXJzYWJsZSBkYXRlcyB5aWVsZCBgdW5kZWZpbmVkYCBzb1xuICogY2FsbGVycyBjYW4gZmFsbCBiYWNrIHRvIGZ1bGwgaml0dGVyLiBBbiBIVFRQLWRhdGUgaW4gdGhlIHBhc3QgeWllbGRzIGAwYC5cbiAqXG4gKiBAcGFyYW0gdmFsdWUgLSBSYXcgYFJldHJ5LUFmdGVyYCBoZWFkZXIgdmFsdWVcbiAqIEBwYXJhbSBub3dNcyAtIEN1cnJlbnQgdGltZSBpbiBtaWxsaXNlY29uZHMgKGluamVjdGFibGUgZm9yIHRlc3RzKVxuICogQHJldHVybnMgRGVsYXkgaW4gbWlsbGlzZWNvbmRzLCBvciBgdW5kZWZpbmVkYCB3aGVuIHRoZSB2YWx1ZSBjYW5ub3QgYmUgcGFyc2VkXG4gKi9cbmV4cG9ydCBjb25zdCBwYXJzZVJldHJ5QWZ0ZXJNcyA9ICh2YWx1ZTogc3RyaW5nLCBub3dNczogbnVtYmVyID0gRGF0ZS5ub3coKSk6IG51bWJlciB8IHVuZGVmaW5lZCA9PiB7XG4gIGNvbnN0IHRyaW1tZWQgPSB2YWx1ZS50cmltKCk7XG4gIGlmICh0cmltbWVkID09PSAnJykge1xuICAgIHJldHVybiB1bmRlZmluZWQ7XG4gIH1cblxuICBpZiAoL15cXGQrJC8udGVzdCh0cmltbWVkKSkge1xuICAgIHJldHVybiBOdW1iZXIucGFyc2VJbnQodHJpbW1lZCwgMTApICogMTAwMDtcbiAgfVxuXG4gIC8vIFJlamVjdCBvdGhlciBudW1lcmljIGZvcm1zIChmbG9hdHMsIG5lZ2F0aXZlcyk7IG5vdCB2YWxpZCBkZWx0YS1zZWNvbmRzIG9yIEhUVFAtZGF0ZS5cbiAgaWYgKC9eLT9cXGQrKFxcLlxcZCspPyQvLnRlc3QodHJpbW1lZCkpIHtcbiAgICByZXR1cm4gdW5kZWZpbmVkO1xuICB9XG5cbiAgY29uc3QgZGF0ZU1zID0gRGF0ZS5wYXJzZSh0cmltbWVkKTtcbiAgaWYgKE51bWJlci5pc05hTihkYXRlTXMpKSB7XG4gICAgcmV0dXJuIHVuZGVmaW5lZDtcbiAgfVxuXG4gIHJldHVybiBNYXRoLm1heCgwLCBkYXRlTXMgLSBub3dNcyk7XG59O1xuXG4vKipcbiAqIENob29zZXMgdGhlIGRlbGF5IGJlZm9yZSB0aGUgbmV4dCBIVFRQIHJldHJ5OiBwcmVmZXIgYSB2YWxpZCBgUmV0cnktQWZ0ZXJgLCBlbHNlIGZ1bGwgaml0dGVyLlxuICpcbiAqIFJlYWRzIGByZXNwb25zZS5oZWFkZXJzLmdldCgnUmV0cnktQWZ0ZXInKWAgYW5kIHBhcnNlcyBpdCB3aXRoIHtAbGluayBwYXJzZVJldHJ5QWZ0ZXJNc30uXG4gKiBNaXNzaW5nIGhlYWRlcnMsIG9yIHZhbHVlcyB0aGF0IHBhcnNlIHRvIGB1bmRlZmluZWRgLCBmYWxsIGJhY2sgdG8ge0BsaW5rIGZ1bGxKaXR0ZXJ9LlxuICogQWJvcnQgYW5kIG5ldHdvcmsgcmV0cmllcyBkbyBub3QgdXNlIHRoaXMgaGVscGVyLlxuICpcbiAqIEBwYXJhbSByZXNwb25zZSAtIE5vbi1PSyByZXNwb25zZSBmcm9tIHRoZSBjdXJyZW50IGF0dGVtcHRcbiAqIEBwYXJhbSBiYXNlQmFja29mZk1zIC0gQmFzZSBiYWNrb2ZmIHBhc3NlZCB0byB7QGxpbmsgZnVsbEppdHRlcn0gd2hlbiBmYWxsaW5nIGJhY2tcbiAqIEBwYXJhbSBhdHRlbXB0IC0gMS1iYXNlZCBhdHRlbXB0IGluZGV4XG4gKiBAcGFyYW0gbWF4QmFja29mZk1zIC0gT3B0aW9uYWwgY2VpbGluZyBhcHBsaWVkIG9ubHkgdG8gdGhlIGppdHRlciBmYWxsYmFja1xuICogQHBhcmFtIG5vd01zIC0gQ3VycmVudCB0aW1lIGluIG1pbGxpc2Vjb25kcyAoaW5qZWN0YWJsZSBmb3IgdGVzdHMpXG4gKiBAcmV0dXJucyBXYWl0IGR1cmF0aW9uIGluIG1pbGxpc2Vjb25kcyBiZWZvcmUgdGhlIG5leHQgYXR0ZW1wdFxuICovXG5leHBvcnQgY29uc3QgcmVzb2x2ZVJldHJ5RGVsYXlNcyA9IChcbiAgcmVzcG9uc2U6IFJlc3BvbnNlLFxuICBiYXNlQmFja29mZk1zOiBudW1iZXIsXG4gIGF0dGVtcHQ6IG51bWJlcixcbiAgbWF4QmFja29mZk1zPzogbnVtYmVyLFxuICBub3dNczogbnVtYmVyID0gRGF0ZS5ub3coKSxcbik6IG51bWJlciA9PiB7XG4gIGNvbnN0IGhlYWRlciA9IHJlc3BvbnNlLmhlYWRlcnM/LmdldCgnUmV0cnktQWZ0ZXInKTtcbiAgaWYgKGhlYWRlciA9PSBudWxsKSB7XG4gICAgcmV0dXJuIGZ1bGxKaXR0ZXIoYmFzZUJhY2tvZmZNcywgYXR0ZW1wdCwgbWF4QmFja29mZk1zKTtcbiAgfVxuXG4gIGNvbnN0IGZyb21IZWFkZXIgPSBwYXJzZVJldHJ5QWZ0ZXJNcyhoZWFkZXIsIG5vd01zKTtcbiAgaWYgKGZyb21IZWFkZXIgPT09IHVuZGVmaW5lZCkge1xuICAgIHJldHVybiBmdWxsSml0dGVyKGJhc2VCYWNrb2ZmTXMsIGF0dGVtcHQsIG1heEJhY2tvZmZNcyk7XG4gIH1cblxuICByZXR1cm4gZnJvbUhlYWRlcjtcbn07XG4iXX0=
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Typed errors thrown by {@link fetchRetrier}.
|
|
3
|
+
*
|
|
4
|
+
* All public error classes extend {@link FetchRetrierError} so callers can catch any library
|
|
5
|
+
* failure with one `instanceof` check.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Base class for every error thrown by this package.
|
|
9
|
+
*
|
|
10
|
+
* Catch {@link FetchRetrierError} to handle any library failure, or a subclass for a specific
|
|
11
|
+
* case. Distinct from native `fetch` `TypeError` and `AbortError` values, which are wrapped
|
|
12
|
+
* before they leave {@link fetchRetrier}.
|
|
13
|
+
*/
|
|
14
|
+
export declare class FetchRetrierError extends Error {
|
|
15
|
+
readonly name: string;
|
|
16
|
+
/**
|
|
17
|
+
* @param message - Human-readable reason
|
|
18
|
+
*/
|
|
19
|
+
constructor(message: string);
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Error thrown when the last attempt is cancelled by per-attempt timeout or an in-flight
|
|
23
|
+
* external {@link AbortSignal}. Remaining retries after an external abort throw
|
|
24
|
+
* {@link FetchRetrierAlreadyAbortedError} instead, because the signal stays aborted.
|
|
25
|
+
*/
|
|
26
|
+
export declare class FetchRetrierAbortError extends FetchRetrierError {
|
|
27
|
+
readonly name: string;
|
|
28
|
+
/**
|
|
29
|
+
* @param message - Human-readable reason (default: `'Aborted'`)
|
|
30
|
+
*/
|
|
31
|
+
constructor(message?: string);
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Error thrown when {@link RequestOptions.signal} is already aborted before an attempt starts.
|
|
35
|
+
*/
|
|
36
|
+
export declare class FetchRetrierAlreadyAbortedError extends FetchRetrierAbortError {
|
|
37
|
+
readonly name: string;
|
|
38
|
+
/**
|
|
39
|
+
* @param message - Human-readable reason (default: `'Signal was already aborted'`)
|
|
40
|
+
*/
|
|
41
|
+
constructor(message?: string);
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Error thrown when the server returns a non-OK HTTP status and no further retry is performed.
|
|
45
|
+
*
|
|
46
|
+
* Carries the last response `status` and the body text already consumed via `response.text()`
|
|
47
|
+
* (the same text passed to {@link RequestOptions.shouldRetry}).
|
|
48
|
+
*
|
|
49
|
+
* @property status - HTTP status code from the last non-OK response
|
|
50
|
+
* @property body - Response body text already read via `response.text()` for that attempt
|
|
51
|
+
*/
|
|
52
|
+
export declare class FetchRetrierHttpError extends FetchRetrierError {
|
|
53
|
+
readonly status: number;
|
|
54
|
+
readonly body: string;
|
|
55
|
+
readonly name: string;
|
|
56
|
+
/**
|
|
57
|
+
* @param message - Error description
|
|
58
|
+
* @param status - HTTP status code from the last non-OK response
|
|
59
|
+
* @param body - Response body text already read via `response.text()` for that attempt
|
|
60
|
+
*/
|
|
61
|
+
constructor(message: string, status: number, body: string);
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Error thrown when a fetch fails with a network-level error (e.g. DNS failure, connection refused).
|
|
65
|
+
*
|
|
66
|
+
* @property cause - Original error from the underlying `fetch`, when available
|
|
67
|
+
*/
|
|
68
|
+
export declare class FetchRetrierNetworkError extends FetchRetrierError {
|
|
69
|
+
readonly cause?: unknown | undefined;
|
|
70
|
+
readonly name: string;
|
|
71
|
+
/**
|
|
72
|
+
* @param message - Human-readable reason (default: `'Network error'`)
|
|
73
|
+
* @param cause - Original error from the underlying `fetch`, when available
|
|
74
|
+
*/
|
|
75
|
+
constructor(message?: string, cause?: unknown | undefined);
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Error thrown when {@link RequestOptions} contains invalid numeric values.
|
|
79
|
+
*
|
|
80
|
+
* Extends {@link FetchRetrierError}, not {@link TypeError}, so it is not treated as a network
|
|
81
|
+
* failure. Native `fetch` `TypeError` values are retried and surfaced as
|
|
82
|
+
* {@link FetchRetrierNetworkError} after the last attempt.
|
|
83
|
+
*/
|
|
84
|
+
export declare class FetchRetrierInvalidOptionsError extends FetchRetrierError {
|
|
85
|
+
readonly name: string;
|
|
86
|
+
/**
|
|
87
|
+
* @param message - Human-readable reason describing the invalid option
|
|
88
|
+
*/
|
|
89
|
+
constructor(message: string);
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Error thrown when an internal invariant fails (should not happen in normal use).
|
|
93
|
+
*/
|
|
94
|
+
export declare class FetchRetrierUnreachableError extends FetchRetrierError {
|
|
95
|
+
readonly name: string;
|
|
96
|
+
/**
|
|
97
|
+
* @param message - Human-readable reason (default: `'Unreachable'`)
|
|
98
|
+
*/
|
|
99
|
+
constructor(message?: string);
|
|
100
|
+
}
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Typed errors thrown by {@link fetchRetrier}.
|
|
4
|
+
*
|
|
5
|
+
* All public error classes extend {@link FetchRetrierError} so callers can catch any library
|
|
6
|
+
* failure with one `instanceof` check.
|
|
7
|
+
*/
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.FetchRetrierUnreachableError = exports.FetchRetrierInvalidOptionsError = exports.FetchRetrierNetworkError = exports.FetchRetrierHttpError = exports.FetchRetrierAlreadyAbortedError = exports.FetchRetrierAbortError = exports.FetchRetrierError = void 0;
|
|
10
|
+
/**
|
|
11
|
+
* Base class for every error thrown by this package.
|
|
12
|
+
*
|
|
13
|
+
* Catch {@link FetchRetrierError} to handle any library failure, or a subclass for a specific
|
|
14
|
+
* case. Distinct from native `fetch` `TypeError` and `AbortError` values, which are wrapped
|
|
15
|
+
* before they leave {@link fetchRetrier}.
|
|
16
|
+
*/
|
|
17
|
+
class FetchRetrierError extends Error {
|
|
18
|
+
/**
|
|
19
|
+
* @param message - Human-readable reason
|
|
20
|
+
*/
|
|
21
|
+
constructor(message) {
|
|
22
|
+
super(message);
|
|
23
|
+
this.name = 'FetchRetrierError';
|
|
24
|
+
Object.setPrototypeOf(this, FetchRetrierError.prototype);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
exports.FetchRetrierError = FetchRetrierError;
|
|
28
|
+
/**
|
|
29
|
+
* Error thrown when the last attempt is cancelled by per-attempt timeout or an in-flight
|
|
30
|
+
* external {@link AbortSignal}. Remaining retries after an external abort throw
|
|
31
|
+
* {@link FetchRetrierAlreadyAbortedError} instead, because the signal stays aborted.
|
|
32
|
+
*/
|
|
33
|
+
class FetchRetrierAbortError extends FetchRetrierError {
|
|
34
|
+
/**
|
|
35
|
+
* @param message - Human-readable reason (default: `'Aborted'`)
|
|
36
|
+
*/
|
|
37
|
+
constructor(message = 'Aborted') {
|
|
38
|
+
super(message);
|
|
39
|
+
this.name = 'FetchRetrierAbortError';
|
|
40
|
+
Object.setPrototypeOf(this, FetchRetrierAbortError.prototype);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
exports.FetchRetrierAbortError = FetchRetrierAbortError;
|
|
44
|
+
/**
|
|
45
|
+
* Error thrown when {@link RequestOptions.signal} is already aborted before an attempt starts.
|
|
46
|
+
*/
|
|
47
|
+
class FetchRetrierAlreadyAbortedError extends FetchRetrierAbortError {
|
|
48
|
+
/**
|
|
49
|
+
* @param message - Human-readable reason (default: `'Signal was already aborted'`)
|
|
50
|
+
*/
|
|
51
|
+
constructor(message = 'Signal was already aborted') {
|
|
52
|
+
super(message);
|
|
53
|
+
this.name = 'FetchRetrierAlreadyAbortedError';
|
|
54
|
+
Object.setPrototypeOf(this, FetchRetrierAlreadyAbortedError.prototype);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
exports.FetchRetrierAlreadyAbortedError = FetchRetrierAlreadyAbortedError;
|
|
58
|
+
/**
|
|
59
|
+
* Error thrown when the server returns a non-OK HTTP status and no further retry is performed.
|
|
60
|
+
*
|
|
61
|
+
* Carries the last response `status` and the body text already consumed via `response.text()`
|
|
62
|
+
* (the same text passed to {@link RequestOptions.shouldRetry}).
|
|
63
|
+
*
|
|
64
|
+
* @property status - HTTP status code from the last non-OK response
|
|
65
|
+
* @property body - Response body text already read via `response.text()` for that attempt
|
|
66
|
+
*/
|
|
67
|
+
class FetchRetrierHttpError extends FetchRetrierError {
|
|
68
|
+
/**
|
|
69
|
+
* @param message - Error description
|
|
70
|
+
* @param status - HTTP status code from the last non-OK response
|
|
71
|
+
* @param body - Response body text already read via `response.text()` for that attempt
|
|
72
|
+
*/
|
|
73
|
+
constructor(message, status, body) {
|
|
74
|
+
super(message);
|
|
75
|
+
this.status = status;
|
|
76
|
+
this.body = body;
|
|
77
|
+
this.name = 'FetchRetrierHttpError';
|
|
78
|
+
Object.setPrototypeOf(this, FetchRetrierHttpError.prototype);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
exports.FetchRetrierHttpError = FetchRetrierHttpError;
|
|
82
|
+
/**
|
|
83
|
+
* Error thrown when a fetch fails with a network-level error (e.g. DNS failure, connection refused).
|
|
84
|
+
*
|
|
85
|
+
* @property cause - Original error from the underlying `fetch`, when available
|
|
86
|
+
*/
|
|
87
|
+
class FetchRetrierNetworkError extends FetchRetrierError {
|
|
88
|
+
/**
|
|
89
|
+
* @param message - Human-readable reason (default: `'Network error'`)
|
|
90
|
+
* @param cause - Original error from the underlying `fetch`, when available
|
|
91
|
+
*/
|
|
92
|
+
constructor(message = 'Network error', cause) {
|
|
93
|
+
super(message);
|
|
94
|
+
this.cause = cause;
|
|
95
|
+
this.name = 'FetchRetrierNetworkError';
|
|
96
|
+
Object.setPrototypeOf(this, FetchRetrierNetworkError.prototype);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
exports.FetchRetrierNetworkError = FetchRetrierNetworkError;
|
|
100
|
+
/**
|
|
101
|
+
* Error thrown when {@link RequestOptions} contains invalid numeric values.
|
|
102
|
+
*
|
|
103
|
+
* Extends {@link FetchRetrierError}, not {@link TypeError}, so it is not treated as a network
|
|
104
|
+
* failure. Native `fetch` `TypeError` values are retried and surfaced as
|
|
105
|
+
* {@link FetchRetrierNetworkError} after the last attempt.
|
|
106
|
+
*/
|
|
107
|
+
class FetchRetrierInvalidOptionsError extends FetchRetrierError {
|
|
108
|
+
/**
|
|
109
|
+
* @param message - Human-readable reason describing the invalid option
|
|
110
|
+
*/
|
|
111
|
+
constructor(message) {
|
|
112
|
+
super(message);
|
|
113
|
+
this.name = 'FetchRetrierInvalidOptionsError';
|
|
114
|
+
Object.setPrototypeOf(this, FetchRetrierInvalidOptionsError.prototype);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
exports.FetchRetrierInvalidOptionsError = FetchRetrierInvalidOptionsError;
|
|
118
|
+
/**
|
|
119
|
+
* Error thrown when an internal invariant fails (should not happen in normal use).
|
|
120
|
+
*/
|
|
121
|
+
class FetchRetrierUnreachableError extends FetchRetrierError {
|
|
122
|
+
/**
|
|
123
|
+
* @param message - Human-readable reason (default: `'Unreachable'`)
|
|
124
|
+
*/
|
|
125
|
+
constructor(message = 'Unreachable') {
|
|
126
|
+
super(message);
|
|
127
|
+
this.name = 'FetchRetrierUnreachableError';
|
|
128
|
+
Object.setPrototypeOf(this, FetchRetrierUnreachableError.prototype);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
exports.FetchRetrierUnreachableError = FetchRetrierUnreachableError;
|
|
132
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZXJyb3JzLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL2NvcmUvZXJyb3JzLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7QUFBQTs7Ozs7R0FLRzs7O0FBRUg7Ozs7OztHQU1HO0FBQ0gsTUFBYSxpQkFBa0IsU0FBUSxLQUFLO0lBRTFDOztPQUVHO0lBQ0gsWUFBWSxPQUFlO1FBQ3pCLEtBQUssQ0FBQyxPQUFPLENBQUMsQ0FBQztRQUxDLFNBQUksR0FBVyxtQkFBbUIsQ0FBQztRQU1uRCxNQUFNLENBQUMsY0FBYyxDQUFDLElBQUksRUFBRSxpQkFBaUIsQ0FBQyxTQUFTLENBQUMsQ0FBQztJQUMzRCxDQUFDO0NBQ0Y7QUFURCw4Q0FTQztBQUVEOzs7O0dBSUc7QUFDSCxNQUFhLHNCQUF1QixTQUFRLGlCQUFpQjtJQUUzRDs7T0FFRztJQUNILFlBQVksT0FBTyxHQUFHLFNBQVM7UUFDN0IsS0FBSyxDQUFDLE9BQU8sQ0FBQyxDQUFDO1FBTEMsU0FBSSxHQUFXLHdCQUF3QixDQUFDO1FBTXhELE1BQU0sQ0FBQyxjQUFjLENBQUMsSUFBSSxFQUFFLHNCQUFzQixDQUFDLFNBQVMsQ0FBQyxDQUFDO0lBQ2hFLENBQUM7Q0FDRjtBQVRELHdEQVNDO0FBRUQ7O0dBRUc7QUFDSCxNQUFhLCtCQUFnQyxTQUFRLHNCQUFzQjtJQUV6RTs7T0FFRztJQUNILFlBQVksT0FBTyxHQUFHLDRCQUE0QjtRQUNoRCxLQUFLLENBQUMsT0FBTyxDQUFDLENBQUM7UUFMQyxTQUFJLEdBQVcsaUNBQWlDLENBQUM7UUFNakUsTUFBTSxDQUFDLGNBQWMsQ0FBQyxJQUFJLEVBQUUsK0JBQStCLENBQUMsU0FBUyxDQUFDLENBQUM7SUFDekUsQ0FBQztDQUNGO0FBVEQsMEVBU0M7QUFFRDs7Ozs7Ozs7R0FRRztBQUNILE1BQWEscUJBQXNCLFNBQVEsaUJBQWlCO0lBRTFEOzs7O09BSUc7SUFDSCxZQUNFLE9BQWUsRUFDQyxNQUFjLEVBQ2QsSUFBWTtRQUU1QixLQUFLLENBQUMsT0FBTyxDQUFDLENBQUM7UUFIQyxXQUFNLEdBQU4sTUFBTSxDQUFRO1FBQ2QsU0FBSSxHQUFKLElBQUksQ0FBUTtRQVRaLFNBQUksR0FBVyx1QkFBdUIsQ0FBQztRQVl2RCxNQUFNLENBQUMsY0FBYyxDQUFDLElBQUksRUFBRSxxQkFBcUIsQ0FBQyxTQUFTLENBQUMsQ0FBQztJQUMvRCxDQUFDO0NBQ0Y7QUFmRCxzREFlQztBQUVEOzs7O0dBSUc7QUFDSCxNQUFhLHdCQUF5QixTQUFRLGlCQUFpQjtJQUU3RDs7O09BR0c7SUFDSCxZQUFZLE9BQU8sR0FBRyxlQUFlLEVBQWtCLEtBQWU7UUFDcEUsS0FBSyxDQUFDLE9BQU8sQ0FBQyxDQUFDO1FBRHNDLFVBQUssR0FBTCxLQUFLLENBQVU7UUFMcEQsU0FBSSxHQUFXLDBCQUEwQixDQUFDO1FBTzFELE1BQU0sQ0FBQyxjQUFjLENBQUMsSUFBSSxFQUFFLHdCQUF3QixDQUFDLFNBQVMsQ0FBQyxDQUFDO0lBQ2xFLENBQUM7Q0FDRjtBQVZELDREQVVDO0FBRUQ7Ozs7OztHQU1HO0FBQ0gsTUFBYSwrQkFBZ0MsU0FBUSxpQkFBaUI7SUFFcEU7O09BRUc7SUFDSCxZQUFZLE9BQWU7UUFDekIsS0FBSyxDQUFDLE9BQU8sQ0FBQyxDQUFDO1FBTEMsU0FBSSxHQUFXLGlDQUFpQyxDQUFDO1FBTWpFLE1BQU0sQ0FBQyxjQUFjLENBQUMsSUFBSSxFQUFFLCtCQUErQixDQUFDLFNBQVMsQ0FBQyxDQUFDO0lBQ3pFLENBQUM7Q0FDRjtBQVRELDBFQVNDO0FBRUQ7O0dBRUc7QUFDSCxNQUFhLDRCQUE2QixTQUFRLGlCQUFpQjtJQUVqRTs7T0FFRztJQUNILFlBQVksT0FBTyxHQUFHLGFBQWE7UUFDakMsS0FBSyxDQUFDLE9BQU8sQ0FBQyxDQUFDO1FBTEMsU0FBSSxHQUFXLDhCQUE4QixDQUFDO1FBTTlELE1BQU0sQ0FBQyxjQUFjLENBQUMsSUFBSSxFQUFFLDRCQUE0QixDQUFDLFNBQVMsQ0FBQyxDQUFDO0lBQ3RFLENBQUM7Q0FDRjtBQVRELG9FQVNDIiwic291cmNlc0NvbnRlbnQiOlsiLyoqXG4gKiBUeXBlZCBlcnJvcnMgdGhyb3duIGJ5IHtAbGluayBmZXRjaFJldHJpZXJ9LlxuICpcbiAqIEFsbCBwdWJsaWMgZXJyb3IgY2xhc3NlcyBleHRlbmQge0BsaW5rIEZldGNoUmV0cmllckVycm9yfSBzbyBjYWxsZXJzIGNhbiBjYXRjaCBhbnkgbGlicmFyeVxuICogZmFpbHVyZSB3aXRoIG9uZSBgaW5zdGFuY2VvZmAgY2hlY2suXG4gKi9cblxuLyoqXG4gKiBCYXNlIGNsYXNzIGZvciBldmVyeSBlcnJvciB0aHJvd24gYnkgdGhpcyBwYWNrYWdlLlxuICpcbiAqIENhdGNoIHtAbGluayBGZXRjaFJldHJpZXJFcnJvcn0gdG8gaGFuZGxlIGFueSBsaWJyYXJ5IGZhaWx1cmUsIG9yIGEgc3ViY2xhc3MgZm9yIGEgc3BlY2lmaWNcbiAqIGNhc2UuIERpc3RpbmN0IGZyb20gbmF0aXZlIGBmZXRjaGAgYFR5cGVFcnJvcmAgYW5kIGBBYm9ydEVycm9yYCB2YWx1ZXMsIHdoaWNoIGFyZSB3cmFwcGVkXG4gKiBiZWZvcmUgdGhleSBsZWF2ZSB7QGxpbmsgZmV0Y2hSZXRyaWVyfS5cbiAqL1xuZXhwb3J0IGNsYXNzIEZldGNoUmV0cmllckVycm9yIGV4dGVuZHMgRXJyb3Ige1xuICBvdmVycmlkZSByZWFkb25seSBuYW1lOiBzdHJpbmcgPSAnRmV0Y2hSZXRyaWVyRXJyb3InO1xuICAvKipcbiAgICogQHBhcmFtIG1lc3NhZ2UgLSBIdW1hbi1yZWFkYWJsZSByZWFzb25cbiAgICovXG4gIGNvbnN0cnVjdG9yKG1lc3NhZ2U6IHN0cmluZykge1xuICAgIHN1cGVyKG1lc3NhZ2UpO1xuICAgIE9iamVjdC5zZXRQcm90b3R5cGVPZih0aGlzLCBGZXRjaFJldHJpZXJFcnJvci5wcm90b3R5cGUpO1xuICB9XG59XG5cbi8qKlxuICogRXJyb3IgdGhyb3duIHdoZW4gdGhlIGxhc3QgYXR0ZW1wdCBpcyBjYW5jZWxsZWQgYnkgcGVyLWF0dGVtcHQgdGltZW91dCBvciBhbiBpbi1mbGlnaHRcbiAqIGV4dGVybmFsIHtAbGluayBBYm9ydFNpZ25hbH0uIFJlbWFpbmluZyByZXRyaWVzIGFmdGVyIGFuIGV4dGVybmFsIGFib3J0IHRocm93XG4gKiB7QGxpbmsgRmV0Y2hSZXRyaWVyQWxyZWFkeUFib3J0ZWRFcnJvcn0gaW5zdGVhZCwgYmVjYXVzZSB0aGUgc2lnbmFsIHN0YXlzIGFib3J0ZWQuXG4gKi9cbmV4cG9ydCBjbGFzcyBGZXRjaFJldHJpZXJBYm9ydEVycm9yIGV4dGVuZHMgRmV0Y2hSZXRyaWVyRXJyb3Ige1xuICBvdmVycmlkZSByZWFkb25seSBuYW1lOiBzdHJpbmcgPSAnRmV0Y2hSZXRyaWVyQWJvcnRFcnJvcic7XG4gIC8qKlxuICAgKiBAcGFyYW0gbWVzc2FnZSAtIEh1bWFuLXJlYWRhYmxlIHJlYXNvbiAoZGVmYXVsdDogYCdBYm9ydGVkJ2ApXG4gICAqL1xuICBjb25zdHJ1Y3RvcihtZXNzYWdlID0gJ0Fib3J0ZWQnKSB7XG4gICAgc3VwZXIobWVzc2FnZSk7XG4gICAgT2JqZWN0LnNldFByb3RvdHlwZU9mKHRoaXMsIEZldGNoUmV0cmllckFib3J0RXJyb3IucHJvdG90eXBlKTtcbiAgfVxufVxuXG4vKipcbiAqIEVycm9yIHRocm93biB3aGVuIHtAbGluayBSZXF1ZXN0T3B0aW9ucy5zaWduYWx9IGlzIGFscmVhZHkgYWJvcnRlZCBiZWZvcmUgYW4gYXR0ZW1wdCBzdGFydHMuXG4gKi9cbmV4cG9ydCBjbGFzcyBGZXRjaFJldHJpZXJBbHJlYWR5QWJvcnRlZEVycm9yIGV4dGVuZHMgRmV0Y2hSZXRyaWVyQWJvcnRFcnJvciB7XG4gIG92ZXJyaWRlIHJlYWRvbmx5IG5hbWU6IHN0cmluZyA9ICdGZXRjaFJldHJpZXJBbHJlYWR5QWJvcnRlZEVycm9yJztcbiAgLyoqXG4gICAqIEBwYXJhbSBtZXNzYWdlIC0gSHVtYW4tcmVhZGFibGUgcmVhc29uIChkZWZhdWx0OiBgJ1NpZ25hbCB3YXMgYWxyZWFkeSBhYm9ydGVkJ2ApXG4gICAqL1xuICBjb25zdHJ1Y3RvcihtZXNzYWdlID0gJ1NpZ25hbCB3YXMgYWxyZWFkeSBhYm9ydGVkJykge1xuICAgIHN1cGVyKG1lc3NhZ2UpO1xuICAgIE9iamVjdC5zZXRQcm90b3R5cGVPZih0aGlzLCBGZXRjaFJldHJpZXJBbHJlYWR5QWJvcnRlZEVycm9yLnByb3RvdHlwZSk7XG4gIH1cbn1cblxuLyoqXG4gKiBFcnJvciB0aHJvd24gd2hlbiB0aGUgc2VydmVyIHJldHVybnMgYSBub24tT0sgSFRUUCBzdGF0dXMgYW5kIG5vIGZ1cnRoZXIgcmV0cnkgaXMgcGVyZm9ybWVkLlxuICpcbiAqIENhcnJpZXMgdGhlIGxhc3QgcmVzcG9uc2UgYHN0YXR1c2AgYW5kIHRoZSBib2R5IHRleHQgYWxyZWFkeSBjb25zdW1lZCB2aWEgYHJlc3BvbnNlLnRleHQoKWBcbiAqICh0aGUgc2FtZSB0ZXh0IHBhc3NlZCB0byB7QGxpbmsgUmVxdWVzdE9wdGlvbnMuc2hvdWxkUmV0cnl9KS5cbiAqXG4gKiBAcHJvcGVydHkgc3RhdHVzIC0gSFRUUCBzdGF0dXMgY29kZSBmcm9tIHRoZSBsYXN0IG5vbi1PSyByZXNwb25zZVxuICogQHByb3BlcnR5IGJvZHkgLSBSZXNwb25zZSBib2R5IHRleHQgYWxyZWFkeSByZWFkIHZpYSBgcmVzcG9uc2UudGV4dCgpYCBmb3IgdGhhdCBhdHRlbXB0XG4gKi9cbmV4cG9ydCBjbGFzcyBGZXRjaFJldHJpZXJIdHRwRXJyb3IgZXh0ZW5kcyBGZXRjaFJldHJpZXJFcnJvciB7XG4gIG92ZXJyaWRlIHJlYWRvbmx5IG5hbWU6IHN0cmluZyA9ICdGZXRjaFJldHJpZXJIdHRwRXJyb3InO1xuICAvKipcbiAgICogQHBhcmFtIG1lc3NhZ2UgLSBFcnJvciBkZXNjcmlwdGlvblxuICAgKiBAcGFyYW0gc3RhdHVzIC0gSFRUUCBzdGF0dXMgY29kZSBmcm9tIHRoZSBsYXN0IG5vbi1PSyByZXNwb25zZVxuICAgKiBAcGFyYW0gYm9keSAtIFJlc3BvbnNlIGJvZHkgdGV4dCBhbHJlYWR5IHJlYWQgdmlhIGByZXNwb25zZS50ZXh0KClgIGZvciB0aGF0IGF0dGVtcHRcbiAgICovXG4gIGNvbnN0cnVjdG9yKFxuICAgIG1lc3NhZ2U6IHN0cmluZyxcbiAgICBwdWJsaWMgcmVhZG9ubHkgc3RhdHVzOiBudW1iZXIsXG4gICAgcHVibGljIHJlYWRvbmx5IGJvZHk6IHN0cmluZyxcbiAgKSB7XG4gICAgc3VwZXIobWVzc2FnZSk7XG4gICAgT2JqZWN0LnNldFByb3RvdHlwZU9mKHRoaXMsIEZldGNoUmV0cmllckh0dHBFcnJvci5wcm90b3R5cGUpO1xuICB9XG59XG5cbi8qKlxuICogRXJyb3IgdGhyb3duIHdoZW4gYSBmZXRjaCBmYWlscyB3aXRoIGEgbmV0d29yay1sZXZlbCBlcnJvciAoZS5nLiBETlMgZmFpbHVyZSwgY29ubmVjdGlvbiByZWZ1c2VkKS5cbiAqXG4gKiBAcHJvcGVydHkgY2F1c2UgLSBPcmlnaW5hbCBlcnJvciBmcm9tIHRoZSB1bmRlcmx5aW5nIGBmZXRjaGAsIHdoZW4gYXZhaWxhYmxlXG4gKi9cbmV4cG9ydCBjbGFzcyBGZXRjaFJldHJpZXJOZXR3b3JrRXJyb3IgZXh0ZW5kcyBGZXRjaFJldHJpZXJFcnJvciB7XG4gIG92ZXJyaWRlIHJlYWRvbmx5IG5hbWU6IHN0cmluZyA9ICdGZXRjaFJldHJpZXJOZXR3b3JrRXJyb3InO1xuICAvKipcbiAgICogQHBhcmFtIG1lc3NhZ2UgLSBIdW1hbi1yZWFkYWJsZSByZWFzb24gKGRlZmF1bHQ6IGAnTmV0d29yayBlcnJvcidgKVxuICAgKiBAcGFyYW0gY2F1c2UgLSBPcmlnaW5hbCBlcnJvciBmcm9tIHRoZSB1bmRlcmx5aW5nIGBmZXRjaGAsIHdoZW4gYXZhaWxhYmxlXG4gICAqL1xuICBjb25zdHJ1Y3RvcihtZXNzYWdlID0gJ05ldHdvcmsgZXJyb3InLCBwdWJsaWMgcmVhZG9ubHkgY2F1c2U/OiB1bmtub3duKSB7XG4gICAgc3VwZXIobWVzc2FnZSk7XG4gICAgT2JqZWN0LnNldFByb3RvdHlwZU9mKHRoaXMsIEZldGNoUmV0cmllck5ldHdvcmtFcnJvci5wcm90b3R5cGUpO1xuICB9XG59XG5cbi8qKlxuICogRXJyb3IgdGhyb3duIHdoZW4ge0BsaW5rIFJlcXVlc3RPcHRpb25zfSBjb250YWlucyBpbnZhbGlkIG51bWVyaWMgdmFsdWVzLlxuICpcbiAqIEV4dGVuZHMge0BsaW5rIEZldGNoUmV0cmllckVycm9yfSwgbm90IHtAbGluayBUeXBlRXJyb3J9LCBzbyBpdCBpcyBub3QgdHJlYXRlZCBhcyBhIG5ldHdvcmtcbiAqIGZhaWx1cmUuIE5hdGl2ZSBgZmV0Y2hgIGBUeXBlRXJyb3JgIHZhbHVlcyBhcmUgcmV0cmllZCBhbmQgc3VyZmFjZWQgYXNcbiAqIHtAbGluayBGZXRjaFJldHJpZXJOZXR3b3JrRXJyb3J9IGFmdGVyIHRoZSBsYXN0IGF0dGVtcHQuXG4gKi9cbmV4cG9ydCBjbGFzcyBGZXRjaFJldHJpZXJJbnZhbGlkT3B0aW9uc0Vycm9yIGV4dGVuZHMgRmV0Y2hSZXRyaWVyRXJyb3Ige1xuICBvdmVycmlkZSByZWFkb25seSBuYW1lOiBzdHJpbmcgPSAnRmV0Y2hSZXRyaWVySW52YWxpZE9wdGlvbnNFcnJvcic7XG4gIC8qKlxuICAgKiBAcGFyYW0gbWVzc2FnZSAtIEh1bWFuLXJlYWRhYmxlIHJlYXNvbiBkZXNjcmliaW5nIHRoZSBpbnZhbGlkIG9wdGlvblxuICAgKi9cbiAgY29uc3RydWN0b3IobWVzc2FnZTogc3RyaW5nKSB7XG4gICAgc3VwZXIobWVzc2FnZSk7XG4gICAgT2JqZWN0LnNldFByb3RvdHlwZU9mKHRoaXMsIEZldGNoUmV0cmllckludmFsaWRPcHRpb25zRXJyb3IucHJvdG90eXBlKTtcbiAgfVxufVxuXG4vKipcbiAqIEVycm9yIHRocm93biB3aGVuIGFuIGludGVybmFsIGludmFyaWFudCBmYWlscyAoc2hvdWxkIG5vdCBoYXBwZW4gaW4gbm9ybWFsIHVzZSkuXG4gKi9cbmV4cG9ydCBjbGFzcyBGZXRjaFJldHJpZXJVbnJlYWNoYWJsZUVycm9yIGV4dGVuZHMgRmV0Y2hSZXRyaWVyRXJyb3Ige1xuICBvdmVycmlkZSByZWFkb25seSBuYW1lOiBzdHJpbmcgPSAnRmV0Y2hSZXRyaWVyVW5yZWFjaGFibGVFcnJvcic7XG4gIC8qKlxuICAgKiBAcGFyYW0gbWVzc2FnZSAtIEh1bWFuLXJlYWRhYmxlIHJlYXNvbiAoZGVmYXVsdDogYCdVbnJlYWNoYWJsZSdgKVxuICAgKi9cbiAgY29uc3RydWN0b3IobWVzc2FnZSA9ICdVbnJlYWNoYWJsZScpIHtcbiAgICBzdXBlcihtZXNzYWdlKTtcbiAgICBPYmplY3Quc2V0UHJvdG90eXBlT2YodGhpcywgRmV0Y2hSZXRyaWVyVW5yZWFjaGFibGVFcnJvci5wcm90b3R5cGUpO1xuICB9XG59XG4iXX0=
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { RequestOptions } from './options';
|
|
2
|
+
/**
|
|
3
|
+
* Wraps `fetch` with retries, per-attempt timeout, Retry-After support, full-jitter backoff, and
|
|
4
|
+
* optional cancellation.
|
|
5
|
+
*
|
|
6
|
+
* Each attempt calls {@link timedFetch} with `timeoutMs`. Non-OK responses are retried when
|
|
7
|
+
* `shouldRetry` returns `true` (default: {@link defaultShouldRetry}). Between HTTP retries, a
|
|
8
|
+
* valid `Retry-After` header (delta-seconds or HTTP-date) is preferred over full jitter; abort
|
|
9
|
+
* and network retries always use full jitter. Optional {@link RequestOptions.maxBackoffMs} clips
|
|
10
|
+
* the jitter result, not a valid `Retry-After`. The same {@link FetchInitOptions} (including
|
|
11
|
+
* `body`) is reused on every attempt.
|
|
12
|
+
*
|
|
13
|
+
* @param url - Request URL passed to `fetch`
|
|
14
|
+
* @param options - {@link RequestOptions} controlling retries, timeout, request init, and cancellation
|
|
15
|
+
* @returns The first {@link Response} for which `ok` is `true`
|
|
16
|
+
* @throws {FetchRetrierError} All failures from this function are subclasses of this class
|
|
17
|
+
* @throws {FetchRetrierInvalidOptionsError} If `retries < 1`, `timeoutMs <= 0`, `baseBackoffMs < 0`,
|
|
18
|
+
* or `maxBackoffMs` is set and `< 0`
|
|
19
|
+
* @throws {FetchRetrierAlreadyAbortedError} If `options.signal` is already aborted before an attempt,
|
|
20
|
+
* including the next attempt after an in-flight external abort while retries remain
|
|
21
|
+
* @throws {FetchRetrierHttpError} On a non-OK response that is not retried or after the last attempt
|
|
22
|
+
* (includes `status` and `body`)
|
|
23
|
+
* @throws {FetchRetrierNetworkError} On a network `TypeError` after the last attempt
|
|
24
|
+
* @throws {FetchRetrierAbortError} On per-attempt timeout after the last attempt, or external abort
|
|
25
|
+
* on the last attempt
|
|
26
|
+
* @throws {FetchRetrierUnreachableError} If the retry loop exits without returning (internal bug)
|
|
27
|
+
*/
|
|
28
|
+
export declare const fetchRetrier: (url: string, options: RequestOptions) => Promise<Response>;
|