fetch-retrier 0.5.6 → 0.6.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/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 +1 -1
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.fetchRetrier = void 0;
|
|
4
|
+
const jitter_1 = require("./backoff/jitter");
|
|
5
|
+
const retry_after_1 = require("./backoff/retry-after");
|
|
6
|
+
const errors_1 = require("./errors");
|
|
7
|
+
const timed_fetch_1 = require("./http/timed-fetch");
|
|
8
|
+
const default_should_retry_1 = require("./policy/default-should-retry");
|
|
9
|
+
const validate_options_1 = require("./policy/validate-options");
|
|
10
|
+
const retry_predicates_1 = require("./retry-predicates");
|
|
11
|
+
const wait_1 = require("./time/wait");
|
|
12
|
+
/**
|
|
13
|
+
* Wraps `fetch` with retries, per-attempt timeout, Retry-After support, full-jitter backoff, and
|
|
14
|
+
* optional cancellation.
|
|
15
|
+
*
|
|
16
|
+
* Each attempt calls {@link timedFetch} with `timeoutMs`. Non-OK responses are retried when
|
|
17
|
+
* `shouldRetry` returns `true` (default: {@link defaultShouldRetry}). Between HTTP retries, a
|
|
18
|
+
* valid `Retry-After` header (delta-seconds or HTTP-date) is preferred over full jitter; abort
|
|
19
|
+
* and network retries always use full jitter. Optional {@link RequestOptions.maxBackoffMs} clips
|
|
20
|
+
* the jitter result, not a valid `Retry-After`. The same {@link FetchInitOptions} (including
|
|
21
|
+
* `body`) is reused on every attempt.
|
|
22
|
+
*
|
|
23
|
+
* @param url - Request URL passed to `fetch`
|
|
24
|
+
* @param options - {@link RequestOptions} controlling retries, timeout, request init, and cancellation
|
|
25
|
+
* @returns The first {@link Response} for which `ok` is `true`
|
|
26
|
+
* @throws {FetchRetrierError} All failures from this function are subclasses of this class
|
|
27
|
+
* @throws {FetchRetrierInvalidOptionsError} If `retries < 1`, `timeoutMs <= 0`, `baseBackoffMs < 0`,
|
|
28
|
+
* or `maxBackoffMs` is set and `< 0`
|
|
29
|
+
* @throws {FetchRetrierAlreadyAbortedError} If `options.signal` is already aborted before an attempt,
|
|
30
|
+
* including the next attempt after an in-flight external abort while retries remain
|
|
31
|
+
* @throws {FetchRetrierHttpError} On a non-OK response that is not retried or after the last attempt
|
|
32
|
+
* (includes `status` and `body`)
|
|
33
|
+
* @throws {FetchRetrierNetworkError} On a network `TypeError` after the last attempt
|
|
34
|
+
* @throws {FetchRetrierAbortError} On per-attempt timeout after the last attempt, or external abort
|
|
35
|
+
* on the last attempt
|
|
36
|
+
* @throws {FetchRetrierUnreachableError} If the retry loop exits without returning (internal bug)
|
|
37
|
+
*/
|
|
38
|
+
const fetchRetrier = async (url, options) => {
|
|
39
|
+
const { headers, init, retries, timeoutMs, baseBackoffMs, maxBackoffMs, signal: externalSignal, shouldRetry = default_should_retry_1.defaultShouldRetry, } = options;
|
|
40
|
+
(0, validate_options_1.validateRequestOptions)({ retries, timeoutMs, baseBackoffMs, maxBackoffMs });
|
|
41
|
+
for (let attempt = 1; attempt <= retries; attempt++) {
|
|
42
|
+
if (externalSignal?.aborted) {
|
|
43
|
+
throw new errors_1.FetchRetrierAlreadyAbortedError();
|
|
44
|
+
}
|
|
45
|
+
try {
|
|
46
|
+
const res = await (0, timed_fetch_1.timedFetch)(url, {
|
|
47
|
+
timeoutMs,
|
|
48
|
+
init,
|
|
49
|
+
headers,
|
|
50
|
+
signal: externalSignal,
|
|
51
|
+
});
|
|
52
|
+
if (res.ok) {
|
|
53
|
+
return res;
|
|
54
|
+
}
|
|
55
|
+
const text = await res.text();
|
|
56
|
+
const isContinue = shouldRetry(res, text);
|
|
57
|
+
if (!isContinue) {
|
|
58
|
+
throw new errors_1.FetchRetrierHttpError(`Non-retriable HTTP error: ${res.status}`, res.status, text);
|
|
59
|
+
}
|
|
60
|
+
if ((0, retry_predicates_1.isLastAttempt)(attempt, retries)) {
|
|
61
|
+
throw new errors_1.FetchRetrierHttpError(`HTTP ${res.status}`, res.status, text);
|
|
62
|
+
}
|
|
63
|
+
await (0, wait_1.wait)((0, retry_after_1.resolveRetryDelayMs)(res, baseBackoffMs, attempt, maxBackoffMs));
|
|
64
|
+
}
|
|
65
|
+
catch (err) {
|
|
66
|
+
if (err instanceof Error && err.name === 'AbortError') {
|
|
67
|
+
if ((0, retry_predicates_1.isLastAttempt)(attempt, retries)) {
|
|
68
|
+
throw err instanceof errors_1.FetchRetrierAbortError ? err : new errors_1.FetchRetrierAbortError();
|
|
69
|
+
}
|
|
70
|
+
await (0, wait_1.wait)((0, jitter_1.fullJitter)(baseBackoffMs, attempt, maxBackoffMs));
|
|
71
|
+
continue;
|
|
72
|
+
}
|
|
73
|
+
if (err instanceof TypeError) {
|
|
74
|
+
if ((0, retry_predicates_1.isLastAttempt)(attempt, retries)) {
|
|
75
|
+
throw new errors_1.FetchRetrierNetworkError('Network error', err);
|
|
76
|
+
}
|
|
77
|
+
await (0, wait_1.wait)((0, jitter_1.fullJitter)(baseBackoffMs, attempt, maxBackoffMs));
|
|
78
|
+
continue;
|
|
79
|
+
}
|
|
80
|
+
throw err;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
throw new errors_1.FetchRetrierUnreachableError();
|
|
84
|
+
};
|
|
85
|
+
exports.fetchRetrier = fetchRetrier;
|
|
86
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZmV0Y2gtcmV0cmllci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9jb3JlL2ZldGNoLXJldHJpZXIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7O0FBQUEsNkNBQThDO0FBQzlDLHVEQUE0RDtBQUM1RCxxQ0FNa0I7QUFDbEIsb0RBQWdEO0FBRWhELHdFQUFtRTtBQUNuRSxnRUFBbUU7QUFDbkUseURBQW1EO0FBQ25ELHNDQUFtQztBQUVuQzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztHQXlCRztBQUNJLE1BQU0sWUFBWSxHQUFHLEtBQUssRUFBRSxHQUFXLEVBQUUsT0FBdUIsRUFBcUIsRUFBRTtJQUM1RixNQUFNLEVBQ0osT0FBTyxFQUNQLElBQUksRUFDSixPQUFPLEVBQ1AsU0FBUyxFQUNULGFBQWEsRUFDYixZQUFZLEVBQ1osTUFBTSxFQUFFLGNBQWMsRUFDdEIsV0FBVyxHQUFHLHlDQUFrQixHQUNqQyxHQUFHLE9BQU8sQ0FBQztJQUVaLElBQUEseUNBQXNCLEVBQUMsRUFBRSxPQUFPLEVBQUUsU0FBUyxFQUFFLGFBQWEsRUFBRSxZQUFZLEVBQUUsQ0FBQyxDQUFDO0lBRTVFLEtBQUssSUFBSSxPQUFPLEdBQUcsQ0FBQyxFQUFFLE9BQU8sSUFBSSxPQUFPLEVBQUUsT0FBTyxFQUFFLEVBQUUsQ0FBQztRQUNwRCxJQUFJLGNBQWMsRUFBRSxPQUFPLEVBQUUsQ0FBQztZQUM1QixNQUFNLElBQUksd0NBQStCLEVBQUUsQ0FBQztRQUM5QyxDQUFDO1FBRUQsSUFBSSxDQUFDO1lBQ0gsTUFBTSxHQUFHLEdBQUcsTUFBTSxJQUFBLHdCQUFVLEVBQUMsR0FBRyxFQUFFO2dCQUNoQyxTQUFTO2dCQUNULElBQUk7Z0JBQ0osT0FBTztnQkFDUCxNQUFNLEVBQUUsY0FBYzthQUN2QixDQUFDLENBQUM7WUFFSCxJQUFJLEdBQUcsQ0FBQyxFQUFFLEVBQUUsQ0FBQztnQkFDWCxPQUFPLEdBQUcsQ0FBQztZQUNiLENBQUM7WUFFRCxNQUFNLElBQUksR0FBRyxNQUFNLEdBQUcsQ0FBQyxJQUFJLEVBQUUsQ0FBQztZQUM5QixNQUFNLFVBQVUsR0FBRyxXQUFXLENBQUMsR0FBRyxFQUFFLElBQUksQ0FBQyxDQUFDO1lBRTFDLElBQUksQ0FBQyxVQUFVLEVBQUUsQ0FBQztnQkFDaEIsTUFBTSxJQUFJLDhCQUFxQixDQUFDLDZCQUE2QixHQUFHLENBQUMsTUFBTSxFQUFFLEVBQUUsR0FBRyxDQUFDLE1BQU0sRUFBRSxJQUFJLENBQUMsQ0FBQztZQUMvRixDQUFDO1lBRUQsSUFBSSxJQUFBLGdDQUFhLEVBQUMsT0FBTyxFQUFFLE9BQU8sQ0FBQyxFQUFFLENBQUM7Z0JBQ3BDLE1BQU0sSUFBSSw4QkFBcUIsQ0FBQyxRQUFRLEdBQUcsQ0FBQyxNQUFNLEVBQUUsRUFBRSxHQUFHLENBQUMsTUFBTSxFQUFFLElBQUksQ0FBQyxDQUFDO1lBQzFFLENBQUM7WUFFRCxNQUFNLElBQUEsV0FBSSxFQUFDLElBQUEsaUNBQW1CLEVBQUMsR0FBRyxFQUFFLGFBQWEsRUFBRSxPQUFPLEVBQUUsWUFBWSxDQUFDLENBQUMsQ0FBQztRQUM3RSxDQUFDO1FBQUMsT0FBTyxHQUFZLEVBQUUsQ0FBQztZQUN0QixJQUFJLEdBQUcsWUFBWSxLQUFLLElBQUksR0FBRyxDQUFDLElBQUksS0FBSyxZQUFZLEVBQUUsQ0FBQztnQkFDdEQsSUFBSSxJQUFBLGdDQUFhLEVBQUMsT0FBTyxFQUFFLE9BQU8sQ0FBQyxFQUFFLENBQUM7b0JBQ3BDLE1BQU0sR0FBRyxZQUFZLCtCQUFzQixDQUFDLENBQUMsQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDLElBQUksK0JBQXNCLEVBQUUsQ0FBQztnQkFDbkYsQ0FBQztnQkFDRCxNQUFNLElBQUEsV0FBSSxFQUFDLElBQUEsbUJBQVUsRUFBQyxhQUFhLEVBQUUsT0FBTyxFQUFFLFlBQVksQ0FBQyxDQUFDLENBQUM7Z0JBQzdELFNBQVM7WUFDWCxDQUFDO1lBRUQsSUFBSSxHQUFHLFlBQVksU0FBUyxFQUFFLENBQUM7Z0JBQzdCLElBQUksSUFBQSxnQ0FBYSxFQUFDLE9BQU8sRUFBRSxPQUFPLENBQUMsRUFBRSxDQUFDO29CQUNwQyxNQUFNLElBQUksaUNBQXdCLENBQUMsZUFBZSxFQUFFLEdBQUcsQ0FBQyxDQUFDO2dCQUMzRCxDQUFDO2dCQUNELE1BQU0sSUFBQSxXQUFJLEVBQUMsSUFBQSxtQkFBVSxFQUFDLGFBQWEsRUFBRSxPQUFPLEVBQUUsWUFBWSxDQUFDLENBQUMsQ0FBQztnQkFDN0QsU0FBUztZQUNYLENBQUM7WUFFRCxNQUFNLEdBQUcsQ0FBQztRQUNaLENBQUM7SUFDSCxDQUFDO0lBRUQsTUFBTSxJQUFJLHFDQUE0QixFQUFFLENBQUM7QUFDM0MsQ0FBQyxDQUFDO0FBakVXLFFBQUEsWUFBWSxnQkFpRXZCIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgZnVsbEppdHRlciB9IGZyb20gJy4vYmFja29mZi9qaXR0ZXInO1xuaW1wb3J0IHsgcmVzb2x2ZVJldHJ5RGVsYXlNcyB9IGZyb20gJy4vYmFja29mZi9yZXRyeS1hZnRlcic7XG5pbXBvcnQge1xuICBGZXRjaFJldHJpZXJBYm9ydEVycm9yLFxuICBGZXRjaFJldHJpZXJBbHJlYWR5QWJvcnRlZEVycm9yLFxuICBGZXRjaFJldHJpZXJIdHRwRXJyb3IsXG4gIEZldGNoUmV0cmllck5ldHdvcmtFcnJvcixcbiAgRmV0Y2hSZXRyaWVyVW5yZWFjaGFibGVFcnJvcixcbn0gZnJvbSAnLi9lcnJvcnMnO1xuaW1wb3J0IHsgdGltZWRGZXRjaCB9IGZyb20gJy4vaHR0cC90aW1lZC1mZXRjaCc7XG5pbXBvcnQgeyBSZXF1ZXN0T3B0aW9ucyB9IGZyb20gJy4vb3B0aW9ucyc7XG5pbXBvcnQgeyBkZWZhdWx0U2hvdWxkUmV0cnkgfSBmcm9tICcuL3BvbGljeS9kZWZhdWx0LXNob3VsZC1yZXRyeSc7XG5pbXBvcnQgeyB2YWxpZGF0ZVJlcXVlc3RPcHRpb25zIH0gZnJvbSAnLi9wb2xpY3kvdmFsaWRhdGUtb3B0aW9ucyc7XG5pbXBvcnQgeyBpc0xhc3RBdHRlbXB0IH0gZnJvbSAnLi9yZXRyeS1wcmVkaWNhdGVzJztcbmltcG9ydCB7IHdhaXQgfSBmcm9tICcuL3RpbWUvd2FpdCc7XG5cbi8qKlxuICogV3JhcHMgYGZldGNoYCB3aXRoIHJldHJpZXMsIHBlci1hdHRlbXB0IHRpbWVvdXQsIFJldHJ5LUFmdGVyIHN1cHBvcnQsIGZ1bGwtaml0dGVyIGJhY2tvZmYsIGFuZFxuICogb3B0aW9uYWwgY2FuY2VsbGF0aW9uLlxuICpcbiAqIEVhY2ggYXR0ZW1wdCBjYWxscyB7QGxpbmsgdGltZWRGZXRjaH0gd2l0aCBgdGltZW91dE1zYC4gTm9uLU9LIHJlc3BvbnNlcyBhcmUgcmV0cmllZCB3aGVuXG4gKiBgc2hvdWxkUmV0cnlgIHJldHVybnMgYHRydWVgIChkZWZhdWx0OiB7QGxpbmsgZGVmYXVsdFNob3VsZFJldHJ5fSkuIEJldHdlZW4gSFRUUCByZXRyaWVzLCBhXG4gKiB2YWxpZCBgUmV0cnktQWZ0ZXJgIGhlYWRlciAoZGVsdGEtc2Vjb25kcyBvciBIVFRQLWRhdGUpIGlzIHByZWZlcnJlZCBvdmVyIGZ1bGwgaml0dGVyOyBhYm9ydFxuICogYW5kIG5ldHdvcmsgcmV0cmllcyBhbHdheXMgdXNlIGZ1bGwgaml0dGVyLiBPcHRpb25hbCB7QGxpbmsgUmVxdWVzdE9wdGlvbnMubWF4QmFja29mZk1zfSBjbGlwc1xuICogdGhlIGppdHRlciByZXN1bHQsIG5vdCBhIHZhbGlkIGBSZXRyeS1BZnRlcmAuIFRoZSBzYW1lIHtAbGluayBGZXRjaEluaXRPcHRpb25zfSAoaW5jbHVkaW5nXG4gKiBgYm9keWApIGlzIHJldXNlZCBvbiBldmVyeSBhdHRlbXB0LlxuICpcbiAqIEBwYXJhbSB1cmwgLSBSZXF1ZXN0IFVSTCBwYXNzZWQgdG8gYGZldGNoYFxuICogQHBhcmFtIG9wdGlvbnMgLSB7QGxpbmsgUmVxdWVzdE9wdGlvbnN9IGNvbnRyb2xsaW5nIHJldHJpZXMsIHRpbWVvdXQsIHJlcXVlc3QgaW5pdCwgYW5kIGNhbmNlbGxhdGlvblxuICogQHJldHVybnMgVGhlIGZpcnN0IHtAbGluayBSZXNwb25zZX0gZm9yIHdoaWNoIGBva2AgaXMgYHRydWVgXG4gKiBAdGhyb3dzIHtGZXRjaFJldHJpZXJFcnJvcn0gQWxsIGZhaWx1cmVzIGZyb20gdGhpcyBmdW5jdGlvbiBhcmUgc3ViY2xhc3NlcyBvZiB0aGlzIGNsYXNzXG4gKiBAdGhyb3dzIHtGZXRjaFJldHJpZXJJbnZhbGlkT3B0aW9uc0Vycm9yfSBJZiBgcmV0cmllcyA8IDFgLCBgdGltZW91dE1zIDw9IDBgLCBgYmFzZUJhY2tvZmZNcyA8IDBgLFxuICogICBvciBgbWF4QmFja29mZk1zYCBpcyBzZXQgYW5kIGA8IDBgXG4gKiBAdGhyb3dzIHtGZXRjaFJldHJpZXJBbHJlYWR5QWJvcnRlZEVycm9yfSBJZiBgb3B0aW9ucy5zaWduYWxgIGlzIGFscmVhZHkgYWJvcnRlZCBiZWZvcmUgYW4gYXR0ZW1wdCxcbiAqICAgaW5jbHVkaW5nIHRoZSBuZXh0IGF0dGVtcHQgYWZ0ZXIgYW4gaW4tZmxpZ2h0IGV4dGVybmFsIGFib3J0IHdoaWxlIHJldHJpZXMgcmVtYWluXG4gKiBAdGhyb3dzIHtGZXRjaFJldHJpZXJIdHRwRXJyb3J9IE9uIGEgbm9uLU9LIHJlc3BvbnNlIHRoYXQgaXMgbm90IHJldHJpZWQgb3IgYWZ0ZXIgdGhlIGxhc3QgYXR0ZW1wdFxuICogICAoaW5jbHVkZXMgYHN0YXR1c2AgYW5kIGBib2R5YClcbiAqIEB0aHJvd3Mge0ZldGNoUmV0cmllck5ldHdvcmtFcnJvcn0gT24gYSBuZXR3b3JrIGBUeXBlRXJyb3JgIGFmdGVyIHRoZSBsYXN0IGF0dGVtcHRcbiAqIEB0aHJvd3Mge0ZldGNoUmV0cmllckFib3J0RXJyb3J9IE9uIHBlci1hdHRlbXB0IHRpbWVvdXQgYWZ0ZXIgdGhlIGxhc3QgYXR0ZW1wdCwgb3IgZXh0ZXJuYWwgYWJvcnRcbiAqICAgb24gdGhlIGxhc3QgYXR0ZW1wdFxuICogQHRocm93cyB7RmV0Y2hSZXRyaWVyVW5yZWFjaGFibGVFcnJvcn0gSWYgdGhlIHJldHJ5IGxvb3AgZXhpdHMgd2l0aG91dCByZXR1cm5pbmcgKGludGVybmFsIGJ1ZylcbiAqL1xuZXhwb3J0IGNvbnN0IGZldGNoUmV0cmllciA9IGFzeW5jICh1cmw6IHN0cmluZywgb3B0aW9uczogUmVxdWVzdE9wdGlvbnMpOiBQcm9taXNlPFJlc3BvbnNlPiA9PiB7XG4gIGNvbnN0IHtcbiAgICBoZWFkZXJzLFxuICAgIGluaXQsXG4gICAgcmV0cmllcyxcbiAgICB0aW1lb3V0TXMsXG4gICAgYmFzZUJhY2tvZmZNcyxcbiAgICBtYXhCYWNrb2ZmTXMsXG4gICAgc2lnbmFsOiBleHRlcm5hbFNpZ25hbCxcbiAgICBzaG91bGRSZXRyeSA9IGRlZmF1bHRTaG91bGRSZXRyeSxcbiAgfSA9IG9wdGlvbnM7XG5cbiAgdmFsaWRhdGVSZXF1ZXN0T3B0aW9ucyh7IHJldHJpZXMsIHRpbWVvdXRNcywgYmFzZUJhY2tvZmZNcywgbWF4QmFja29mZk1zIH0pO1xuXG4gIGZvciAobGV0IGF0dGVtcHQgPSAxOyBhdHRlbXB0IDw9IHJldHJpZXM7IGF0dGVtcHQrKykge1xuICAgIGlmIChleHRlcm5hbFNpZ25hbD8uYWJvcnRlZCkge1xuICAgICAgdGhyb3cgbmV3IEZldGNoUmV0cmllckFscmVhZHlBYm9ydGVkRXJyb3IoKTtcbiAgICB9XG5cbiAgICB0cnkge1xuICAgICAgY29uc3QgcmVzID0gYXdhaXQgdGltZWRGZXRjaCh1cmwsIHtcbiAgICAgICAgdGltZW91dE1zLFxuICAgICAgICBpbml0LFxuICAgICAgICBoZWFkZXJzLFxuICAgICAgICBzaWduYWw6IGV4dGVybmFsU2lnbmFsLFxuICAgICAgfSk7XG5cbiAgICAgIGlmIChyZXMub2spIHtcbiAgICAgICAgcmV0dXJuIHJlcztcbiAgICAgIH1cblxuICAgICAgY29uc3QgdGV4dCA9IGF3YWl0IHJlcy50ZXh0KCk7XG4gICAgICBjb25zdCBpc0NvbnRpbnVlID0gc2hvdWxkUmV0cnkocmVzLCB0ZXh0KTtcblxuICAgICAgaWYgKCFpc0NvbnRpbnVlKSB7XG4gICAgICAgIHRocm93IG5ldyBGZXRjaFJldHJpZXJIdHRwRXJyb3IoYE5vbi1yZXRyaWFibGUgSFRUUCBlcnJvcjogJHtyZXMuc3RhdHVzfWAsIHJlcy5zdGF0dXMsIHRleHQpO1xuICAgICAgfVxuXG4gICAgICBpZiAoaXNMYXN0QXR0ZW1wdChhdHRlbXB0LCByZXRyaWVzKSkge1xuICAgICAgICB0aHJvdyBuZXcgRmV0Y2hSZXRyaWVySHR0cEVycm9yKGBIVFRQICR7cmVzLnN0YXR1c31gLCByZXMuc3RhdHVzLCB0ZXh0KTtcbiAgICAgIH1cblxuICAgICAgYXdhaXQgd2FpdChyZXNvbHZlUmV0cnlEZWxheU1zKHJlcywgYmFzZUJhY2tvZmZNcywgYXR0ZW1wdCwgbWF4QmFja29mZk1zKSk7XG4gICAgfSBjYXRjaCAoZXJyOiB1bmtub3duKSB7XG4gICAgICBpZiAoZXJyIGluc3RhbmNlb2YgRXJyb3IgJiYgZXJyLm5hbWUgPT09ICdBYm9ydEVycm9yJykge1xuICAgICAgICBpZiAoaXNMYXN0QXR0ZW1wdChhdHRlbXB0LCByZXRyaWVzKSkge1xuICAgICAgICAgIHRocm93IGVyciBpbnN0YW5jZW9mIEZldGNoUmV0cmllckFib3J0RXJyb3IgPyBlcnIgOiBuZXcgRmV0Y2hSZXRyaWVyQWJvcnRFcnJvcigpO1xuICAgICAgICB9XG4gICAgICAgIGF3YWl0IHdhaXQoZnVsbEppdHRlcihiYXNlQmFja29mZk1zLCBhdHRlbXB0LCBtYXhCYWNrb2ZmTXMpKTtcbiAgICAgICAgY29udGludWU7XG4gICAgICB9XG5cbiAgICAgIGlmIChlcnIgaW5zdGFuY2VvZiBUeXBlRXJyb3IpIHtcbiAgICAgICAgaWYgKGlzTGFzdEF0dGVtcHQoYXR0ZW1wdCwgcmV0cmllcykpIHtcbiAgICAgICAgICB0aHJvdyBuZXcgRmV0Y2hSZXRyaWVyTmV0d29ya0Vycm9yKCdOZXR3b3JrIGVycm9yJywgZXJyKTtcbiAgICAgICAgfVxuICAgICAgICBhd2FpdCB3YWl0KGZ1bGxKaXR0ZXIoYmFzZUJhY2tvZmZNcywgYXR0ZW1wdCwgbWF4QmFja29mZk1zKSk7XG4gICAgICAgIGNvbnRpbnVlO1xuICAgICAgfVxuXG4gICAgICB0aHJvdyBlcnI7XG4gICAgfVxuICB9XG5cbiAgdGhyb3cgbmV3IEZldGNoUmV0cmllclVucmVhY2hhYmxlRXJyb3IoKTtcbn07XG4iXX0=
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* One HTTP attempt: per-attempt timeout and optional external abort, then `fetch`.
|
|
3
|
+
*
|
|
4
|
+
* Does not retry or map failures to package errors. Native `AbortError` and `TypeError`
|
|
5
|
+
* propagate to the caller.
|
|
6
|
+
*/
|
|
7
|
+
export interface TimedFetchOptions {
|
|
8
|
+
/**
|
|
9
|
+
* Per-attempt timeout in milliseconds.
|
|
10
|
+
*/
|
|
11
|
+
timeoutMs: number;
|
|
12
|
+
/**
|
|
13
|
+
* `fetch` options excluding `signal`.
|
|
14
|
+
*/
|
|
15
|
+
init?: Omit<RequestInit, 'signal'>;
|
|
16
|
+
/**
|
|
17
|
+
* Headers sent on this attempt. Override `init.headers` when both are set.
|
|
18
|
+
*/
|
|
19
|
+
headers?: Record<string, string>;
|
|
20
|
+
/**
|
|
21
|
+
* Optional external abort signal; aborts the in-flight request when fired.
|
|
22
|
+
*/
|
|
23
|
+
signal?: AbortSignal;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Calls `fetch` with an internal {@link AbortSignal} for `timeoutMs`.
|
|
27
|
+
*
|
|
28
|
+
* @param url - Request URL passed to `fetch`
|
|
29
|
+
* @param options - Timeout, init, headers, and optional external signal
|
|
30
|
+
* @returns The {@link Response} from this single attempt
|
|
31
|
+
*/
|
|
32
|
+
export declare const timedFetch: (url: string, options: TimedFetchOptions) => Promise<Response>;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* One HTTP attempt: per-attempt timeout and optional external abort, then `fetch`.
|
|
4
|
+
*
|
|
5
|
+
* Does not retry or map failures to package errors. Native `AbortError` and `TypeError`
|
|
6
|
+
* propagate to the caller.
|
|
7
|
+
*/
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.timedFetch = void 0;
|
|
10
|
+
/**
|
|
11
|
+
* Calls `fetch` with an internal {@link AbortSignal} for `timeoutMs`.
|
|
12
|
+
*
|
|
13
|
+
* @param url - Request URL passed to `fetch`
|
|
14
|
+
* @param options - Timeout, init, headers, and optional external signal
|
|
15
|
+
* @returns The {@link Response} from this single attempt
|
|
16
|
+
*/
|
|
17
|
+
const timedFetch = async (url, options) => {
|
|
18
|
+
const { timeoutMs, init, headers, signal: externalSignal } = options;
|
|
19
|
+
const controller = new AbortController();
|
|
20
|
+
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
21
|
+
const onExternalAbort = () => {
|
|
22
|
+
clearTimeout(timer);
|
|
23
|
+
controller.abort();
|
|
24
|
+
};
|
|
25
|
+
if (externalSignal) {
|
|
26
|
+
externalSignal.addEventListener('abort', onExternalAbort);
|
|
27
|
+
}
|
|
28
|
+
try {
|
|
29
|
+
return await fetch(url, {
|
|
30
|
+
...init,
|
|
31
|
+
...(headers !== undefined ? { headers } : {}),
|
|
32
|
+
signal: controller.signal,
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
finally {
|
|
36
|
+
clearTimeout(timer);
|
|
37
|
+
externalSignal?.removeEventListener('abort', onExternalAbort);
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
exports.timedFetch = timedFetch;
|
|
41
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidGltZWQtZmV0Y2guanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi9zcmMvY29yZS9odHRwL3RpbWVkLWZldGNoLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7QUFBQTs7Ozs7R0FLRzs7O0FBcUJIOzs7Ozs7R0FNRztBQUNJLE1BQU0sVUFBVSxHQUFHLEtBQUssRUFBRSxHQUFXLEVBQUUsT0FBMEIsRUFBcUIsRUFBRTtJQUM3RixNQUFNLEVBQUUsU0FBUyxFQUFFLElBQUksRUFBRSxPQUFPLEVBQUUsTUFBTSxFQUFFLGNBQWMsRUFBRSxHQUFHLE9BQU8sQ0FBQztJQUNyRSxNQUFNLFVBQVUsR0FBRyxJQUFJLGVBQWUsRUFBRSxDQUFDO0lBQ3pDLE1BQU0sS0FBSyxHQUFHLFVBQVUsQ0FBQyxHQUFHLEVBQUUsQ0FBQyxVQUFVLENBQUMsS0FBSyxFQUFFLEVBQUUsU0FBUyxDQUFDLENBQUM7SUFFOUQsTUFBTSxlQUFlLEdBQUcsR0FBUyxFQUFFO1FBQ2pDLFlBQVksQ0FBQyxLQUFLLENBQUMsQ0FBQztRQUNwQixVQUFVLENBQUMsS0FBSyxFQUFFLENBQUM7SUFDckIsQ0FBQyxDQUFDO0lBRUYsSUFBSSxjQUFjLEVBQUUsQ0FBQztRQUNuQixjQUFjLENBQUMsZ0JBQWdCLENBQUMsT0FBTyxFQUFFLGVBQWUsQ0FBQyxDQUFDO0lBQzVELENBQUM7SUFFRCxJQUFJLENBQUM7UUFDSCxPQUFPLE1BQU0sS0FBSyxDQUFDLEdBQUcsRUFBRTtZQUN0QixHQUFHLElBQUk7WUFDUCxHQUFHLENBQUMsT0FBTyxLQUFLLFNBQVMsQ0FBQyxDQUFDLENBQUMsRUFBRSxPQUFPLEVBQUUsQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDO1lBQzdDLE1BQU0sRUFBRSxVQUFVLENBQUMsTUFBTTtTQUMxQixDQUFDLENBQUM7SUFDTCxDQUFDO1lBQVMsQ0FBQztRQUNULFlBQVksQ0FBQyxLQUFLLENBQUMsQ0FBQztRQUNwQixjQUFjLEVBQUUsbUJBQW1CLENBQUMsT0FBTyxFQUFFLGVBQWUsQ0FBQyxDQUFDO0lBQ2hFLENBQUM7QUFDSCxDQUFDLENBQUM7QUF4QlcsUUFBQSxVQUFVLGNBd0JyQiIsInNvdXJjZXNDb250ZW50IjpbIi8qKlxuICogT25lIEhUVFAgYXR0ZW1wdDogcGVyLWF0dGVtcHQgdGltZW91dCBhbmQgb3B0aW9uYWwgZXh0ZXJuYWwgYWJvcnQsIHRoZW4gYGZldGNoYC5cbiAqXG4gKiBEb2VzIG5vdCByZXRyeSBvciBtYXAgZmFpbHVyZXMgdG8gcGFja2FnZSBlcnJvcnMuIE5hdGl2ZSBgQWJvcnRFcnJvcmAgYW5kIGBUeXBlRXJyb3JgXG4gKiBwcm9wYWdhdGUgdG8gdGhlIGNhbGxlci5cbiAqL1xuXG5leHBvcnQgaW50ZXJmYWNlIFRpbWVkRmV0Y2hPcHRpb25zIHtcbiAgLyoqXG4gICAqIFBlci1hdHRlbXB0IHRpbWVvdXQgaW4gbWlsbGlzZWNvbmRzLlxuICAgKi9cbiAgdGltZW91dE1zOiBudW1iZXI7XG4gIC8qKlxuICAgKiBgZmV0Y2hgIG9wdGlvbnMgZXhjbHVkaW5nIGBzaWduYWxgLlxuICAgKi9cbiAgaW5pdD86IE9taXQ8UmVxdWVzdEluaXQsICdzaWduYWwnPjtcbiAgLyoqXG4gICAqIEhlYWRlcnMgc2VudCBvbiB0aGlzIGF0dGVtcHQuIE92ZXJyaWRlIGBpbml0LmhlYWRlcnNgIHdoZW4gYm90aCBhcmUgc2V0LlxuICAgKi9cbiAgaGVhZGVycz86IFJlY29yZDxzdHJpbmcsIHN0cmluZz47XG4gIC8qKlxuICAgKiBPcHRpb25hbCBleHRlcm5hbCBhYm9ydCBzaWduYWw7IGFib3J0cyB0aGUgaW4tZmxpZ2h0IHJlcXVlc3Qgd2hlbiBmaXJlZC5cbiAgICovXG4gIHNpZ25hbD86IEFib3J0U2lnbmFsO1xufVxuXG4vKipcbiAqIENhbGxzIGBmZXRjaGAgd2l0aCBhbiBpbnRlcm5hbCB7QGxpbmsgQWJvcnRTaWduYWx9IGZvciBgdGltZW91dE1zYC5cbiAqXG4gKiBAcGFyYW0gdXJsIC0gUmVxdWVzdCBVUkwgcGFzc2VkIHRvIGBmZXRjaGBcbiAqIEBwYXJhbSBvcHRpb25zIC0gVGltZW91dCwgaW5pdCwgaGVhZGVycywgYW5kIG9wdGlvbmFsIGV4dGVybmFsIHNpZ25hbFxuICogQHJldHVybnMgVGhlIHtAbGluayBSZXNwb25zZX0gZnJvbSB0aGlzIHNpbmdsZSBhdHRlbXB0XG4gKi9cbmV4cG9ydCBjb25zdCB0aW1lZEZldGNoID0gYXN5bmMgKHVybDogc3RyaW5nLCBvcHRpb25zOiBUaW1lZEZldGNoT3B0aW9ucyk6IFByb21pc2U8UmVzcG9uc2U+ID0+IHtcbiAgY29uc3QgeyB0aW1lb3V0TXMsIGluaXQsIGhlYWRlcnMsIHNpZ25hbDogZXh0ZXJuYWxTaWduYWwgfSA9IG9wdGlvbnM7XG4gIGNvbnN0IGNvbnRyb2xsZXIgPSBuZXcgQWJvcnRDb250cm9sbGVyKCk7XG4gIGNvbnN0IHRpbWVyID0gc2V0VGltZW91dCgoKSA9PiBjb250cm9sbGVyLmFib3J0KCksIHRpbWVvdXRNcyk7XG5cbiAgY29uc3Qgb25FeHRlcm5hbEFib3J0ID0gKCk6IHZvaWQgPT4ge1xuICAgIGNsZWFyVGltZW91dCh0aW1lcik7XG4gICAgY29udHJvbGxlci5hYm9ydCgpO1xuICB9O1xuXG4gIGlmIChleHRlcm5hbFNpZ25hbCkge1xuICAgIGV4dGVybmFsU2lnbmFsLmFkZEV2ZW50TGlzdGVuZXIoJ2Fib3J0Jywgb25FeHRlcm5hbEFib3J0KTtcbiAgfVxuXG4gIHRyeSB7XG4gICAgcmV0dXJuIGF3YWl0IGZldGNoKHVybCwge1xuICAgICAgLi4uaW5pdCxcbiAgICAgIC4uLihoZWFkZXJzICE9PSB1bmRlZmluZWQgPyB7IGhlYWRlcnMgfSA6IHt9KSxcbiAgICAgIHNpZ25hbDogY29udHJvbGxlci5zaWduYWwsXG4gICAgfSk7XG4gIH0gZmluYWxseSB7XG4gICAgY2xlYXJUaW1lb3V0KHRpbWVyKTtcbiAgICBleHRlcm5hbFNpZ25hbD8ucmVtb3ZlRXZlbnRMaXN0ZW5lcignYWJvcnQnLCBvbkV4dGVybmFsQWJvcnQpO1xuICB9XG59O1xuIl19
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Public option types for {@link fetchRetrier}.
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* `fetch` options forwarded to every attempt, excluding `signal`.
|
|
6
|
+
*
|
|
7
|
+
* Use for `method`, `body`, `credentials`, `redirect`, `mode`, `cache`, and other
|
|
8
|
+
* {@link https://developer.mozilla.org/en-US/docs/Web/API/RequestInit | RequestInit} fields.
|
|
9
|
+
* Per-attempt abort and timeout are handled internally via `signal` and must not be set here.
|
|
10
|
+
*/
|
|
11
|
+
export type FetchInitOptions = Omit<RequestInit, 'signal'>;
|
|
12
|
+
/**
|
|
13
|
+
* Options for {@link fetchRetrier}: retry policy, timeout, backoff (including `Retry-After`),
|
|
14
|
+
* request payload, and cancellation.
|
|
15
|
+
*
|
|
16
|
+
* Request shape is built as `{ ...init, headers?, signal }` on each attempt. Top-level `headers`
|
|
17
|
+
* override `init.headers` when both are provided.
|
|
18
|
+
*
|
|
19
|
+
* Numeric fields are validated when {@link fetchRetrier} is called; invalid values throw
|
|
20
|
+
* {@link FetchRetrierInvalidOptionsError}.
|
|
21
|
+
*/
|
|
22
|
+
export interface RequestOptions {
|
|
23
|
+
/**
|
|
24
|
+
* HTTP headers sent on every attempt.
|
|
25
|
+
* When `init.headers` is also set, these values take precedence for duplicate keys.
|
|
26
|
+
*/
|
|
27
|
+
headers?: Record<string, string>;
|
|
28
|
+
/**
|
|
29
|
+
* Additional {@link FetchInitOptions} merged into each `fetch` call (e.g. POST `method` and JSON `body`).
|
|
30
|
+
* The same `init` is reused across retries.
|
|
31
|
+
*/
|
|
32
|
+
init?: FetchInitOptions;
|
|
33
|
+
/**
|
|
34
|
+
* Maximum number of attempts, including the first.
|
|
35
|
+
* Must be `>= 1`.
|
|
36
|
+
*/
|
|
37
|
+
retries: number;
|
|
38
|
+
/**
|
|
39
|
+
* Per-attempt timeout in milliseconds; uses an internal {@link AbortController} when exceeded.
|
|
40
|
+
* Must be `> 0`.
|
|
41
|
+
*/
|
|
42
|
+
timeoutMs: number;
|
|
43
|
+
/**
|
|
44
|
+
* Base backoff in milliseconds for full jitter when `Retry-After` is absent or invalid.
|
|
45
|
+
* The exponential span for attempt `n` is `baseBackoffMs * 2^n`, then the result is clipped
|
|
46
|
+
* by {@link RequestOptions.maxBackoffMs} when that option is set.
|
|
47
|
+
* Must be `>= 0` (`0` skips backoff delay between attempts when falling back to jitter).
|
|
48
|
+
*/
|
|
49
|
+
baseBackoffMs: number;
|
|
50
|
+
/**
|
|
51
|
+
* Optional ceiling in milliseconds applied to the full-jitter delay.
|
|
52
|
+
* When set, `Math.min(jitter, maxBackoffMs)` is used. Does not clip a valid `Retry-After`.
|
|
53
|
+
* Must be `>= 0` when provided (`0` skips jitter wait). Omitted means no extra clip.
|
|
54
|
+
*/
|
|
55
|
+
maxBackoffMs?: number;
|
|
56
|
+
/**
|
|
57
|
+
* Optional external {@link AbortSignal}. When aborted during an attempt, the in-flight request
|
|
58
|
+
* is aborted. On the last attempt this surfaces as {@link FetchRetrierAbortError}. If retries
|
|
59
|
+
* remain, the next attempt sees the still-aborted signal and throws
|
|
60
|
+
* {@link FetchRetrierAlreadyAbortedError}. Distinct from per-attempt timeout, which retries
|
|
61
|
+
* remaining attempts and only then throws {@link FetchRetrierAbortError}.
|
|
62
|
+
*/
|
|
63
|
+
signal?: AbortSignal;
|
|
64
|
+
/**
|
|
65
|
+
* Invoked after `response.text()` when `response.ok` is false.
|
|
66
|
+
* Return `true` to schedule another attempt (until `retries` is exhausted).
|
|
67
|
+
* Default: {@link defaultShouldRetry} (see {@link DEFAULT_RETRYABLE_HTTP_STATUSES}).
|
|
68
|
+
*
|
|
69
|
+
* @param response - Non-OK response from the current attempt
|
|
70
|
+
* @param body - Response body text from `response.text()`
|
|
71
|
+
* @returns `true` to schedule another attempt (until `retries` is exhausted)
|
|
72
|
+
*/
|
|
73
|
+
shouldRetry?: (response: Response, body: string) => boolean;
|
|
74
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Public option types for {@link fetchRetrier}.
|
|
4
|
+
*/
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoib3B0aW9ucy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9jb3JlL29wdGlvbnMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IjtBQUFBOztHQUVHIiwic291cmNlc0NvbnRlbnQiOlsiLyoqXG4gKiBQdWJsaWMgb3B0aW9uIHR5cGVzIGZvciB7QGxpbmsgZmV0Y2hSZXRyaWVyfS5cbiAqL1xuXG4vKipcbiAqIGBmZXRjaGAgb3B0aW9ucyBmb3J3YXJkZWQgdG8gZXZlcnkgYXR0ZW1wdCwgZXhjbHVkaW5nIGBzaWduYWxgLlxuICpcbiAqIFVzZSBmb3IgYG1ldGhvZGAsIGBib2R5YCwgYGNyZWRlbnRpYWxzYCwgYHJlZGlyZWN0YCwgYG1vZGVgLCBgY2FjaGVgLCBhbmQgb3RoZXJcbiAqIHtAbGluayBodHRwczovL2RldmVsb3Blci5tb3ppbGxhLm9yZy9lbi1VUy9kb2NzL1dlYi9BUEkvUmVxdWVzdEluaXQgfCBSZXF1ZXN0SW5pdH0gZmllbGRzLlxuICogUGVyLWF0dGVtcHQgYWJvcnQgYW5kIHRpbWVvdXQgYXJlIGhhbmRsZWQgaW50ZXJuYWxseSB2aWEgYHNpZ25hbGAgYW5kIG11c3Qgbm90IGJlIHNldCBoZXJlLlxuICovXG5leHBvcnQgdHlwZSBGZXRjaEluaXRPcHRpb25zID0gT21pdDxSZXF1ZXN0SW5pdCwgJ3NpZ25hbCc+O1xuXG4vKipcbiAqIE9wdGlvbnMgZm9yIHtAbGluayBmZXRjaFJldHJpZXJ9OiByZXRyeSBwb2xpY3ksIHRpbWVvdXQsIGJhY2tvZmYgKGluY2x1ZGluZyBgUmV0cnktQWZ0ZXJgKSxcbiAqIHJlcXVlc3QgcGF5bG9hZCwgYW5kIGNhbmNlbGxhdGlvbi5cbiAqXG4gKiBSZXF1ZXN0IHNoYXBlIGlzIGJ1aWx0IGFzIGB7IC4uLmluaXQsIGhlYWRlcnM/LCBzaWduYWwgfWAgb24gZWFjaCBhdHRlbXB0LiBUb3AtbGV2ZWwgYGhlYWRlcnNgXG4gKiBvdmVycmlkZSBgaW5pdC5oZWFkZXJzYCB3aGVuIGJvdGggYXJlIHByb3ZpZGVkLlxuICpcbiAqIE51bWVyaWMgZmllbGRzIGFyZSB2YWxpZGF0ZWQgd2hlbiB7QGxpbmsgZmV0Y2hSZXRyaWVyfSBpcyBjYWxsZWQ7IGludmFsaWQgdmFsdWVzIHRocm93XG4gKiB7QGxpbmsgRmV0Y2hSZXRyaWVySW52YWxpZE9wdGlvbnNFcnJvcn0uXG4gKi9cbmV4cG9ydCBpbnRlcmZhY2UgUmVxdWVzdE9wdGlvbnMge1xuICAvKipcbiAgICogSFRUUCBoZWFkZXJzIHNlbnQgb24gZXZlcnkgYXR0ZW1wdC5cbiAgICogV2hlbiBgaW5pdC5oZWFkZXJzYCBpcyBhbHNvIHNldCwgdGhlc2UgdmFsdWVzIHRha2UgcHJlY2VkZW5jZSBmb3IgZHVwbGljYXRlIGtleXMuXG4gICAqL1xuICBoZWFkZXJzPzogUmVjb3JkPHN0cmluZywgc3RyaW5nPjtcbiAgLyoqXG4gICAqIEFkZGl0aW9uYWwge0BsaW5rIEZldGNoSW5pdE9wdGlvbnN9IG1lcmdlZCBpbnRvIGVhY2ggYGZldGNoYCBjYWxsIChlLmcuIFBPU1QgYG1ldGhvZGAgYW5kIEpTT04gYGJvZHlgKS5cbiAgICogVGhlIHNhbWUgYGluaXRgIGlzIHJldXNlZCBhY3Jvc3MgcmV0cmllcy5cbiAgICovXG4gIGluaXQ/OiBGZXRjaEluaXRPcHRpb25zO1xuICAvKipcbiAgICogTWF4aW11bSBudW1iZXIgb2YgYXR0ZW1wdHMsIGluY2x1ZGluZyB0aGUgZmlyc3QuXG4gICAqIE11c3QgYmUgYD49IDFgLlxuICAgKi9cbiAgcmV0cmllczogbnVtYmVyO1xuICAvKipcbiAgICogUGVyLWF0dGVtcHQgdGltZW91dCBpbiBtaWxsaXNlY29uZHM7IHVzZXMgYW4gaW50ZXJuYWwge0BsaW5rIEFib3J0Q29udHJvbGxlcn0gd2hlbiBleGNlZWRlZC5cbiAgICogTXVzdCBiZSBgPiAwYC5cbiAgICovXG4gIHRpbWVvdXRNczogbnVtYmVyO1xuICAvKipcbiAgICogQmFzZSBiYWNrb2ZmIGluIG1pbGxpc2Vjb25kcyBmb3IgZnVsbCBqaXR0ZXIgd2hlbiBgUmV0cnktQWZ0ZXJgIGlzIGFic2VudCBvciBpbnZhbGlkLlxuICAgKiBUaGUgZXhwb25lbnRpYWwgc3BhbiBmb3IgYXR0ZW1wdCBgbmAgaXMgYGJhc2VCYWNrb2ZmTXMgKiAyXm5gLCB0aGVuIHRoZSByZXN1bHQgaXMgY2xpcHBlZFxuICAgKiBieSB7QGxpbmsgUmVxdWVzdE9wdGlvbnMubWF4QmFja29mZk1zfSB3aGVuIHRoYXQgb3B0aW9uIGlzIHNldC5cbiAgICogTXVzdCBiZSBgPj0gMGAgKGAwYCBza2lwcyBiYWNrb2ZmIGRlbGF5IGJldHdlZW4gYXR0ZW1wdHMgd2hlbiBmYWxsaW5nIGJhY2sgdG8gaml0dGVyKS5cbiAgICovXG4gIGJhc2VCYWNrb2ZmTXM6IG51bWJlcjtcbiAgLyoqXG4gICAqIE9wdGlvbmFsIGNlaWxpbmcgaW4gbWlsbGlzZWNvbmRzIGFwcGxpZWQgdG8gdGhlIGZ1bGwtaml0dGVyIGRlbGF5LlxuICAgKiBXaGVuIHNldCwgYE1hdGgubWluKGppdHRlciwgbWF4QmFja29mZk1zKWAgaXMgdXNlZC4gRG9lcyBub3QgY2xpcCBhIHZhbGlkIGBSZXRyeS1BZnRlcmAuXG4gICAqIE11c3QgYmUgYD49IDBgIHdoZW4gcHJvdmlkZWQgKGAwYCBza2lwcyBqaXR0ZXIgd2FpdCkuIE9taXR0ZWQgbWVhbnMgbm8gZXh0cmEgY2xpcC5cbiAgICovXG4gIG1heEJhY2tvZmZNcz86IG51bWJlcjtcbiAgLyoqXG4gICAqIE9wdGlvbmFsIGV4dGVybmFsIHtAbGluayBBYm9ydFNpZ25hbH0uIFdoZW4gYWJvcnRlZCBkdXJpbmcgYW4gYXR0ZW1wdCwgdGhlIGluLWZsaWdodCByZXF1ZXN0XG4gICAqIGlzIGFib3J0ZWQuIE9uIHRoZSBsYXN0IGF0dGVtcHQgdGhpcyBzdXJmYWNlcyBhcyB7QGxpbmsgRmV0Y2hSZXRyaWVyQWJvcnRFcnJvcn0uIElmIHJldHJpZXNcbiAgICogcmVtYWluLCB0aGUgbmV4dCBhdHRlbXB0IHNlZXMgdGhlIHN0aWxsLWFib3J0ZWQgc2lnbmFsIGFuZCB0aHJvd3NcbiAgICoge0BsaW5rIEZldGNoUmV0cmllckFscmVhZHlBYm9ydGVkRXJyb3J9LiBEaXN0aW5jdCBmcm9tIHBlci1hdHRlbXB0IHRpbWVvdXQsIHdoaWNoIHJldHJpZXNcbiAgICogcmVtYWluaW5nIGF0dGVtcHRzIGFuZCBvbmx5IHRoZW4gdGhyb3dzIHtAbGluayBGZXRjaFJldHJpZXJBYm9ydEVycm9yfS5cbiAgICovXG4gIHNpZ25hbD86IEFib3J0U2lnbmFsO1xuICAvKipcbiAgICogSW52b2tlZCBhZnRlciBgcmVzcG9uc2UudGV4dCgpYCB3aGVuIGByZXNwb25zZS5va2AgaXMgZmFsc2UuXG4gICAqIFJldHVybiBgdHJ1ZWAgdG8gc2NoZWR1bGUgYW5vdGhlciBhdHRlbXB0ICh1bnRpbCBgcmV0cmllc2AgaXMgZXhoYXVzdGVkKS5cbiAgICogRGVmYXVsdDoge0BsaW5rIGRlZmF1bHRTaG91bGRSZXRyeX0gKHNlZSB7QGxpbmsgREVGQVVMVF9SRVRSWUFCTEVfSFRUUF9TVEFUVVNFU30pLlxuICAgKlxuICAgKiBAcGFyYW0gcmVzcG9uc2UgLSBOb24tT0sgcmVzcG9uc2UgZnJvbSB0aGUgY3VycmVudCBhdHRlbXB0XG4gICAqIEBwYXJhbSBib2R5IC0gUmVzcG9uc2UgYm9keSB0ZXh0IGZyb20gYHJlc3BvbnNlLnRleHQoKWBcbiAgICogQHJldHVybnMgYHRydWVgIHRvIHNjaGVkdWxlIGFub3RoZXIgYXR0ZW1wdCAodW50aWwgYHJldHJpZXNgIGlzIGV4aGF1c3RlZClcbiAgICovXG4gIHNob3VsZFJldHJ5PzogKHJlc3BvbnNlOiBSZXNwb25zZSwgYm9keTogc3RyaW5nKSA9PiBib29sZWFuO1xufVxuIl19
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HTTP status codes retried by default when {@link RequestOptions.shouldRetry} is omitted.
|
|
3
|
+
*
|
|
4
|
+
* Includes transient client/server errors: 408, 425, 429, and common 5xx gateway or overload responses.
|
|
5
|
+
*/
|
|
6
|
+
export declare const DEFAULT_RETRYABLE_HTTP_STATUSES: readonly number[];
|
|
7
|
+
/**
|
|
8
|
+
* Default {@link RequestOptions.shouldRetry}: retries responses whose status is in
|
|
9
|
+
* {@link DEFAULT_RETRYABLE_HTTP_STATUSES}.
|
|
10
|
+
*
|
|
11
|
+
* Compose with custom logic, for example:
|
|
12
|
+
* `(res, body) => defaultShouldRetry(res, body) || res.status === 418`.
|
|
13
|
+
*
|
|
14
|
+
* @param response - Response from the failed attempt
|
|
15
|
+
* @param _body - Response body text (unused by the default predicate)
|
|
16
|
+
* @returns `true` when another attempt should be scheduled
|
|
17
|
+
*/
|
|
18
|
+
export declare const defaultShouldRetry: (response: Response, _body: string) => boolean;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.defaultShouldRetry = exports.DEFAULT_RETRYABLE_HTTP_STATUSES = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* HTTP status codes retried by default when {@link RequestOptions.shouldRetry} is omitted.
|
|
6
|
+
*
|
|
7
|
+
* Includes transient client/server errors: 408, 425, 429, and common 5xx gateway or overload responses.
|
|
8
|
+
*/
|
|
9
|
+
exports.DEFAULT_RETRYABLE_HTTP_STATUSES = [408, 425, 429, 500, 502, 503, 504];
|
|
10
|
+
/**
|
|
11
|
+
* Default {@link RequestOptions.shouldRetry}: retries responses whose status is in
|
|
12
|
+
* {@link DEFAULT_RETRYABLE_HTTP_STATUSES}.
|
|
13
|
+
*
|
|
14
|
+
* Compose with custom logic, for example:
|
|
15
|
+
* `(res, body) => defaultShouldRetry(res, body) || res.status === 418`.
|
|
16
|
+
*
|
|
17
|
+
* @param response - Response from the failed attempt
|
|
18
|
+
* @param _body - Response body text (unused by the default predicate)
|
|
19
|
+
* @returns `true` when another attempt should be scheduled
|
|
20
|
+
*/
|
|
21
|
+
const defaultShouldRetry = (response, _body) => {
|
|
22
|
+
return exports.DEFAULT_RETRYABLE_HTTP_STATUSES.includes(response.status);
|
|
23
|
+
};
|
|
24
|
+
exports.defaultShouldRetry = defaultShouldRetry;
|
|
25
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZGVmYXVsdC1zaG91bGQtcmV0cnkuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi9zcmMvY29yZS9wb2xpY3kvZGVmYXVsdC1zaG91bGQtcmV0cnkudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7O0FBQUE7Ozs7R0FJRztBQUNVLFFBQUEsK0JBQStCLEdBQXNCLENBQUMsR0FBRyxFQUFFLEdBQUcsRUFBRSxHQUFHLEVBQUUsR0FBRyxFQUFFLEdBQUcsRUFBRSxHQUFHLEVBQUUsR0FBRyxDQUFDLENBQUM7QUFFdEc7Ozs7Ozs7Ozs7R0FVRztBQUNJLE1BQU0sa0JBQWtCLEdBQUcsQ0FBQyxRQUFrQixFQUFFLEtBQWEsRUFBVyxFQUFFO0lBQy9FLE9BQU8sdUNBQStCLENBQUMsUUFBUSxDQUFDLFFBQVEsQ0FBQyxNQUFNLENBQUMsQ0FBQztBQUNuRSxDQUFDLENBQUM7QUFGVyxRQUFBLGtCQUFrQixzQkFFN0IiLCJzb3VyY2VzQ29udGVudCI6WyIvKipcbiAqIEhUVFAgc3RhdHVzIGNvZGVzIHJldHJpZWQgYnkgZGVmYXVsdCB3aGVuIHtAbGluayBSZXF1ZXN0T3B0aW9ucy5zaG91bGRSZXRyeX0gaXMgb21pdHRlZC5cbiAqXG4gKiBJbmNsdWRlcyB0cmFuc2llbnQgY2xpZW50L3NlcnZlciBlcnJvcnM6IDQwOCwgNDI1LCA0MjksIGFuZCBjb21tb24gNXh4IGdhdGV3YXkgb3Igb3ZlcmxvYWQgcmVzcG9uc2VzLlxuICovXG5leHBvcnQgY29uc3QgREVGQVVMVF9SRVRSWUFCTEVfSFRUUF9TVEFUVVNFUzogcmVhZG9ubHkgbnVtYmVyW10gPSBbNDA4LCA0MjUsIDQyOSwgNTAwLCA1MDIsIDUwMywgNTA0XTtcblxuLyoqXG4gKiBEZWZhdWx0IHtAbGluayBSZXF1ZXN0T3B0aW9ucy5zaG91bGRSZXRyeX06IHJldHJpZXMgcmVzcG9uc2VzIHdob3NlIHN0YXR1cyBpcyBpblxuICoge0BsaW5rIERFRkFVTFRfUkVUUllBQkxFX0hUVFBfU1RBVFVTRVN9LlxuICpcbiAqIENvbXBvc2Ugd2l0aCBjdXN0b20gbG9naWMsIGZvciBleGFtcGxlOlxuICogYChyZXMsIGJvZHkpID0+IGRlZmF1bHRTaG91bGRSZXRyeShyZXMsIGJvZHkpIHx8IHJlcy5zdGF0dXMgPT09IDQxOGAuXG4gKlxuICogQHBhcmFtIHJlc3BvbnNlIC0gUmVzcG9uc2UgZnJvbSB0aGUgZmFpbGVkIGF0dGVtcHRcbiAqIEBwYXJhbSBfYm9keSAtIFJlc3BvbnNlIGJvZHkgdGV4dCAodW51c2VkIGJ5IHRoZSBkZWZhdWx0IHByZWRpY2F0ZSlcbiAqIEByZXR1cm5zIGB0cnVlYCB3aGVuIGFub3RoZXIgYXR0ZW1wdCBzaG91bGQgYmUgc2NoZWR1bGVkXG4gKi9cbmV4cG9ydCBjb25zdCBkZWZhdWx0U2hvdWxkUmV0cnkgPSAocmVzcG9uc2U6IFJlc3BvbnNlLCBfYm9keTogc3RyaW5nKTogYm9vbGVhbiA9PiB7XG4gIHJldHVybiBERUZBVUxUX1JFVFJZQUJMRV9IVFRQX1NUQVRVU0VTLmluY2x1ZGVzKHJlc3BvbnNlLnN0YXR1cyk7XG59O1xuIl19
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { RequestOptions } from '../options';
|
|
2
|
+
/**
|
|
3
|
+
* Validates retry policy numeric fields on {@link RequestOptions}.
|
|
4
|
+
*
|
|
5
|
+
* Constraints: `retries >= 1`, `timeoutMs > 0`, `baseBackoffMs >= 0`, and when set
|
|
6
|
+
* `maxBackoffMs >= 0`.
|
|
7
|
+
*
|
|
8
|
+
* @param options - Options whose `retries`, `timeoutMs`, `baseBackoffMs`, and optional
|
|
9
|
+
* `maxBackoffMs` are checked
|
|
10
|
+
* @throws {FetchRetrierInvalidOptionsError} When any constraint is violated
|
|
11
|
+
*/
|
|
12
|
+
export declare const validateRequestOptions: (options: Pick<RequestOptions, "retries" | "timeoutMs" | "baseBackoffMs" | "maxBackoffMs">) => void;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.validateRequestOptions = void 0;
|
|
4
|
+
const errors_1 = require("../errors");
|
|
5
|
+
/**
|
|
6
|
+
* Validates retry policy numeric fields on {@link RequestOptions}.
|
|
7
|
+
*
|
|
8
|
+
* Constraints: `retries >= 1`, `timeoutMs > 0`, `baseBackoffMs >= 0`, and when set
|
|
9
|
+
* `maxBackoffMs >= 0`.
|
|
10
|
+
*
|
|
11
|
+
* @param options - Options whose `retries`, `timeoutMs`, `baseBackoffMs`, and optional
|
|
12
|
+
* `maxBackoffMs` are checked
|
|
13
|
+
* @throws {FetchRetrierInvalidOptionsError} When any constraint is violated
|
|
14
|
+
*/
|
|
15
|
+
const validateRequestOptions = (options) => {
|
|
16
|
+
const { retries, timeoutMs, baseBackoffMs, maxBackoffMs } = options;
|
|
17
|
+
if (retries < 1) {
|
|
18
|
+
throw new errors_1.FetchRetrierInvalidOptionsError('retries must be >= 1');
|
|
19
|
+
}
|
|
20
|
+
if (timeoutMs <= 0) {
|
|
21
|
+
throw new errors_1.FetchRetrierInvalidOptionsError('timeoutMs must be > 0');
|
|
22
|
+
}
|
|
23
|
+
if (baseBackoffMs < 0) {
|
|
24
|
+
throw new errors_1.FetchRetrierInvalidOptionsError('baseBackoffMs must be >= 0');
|
|
25
|
+
}
|
|
26
|
+
if (maxBackoffMs !== undefined && maxBackoffMs < 0) {
|
|
27
|
+
throw new errors_1.FetchRetrierInvalidOptionsError('maxBackoffMs must be >= 0');
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
exports.validateRequestOptions = validateRequestOptions;
|
|
31
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidmFsaWRhdGUtb3B0aW9ucy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3NyYy9jb3JlL3BvbGljeS92YWxpZGF0ZS1vcHRpb25zLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7OztBQUFBLHNDQUE0RDtBQUc1RDs7Ozs7Ozs7O0dBU0c7QUFDSSxNQUFNLHNCQUFzQixHQUFHLENBQ3BDLE9BQXlGLEVBQ25GLEVBQUU7SUFDUixNQUFNLEVBQUUsT0FBTyxFQUFFLFNBQVMsRUFBRSxhQUFhLEVBQUUsWUFBWSxFQUFFLEdBQUcsT0FBTyxDQUFDO0lBRXBFLElBQUksT0FBTyxHQUFHLENBQUMsRUFBRSxDQUFDO1FBQ2hCLE1BQU0sSUFBSSx3Q0FBK0IsQ0FBQyxzQkFBc0IsQ0FBQyxDQUFDO0lBQ3BFLENBQUM7SUFDRCxJQUFJLFNBQVMsSUFBSSxDQUFDLEVBQUUsQ0FBQztRQUNuQixNQUFNLElBQUksd0NBQStCLENBQUMsdUJBQXVCLENBQUMsQ0FBQztJQUNyRSxDQUFDO0lBQ0QsSUFBSSxhQUFhLEdBQUcsQ0FBQyxFQUFFLENBQUM7UUFDdEIsTUFBTSxJQUFJLHdDQUErQixDQUFDLDRCQUE0QixDQUFDLENBQUM7SUFDMUUsQ0FBQztJQUNELElBQUksWUFBWSxLQUFLLFNBQVMsSUFBSSxZQUFZLEdBQUcsQ0FBQyxFQUFFLENBQUM7UUFDbkQsTUFBTSxJQUFJLHdDQUErQixDQUFDLDJCQUEyQixDQUFDLENBQUM7SUFDekUsQ0FBQztBQUNILENBQUMsQ0FBQztBQWpCVyxRQUFBLHNCQUFzQiwwQkFpQmpDIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgRmV0Y2hSZXRyaWVySW52YWxpZE9wdGlvbnNFcnJvciB9IGZyb20gJy4uL2Vycm9ycyc7XG5pbXBvcnQgeyBSZXF1ZXN0T3B0aW9ucyB9IGZyb20gJy4uL29wdGlvbnMnO1xuXG4vKipcbiAqIFZhbGlkYXRlcyByZXRyeSBwb2xpY3kgbnVtZXJpYyBmaWVsZHMgb24ge0BsaW5rIFJlcXVlc3RPcHRpb25zfS5cbiAqXG4gKiBDb25zdHJhaW50czogYHJldHJpZXMgPj0gMWAsIGB0aW1lb3V0TXMgPiAwYCwgYGJhc2VCYWNrb2ZmTXMgPj0gMGAsIGFuZCB3aGVuIHNldFxuICogYG1heEJhY2tvZmZNcyA+PSAwYC5cbiAqXG4gKiBAcGFyYW0gb3B0aW9ucyAtIE9wdGlvbnMgd2hvc2UgYHJldHJpZXNgLCBgdGltZW91dE1zYCwgYGJhc2VCYWNrb2ZmTXNgLCBhbmQgb3B0aW9uYWxcbiAqICAgYG1heEJhY2tvZmZNc2AgYXJlIGNoZWNrZWRcbiAqIEB0aHJvd3Mge0ZldGNoUmV0cmllckludmFsaWRPcHRpb25zRXJyb3J9IFdoZW4gYW55IGNvbnN0cmFpbnQgaXMgdmlvbGF0ZWRcbiAqL1xuZXhwb3J0IGNvbnN0IHZhbGlkYXRlUmVxdWVzdE9wdGlvbnMgPSAoXG4gIG9wdGlvbnM6IFBpY2s8UmVxdWVzdE9wdGlvbnMsICdyZXRyaWVzJyB8ICd0aW1lb3V0TXMnIHwgJ2Jhc2VCYWNrb2ZmTXMnIHwgJ21heEJhY2tvZmZNcyc+LFxuKTogdm9pZCA9PiB7XG4gIGNvbnN0IHsgcmV0cmllcywgdGltZW91dE1zLCBiYXNlQmFja29mZk1zLCBtYXhCYWNrb2ZmTXMgfSA9IG9wdGlvbnM7XG5cbiAgaWYgKHJldHJpZXMgPCAxKSB7XG4gICAgdGhyb3cgbmV3IEZldGNoUmV0cmllckludmFsaWRPcHRpb25zRXJyb3IoJ3JldHJpZXMgbXVzdCBiZSA+PSAxJyk7XG4gIH1cbiAgaWYgKHRpbWVvdXRNcyA8PSAwKSB7XG4gICAgdGhyb3cgbmV3IEZldGNoUmV0cmllckludmFsaWRPcHRpb25zRXJyb3IoJ3RpbWVvdXRNcyBtdXN0IGJlID4gMCcpO1xuICB9XG4gIGlmIChiYXNlQmFja29mZk1zIDwgMCkge1xuICAgIHRocm93IG5ldyBGZXRjaFJldHJpZXJJbnZhbGlkT3B0aW9uc0Vycm9yKCdiYXNlQmFja29mZk1zIG11c3QgYmUgPj0gMCcpO1xuICB9XG4gIGlmIChtYXhCYWNrb2ZmTXMgIT09IHVuZGVmaW5lZCAmJiBtYXhCYWNrb2ZmTXMgPCAwKSB7XG4gICAgdGhyb3cgbmV3IEZldGNoUmV0cmllckludmFsaWRPcHRpb25zRXJyb3IoJ21heEJhY2tvZmZNcyBtdXN0IGJlID49IDAnKTtcbiAgfVxufTtcbiJdfQ==
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Retry-loop predicates with no fetch, timer, or environment dependencies.
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Whether the current 1-based attempt is the last allowed attempt.
|
|
6
|
+
*
|
|
7
|
+
* @param attempt - 1-based attempt index
|
|
8
|
+
* @param retries - Maximum number of attempts, including the first
|
|
9
|
+
* @returns `true` when no further attempt should be scheduled
|
|
10
|
+
*/
|
|
11
|
+
export declare const isLastAttempt: (attempt: number, retries: number) => boolean;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Retry-loop predicates with no fetch, timer, or environment dependencies.
|
|
4
|
+
*/
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.isLastAttempt = void 0;
|
|
7
|
+
/**
|
|
8
|
+
* Whether the current 1-based attempt is the last allowed attempt.
|
|
9
|
+
*
|
|
10
|
+
* @param attempt - 1-based attempt index
|
|
11
|
+
* @param retries - Maximum number of attempts, including the first
|
|
12
|
+
* @returns `true` when no further attempt should be scheduled
|
|
13
|
+
*/
|
|
14
|
+
const isLastAttempt = (attempt, retries) => {
|
|
15
|
+
return attempt === retries;
|
|
16
|
+
};
|
|
17
|
+
exports.isLastAttempt = isLastAttempt;
|
|
18
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicmV0cnktcHJlZGljYXRlcy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9jb3JlL3JldHJ5LXByZWRpY2F0ZXMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IjtBQUFBOztHQUVHOzs7QUFFSDs7Ozs7O0dBTUc7QUFDSSxNQUFNLGFBQWEsR0FBRyxDQUFDLE9BQWUsRUFBRSxPQUFlLEVBQVcsRUFBRTtJQUN6RSxPQUFPLE9BQU8sS0FBSyxPQUFPLENBQUM7QUFDN0IsQ0FBQyxDQUFDO0FBRlcsUUFBQSxhQUFhLGlCQUV4QiIsInNvdXJjZXNDb250ZW50IjpbIi8qKlxuICogUmV0cnktbG9vcCBwcmVkaWNhdGVzIHdpdGggbm8gZmV0Y2gsIHRpbWVyLCBvciBlbnZpcm9ubWVudCBkZXBlbmRlbmNpZXMuXG4gKi9cblxuLyoqXG4gKiBXaGV0aGVyIHRoZSBjdXJyZW50IDEtYmFzZWQgYXR0ZW1wdCBpcyB0aGUgbGFzdCBhbGxvd2VkIGF0dGVtcHQuXG4gKlxuICogQHBhcmFtIGF0dGVtcHQgLSAxLWJhc2VkIGF0dGVtcHQgaW5kZXhcbiAqIEBwYXJhbSByZXRyaWVzIC0gTWF4aW11bSBudW1iZXIgb2YgYXR0ZW1wdHMsIGluY2x1ZGluZyB0aGUgZmlyc3RcbiAqIEByZXR1cm5zIGB0cnVlYCB3aGVuIG5vIGZ1cnRoZXIgYXR0ZW1wdCBzaG91bGQgYmUgc2NoZWR1bGVkXG4gKi9cbmV4cG9ydCBjb25zdCBpc0xhc3RBdHRlbXB0ID0gKGF0dGVtcHQ6IG51bWJlciwgcmV0cmllczogbnVtYmVyKTogYm9vbGVhbiA9PiB7XG4gIHJldHVybiBhdHRlbXB0ID09PSByZXRyaWVzO1xufTtcbiJdfQ==
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.wait = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Delays execution for the given duration (used between retry attempts).
|
|
6
|
+
*
|
|
7
|
+
* @param ms - Delay in milliseconds
|
|
8
|
+
* @returns A promise that resolves after `ms`
|
|
9
|
+
*/
|
|
10
|
+
const wait = (ms) => {
|
|
11
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
12
|
+
};
|
|
13
|
+
exports.wait = wait;
|
|
14
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoid2FpdC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3NyYy9jb3JlL3RpbWUvd2FpdC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7QUFBQTs7Ozs7R0FLRztBQUNJLE1BQU0sSUFBSSxHQUFHLENBQUMsRUFBVSxFQUFpQixFQUFFO0lBQ2hELE9BQU8sSUFBSSxPQUFPLENBQUMsQ0FBQyxPQUFPLEVBQUUsRUFBRSxDQUFDLFVBQVUsQ0FBQyxPQUFPLEVBQUUsRUFBRSxDQUFDLENBQUMsQ0FBQztBQUMzRCxDQUFDLENBQUM7QUFGVyxRQUFBLElBQUksUUFFZiIsInNvdXJjZXNDb250ZW50IjpbIi8qKlxuICogRGVsYXlzIGV4ZWN1dGlvbiBmb3IgdGhlIGdpdmVuIGR1cmF0aW9uICh1c2VkIGJldHdlZW4gcmV0cnkgYXR0ZW1wdHMpLlxuICpcbiAqIEBwYXJhbSBtcyAtIERlbGF5IGluIG1pbGxpc2Vjb25kc1xuICogQHJldHVybnMgQSBwcm9taXNlIHRoYXQgcmVzb2x2ZXMgYWZ0ZXIgYG1zYFxuICovXG5leHBvcnQgY29uc3Qgd2FpdCA9IChtczogbnVtYmVyKTogUHJvbWlzZTx2b2lkPiA9PiB7XG4gIHJldHVybiBuZXcgUHJvbWlzZSgocmVzb2x2ZSkgPT4gc2V0VGltZW91dChyZXNvbHZlLCBtcykpO1xufTtcbiJdfQ==
|
package/lib/index.d.ts
CHANGED
|
@@ -4,195 +4,8 @@
|
|
|
4
4
|
*
|
|
5
5
|
* @module fetch-retrier
|
|
6
6
|
*/
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
* Per-attempt abort and timeout are handled internally via `signal` and must not be set here.
|
|
13
|
-
*/
|
|
14
|
-
export type FetchInitOptions = Omit<RequestInit, 'signal'>;
|
|
15
|
-
/**
|
|
16
|
-
* Options for {@link fetchRetrier}: retry policy, timeout, backoff (including `Retry-After`),
|
|
17
|
-
* request payload, and cancellation.
|
|
18
|
-
*
|
|
19
|
-
* Request shape is built as `{ ...init, headers?, signal }` on each attempt. Top-level `headers`
|
|
20
|
-
* override `init.headers` when both are provided.
|
|
21
|
-
*
|
|
22
|
-
* Numeric fields are validated when {@link fetchRetrier} is called; invalid values throw
|
|
23
|
-
* {@link FetchRetrierInvalidOptionsError}.
|
|
24
|
-
*/
|
|
25
|
-
export interface RequestOptions {
|
|
26
|
-
/**
|
|
27
|
-
* HTTP headers sent on every attempt.
|
|
28
|
-
* When `init.headers` is also set, these values take precedence for duplicate keys.
|
|
29
|
-
*/
|
|
30
|
-
headers?: Record<string, string>;
|
|
31
|
-
/**
|
|
32
|
-
* Additional {@link FetchInitOptions} merged into each `fetch` call (e.g. POST `method` and JSON `body`).
|
|
33
|
-
* The same `init` is reused across retries.
|
|
34
|
-
*/
|
|
35
|
-
init?: FetchInitOptions;
|
|
36
|
-
/**
|
|
37
|
-
* Maximum number of attempts, including the first.
|
|
38
|
-
* Must be `>= 1`.
|
|
39
|
-
*/
|
|
40
|
-
retries: number;
|
|
41
|
-
/**
|
|
42
|
-
* Per-attempt timeout in milliseconds; uses an internal {@link AbortController} when exceeded.
|
|
43
|
-
* Must be `> 0`.
|
|
44
|
-
*/
|
|
45
|
-
timeoutMs: number;
|
|
46
|
-
/**
|
|
47
|
-
* Base backoff in milliseconds for full jitter when `Retry-After` is absent or invalid.
|
|
48
|
-
* The cap for attempt `n` is `baseBackoffMs * 2^n`.
|
|
49
|
-
* Must be `>= 0` (`0` skips backoff delay between attempts when falling back to jitter).
|
|
50
|
-
*/
|
|
51
|
-
baseBackoffMs: number;
|
|
52
|
-
/**
|
|
53
|
-
* Optional external {@link AbortSignal}. When aborted, the in-flight request is aborted; on the
|
|
54
|
-
* final attempt, cancellation surfaces as {@link FetchRetrierAbortError}.
|
|
55
|
-
*/
|
|
56
|
-
signal?: AbortSignal;
|
|
57
|
-
/**
|
|
58
|
-
* Invoked after `response.text()` when `response.ok` is false.
|
|
59
|
-
* Return `true` to schedule another attempt (until `retries` is exhausted).
|
|
60
|
-
* Default: {@link defaultShouldRetry} (see {@link DEFAULT_RETRYABLE_HTTP_STATUSES}).
|
|
61
|
-
*
|
|
62
|
-
* @param response - Non-OK response from the current attempt
|
|
63
|
-
* @param body - Response body text from `response.text()`
|
|
64
|
-
* @returns `true` to schedule another attempt (until `retries` is exhausted)
|
|
65
|
-
*/
|
|
66
|
-
shouldRetry?: (response: Response, body: string) => boolean;
|
|
67
|
-
}
|
|
68
|
-
/**
|
|
69
|
-
* Error thrown when a request is cancelled by timeout or an external {@link AbortSignal}.
|
|
70
|
-
*/
|
|
71
|
-
export declare class FetchRetrierAbortError extends Error {
|
|
72
|
-
readonly name: string;
|
|
73
|
-
/**
|
|
74
|
-
* @param message - Human-readable reason (default: `'Aborted'`)
|
|
75
|
-
*/
|
|
76
|
-
constructor(message?: string);
|
|
77
|
-
}
|
|
78
|
-
/**
|
|
79
|
-
* Error thrown when {@link RequestOptions.signal} is already aborted before an attempt starts.
|
|
80
|
-
*/
|
|
81
|
-
export declare class FetchRetrierAlreadyAbortedError extends FetchRetrierAbortError {
|
|
82
|
-
readonly name: string;
|
|
83
|
-
/**
|
|
84
|
-
* @param message - Human-readable reason (default: `'Signal was already aborted'`)
|
|
85
|
-
*/
|
|
86
|
-
constructor(message?: string);
|
|
87
|
-
}
|
|
88
|
-
/**
|
|
89
|
-
* Error thrown when the server returns a non-OK HTTP status and no further retry is performed.
|
|
90
|
-
*
|
|
91
|
-
* Carries the last response `status` and the body text already consumed via `response.text()`
|
|
92
|
-
* (the same text passed to {@link RequestOptions.shouldRetry}).
|
|
93
|
-
*
|
|
94
|
-
* @property status - HTTP status code from the last non-OK response
|
|
95
|
-
* @property body - Response body text already read via `response.text()` for that attempt
|
|
96
|
-
*/
|
|
97
|
-
export declare class FetchRetrierHttpError extends Error {
|
|
98
|
-
readonly status: number;
|
|
99
|
-
readonly body: string;
|
|
100
|
-
readonly name: string;
|
|
101
|
-
/**
|
|
102
|
-
* @param message - Error description
|
|
103
|
-
* @param status - HTTP status code from the last non-OK response
|
|
104
|
-
* @param body - Response body text already read via `response.text()` for that attempt
|
|
105
|
-
*/
|
|
106
|
-
constructor(message: string, status: number, body: string);
|
|
107
|
-
}
|
|
108
|
-
/**
|
|
109
|
-
* Error thrown when a fetch fails with a network-level error (e.g. DNS failure, connection refused).
|
|
110
|
-
*
|
|
111
|
-
* @property cause - Original error from the underlying `fetch`, when available
|
|
112
|
-
*/
|
|
113
|
-
export declare class FetchRetrierNetworkError extends Error {
|
|
114
|
-
readonly cause?: unknown | undefined;
|
|
115
|
-
readonly name: string;
|
|
116
|
-
/**
|
|
117
|
-
* @param message - Human-readable reason (default: `'Network error'`)
|
|
118
|
-
* @param cause - Original error from the underlying `fetch`, when available
|
|
119
|
-
*/
|
|
120
|
-
constructor(message?: string, cause?: unknown | undefined);
|
|
121
|
-
}
|
|
122
|
-
/**
|
|
123
|
-
* Error thrown when {@link RequestOptions} contains invalid numeric values.
|
|
124
|
-
*
|
|
125
|
-
* Subclass of {@link TypeError} for compatibility with `instanceof TypeError`. Distinct from
|
|
126
|
-
* network-level `TypeError` values thrown by `fetch`, which are retried and surfaced as
|
|
127
|
-
* {@link FetchRetrierNetworkError} after the last attempt.
|
|
128
|
-
*/
|
|
129
|
-
export declare class FetchRetrierInvalidOptionsError extends TypeError {
|
|
130
|
-
readonly name: string;
|
|
131
|
-
/**
|
|
132
|
-
* @param message - Human-readable reason describing the invalid option
|
|
133
|
-
*/
|
|
134
|
-
constructor(message: string);
|
|
135
|
-
}
|
|
136
|
-
/**
|
|
137
|
-
* Error thrown when an internal invariant fails (should not happen in normal use).
|
|
138
|
-
*/
|
|
139
|
-
export declare class FetchRetrierUnreachableError extends Error {
|
|
140
|
-
readonly name: string;
|
|
141
|
-
/**
|
|
142
|
-
* @param message - Human-readable reason (default: `'Unreachable'`)
|
|
143
|
-
*/
|
|
144
|
-
constructor(message?: string);
|
|
145
|
-
}
|
|
146
|
-
/**
|
|
147
|
-
* HTTP status codes retried by default when {@link RequestOptions.shouldRetry} is omitted.
|
|
148
|
-
*
|
|
149
|
-
* Includes transient client/server errors: 408, 425, 429, and common 5xx gateway or overload responses.
|
|
150
|
-
*/
|
|
151
|
-
export declare const DEFAULT_RETRYABLE_HTTP_STATUSES: readonly number[];
|
|
152
|
-
/**
|
|
153
|
-
* Default {@link RequestOptions.shouldRetry}: retries responses whose status is in
|
|
154
|
-
* {@link DEFAULT_RETRYABLE_HTTP_STATUSES}.
|
|
155
|
-
*
|
|
156
|
-
* Compose with custom logic, for example:
|
|
157
|
-
* `(res, body) => defaultShouldRetry(res, body) || res.status === 418`.
|
|
158
|
-
*
|
|
159
|
-
* @param response - Response from the failed attempt
|
|
160
|
-
* @param _body - Response body text (unused by the default predicate)
|
|
161
|
-
* @returns `true` when another attempt should be scheduled
|
|
162
|
-
*/
|
|
163
|
-
export declare const defaultShouldRetry: (response: Response, _body: string) => boolean;
|
|
164
|
-
/**
|
|
165
|
-
* Wraps `fetch` with retries, per-attempt timeout, Retry-After support, full-jitter backoff, and
|
|
166
|
-
* optional cancellation.
|
|
167
|
-
*
|
|
168
|
-
* Each attempt calls `fetch(url, { ...options.init, headers?, signal })` with an internal
|
|
169
|
-
* {@link AbortSignal} for `timeoutMs`. Non-OK responses are retried when `shouldRetry` returns
|
|
170
|
-
* `true` (default: {@link defaultShouldRetry}). Between HTTP retries, a valid `Retry-After`
|
|
171
|
-
* header (delta-seconds or HTTP-date) is preferred over full jitter; abort and network retries
|
|
172
|
-
* always use full jitter. The same {@link FetchInitOptions} (including `body`) is reused on
|
|
173
|
-
* every attempt.
|
|
174
|
-
*
|
|
175
|
-
* @param url - Request URL passed to `fetch`
|
|
176
|
-
* @param options - {@link RequestOptions} controlling retries, timeout, request init, and cancellation
|
|
177
|
-
* @returns The first {@link Response} for which `ok` is `true`
|
|
178
|
-
* @throws {FetchRetrierInvalidOptionsError} If `retries < 1`, `timeoutMs <= 0`, or `baseBackoffMs < 0`
|
|
179
|
-
* @throws {FetchRetrierAlreadyAbortedError} If `options.signal` is already aborted before an attempt
|
|
180
|
-
* @throws {FetchRetrierHttpError} On a non-OK response that is not retried or after the last attempt
|
|
181
|
-
* (includes `status` and `body`)
|
|
182
|
-
* @throws {FetchRetrierNetworkError} On a network `TypeError` after the last attempt
|
|
183
|
-
* @throws {FetchRetrierAbortError} On timeout or external abort after the last attempt
|
|
184
|
-
* @throws {FetchRetrierUnreachableError} If the retry loop exits without returning (internal bug)
|
|
185
|
-
*/
|
|
186
|
-
export declare const fetchRetrier: (url: string, options: RequestOptions) => Promise<Response>;
|
|
187
|
-
/**
|
|
188
|
-
* Parses a `Retry-After` header value into a delay in milliseconds.
|
|
189
|
-
*
|
|
190
|
-
* Supports RFC 7231 forms: non-negative integer delta-seconds, or an HTTP-date. Empty values,
|
|
191
|
-
* non-integer numerics (e.g. floats or negatives), and unparsable dates yield `undefined` so
|
|
192
|
-
* callers can fall back to full jitter. An HTTP-date in the past yields `0`.
|
|
193
|
-
*
|
|
194
|
-
* @param value - Raw `Retry-After` header value
|
|
195
|
-
* @param nowMs - Current time in milliseconds (injectable for tests)
|
|
196
|
-
* @returns Delay in milliseconds, or `undefined` when the value cannot be parsed
|
|
197
|
-
*/
|
|
198
|
-
export declare const parseRetryAfterMs: (value: string, nowMs?: number) => number | undefined;
|
|
7
|
+
export { FetchRetrierAbortError, FetchRetrierAlreadyAbortedError, FetchRetrierError, FetchRetrierHttpError, FetchRetrierInvalidOptionsError, FetchRetrierNetworkError, FetchRetrierUnreachableError, } from './core/errors';
|
|
8
|
+
export type { FetchInitOptions, RequestOptions } from './core/options';
|
|
9
|
+
export { parseRetryAfterMs } from './core/backoff/retry-after';
|
|
10
|
+
export { fetchRetrier } from './core/fetch-retrier';
|
|
11
|
+
export { DEFAULT_RETRYABLE_HTTP_STATUSES, defaultShouldRetry, } from './core/policy/default-should-retry';
|