@gajae-code/utils 0.12.12 → 0.12.13
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/dist/types/fetch-retry.d.ts +5 -2
- package/package.json +2 -2
- package/src/fetch-retry.ts +21 -13
|
@@ -22,13 +22,16 @@ export interface FetchWithRetryOptions extends RequestInit {
|
|
|
22
22
|
/**
|
|
23
23
|
* Per-delay cap. Server-provided `Retry-After` hints exceeding this return
|
|
24
24
|
* the current response immediately — caller deals with the `!response.ok`.
|
|
25
|
-
* Default
|
|
25
|
+
* Scheduled values are also capped at the platform timer ceiling. Default
|
|
26
|
+
* `60_000`.
|
|
26
27
|
*/
|
|
27
28
|
maxDelayMs?: number;
|
|
28
29
|
/**
|
|
29
30
|
* Fallback delay schedule when no server hint is present. Number, array
|
|
30
31
|
* (indexed by attempt, clamped to last), or function. Default exponential
|
|
31
|
-
* `500ms * 2 ** attempt` capped at `maxDelayMs
|
|
32
|
+
* `500ms * 2 ** attempt` capped at `maxDelayMs` and the platform timer
|
|
33
|
+
* ceiling. Values that remain negative or non-finite after capping retry
|
|
34
|
+
* immediately.
|
|
32
35
|
*/
|
|
33
36
|
defaultDelayMs?: number | readonly number[] | ((attempt: number) => number);
|
|
34
37
|
/**
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"type": "module",
|
|
3
3
|
"name": "@gajae-code/utils",
|
|
4
|
-
"version": "0.12.
|
|
4
|
+
"version": "0.12.13",
|
|
5
5
|
"description": "Shared utilities for pi packages",
|
|
6
6
|
"homepage": "https://gajae-code.com",
|
|
7
7
|
"author": "Yeachan-Heo",
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
"fmt": "biome format --write ."
|
|
32
32
|
},
|
|
33
33
|
"dependencies": {
|
|
34
|
-
"@gajae-code/natives": "0.12.
|
|
34
|
+
"@gajae-code/natives": "0.12.13",
|
|
35
35
|
"beautiful-mermaid": "^1.1.3",
|
|
36
36
|
"handlebars": "^4.7.9",
|
|
37
37
|
"winston": "^3.19.0",
|
package/src/fetch-retry.ts
CHANGED
|
@@ -81,13 +81,16 @@ export interface FetchWithRetryOptions extends RequestInit {
|
|
|
81
81
|
/**
|
|
82
82
|
* Per-delay cap. Server-provided `Retry-After` hints exceeding this return
|
|
83
83
|
* the current response immediately — caller deals with the `!response.ok`.
|
|
84
|
-
* Default
|
|
84
|
+
* Scheduled values are also capped at the platform timer ceiling. Default
|
|
85
|
+
* `60_000`.
|
|
85
86
|
*/
|
|
86
87
|
maxDelayMs?: number;
|
|
87
88
|
/**
|
|
88
89
|
* Fallback delay schedule when no server hint is present. Number, array
|
|
89
90
|
* (indexed by attempt, clamped to last), or function. Default exponential
|
|
90
|
-
* `500ms * 2 ** attempt` capped at `maxDelayMs
|
|
91
|
+
* `500ms * 2 ** attempt` capped at `maxDelayMs` and the platform timer
|
|
92
|
+
* ceiling. Values that remain negative or non-finite after capping retry
|
|
93
|
+
* immediately.
|
|
91
94
|
*/
|
|
92
95
|
defaultDelayMs?: number | readonly number[] | ((attempt: number) => number);
|
|
93
96
|
/**
|
|
@@ -106,6 +109,9 @@ export interface FetchWithRetryOptions extends RequestInit {
|
|
|
106
109
|
|
|
107
110
|
const DEFAULT_MAX_DELAY_MS = 60_000;
|
|
108
111
|
const DEFAULT_MAX_ATTEMPTS = 5;
|
|
112
|
+
// Node-compatible timers coerce larger delays to 1 ms. Bun follows the same
|
|
113
|
+
// signed 32-bit boundary, so every scheduler input must stay at or below it.
|
|
114
|
+
const MAX_TIMER_DELAY_MS = 2_147_483_647;
|
|
109
115
|
|
|
110
116
|
/**
|
|
111
117
|
* Fetch with bounded retries and sensible defaults. Retries on any
|
|
@@ -142,7 +148,8 @@ export async function fetchWithRetry(
|
|
|
142
148
|
if (signal?.aborted) throw new Error("Request was aborted");
|
|
143
149
|
const wrapped = wrapNetworkError(error);
|
|
144
150
|
if (attempt + 1 >= maxAttempts) throw wrapped;
|
|
145
|
-
|
|
151
|
+
const delayMs = normalizeRetryDelay(resolveDefaultDelay(defaultDelayMs, attempt), maxDelayMs);
|
|
152
|
+
await scheduler.wait(delayMs, { signal });
|
|
146
153
|
continue;
|
|
147
154
|
}
|
|
148
155
|
|
|
@@ -152,7 +159,7 @@ export async function fetchWithRetry(
|
|
|
152
159
|
const hint = extractRetryHint(response, await response.clone().text());
|
|
153
160
|
if (hint !== undefined && hint > maxDelayMs) return response;
|
|
154
161
|
|
|
155
|
-
const delayMs =
|
|
162
|
+
const delayMs = normalizeRetryDelay(hint ?? resolveDefaultDelay(defaultDelayMs, attempt), maxDelayMs);
|
|
156
163
|
void response.body?.cancel().catch(() => undefined);
|
|
157
164
|
await scheduler.wait(delayMs, { signal });
|
|
158
165
|
}
|
|
@@ -184,15 +191,16 @@ function wrapNetworkError(error: unknown): Error {
|
|
|
184
191
|
return new Error(String(error));
|
|
185
192
|
}
|
|
186
193
|
|
|
187
|
-
function resolveDefaultDelay(
|
|
188
|
-
option
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
)
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
194
|
+
function resolveDefaultDelay(option: FetchWithRetryOptions["defaultDelayMs"], attempt: number): number {
|
|
195
|
+
if (option === undefined) return 500 * 2 ** attempt;
|
|
196
|
+
if (typeof option === "number") return option;
|
|
197
|
+
if (typeof option === "function") return option(attempt);
|
|
198
|
+
return option[Math.min(attempt, option.length - 1)] ?? 0;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
function normalizeRetryDelay(delayMs: number, maxDelayMs: number): number {
|
|
202
|
+
const cappedDelayMs = Math.min(delayMs, maxDelayMs, MAX_TIMER_DELAY_MS);
|
|
203
|
+
return Number.isFinite(cappedDelayMs) && cappedDelayMs >= 0 ? cappedDelayMs : 0;
|
|
196
204
|
}
|
|
197
205
|
|
|
198
206
|
/**
|