@anmetric/neta 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +623 -0
- package/dist/index.cjs +2 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -0
- package/package.json +38 -0
- package/types/constants.d.ts +12 -0
- package/types/core.d.ts +6 -0
- package/types/errors.d.ts +30 -0
- package/types/index.d.ts +36 -0
- package/types/retry.d.ts +22 -0
- package/types/stream.d.ts +12 -0
- package/types/types.d.ts +164 -0
- package/types/utils.d.ts +15 -0
package/README.md
ADDED
|
@@ -0,0 +1,623 @@
|
|
|
1
|
+
# neta
|
|
2
|
+
|
|
3
|
+
> Tiny, elegant HTTP client built on [`fetch`](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API) for browser and Node.js
|
|
4
|
+
|
|
5
|
+
- **Zero dependencies** — uses native `globalThis.fetch`
|
|
6
|
+
- **~12 KB** minified, ESM + CJS
|
|
7
|
+
- **TypeScript ready** — full `.d.ts` type definitions
|
|
8
|
+
- **Works everywhere** — browser, Node.js 18+, Deno, Bun
|
|
9
|
+
|
|
10
|
+
## Install
|
|
11
|
+
|
|
12
|
+
```sh
|
|
13
|
+
npm install neta
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## Quick Start
|
|
17
|
+
|
|
18
|
+
```js
|
|
19
|
+
import neta from 'neta';
|
|
20
|
+
|
|
21
|
+
// GET with JSON parsing
|
|
22
|
+
const data = await neta.get('https://api.example.com/users').json();
|
|
23
|
+
|
|
24
|
+
// POST with JSON body
|
|
25
|
+
const user = await neta.post('https://api.example.com/users', {
|
|
26
|
+
json: { name: 'John', email: 'john@example.com' },
|
|
27
|
+
}).json();
|
|
28
|
+
|
|
29
|
+
// Direct callable
|
|
30
|
+
const res = await neta('https://api.example.com/users', { method: 'get' });
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## API
|
|
34
|
+
|
|
35
|
+
### `neta(input, options?)`
|
|
36
|
+
|
|
37
|
+
Returns a [`ResponsePromise`](#responsepromise).
|
|
38
|
+
|
|
39
|
+
#### input
|
|
40
|
+
|
|
41
|
+
Type: `string | URL | Request`
|
|
42
|
+
|
|
43
|
+
#### options
|
|
44
|
+
|
|
45
|
+
Type: `object`
|
|
46
|
+
|
|
47
|
+
All [`fetch` options](https://developer.mozilla.org/en-US/docs/Web/API/fetch#parameters) plus:
|
|
48
|
+
|
|
49
|
+
##### json
|
|
50
|
+
|
|
51
|
+
Type: `unknown`
|
|
52
|
+
|
|
53
|
+
JSON body. Automatically stringified and sets `Content-Type: application/json`.
|
|
54
|
+
|
|
55
|
+
```js
|
|
56
|
+
const data = await neta.post('https://api.example.com/items', {
|
|
57
|
+
json: { title: 'New Item' },
|
|
58
|
+
}).json();
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
##### searchParams
|
|
62
|
+
|
|
63
|
+
Type: `string | object | URLSearchParams | Array<[string, string]>`
|
|
64
|
+
|
|
65
|
+
Query parameters appended to the URL.
|
|
66
|
+
|
|
67
|
+
```js
|
|
68
|
+
const data = await neta.get('https://api.example.com/search', {
|
|
69
|
+
searchParams: { q: 'hello', page: 2 },
|
|
70
|
+
}).json();
|
|
71
|
+
// => GET https://api.example.com/search?q=hello&page=2
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
Values set to `undefined` are filtered out.
|
|
75
|
+
|
|
76
|
+
##### prefix
|
|
77
|
+
|
|
78
|
+
Type: `string | URL`
|
|
79
|
+
|
|
80
|
+
Prefix prepended to the input URL. Useful for API base paths.
|
|
81
|
+
|
|
82
|
+
```js
|
|
83
|
+
const api = neta.create({ prefix: 'https://api.example.com/v2' });
|
|
84
|
+
|
|
85
|
+
await api.get('users').json();
|
|
86
|
+
// => GET https://api.example.com/v2/users
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
##### baseUrl
|
|
90
|
+
|
|
91
|
+
Type: `string | URL`
|
|
92
|
+
|
|
93
|
+
Base URL for resolving relative inputs using standard [URL resolution](https://developer.mozilla.org/en-US/docs/Web/API/URL/URL).
|
|
94
|
+
|
|
95
|
+
```js
|
|
96
|
+
const api = neta.create({ baseUrl: 'https://api.example.com/v2/' });
|
|
97
|
+
|
|
98
|
+
await api.get('users').json();
|
|
99
|
+
// => GET https://api.example.com/v2/users
|
|
100
|
+
|
|
101
|
+
await api.get('../v1/legacy').json();
|
|
102
|
+
// => GET https://api.example.com/v1/legacy
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
##### timeout
|
|
106
|
+
|
|
107
|
+
Type: `number | false`\
|
|
108
|
+
Default: `10000` (10 seconds)
|
|
109
|
+
|
|
110
|
+
Request timeout in milliseconds. Set to `false` to disable.
|
|
111
|
+
|
|
112
|
+
##### totalTimeout
|
|
113
|
+
|
|
114
|
+
Type: `number | false`\
|
|
115
|
+
Default: `false`
|
|
116
|
+
|
|
117
|
+
Total timeout across all retries in milliseconds.
|
|
118
|
+
|
|
119
|
+
```js
|
|
120
|
+
await neta.get('https://api.example.com/slow', {
|
|
121
|
+
timeout: 5000,
|
|
122
|
+
totalTimeout: 30000,
|
|
123
|
+
retry: 5,
|
|
124
|
+
});
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
##### retry
|
|
128
|
+
|
|
129
|
+
Type: `number | object`\
|
|
130
|
+
Default: `{ limit: 2 }`
|
|
131
|
+
|
|
132
|
+
Retry configuration. Pass a number for simple retry limit, or an object for full control.
|
|
133
|
+
|
|
134
|
+
```js
|
|
135
|
+
// Simple
|
|
136
|
+
await neta.get(url, { retry: 3 });
|
|
137
|
+
|
|
138
|
+
// Full control
|
|
139
|
+
await neta.get(url, {
|
|
140
|
+
retry: {
|
|
141
|
+
limit: 3,
|
|
142
|
+
methods: ['get', 'put', 'head', 'delete', 'options'],
|
|
143
|
+
statusCodes: [408, 413, 429, 500, 502, 503, 504],
|
|
144
|
+
afterStatusCodes: [413, 429, 503],
|
|
145
|
+
maxRetryAfter: Infinity,
|
|
146
|
+
backoffLimit: Infinity,
|
|
147
|
+
delay: (attemptCount) => 300 * 2 ** (attemptCount - 1),
|
|
148
|
+
jitter: false,
|
|
149
|
+
retryOnTimeout: false,
|
|
150
|
+
shouldRetry: undefined,
|
|
151
|
+
},
|
|
152
|
+
});
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
###### retry.limit
|
|
156
|
+
|
|
157
|
+
Type: `number`\
|
|
158
|
+
Default: `2`
|
|
159
|
+
|
|
160
|
+
Maximum number of retries.
|
|
161
|
+
|
|
162
|
+
###### retry.methods
|
|
163
|
+
|
|
164
|
+
Type: `string[]`\
|
|
165
|
+
Default: `['get', 'put', 'head', 'delete', 'options']`
|
|
166
|
+
|
|
167
|
+
HTTP methods eligible for retry.
|
|
168
|
+
|
|
169
|
+
###### retry.statusCodes
|
|
170
|
+
|
|
171
|
+
Type: `number[]`\
|
|
172
|
+
Default: `[408, 413, 429, 500, 502, 503, 504]`
|
|
173
|
+
|
|
174
|
+
HTTP status codes that trigger a retry.
|
|
175
|
+
|
|
176
|
+
###### retry.afterStatusCodes
|
|
177
|
+
|
|
178
|
+
Type: `number[]`\
|
|
179
|
+
Default: `[413, 429, 503]`
|
|
180
|
+
|
|
181
|
+
Status codes where the `Retry-After` header is honored.
|
|
182
|
+
|
|
183
|
+
###### retry.maxRetryAfter
|
|
184
|
+
|
|
185
|
+
Type: `number`\
|
|
186
|
+
Default: `Infinity`
|
|
187
|
+
|
|
188
|
+
Maximum `Retry-After` delay (ms) to accept.
|
|
189
|
+
|
|
190
|
+
###### retry.backoffLimit
|
|
191
|
+
|
|
192
|
+
Type: `number`\
|
|
193
|
+
Default: `Infinity`
|
|
194
|
+
|
|
195
|
+
Maximum backoff delay (ms).
|
|
196
|
+
|
|
197
|
+
###### retry.delay
|
|
198
|
+
|
|
199
|
+
Type: `(attemptCount: number) => number`\
|
|
200
|
+
Default: `(n) => 300 * 2 ** (n - 1)`
|
|
201
|
+
|
|
202
|
+
Function returning delay in ms for each attempt.
|
|
203
|
+
|
|
204
|
+
###### retry.jitter
|
|
205
|
+
|
|
206
|
+
Type: `boolean | ((delay: number) => number)`\
|
|
207
|
+
Default: `false`
|
|
208
|
+
|
|
209
|
+
Add randomness to retry delay to prevent thundering herd.
|
|
210
|
+
|
|
211
|
+
- `true` — random value between 0 and computed delay
|
|
212
|
+
- `function` — custom jitter function
|
|
213
|
+
|
|
214
|
+
```js
|
|
215
|
+
await neta.get(url, {
|
|
216
|
+
retry: {
|
|
217
|
+
limit: 5,
|
|
218
|
+
delay: (n) => 1000 * 2 ** (n - 1),
|
|
219
|
+
jitter: true,
|
|
220
|
+
},
|
|
221
|
+
});
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
###### retry.retryOnTimeout
|
|
225
|
+
|
|
226
|
+
Type: `boolean`\
|
|
227
|
+
Default: `false`
|
|
228
|
+
|
|
229
|
+
Whether to retry when a request times out.
|
|
230
|
+
|
|
231
|
+
###### retry.shouldRetry
|
|
232
|
+
|
|
233
|
+
Type: `({ error, retryCount }) => boolean | undefined | Promise<boolean | undefined>`
|
|
234
|
+
|
|
235
|
+
Custom function to decide whether to retry. Takes precedence over default checks.
|
|
236
|
+
|
|
237
|
+
- Return `true` to force retry
|
|
238
|
+
- Return `false` to prevent retry
|
|
239
|
+
- Return `undefined` to fall through to default behavior
|
|
240
|
+
|
|
241
|
+
```js
|
|
242
|
+
await neta.get(url, {
|
|
243
|
+
retry: {
|
|
244
|
+
limit: 3,
|
|
245
|
+
shouldRetry: ({ error, retryCount }) => {
|
|
246
|
+
if (error.response?.status === 401) return false; // Don't retry auth errors
|
|
247
|
+
return undefined; // Default behavior for others
|
|
248
|
+
},
|
|
249
|
+
},
|
|
250
|
+
});
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
##### throwHttpErrors
|
|
254
|
+
|
|
255
|
+
Type: `boolean | ((status: number) => boolean)`\
|
|
256
|
+
Default: `true`
|
|
257
|
+
|
|
258
|
+
Throw `HTTPError` for non-2xx responses. Pass a function for custom logic.
|
|
259
|
+
|
|
260
|
+
```js
|
|
261
|
+
// Never throw
|
|
262
|
+
const response = await neta.get(url, { throwHttpErrors: false });
|
|
263
|
+
|
|
264
|
+
// Only throw on 5xx
|
|
265
|
+
const response = await neta.get(url, {
|
|
266
|
+
throwHttpErrors: (status) => status >= 500,
|
|
267
|
+
});
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
##### parseJson
|
|
271
|
+
|
|
272
|
+
Type: `(text: string, context: { request, response }) => unknown`
|
|
273
|
+
|
|
274
|
+
Custom JSON parser. Useful for reviving dates, BigInts, etc.
|
|
275
|
+
|
|
276
|
+
```js
|
|
277
|
+
import LosslessJSON from 'lossless-json';
|
|
278
|
+
|
|
279
|
+
const data = await neta.get(url, {
|
|
280
|
+
parseJson: (text) => LosslessJSON.parse(text),
|
|
281
|
+
}).json();
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
##### stringifyJson
|
|
285
|
+
|
|
286
|
+
Type: `(value: unknown) => string`
|
|
287
|
+
|
|
288
|
+
Custom JSON serializer for the `json` option.
|
|
289
|
+
|
|
290
|
+
```js
|
|
291
|
+
import LosslessJSON from 'lossless-json';
|
|
292
|
+
|
|
293
|
+
await neta.post(url, {
|
|
294
|
+
json: data,
|
|
295
|
+
stringifyJson: (value) => LosslessJSON.stringify(value),
|
|
296
|
+
});
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
##### context
|
|
300
|
+
|
|
301
|
+
Type: `Record<string, unknown>`\
|
|
302
|
+
Default: `{}`
|
|
303
|
+
|
|
304
|
+
Arbitrary data passed through to hooks. Not sent with the request.
|
|
305
|
+
|
|
306
|
+
```js
|
|
307
|
+
await neta.get(url, {
|
|
308
|
+
context: { token: 'abc123' },
|
|
309
|
+
hooks: {
|
|
310
|
+
init: [(options) => {
|
|
311
|
+
options.headers = { ...options.headers, Authorization: `Bearer ${options.context.token}` };
|
|
312
|
+
}],
|
|
313
|
+
},
|
|
314
|
+
});
|
|
315
|
+
```
|
|
316
|
+
|
|
317
|
+
##### fetch
|
|
318
|
+
|
|
319
|
+
Type: `typeof globalThis.fetch`
|
|
320
|
+
|
|
321
|
+
Custom fetch implementation.
|
|
322
|
+
|
|
323
|
+
```js
|
|
324
|
+
import { fetch } from 'undici';
|
|
325
|
+
|
|
326
|
+
const api = neta.create({ fetch });
|
|
327
|
+
```
|
|
328
|
+
|
|
329
|
+
##### onDownloadProgress
|
|
330
|
+
|
|
331
|
+
Type: `(progress: { percent, transferredBytes, totalBytes }) => void`
|
|
332
|
+
|
|
333
|
+
Download progress callback. Requires `ReadableStream` support.
|
|
334
|
+
|
|
335
|
+
```js
|
|
336
|
+
await neta.get('https://example.com/large-file', {
|
|
337
|
+
onDownloadProgress: ({ percent, transferredBytes, totalBytes }) => {
|
|
338
|
+
console.log(`${Math.round(percent * 100)}% (${transferredBytes}/${totalBytes})`);
|
|
339
|
+
},
|
|
340
|
+
});
|
|
341
|
+
```
|
|
342
|
+
|
|
343
|
+
##### onUploadProgress
|
|
344
|
+
|
|
345
|
+
Type: `(progress: { percent, transferredBytes, totalBytes }) => void`
|
|
346
|
+
|
|
347
|
+
Upload progress callback. Requires request streams support (`duplex: 'half'`).
|
|
348
|
+
|
|
349
|
+
### HTTP Method Shortcuts
|
|
350
|
+
|
|
351
|
+
```js
|
|
352
|
+
neta.get(input, options?)
|
|
353
|
+
neta.post(input, options?)
|
|
354
|
+
neta.put(input, options?)
|
|
355
|
+
neta.patch(input, options?)
|
|
356
|
+
neta.delete(input, options?)
|
|
357
|
+
neta.head(input, options?)
|
|
358
|
+
neta.options(input, options?)
|
|
359
|
+
```
|
|
360
|
+
|
|
361
|
+
### ResponsePromise
|
|
362
|
+
|
|
363
|
+
`neta` methods return a `ResponsePromise` — a `Promise<Response>` with body parsing shortcuts:
|
|
364
|
+
|
|
365
|
+
```js
|
|
366
|
+
const json = await neta.get(url).json();
|
|
367
|
+
const text = await neta.get(url).text();
|
|
368
|
+
const blob = await neta.get(url).blob();
|
|
369
|
+
const buffer = await neta.get(url).arrayBuffer();
|
|
370
|
+
const form = await neta.get(url).formData();
|
|
371
|
+
```
|
|
372
|
+
|
|
373
|
+
#### .json(schema?)
|
|
374
|
+
|
|
375
|
+
Parse response as JSON. Optionally validate against a [Standard Schema](https://github.com/standard-schema/standard-schema):
|
|
376
|
+
|
|
377
|
+
```js
|
|
378
|
+
import { z } from 'zod';
|
|
379
|
+
|
|
380
|
+
const user = await neta.get('/user/1').json(z.object({
|
|
381
|
+
id: z.number(),
|
|
382
|
+
name: z.string(),
|
|
383
|
+
}));
|
|
384
|
+
// Throws SchemaValidationError if validation fails
|
|
385
|
+
```
|
|
386
|
+
|
|
387
|
+
### Instance Creation
|
|
388
|
+
|
|
389
|
+
#### neta.create(defaults?)
|
|
390
|
+
|
|
391
|
+
Create a new instance with default options:
|
|
392
|
+
|
|
393
|
+
```js
|
|
394
|
+
const api = neta.create({
|
|
395
|
+
prefix: 'https://api.example.com',
|
|
396
|
+
headers: { Authorization: 'Bearer token' },
|
|
397
|
+
timeout: 30000,
|
|
398
|
+
retry: 3,
|
|
399
|
+
});
|
|
400
|
+
|
|
401
|
+
const data = await api.get('users').json();
|
|
402
|
+
```
|
|
403
|
+
|
|
404
|
+
#### neta.extend(defaults?)
|
|
405
|
+
|
|
406
|
+
Alias for `neta.create()`. Creates a new instance by extending existing defaults:
|
|
407
|
+
|
|
408
|
+
```js
|
|
409
|
+
const api = neta.create({ prefix: 'https://api.example.com' });
|
|
410
|
+
const authApi = api.extend({ headers: { Authorization: 'Bearer token' } });
|
|
411
|
+
```
|
|
412
|
+
|
|
413
|
+
## Hooks
|
|
414
|
+
|
|
415
|
+
Five hook points for intercepting the request lifecycle.
|
|
416
|
+
|
|
417
|
+
### hooks.init
|
|
418
|
+
|
|
419
|
+
Type: `Array<(options) => void>`
|
|
420
|
+
|
|
421
|
+
Called synchronously before anything else. Can mutate options directly.
|
|
422
|
+
|
|
423
|
+
```js
|
|
424
|
+
neta.create({
|
|
425
|
+
hooks: {
|
|
426
|
+
init: [(options) => {
|
|
427
|
+
options.headers = { ...options.headers, 'X-Request-Id': crypto.randomUUID() };
|
|
428
|
+
}],
|
|
429
|
+
},
|
|
430
|
+
});
|
|
431
|
+
```
|
|
432
|
+
|
|
433
|
+
### hooks.beforeRequest
|
|
434
|
+
|
|
435
|
+
Type: `Array<({ request, options, retryCount }) => Request | Response | void>`
|
|
436
|
+
|
|
437
|
+
Called before each request. Return a `Request` to replace it, a `Response` to short-circuit, or nothing.
|
|
438
|
+
|
|
439
|
+
```js
|
|
440
|
+
neta.create({
|
|
441
|
+
hooks: {
|
|
442
|
+
beforeRequest: [({ request }) => {
|
|
443
|
+
console.log(`${request.method} ${request.url}`);
|
|
444
|
+
}],
|
|
445
|
+
},
|
|
446
|
+
});
|
|
447
|
+
```
|
|
448
|
+
|
|
449
|
+
### hooks.afterResponse
|
|
450
|
+
|
|
451
|
+
Type: `Array<({ request, options, response, retryCount }) => Response | RetryMarker | void>`
|
|
452
|
+
|
|
453
|
+
Called after a successful response. Return a `Response` to replace it, or `neta.retry()` to force a retry.
|
|
454
|
+
|
|
455
|
+
```js
|
|
456
|
+
const api = neta.create({
|
|
457
|
+
hooks: {
|
|
458
|
+
afterResponse: [async ({ request, response }) => {
|
|
459
|
+
if (response.status === 401) {
|
|
460
|
+
const token = await refreshToken();
|
|
461
|
+
return neta.retry({
|
|
462
|
+
request: new Request(request, {
|
|
463
|
+
headers: { ...Object.fromEntries(request.headers), Authorization: `Bearer ${token}` },
|
|
464
|
+
}),
|
|
465
|
+
});
|
|
466
|
+
}
|
|
467
|
+
}],
|
|
468
|
+
},
|
|
469
|
+
});
|
|
470
|
+
```
|
|
471
|
+
|
|
472
|
+
### hooks.beforeError
|
|
473
|
+
|
|
474
|
+
Type: `Array<({ request, options, error, retryCount }) => Error | void>`
|
|
475
|
+
|
|
476
|
+
Called before an error is thrown. Return an `Error` to replace it.
|
|
477
|
+
|
|
478
|
+
```js
|
|
479
|
+
neta.create({
|
|
480
|
+
hooks: {
|
|
481
|
+
beforeError: [({ error }) => {
|
|
482
|
+
if (error instanceof HTTPError) {
|
|
483
|
+
error.message = `API Error: ${error.response.status}`;
|
|
484
|
+
}
|
|
485
|
+
return error;
|
|
486
|
+
}],
|
|
487
|
+
},
|
|
488
|
+
});
|
|
489
|
+
```
|
|
490
|
+
|
|
491
|
+
### hooks.beforeRetry
|
|
492
|
+
|
|
493
|
+
Type: `Array<({ request, options, error, retryCount }) => Request | Response | symbol | void>`
|
|
494
|
+
|
|
495
|
+
Called before each retry attempt. Return:
|
|
496
|
+
|
|
497
|
+
- `Request` — use this request for the retry
|
|
498
|
+
- `Response` — skip the retry and use this response
|
|
499
|
+
- `stop` — abort the retry loop
|
|
500
|
+
- nothing — proceed normally
|
|
501
|
+
|
|
502
|
+
```js
|
|
503
|
+
import { stop } from 'neta';
|
|
504
|
+
|
|
505
|
+
neta.create({
|
|
506
|
+
hooks: {
|
|
507
|
+
beforeRetry: [({ error, retryCount }) => {
|
|
508
|
+
console.log(`Retry #${retryCount}: ${error.message}`);
|
|
509
|
+
if (retryCount > 3) return stop;
|
|
510
|
+
}],
|
|
511
|
+
},
|
|
512
|
+
});
|
|
513
|
+
```
|
|
514
|
+
|
|
515
|
+
## Error Handling
|
|
516
|
+
|
|
517
|
+
### HTTPError
|
|
518
|
+
|
|
519
|
+
Thrown for non-2xx responses (when `throwHttpErrors` is true).
|
|
520
|
+
|
|
521
|
+
```js
|
|
522
|
+
import { HTTPError } from 'neta';
|
|
523
|
+
|
|
524
|
+
try {
|
|
525
|
+
await neta.get('https://api.example.com/missing');
|
|
526
|
+
} catch (error) {
|
|
527
|
+
if (error instanceof HTTPError) {
|
|
528
|
+
console.log(error.response.status); // 404
|
|
529
|
+
console.log(error.data); // Auto-parsed response body
|
|
530
|
+
console.log(error.request); // The Request object
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
```
|
|
534
|
+
|
|
535
|
+
### TimeoutError
|
|
536
|
+
|
|
537
|
+
Thrown when a request exceeds the `timeout` or `totalTimeout`.
|
|
538
|
+
|
|
539
|
+
```js
|
|
540
|
+
import { TimeoutError } from 'neta';
|
|
541
|
+
|
|
542
|
+
try {
|
|
543
|
+
await neta.get(url, { timeout: 1000 });
|
|
544
|
+
} catch (error) {
|
|
545
|
+
if (error instanceof TimeoutError) {
|
|
546
|
+
console.log('Request timed out:', error.request.url);
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
```
|
|
550
|
+
|
|
551
|
+
### NetworkError
|
|
552
|
+
|
|
553
|
+
Thrown on network failures (DNS, connection refused, etc.).
|
|
554
|
+
|
|
555
|
+
```js
|
|
556
|
+
import { NetworkError } from 'neta';
|
|
557
|
+
|
|
558
|
+
try {
|
|
559
|
+
await neta.get('https://nonexistent.invalid');
|
|
560
|
+
} catch (error) {
|
|
561
|
+
if (error instanceof NetworkError) {
|
|
562
|
+
console.log('Network error:', error.message);
|
|
563
|
+
console.log('Cause:', error.cause);
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
```
|
|
567
|
+
|
|
568
|
+
### SchemaValidationError
|
|
569
|
+
|
|
570
|
+
Thrown when JSON response fails schema validation.
|
|
571
|
+
|
|
572
|
+
```js
|
|
573
|
+
import { SchemaValidationError } from 'neta';
|
|
574
|
+
|
|
575
|
+
try {
|
|
576
|
+
await neta.get(url).json(mySchema);
|
|
577
|
+
} catch (error) {
|
|
578
|
+
if (error instanceof SchemaValidationError) {
|
|
579
|
+
console.log('Validation issues:', error.issues);
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
```
|
|
583
|
+
|
|
584
|
+
## TypeScript
|
|
585
|
+
|
|
586
|
+
neta ships with full TypeScript definitions. Generic type parameters work on `.json()`:
|
|
587
|
+
|
|
588
|
+
```ts
|
|
589
|
+
interface User {
|
|
590
|
+
id: number;
|
|
591
|
+
name: string;
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
const user = await neta.get('https://api.example.com/user/1').json<User>();
|
|
595
|
+
// user is typed as User
|
|
596
|
+
```
|
|
597
|
+
|
|
598
|
+
## Retry-After Header Support
|
|
599
|
+
|
|
600
|
+
neta automatically parses these headers during retry:
|
|
601
|
+
|
|
602
|
+
- `Retry-After`
|
|
603
|
+
- `RateLimit-Reset`
|
|
604
|
+
- `X-RateLimit-Retry-After`
|
|
605
|
+
- `X-RateLimit-Reset`
|
|
606
|
+
- `X-Rate-Limit-Reset`
|
|
607
|
+
|
|
608
|
+
Supports seconds, timestamps, and HTTP dates.
|
|
609
|
+
|
|
610
|
+
## Browser + Node.js
|
|
611
|
+
|
|
612
|
+
neta uses `globalThis.fetch` which is available natively in:
|
|
613
|
+
|
|
614
|
+
- All modern browsers
|
|
615
|
+
- Node.js 18+
|
|
616
|
+
- Deno
|
|
617
|
+
- Bun
|
|
618
|
+
|
|
619
|
+
No polyfills needed.
|
|
620
|
+
|
|
621
|
+
## License
|
|
622
|
+
|
|
623
|
+
MIT
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
"use strict";var M=Object.defineProperty;var pe=Object.getOwnPropertyDescriptor;var ye=Object.getOwnPropertyNames;var we=Object.prototype.hasOwnProperty;var Re=(t,e)=>{for(var r in e)M(t,r,{get:e[r],enumerable:!0})},ge=(t,e,r,o)=>{if(e&&typeof e=="object"||typeof e=="function")for(let n of ye(e))!we.call(t,n)&&n!==r&&M(t,n,{get:()=>e[n],enumerable:!(o=pe(e,n))||o.enumerable});return t};var be=t=>ge(M({},"__esModule",{value:!0}),t);var Ue={};Re(Ue,{ForceRetryError:()=>k,HTTPError:()=>E,NetaClient:()=>z,NetaError:()=>E,NetworkError:()=>S,SchemaValidationError:()=>D,TimeoutError:()=>R,createInstance:()=>z,default:()=>Oe,neta:()=>fe,stop:()=>T});module.exports=be(Ue);var W=["get","post","put","patch","delete","head","options"],_={limit:2,methods:["get","put","head","delete","options"],statusCodes:[408,413,429,500,502,503,504],afterStatusCodes:[413,429,503],maxRetryAfter:1/0,backoffLimit:1/0,delay:t=>300*2**(t-1),jitter:!1,retryOnTimeout:!1,shouldRetry:void 0},G=1e4,q=2147483647,K={json:"application/json",text:"text/*",formData:"multipart/form-data",arrayBuffer:"*/*",blob:"*/*",bytes:"*/*"},Fe=typeof globalThis.AbortController=="function",Me=typeof globalThis.AbortSignal<"u",_e=typeof globalThis.FormData=="function",Q=(()=>{try{return typeof globalThis.ReadableStream=="function"}catch{return!1}})(),X=(()=>{try{let t=!1,e=new Request(new URL("https://empty.invalid"),{body:new ReadableStream,method:"POST",get duplex(){return t=!0,"half"}}).headers.has("Content-Type");return t&&!e}catch{return!1}})();var E=class extends Error{constructor(e,r,o){let n=`${e.status}${e.statusText?` ${e.statusText}`:""}`;super(`Request failed with status code ${n}`),this.name="HTTPError",this.response=e,this.request=r,this.options=o,this.data=void 0}},R=class extends Error{constructor(e){super("Request timed out"),this.name="TimeoutError",this.request=e}},S=class extends Error{constructor(e,r){super("Network error",r),this.name="NetworkError",this.request=e}},k=class extends Error{constructor(e){super("Force retry"),this.name="ForceRetryError",this.customDelay=e?.delay,this.customRequest=e?.request}},D=class extends Error{constructor(e){let r=e.map(o=>o.message??"Unknown validation error").join("; ");super(`Schema validation failed: ${r}`),this.name="SchemaValidationError",this.issues=e}};var T=Symbol("neta.stop"),N=class{constructor(e){this.options=e}};function Z(t,e){let r=Number(t.headers.get("content-length"))||0,o=0,n=t.body.getReader(),i=new ReadableStream({async pull(p){let{done:y,value:d}=await n.read();if(y){e({percent:1,transferredBytes:o,totalBytes:r||o}),p.close();return}o+=d.byteLength;let g=r?o/r:0;e({percent:g,transferredBytes:o,totalBytes:r}),p.enqueue(d)},cancel(p){return n.cancel(p)}});return new Response(i,{status:t.status,statusText:t.statusText,headers:t.headers})}function ee(t,e,r){let o=Number(t.headers.get("content-length"))||0,n=0,i=t.body.getReader(),p=new ReadableStream({async pull(y){let{done:d,value:g}=await i.read();if(d){e({percent:1,transferredBytes:n,totalBytes:o||n}),y.close();return}n+=g.byteLength;let u=o?n/o:0;e({percent:u,transferredBytes:n,totalBytes:o}),y.enqueue(g)},cancel(y){return i.cancel(y)}});return new Request(t.url,{method:t.method,headers:t.headers,body:p,duplex:"half",signal:t.signal})}function xe(t){return typeof t=="number"?{..._,limit:t}:{..._,...t}}function Te(t){return{init:[...t?.init??[]],beforeRequest:[...t?.beforeRequest??[]],afterResponse:[...t?.afterResponse??[]],beforeError:[...t?.beforeError??[]],beforeRetry:[...t?.beforeRetry??[]]}}function Ee(t,e){let r=new Headers(t);return e&&new Headers(e).forEach((n,i)=>{r.set(i,n)}),r}function Se(t,e){return{init:[...t?.init??[],...e?.init??[]],beforeRequest:[...t?.beforeRequest??[],...e?.beforeRequest??[]],afterResponse:[...t?.afterResponse??[],...e?.afterResponse??[]],beforeError:[...t?.beforeError??[],...e?.beforeError??[]],beforeRetry:[...t?.beforeRetry??[],...e?.beforeRetry??[]]}}function $(t,e){if(!t)return e??{};if(!e)return t;let r={...t,...e};return(t.headers||e.headers)&&(r.headers=Ee(t.headers,e.headers)),(t.hooks||e.hooks)&&(r.hooks=Se(t.hooks,e.hooks)),r}function te(t,e){let r=t instanceof Request?t.url:String(t);if(e?.prefix){let o=String(e.prefix).replace(/\/+$/,""),n=r.replace(/^\/+/,"");r=`${o}/${n}`}if(e?.baseUrl)try{new URL(r)}catch{r=new URL(r,new Request(String(e.baseUrl)).url).href}return r}function re(t,e){if(!e)return t;if(typeof e=="string"){let o=e.replace(/^\?/,"");return o&&(t.search=t.search?`${t.search}&${o}`:`?${o}`),t}let r;if(e instanceof URLSearchParams)r=e;else if(Array.isArray(e))r=new URLSearchParams(e);else{r=new URLSearchParams;for(let[o,n]of Object.entries(e))n!==void 0&&r.set(o,String(n))}return r.forEach((o,n)=>{t.searchParams.append(n,o)}),t}function ne(t){let e={...t,method:ke(t.method??"get"),retry:xe(t.retry),timeout:t.timeout??G,totalTimeout:t.totalTimeout??!1,hooks:Te(t.hooks),throwHttpErrors:t.throwHttpErrors??!0,fetch:t.fetch??globalThis.fetch.bind(globalThis),context:t.context??{},prefix:t.prefix?String(t.prefix):""};if(t.json!==void 0){e.body=e.stringifyJson?e.stringifyJson(t.json):JSON.stringify(t.json);let r=new Headers(e.headers);r.has("content-type")||r.set("content-type","application/json"),e.headers=r}return e}function ke(t){return(t??"get").toLowerCase()}function v(t,e){return new Promise((r,o)=>{let n=e?.signal;if(n?.aborted){o(n.reason??new DOMException("Aborted","AbortError"));return}let i=setTimeout(r,t);n&&n.addEventListener("abort",()=>{clearTimeout(i),o(n.reason??new DOMException("Aborted","AbortError"))},{once:!0})})}function oe(t){let e=t.headers.get("retry-after")??t.headers.get("ratelimit-reset")??t.headers.get("x-ratelimit-retry-after")??t.headers.get("x-ratelimit-reset")??t.headers.get("x-rate-limit-reset");if(!e)return;let r=Number(e);if(!Number.isNaN(r))return r>=Date.parse("2024-01-01")/1e3?Math.max(0,r*1e3-Date.now()):r*1e3;let o=Date.parse(e);if(!Number.isNaN(o))return Math.max(0,o-Date.now())}function se(t,e){if(e===!0)return Math.random()*t;if(typeof e=="function"){let r=e(t);return Number.isFinite(r)&&r>=0?r:t}return t}function ae(t){return t instanceof TypeError&&(t.message==="Failed to fetch"||t.message==="fetch failed"||t.message==="NetworkError when attempting to fetch resource."||t.message.includes("network")||t.message.includes("ECONNREFUSED")||t.message.includes("ENOTFOUND")||t.message.includes("ETIMEDOUT")||t.message.includes("ECONNRESET"))}var ie="The `schema` argument must follow the Standard Schema specification";async function ce(t,e){if(typeof e!="object"&&typeof e!="function"||e===null)throw new TypeError(ie);let r=e["~standard"];if(typeof r!="object"||r===null||typeof r.validate!="function")throw new TypeError(ie);let o=await r.validate(t);if(o.issues)throw new D(o.issues);return o.value}function qe(t,e){let r=new AbortController,o;return t!==!1&&typeof t=="number"&&(o=setTimeout(()=>r.abort("timeout"),t)),e&&(e.aborted?r.abort(e.reason):e.addEventListener("abort",()=>r.abort(e.reason),{once:!0})),{signal:r.signal,cleanup:()=>{o&&clearTimeout(o)}}}async function De(t,e){let{body:r}=t;if(!r)try{return await t.text()}catch{return}let o;try{o=r.getReader()}catch{return}let n=new TextDecoder,i=[],p=0,y=10*1024*1024,d=(async()=>{try{for(;;){let{done:b,value:w}=await o.read();if(b)break;if(p+=w.byteLength,p>y){o.cancel().catch(()=>{});return}i.push(n.decode(w,{stream:!0}))}}catch{return}return i.push(n.decode()),i.join("")})(),g=new Promise(b=>{let w=setTimeout(()=>b(void 0),e);d.finally(()=>clearTimeout(w))}),u=await Promise.race([d,g]);return u===void 0&&o.cancel().catch(()=>{}),u}async function Ne(t,e,r,o){let n=await De(t,e);if(!n)return;let i=(t.headers.get("content-type")??"").split(";",1)[0].trim().toLowerCase();if(!/\/(?:.*[.+-])?json$/.test(i))return n;try{return r.parseJson?await r.parseJson(n,{request:o,response:t}):JSON.parse(n)}catch{return}}function A(t){let{hooks:e,json:r,parseJson:o,stringifyJson:n,searchParams:i,timeout:p,totalTimeout:y,throwHttpErrors:d,fetch:g,context:u,_userSignal:b,prefix:w,baseUrl:U,onDownloadProgress:c,onUploadProgress:J,...I}=t;return Object.freeze(I)}function O(t,e){let r=t.delay(e),o=se(r,t.jitter);return Math.min(t.backoffLimit,o)}function ue(t,e){for(let u of e.hooks.init)u(e);let r=0,o=typeof e.totalTimeout=="number"?performance.now():void 0,n,i=()=>{if(o===void 0)return;let u=performance.now()-o;return Math.max(0,e.totalTimeout-u)},p=()=>{let u=i();return e.timeout===!1?u:u===void 0?e.timeout:Math.min(e.timeout,u)},y=()=>{let u=i();if(u!==void 0&&u<=0)throw new R(n)},d=(async()=>{if(typeof e.timeout=="number"&&e.timeout>q)throw new RangeError(`The \`timeout\` option cannot be greater than ${q}`);if(typeof e.totalTimeout=="number"&&e.totalTimeout>q)throw new RangeError(`The \`totalTimeout\` option cannot be greater than ${q}`);let u=te(t,{prefix:e.prefix,baseUrl:e.baseUrl}),b=new URL(u);b=re(b,e.searchParams);let{prefix:w,baseUrl:U,retry:c,timeout:J,totalTimeout:I,hooks:H,searchParams:Ae,json:Je,throwHttpErrors:P,fetch:le,parseJson:He,stringifyJson:Pe,context:Ce,_userSignal:Le,onDownloadProgress:C,onUploadProgress:B,...de}=e;n=new Request(b.href,{...de,method:e.method.toUpperCase()}),await Promise.resolve();for(let a of H.beforeRequest){let s=await a({request:n,options:A(e),retryCount:0});if(s instanceof Response)return s;s instanceof Request&&(n=s)}let V=e._userSignal,Y=async()=>{let a=p(),s=i();if(s!==void 0&&s<=0)throw new R(n);let l=qe(a,V),f=n.clone();B&&f.body&&X&&(f=ee(f,B,e.body));try{let h=await le(f,{signal:l.signal});return l.cleanup(),h}catch(h){throw l.cleanup(),l.signal.aborted&&l.signal.reason==="timeout"?new R(n):ae(h)?new S(n,{cause:h}):h}},L=async(a,s)=>{let l=Math.min(s,q),f={signal:V},h=i();if(h!==void 0){if(h<=0)throw new R(n);if(l>=h)throw await v(h,f),new R(n)}await v(l,f),y();for(let F of H.beforeRetry){let x=await F({request:n,options:A(e),error:a,retryCount:r+1});if(x instanceof Request){n=x;break}if(x instanceof Response)return r++,x;if(x===T)return T}return y(),r++,Y()},me=async a=>{if(r>=c.limit||!c.methods.includes(e.method))throw a;if(c.shouldRetry!==void 0){let s=await c.shouldRetry({error:a,retryCount:r+1});if(s===!1)throw a;if(s===!0)return O(c,r+1)}if(a instanceof R){if(!c.retryOnTimeout)throw a;return O(c,r+1)}if(a instanceof S)return O(c,r+1);throw a},he=async a=>{if(r>=c.limit||!c.methods.includes(e.method))throw a;if(c.shouldRetry!==void 0){let s=await c.shouldRetry({error:a,retryCount:r+1});if(s===!1)throw a;if(s===!0)return O(c,r+1)}if(!c.statusCodes.includes(a.response.status))throw a;if(c.afterStatusCodes.includes(a.response.status)){let s=oe(a.response);if(s!==void 0)return Math.min(c.maxRetryAfter,Math.max(0,s))}if(a.response.status===413)throw a;return O(c,r+1)},m;for(;;)try{m=await Y();break}catch(a){let s=await me(a),l=await L(a,s);if(l===T)return;if(l instanceof Response){m=l;break}}let j=!1;for(;;){try{for(let s of H.afterResponse){let l=m.clone(),f=await s({request:n,options:A(e),response:l,retryCount:r});if(f instanceof N)throw new k(f.options);f instanceof Response&&(m=f)}}catch(s){if(!(s instanceof k))throw s;let l=s.customDelay??O(c,r+1);s.customRequest&&(n=s.customRequest);let f=await L(s,l);if(f===T)return;if(f instanceof Response){m=f,j=!0;continue}continue}let a=typeof P=="function"?P(m.status):P;if(!m.ok&&m.type!=="opaque"&&a){let s=new E(m,n,A(e));if(s.data=await Ne(m,e.timeout===!1?1e4:e.timeout,e,n),j)throw s;let l;try{l=await he(s)}catch{let h=s;for(let F of H.beforeError){let x=await F({request:n,options:A(e),error:h,retryCount:r});x instanceof Error&&(h=x)}throw h}let f=await L(s,l);if(f===T)return;if(f instanceof Response){m=f,j=!1;continue}continue}break}if(e.parseJson&&(m.json=async()=>{let a=await m.clone().text();return a===""?JSON.parse(a):e.parseJson(a,{request:n,response:m})}),C){if(typeof C!="function")throw new TypeError("The `onDownloadProgress` option must be a function");if(!Q)throw new Error("Streams are not supported. `ReadableStream` is missing.");return Z(m,C)}return m})(),g={then:d.then.bind(d),catch:d.catch.bind(d),finally:d.finally.bind(d),[Symbol.toStringTag]:"ResponsePromise"};for(let[u,b]of Object.entries(K))g[u]=async w=>{n&&n.headers.set("accept",n.headers.get("accept")||b);let U=await d;if(u!=="json")return U[u]();let c=await U.text();if(c==="")return w!==void 0?ce(void 0,w):JSON.parse(c);let J=e.parseJson?await e.parseJson(c,{request:n,response:U}):JSON.parse(c);return w===void 0?J:ce(J,w)};return g}function z(t){let e=(r,o)=>{let n=$(t,o),i=ne(n);return i._userSignal=i.signal??void 0,ue(r,i)};for(let r of W)e[r]=(o,n)=>e(o,{...n,method:r});return e.create=r=>z($(t,r)),e.extend=e.create,e.retry=r=>new N(r),e}var fe=z(),Oe=fe;0&&(module.exports={ForceRetryError,HTTPError,NetaClient,NetaError,NetworkError,SchemaValidationError,TimeoutError,createInstance,neta,stop});
|
|
2
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.js","../src/constants.js","../src/errors.js","../src/types.js","../src/stream.js","../src/utils.js","../src/core.js"],"sourcesContent":["import { HTTP_METHODS } from './constants.js';\nimport { createResponsePromise } from './core.js';\nimport { RetryMarker } from './types.js';\nimport { mergeOptions, normalizeOptions } from './utils.js';\n\n/**\n * @param {import('../types/types.js').Options} [defaults]\n * @returns {import('../types/types.js').NetaInstance}\n */\nexport function createInstance(defaults) {\n const fn = (input, options) => {\n const merged = mergeOptions(defaults, options);\n const normalized = normalizeOptions(merged);\n // Stash user signal before we override it internally\n normalized._userSignal = normalized.signal ?? undefined;\n return createResponsePromise(input, normalized);\n };\n\n for (const method of HTTP_METHODS) {\n fn[method] = (input, options) => fn(input, { ...options, method });\n }\n\n fn.create = (newDefaults) => createInstance(mergeOptions(defaults, newDefaults));\n fn.extend = fn.create;\n\n /**\n * Signal forced retry from an afterResponse hook.\n * @param {{ delay?: number, request?: Request }} [options]\n * @returns {RetryMarker}\n */\n fn.retry = (options) => new RetryMarker(options);\n\n return fn;\n}\n\nconst neta = createInstance();\n\nexport default neta;\nexport { neta };\nexport { HTTPError, TimeoutError, NetworkError, ForceRetryError, SchemaValidationError } from './errors.js';\nexport { HTTPError as NetaError } from './errors.js';\nexport { createInstance as NetaClient };\nexport { stop } from './types.js';\n","/** @type {import('../types/types.js').HttpMethod[]} */\nexport const HTTP_METHODS = [\n 'get', 'post', 'put', 'patch', 'delete', 'head', 'options',\n];\n\n/** @type {import('../types/types.js').RetryOptions} */\nexport const DEFAULT_RETRY = {\n limit: 2,\n methods: ['get', 'put', 'head', 'delete', 'options'],\n statusCodes: [408, 413, 429, 500, 502, 503, 504],\n afterStatusCodes: [413, 429, 503],\n maxRetryAfter: Infinity,\n backoffLimit: Infinity,\n delay: (attemptCount) => 300 * 2 ** (attemptCount - 1),\n jitter: false,\n retryOnTimeout: false,\n shouldRetry: undefined,\n};\n\nexport const DEFAULT_TIMEOUT = 10_000;\n\nexport const maxSafeTimeout = 2_147_483_647; // 2^31 - 1\n\n/** @type {Record<string, string>} */\nexport const responseTypes = {\n json: 'application/json',\n text: 'text/*',\n formData: 'multipart/form-data',\n arrayBuffer: '*/*',\n blob: '*/*',\n bytes: '*/*',\n};\n\nexport const supportsAbortController = typeof globalThis.AbortController === 'function';\nexport const supportsAbortSignal = typeof globalThis.AbortSignal !== 'undefined';\nexport const supportsFormData = typeof globalThis.FormData === 'function';\n\nexport const supportsResponseStreams = (() => {\n try {\n return typeof globalThis.ReadableStream === 'function';\n } catch {\n return false;\n }\n})();\n\nexport const supportsRequestStreams = (() => {\n try {\n let duplexAccessed = false;\n const hasContentType = new Request(\n new URL('https://empty.invalid'),\n {\n body: new ReadableStream(),\n method: 'POST',\n get duplex() {\n duplexAccessed = true;\n return 'half';\n },\n },\n ).headers.has('Content-Type');\n return duplexAccessed && !hasContentType;\n } catch {\n return false;\n }\n})();\n","export class HTTPError extends Error {\n /**\n * @param {Response} response\n * @param {Request} request\n * @param {import('./types.js').NormalizedOptions} options\n */\n constructor(response, request, options) {\n const status = `${response.status}${response.statusText ? ` ${response.statusText}` : ''}`;\n super(`Request failed with status code ${status}`);\n this.name = 'HTTPError';\n this.response = response;\n this.request = request;\n this.options = options;\n /** @type {unknown} */\n this.data = undefined;\n }\n}\n\nexport class TimeoutError extends Error {\n /**\n * @param {Request} request\n */\n constructor(request) {\n super('Request timed out');\n this.name = 'TimeoutError';\n this.request = request;\n }\n}\n\nexport class NetworkError extends Error {\n /**\n * @param {Request} request\n * @param {{ cause?: Error }} [options]\n */\n constructor(request, options) {\n super('Network error', options);\n this.name = 'NetworkError';\n this.request = request;\n }\n}\n\nexport class ForceRetryError extends Error {\n /**\n * @param {{ delay?: number, request?: Request }} [options]\n */\n constructor(options) {\n super('Force retry');\n this.name = 'ForceRetryError';\n this.customDelay = options?.delay;\n this.customRequest = options?.request;\n }\n}\n\nexport class SchemaValidationError extends Error {\n /**\n * @param {Array<{ message?: string, path?: Array<string | number | symbol> }>} issues\n */\n constructor(issues) {\n const message = issues.map((i) => i.message ?? 'Unknown validation error').join('; ');\n super(`Schema validation failed: ${message}`);\n this.name = 'SchemaValidationError';\n this.issues = issues;\n }\n}\n","/** @type {unique symbol} */\nexport const stop = Symbol('neta.stop');\n\nexport class RetryMarker {\n /**\n * @param {{ delay?: number, request?: Request }} [options]\n */\n constructor(options) {\n this.options = options;\n }\n}\n","/**\n * @param {Response} response\n * @param {(progress: { percent: number, transferredBytes: number, totalBytes: number }) => void} onDownloadProgress\n * @returns {Response}\n */\nexport function streamResponse(response, onDownloadProgress) {\n const totalBytes = Number(response.headers.get('content-length')) || 0;\n let transferredBytes = 0;\n\n const reader = response.body.getReader();\n\n const stream = new ReadableStream({\n async pull(controller) {\n const { done, value } = await reader.read();\n if (done) {\n onDownloadProgress({\n percent: 1,\n transferredBytes,\n totalBytes: totalBytes || transferredBytes,\n });\n controller.close();\n return;\n }\n\n transferredBytes += value.byteLength;\n const percent = totalBytes ? transferredBytes / totalBytes : 0;\n onDownloadProgress({ percent, transferredBytes, totalBytes });\n controller.enqueue(value);\n },\n cancel(reason) {\n return reader.cancel(reason);\n },\n });\n\n return new Response(stream, {\n status: response.status,\n statusText: response.statusText,\n headers: response.headers,\n });\n}\n\n/**\n * @param {Request} request\n * @param {(progress: { percent: number, transferredBytes: number, totalBytes: number }) => void} onUploadProgress\n * @param {BodyInit} [originalBody]\n * @returns {Request}\n */\nexport function streamRequest(request, onUploadProgress, originalBody) {\n const totalBytes = Number(request.headers.get('content-length')) || 0;\n let transferredBytes = 0;\n\n const reader = request.body.getReader();\n\n const stream = new ReadableStream({\n async pull(controller) {\n const { done, value } = await reader.read();\n if (done) {\n onUploadProgress({\n percent: 1,\n transferredBytes,\n totalBytes: totalBytes || transferredBytes,\n });\n controller.close();\n return;\n }\n\n transferredBytes += value.byteLength;\n const percent = totalBytes ? transferredBytes / totalBytes : 0;\n onUploadProgress({ percent, transferredBytes, totalBytes });\n controller.enqueue(value);\n },\n cancel(reason) {\n return reader.cancel(reason);\n },\n });\n\n return new Request(request.url, {\n method: request.method,\n headers: request.headers,\n body: stream,\n duplex: 'half',\n signal: request.signal,\n });\n}\n","import { DEFAULT_RETRY, DEFAULT_TIMEOUT } from './constants.js';\n\n/**\n * @param {import('../types/types.js').Options['retry']} retry\n * @returns {import('../types/types.js').RetryOptions}\n */\nexport function normalizeRetry(retry) {\n if (typeof retry === 'number') {\n return { ...DEFAULT_RETRY, limit: retry };\n }\n return { ...DEFAULT_RETRY, ...retry };\n}\n\n/**\n * @param {import('../types/types.js').Hooks} [hooks]\n * @returns {import('../types/types.js').NormalizedHooks}\n */\nexport function normalizeHooks(hooks) {\n return {\n init: [...(hooks?.init ?? [])],\n beforeRequest: [...(hooks?.beforeRequest ?? [])],\n afterResponse: [...(hooks?.afterResponse ?? [])],\n beforeError: [...(hooks?.beforeError ?? [])],\n beforeRetry: [...(hooks?.beforeRetry ?? [])],\n };\n}\n\n/**\n * @param {HeadersInit} [target]\n * @param {HeadersInit} [source]\n * @returns {Headers}\n */\nexport function mergeHeaders(target, source) {\n const result = new Headers(target);\n if (source) {\n const sourceHeaders = new Headers(source);\n sourceHeaders.forEach((value, key) => {\n result.set(key, value);\n });\n }\n return result;\n}\n\n/**\n * Merge hooks by concatenating arrays.\n * @param {import('../types/types.js').Hooks} [base]\n * @param {import('../types/types.js').Hooks} [override]\n * @returns {import('../types/types.js').Hooks}\n */\nexport function mergeHooks(base, override) {\n return {\n init: [...(base?.init ?? []), ...(override?.init ?? [])],\n beforeRequest: [...(base?.beforeRequest ?? []), ...(override?.beforeRequest ?? [])],\n afterResponse: [...(base?.afterResponse ?? []), ...(override?.afterResponse ?? [])],\n beforeError: [...(base?.beforeError ?? []), ...(override?.beforeError ?? [])],\n beforeRetry: [...(base?.beforeRetry ?? []), ...(override?.beforeRetry ?? [])],\n };\n}\n\n/**\n * @param {import('../types/types.js').Options} [defaults]\n * @param {import('../types/types.js').Options} [overrides]\n * @returns {import('../types/types.js').Options}\n */\nexport function mergeOptions(defaults, overrides) {\n if (!defaults) return overrides ?? {};\n if (!overrides) return defaults;\n\n const merged = { ...defaults, ...overrides };\n\n if (defaults.headers || overrides.headers) {\n merged.headers = mergeHeaders(defaults.headers, overrides.headers);\n }\n\n if (defaults.hooks || overrides.hooks) {\n merged.hooks = mergeHooks(defaults.hooks, overrides.hooks);\n }\n\n return merged;\n}\n\n/**\n * @param {string | URL | Request} input\n * @param {{ prefix?: string, baseUrl?: string | URL }} [options]\n * @returns {string}\n */\nexport function resolveInput(input, options) {\n let inputStr = input instanceof Request ? input.url : String(input);\n\n if (options?.prefix) {\n const prefix = String(options.prefix).replace(/\\/+$/, '');\n const path = inputStr.replace(/^\\/+/, '');\n inputStr = `${prefix}/${path}`;\n }\n\n if (options?.baseUrl) {\n try {\n // If already absolute, keep as-is\n new URL(inputStr);\n } catch {\n // Relative — resolve against baseUrl\n inputStr = new URL(inputStr, new Request(String(options.baseUrl)).url).href;\n }\n }\n\n return inputStr;\n}\n\n/**\n * @param {URL} url\n * @param {import('../types/types.js').SearchParamsInit} [searchParams]\n * @returns {URL}\n */\nexport function appendSearchParams(url, searchParams) {\n if (!searchParams) return url;\n\n if (typeof searchParams === 'string') {\n const cleaned = searchParams.replace(/^\\?/, '');\n if (cleaned) {\n url.search = url.search ? `${url.search}&${cleaned}` : `?${cleaned}`;\n }\n return url;\n }\n\n /** @type {URLSearchParams} */\n let params;\n\n if (searchParams instanceof URLSearchParams) {\n params = searchParams;\n } else if (Array.isArray(searchParams)) {\n params = new URLSearchParams(searchParams);\n } else {\n params = new URLSearchParams();\n for (const [key, value] of Object.entries(searchParams)) {\n if (value !== undefined) {\n params.set(key, String(value));\n }\n }\n }\n\n params.forEach((value, key) => {\n url.searchParams.append(key, value);\n });\n\n return url;\n}\n\n/**\n * @param {import('../types/types.js').Options} options\n * @returns {import('../types/types.js').InternalOptions}\n */\nexport function normalizeOptions(options) {\n const normalized = {\n ...options,\n method: normalizeRequestMethod(options.method ?? 'get'),\n retry: normalizeRetry(options.retry),\n timeout: options.timeout ?? DEFAULT_TIMEOUT,\n totalTimeout: options.totalTimeout ?? false,\n hooks: normalizeHooks(options.hooks),\n throwHttpErrors: options.throwHttpErrors ?? true,\n fetch: options.fetch ?? globalThis.fetch.bind(globalThis),\n context: options.context ?? {},\n prefix: options.prefix ? String(options.prefix) : '',\n };\n\n if (options.json !== undefined) {\n normalized.body = normalized.stringifyJson\n ? normalized.stringifyJson(options.json)\n : JSON.stringify(options.json);\n const headers = new Headers(normalized.headers);\n if (!headers.has('content-type')) {\n headers.set('content-type', 'application/json');\n }\n normalized.headers = headers;\n }\n\n return normalized;\n}\n\n/**\n * @param {string} [method]\n * @returns {string}\n */\nexport function normalizeRequestMethod(method) {\n return (method ?? 'get').toLowerCase();\n}\n\n/**\n * @param {number} ms\n * @param {{ signal?: AbortSignal }} [options]\n * @returns {Promise<void>}\n */\nexport function delay(ms, options) {\n return new Promise((resolve, reject) => {\n const signal = options?.signal;\n\n if (signal?.aborted) {\n reject(signal.reason ?? new DOMException('Aborted', 'AbortError'));\n return;\n }\n\n const timer = setTimeout(resolve, ms);\n\n if (signal) {\n signal.addEventListener(\n 'abort',\n () => {\n clearTimeout(timer);\n reject(signal.reason ?? new DOMException('Aborted', 'AbortError'));\n },\n { once: true },\n );\n }\n });\n}\n\n/**\n * Parse Retry-After from multiple common header formats.\n * @param {Response} response\n * @returns {number | undefined}\n */\nexport function parseRetryAfter(response) {\n const header =\n response.headers.get('retry-after') ??\n response.headers.get('ratelimit-reset') ??\n response.headers.get('x-ratelimit-retry-after') ??\n response.headers.get('x-ratelimit-reset') ??\n response.headers.get('x-rate-limit-reset');\n\n if (!header) return undefined;\n\n const seconds = Number(header);\n if (!Number.isNaN(seconds)) {\n // Large numbers are treated as timestamps (threshold: 2024-01-01 epoch)\n if (seconds >= Date.parse('2024-01-01') / 1000) {\n return Math.max(0, seconds * 1000 - Date.now());\n }\n return seconds * 1000;\n }\n\n const date = Date.parse(header);\n if (!Number.isNaN(date)) {\n return Math.max(0, date - Date.now());\n }\n\n return undefined;\n}\n\n/**\n * Apply jitter to a delay value.\n * @param {number} delayMs\n * @param {boolean | ((delay: number) => number)} jitter\n * @returns {number}\n */\nexport function applyJitter(delayMs, jitter) {\n if (jitter === true) {\n return Math.random() * delayMs;\n }\n if (typeof jitter === 'function') {\n const result = jitter(delayMs);\n return Number.isFinite(result) && result >= 0 ? result : delayMs;\n }\n return delayMs;\n}\n\n/**\n * Check if error is a raw network error (TypeError from fetch).\n * @param {unknown} error\n * @returns {boolean}\n */\nexport function isNetworkError(error) {\n return (\n error instanceof TypeError &&\n (error.message === 'Failed to fetch' ||\n error.message === 'fetch failed' ||\n error.message === 'NetworkError when attempting to fetch resource.' ||\n error.message.includes('network') ||\n error.message.includes('ECONNREFUSED') ||\n error.message.includes('ENOTFOUND') ||\n error.message.includes('ETIMEDOUT') ||\n error.message.includes('ECONNRESET'))\n );\n}\n","import { HTTPError, TimeoutError, NetworkError, ForceRetryError, SchemaValidationError } from './errors.js';\nimport { RetryMarker } from './types.js';\nimport { stop } from './types.js';\nimport { streamResponse, streamRequest } from './stream.js';\nimport {\n appendSearchParams,\n resolveInput,\n delay,\n isNetworkError,\n applyJitter,\n parseRetryAfter,\n} from './utils.js';\nimport {\n maxSafeTimeout,\n responseTypes,\n supportsResponseStreams,\n supportsRequestStreams,\n} from './constants.js';\n\nconst invalidSchemaMessage = 'The `schema` argument must follow the Standard Schema specification';\n\n/**\n * @param {unknown} jsonValue\n * @param {any} schema\n * @returns {Promise<unknown>}\n */\nasync function validateJsonWithSchema(jsonValue, schema) {\n if ((typeof schema !== 'object' && typeof schema !== 'function') || schema === null) {\n throw new TypeError(invalidSchemaMessage);\n }\n\n const standardSchema = schema['~standard'];\n if (\n typeof standardSchema !== 'object' ||\n standardSchema === null ||\n typeof standardSchema.validate !== 'function'\n ) {\n throw new TypeError(invalidSchemaMessage);\n }\n\n const result = await standardSchema.validate(jsonValue);\n if (result.issues) {\n throw new SchemaValidationError(result.issues);\n }\n\n return result.value;\n}\n\n/**\n * @param {number | false} timeout\n * @param {AbortSignal} [userSignal]\n * @returns {{ signal: AbortSignal, cleanup: () => void }}\n */\nfunction createManagedSignal(timeout, userSignal) {\n const controller = new AbortController();\n /** @type {ReturnType<typeof setTimeout> | undefined} */\n let timer;\n\n if (timeout !== false && typeof timeout === 'number') {\n timer = setTimeout(() => controller.abort('timeout'), timeout);\n }\n\n if (userSignal) {\n if (userSignal.aborted) {\n controller.abort(userSignal.reason);\n } else {\n userSignal.addEventListener('abort', () => controller.abort(userSignal.reason), { once: true });\n }\n }\n\n return {\n signal: controller.signal,\n cleanup: () => {\n if (timer) clearTimeout(timer);\n },\n };\n}\n\n/**\n * Read response text with timeout and size limit.\n * @param {Response} response\n * @param {number} timeoutMs\n * @returns {Promise<string | undefined>}\n */\nasync function readResponseText(response, timeoutMs) {\n const { body } = response;\n if (!body) {\n try {\n return await response.text();\n } catch {\n return undefined;\n }\n }\n\n /** @type {ReadableStreamDefaultReader<Uint8Array>} */\n let reader;\n try {\n reader = body.getReader();\n } catch {\n return undefined;\n }\n\n const decoder = new TextDecoder();\n const chunks = [];\n let totalBytes = 0;\n const maxSize = 10 * 1024 * 1024;\n\n const readAll = (async () => {\n try {\n for (;;) {\n const { done, value } = await reader.read();\n if (done) break;\n totalBytes += value.byteLength;\n if (totalBytes > maxSize) {\n void reader.cancel().catch(() => {});\n return undefined;\n }\n chunks.push(decoder.decode(value, { stream: true }));\n }\n } catch {\n return undefined;\n }\n chunks.push(decoder.decode());\n return chunks.join('');\n })();\n\n const timeoutPromise = new Promise((resolve) => {\n const id = setTimeout(() => resolve(undefined), timeoutMs);\n void readAll.finally(() => clearTimeout(id));\n });\n\n const result = await Promise.race([readAll, timeoutPromise]);\n if (result === undefined) void reader.cancel().catch(() => {});\n return result;\n}\n\n/**\n * @param {Response} response\n * @param {number} timeoutMs\n * @param {any} options\n * @param {Request} request\n * @returns {Promise<unknown>}\n */\nasync function getResponseData(response, timeoutMs, options, request) {\n const text = await readResponseText(response, timeoutMs);\n if (!text) return undefined;\n\n const contentType = (response.headers.get('content-type') ?? '').split(';', 1)[0].trim().toLowerCase();\n const isJson = /\\/(?:.*[.+-])?json$/.test(contentType);\n if (!isJson) return text;\n\n try {\n return options.parseJson\n ? await options.parseJson(text, { request, response })\n : JSON.parse(text);\n } catch {\n return undefined;\n }\n}\n\n/**\n * Strip internal-only options.\n * @param {any} options\n * @returns {any}\n */\nfunction getNormalizedOptions(options) {\n const {\n hooks, json, parseJson, stringifyJson, searchParams,\n timeout, totalTimeout, throwHttpErrors, fetch,\n context, _userSignal, prefix, baseUrl,\n onDownloadProgress, onUploadProgress,\n ...rest\n } = options;\n return Object.freeze(rest);\n}\n\n/**\n * Calculate retry delay.\n * @param {any} retry\n * @param {number} retryCount\n * @returns {number}\n */\nfunction calculateDelay(retry, retryCount) {\n const base = retry.delay(retryCount);\n const jittered = applyJitter(base, retry.jitter);\n return Math.min(retry.backoffLimit, jittered);\n}\n\n/**\n * @param {string | URL | Request} input\n * @param {any} options\n * @returns {any}\n */\nexport function createResponsePromise(input, options) {\n // Run init hooks (synchronous, mutate options clone)\n for (const hook of options.hooks.init) {\n hook(options);\n }\n\n let retryCount = 0;\n const startTime = typeof options.totalTimeout === 'number' ? performance.now() : undefined;\n /** @type {Request} */\n let currentRequest;\n\n const getRemainingTotalTimeout = () => {\n if (startTime === undefined) return undefined;\n const elapsed = performance.now() - startTime;\n return Math.max(0, options.totalTimeout - elapsed);\n };\n\n const getEffectiveTimeout = () => {\n const remaining = getRemainingTotalTimeout();\n if (options.timeout === false) return remaining;\n if (remaining === undefined) return options.timeout;\n return Math.min(options.timeout, remaining);\n };\n\n const throwIfTotalTimeoutExhausted = () => {\n const remaining = getRemainingTotalTimeout();\n if (remaining !== undefined && remaining <= 0) {\n throw new TimeoutError(currentRequest);\n }\n };\n\n const innerPromise = (async () => {\n if (typeof options.timeout === 'number' && options.timeout > maxSafeTimeout) {\n throw new RangeError(`The \\`timeout\\` option cannot be greater than ${maxSafeTimeout}`);\n }\n if (typeof options.totalTimeout === 'number' && options.totalTimeout > maxSafeTimeout) {\n throw new RangeError(`The \\`totalTimeout\\` option cannot be greater than ${maxSafeTimeout}`);\n }\n\n // Resolve URL\n const inputStr = resolveInput(input, { prefix: options.prefix, baseUrl: options.baseUrl });\n let url = new URL(inputStr);\n url = appendSearchParams(url, options.searchParams);\n\n // Build request init (strip neta-specific options)\n const {\n prefix: _prefix, baseUrl: _baseUrl, retry, timeout, totalTimeout,\n hooks, searchParams, json, throwHttpErrors, fetch: fetchFn,\n parseJson, stringifyJson, context, _userSignal,\n onDownloadProgress, onUploadProgress,\n ...requestInit\n } = options;\n\n currentRequest = new Request(url.href, {\n ...requestInit,\n method: options.method.toUpperCase(),\n });\n\n // Defer so body shortcuts can set Accept header\n await Promise.resolve();\n\n // beforeRequest hooks\n for (const hook of hooks.beforeRequest) {\n const result = await hook({\n request: currentRequest,\n options: getNormalizedOptions(options),\n retryCount: 0,\n });\n\n if (result instanceof Response) return result;\n if (result instanceof Request) currentRequest = result;\n }\n\n const userSignal = options._userSignal;\n\n /**\n * Perform a single fetch attempt with timeout.\n * @returns {Promise<Response>}\n */\n const doFetch = async () => {\n const effectiveTimeout = getEffectiveTimeout();\n const remaining = getRemainingTotalTimeout();\n if (remaining !== undefined && remaining <= 0) throw new TimeoutError(currentRequest);\n\n const managed = createManagedSignal(effectiveTimeout, userSignal);\n\n let fetchRequest = currentRequest.clone();\n if (onUploadProgress && fetchRequest.body && supportsRequestStreams) {\n fetchRequest = streamRequest(fetchRequest, onUploadProgress, options.body);\n }\n\n try {\n const response = await fetchFn(fetchRequest, { signal: managed.signal });\n managed.cleanup();\n return response;\n } catch (error) {\n managed.cleanup();\n if (managed.signal.aborted && managed.signal.reason === 'timeout') {\n throw new TimeoutError(currentRequest);\n }\n if (isNetworkError(error)) {\n throw new NetworkError(currentRequest, { cause: error });\n }\n throw error;\n }\n };\n\n /**\n * Attempt retry: wait, run beforeRetry hooks, then fetch again.\n * @param {Error} error\n * @param {number} delayMs\n * @returns {Promise<Response | typeof stop>}\n */\n const attemptRetry = async (error, delayMs) => {\n const safeDelay = Math.min(delayMs, maxSafeTimeout);\n const delayOptions = { signal: userSignal };\n\n const remaining = getRemainingTotalTimeout();\n if (remaining !== undefined) {\n if (remaining <= 0) throw new TimeoutError(currentRequest);\n if (safeDelay >= remaining) {\n await delay(remaining, delayOptions);\n throw new TimeoutError(currentRequest);\n }\n }\n\n await delay(safeDelay, delayOptions);\n throwIfTotalTimeoutExhausted();\n\n // Run beforeRetry hooks\n for (const hook of hooks.beforeRetry) {\n const result = await hook({\n request: currentRequest,\n options: getNormalizedOptions(options),\n error,\n retryCount: retryCount + 1,\n });\n\n if (result instanceof Request) { currentRequest = result; break; }\n if (result instanceof Response) { retryCount++; return result; }\n if (result === stop) return stop;\n }\n\n throwIfTotalTimeoutExhausted();\n retryCount++;\n return doFetch();\n };\n\n /**\n * Check if we should retry a fetch-level error (timeout, network).\n * @param {Error} error\n * @returns {Promise<number>} delay in ms, or throws if no retry\n */\n const getRetryDelayForFetchError = async (error) => {\n if (retryCount >= retry.limit) throw error;\n if (!retry.methods.includes(options.method)) throw error;\n\n if (retry.shouldRetry !== undefined) {\n const result = await retry.shouldRetry({ error, retryCount: retryCount + 1 });\n if (result === false) throw error;\n if (result === true) return calculateDelay(retry, retryCount + 1);\n }\n\n if (error instanceof TimeoutError) {\n if (!retry.retryOnTimeout) throw error;\n return calculateDelay(retry, retryCount + 1);\n }\n\n if (error instanceof NetworkError) {\n return calculateDelay(retry, retryCount + 1);\n }\n\n throw error;\n };\n\n /**\n * Check if we should retry an HTTP error.\n * @param {HTTPError} error\n * @returns {Promise<number>} delay in ms, or throws if no retry\n */\n const getRetryDelayForHttpError = async (error) => {\n if (retryCount >= retry.limit) throw error;\n if (!retry.methods.includes(options.method)) throw error;\n\n if (retry.shouldRetry !== undefined) {\n const result = await retry.shouldRetry({ error, retryCount: retryCount + 1 });\n if (result === false) throw error;\n if (result === true) return calculateDelay(retry, retryCount + 1);\n }\n\n if (!retry.statusCodes.includes(error.response.status)) throw error;\n\n // Handle Retry-After\n if (retry.afterStatusCodes.includes(error.response.status)) {\n const retryAfter = parseRetryAfter(error.response);\n if (retryAfter !== undefined) {\n return Math.min(retry.maxRetryAfter, Math.max(0, retryAfter));\n }\n }\n\n if (error.response.status === 413) throw error;\n\n return calculateDelay(retry, retryCount + 1);\n };\n\n // === Main request loop ===\n /** @type {Response} */\n let response;\n\n // Initial fetch with fetch-error retry loop\n for (;;) {\n try {\n response = await doFetch();\n break;\n } catch (error) {\n const retryDelay = await getRetryDelayForFetchError(error);\n const retryResult = await attemptRetry(error, retryDelay);\n if (retryResult === stop) return undefined;\n if (retryResult instanceof Response) { response = retryResult; break; }\n }\n }\n\n // afterResponse hooks + HTTP error retry loop\n let responseFromHook = false;\n\n for (;;) {\n // Run afterResponse hooks\n try {\n for (const hook of hooks.afterResponse) {\n const clonedResponse = response.clone();\n const hookResult = await hook({\n request: currentRequest,\n options: getNormalizedOptions(options),\n response: clonedResponse,\n retryCount,\n });\n\n if (hookResult instanceof RetryMarker) {\n throw new ForceRetryError(hookResult.options);\n }\n\n if (hookResult instanceof Response) {\n response = hookResult;\n }\n }\n } catch (error) {\n if (!(error instanceof ForceRetryError)) throw error;\n\n // Forced retry from afterResponse hook\n const retryDelay = error.customDelay ?? calculateDelay(retry, retryCount + 1);\n if (error.customRequest) currentRequest = error.customRequest;\n const retryResult = await attemptRetry(error, retryDelay);\n if (retryResult === stop) return undefined;\n if (retryResult instanceof Response) {\n response = retryResult;\n responseFromHook = true;\n continue;\n }\n continue;\n }\n\n // Check HTTP errors\n const shouldThrow = typeof throwHttpErrors === 'function'\n ? throwHttpErrors(response.status)\n : throwHttpErrors;\n\n if (!response.ok && response.type !== 'opaque' && shouldThrow) {\n const error = new HTTPError(response, currentRequest, getNormalizedOptions(options));\n error.data = await getResponseData(\n response,\n options.timeout === false ? 10_000 : options.timeout,\n options,\n currentRequest,\n );\n\n if (responseFromHook) throw error;\n\n // Try to retry\n let retryDelay;\n try {\n retryDelay = await getRetryDelayForHttpError(error);\n } catch {\n // Run beforeError hooks, then throw\n let processedError = error;\n for (const hook of hooks.beforeError) {\n const hookResult = await hook({\n request: currentRequest,\n options: getNormalizedOptions(options),\n error: processedError,\n retryCount,\n });\n if (hookResult instanceof Error) processedError = hookResult;\n }\n throw processedError;\n }\n\n const retryResult = await attemptRetry(error, retryDelay);\n if (retryResult === stop) return undefined;\n if (retryResult instanceof Response) {\n response = retryResult;\n responseFromHook = false; // Allow further retries\n continue;\n }\n continue;\n }\n\n break;\n }\n\n // Decorate response with custom parseJson\n if (options.parseJson) {\n response.json = async () => {\n const text = await response.clone().text();\n if (text === '') return JSON.parse(text);\n return options.parseJson(text, { request: currentRequest, response });\n };\n }\n\n // Handle download progress\n if (onDownloadProgress) {\n if (typeof onDownloadProgress !== 'function') {\n throw new TypeError('The `onDownloadProgress` option must be a function');\n }\n if (!supportsResponseStreams) {\n throw new Error('Streams are not supported. `ReadableStream` is missing.');\n }\n return streamResponse(response, onDownloadProgress);\n }\n\n return response;\n })();\n\n // Build ResponsePromise thenable\n /** @type {any} */\n const responsePromise = {\n then: innerPromise.then.bind(innerPromise),\n catch: innerPromise.catch.bind(innerPromise),\n finally: innerPromise.finally.bind(innerPromise),\n [Symbol.toStringTag]: 'ResponsePromise',\n };\n\n for (const [type, mimeType] of Object.entries(responseTypes)) {\n responsePromise[type] = async (schema) => {\n if (currentRequest) {\n currentRequest.headers.set('accept', currentRequest.headers.get('accept') || mimeType);\n }\n\n const response = await innerPromise;\n if (type !== 'json') return response[type]();\n\n const text = await response.text();\n if (text === '') {\n if (schema !== undefined) return validateJsonWithSchema(undefined, schema);\n return JSON.parse(text);\n }\n\n const jsonValue = options.parseJson\n ? await options.parseJson(text, { request: currentRequest, response })\n : JSON.parse(text);\n\n return schema === undefined ? jsonValue : validateJsonWithSchema(jsonValue, schema);\n };\n }\n\n return responsePromise;\n}\n"],"mappings":"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","names":["index_exports","__export","ForceRetryError","HTTPError","createInstance","NetworkError","SchemaValidationError","TimeoutError","index_default","neta","stop","__toCommonJS","HTTP_METHODS","DEFAULT_RETRY","attemptCount","DEFAULT_TIMEOUT","maxSafeTimeout","responseTypes","supportsAbortController","supportsAbortSignal","supportsFormData","supportsResponseStreams","supportsRequestStreams","duplexAccessed","hasContentType","HTTPError","response","request","options","status","TimeoutError","NetworkError","ForceRetryError","SchemaValidationError","issues","message","i","stop","RetryMarker","options","streamResponse","response","onDownloadProgress","totalBytes","transferredBytes","reader","stream","controller","done","value","percent","reason","streamRequest","request","onUploadProgress","originalBody","normalizeRetry","retry","DEFAULT_RETRY","normalizeHooks","hooks","mergeHeaders","target","source","result","value","key","mergeHooks","base","override","mergeOptions","defaults","overrides","merged","resolveInput","input","options","inputStr","prefix","path","appendSearchParams","url","searchParams","cleaned","params","normalizeOptions","normalized","normalizeRequestMethod","DEFAULT_TIMEOUT","headers","method","delay","ms","resolve","reject","signal","timer","parseRetryAfter","response","header","seconds","date","applyJitter","delayMs","jitter","isNetworkError","error","invalidSchemaMessage","validateJsonWithSchema","jsonValue","schema","standardSchema","result","SchemaValidationError","createManagedSignal","timeout","userSignal","controller","timer","readResponseText","response","timeoutMs","body","reader","decoder","chunks","totalBytes","maxSize","readAll","done","value","timeoutPromise","resolve","id","getResponseData","options","request","text","contentType","getNormalizedOptions","hooks","json","parseJson","stringifyJson","searchParams","totalTimeout","throwHttpErrors","fetch","context","_userSignal","prefix","baseUrl","onDownloadProgress","onUploadProgress","rest","calculateDelay","retry","retryCount","base","jittered","applyJitter","createResponsePromise","input","hook","startTime","currentRequest","getRemainingTotalTimeout","elapsed","getEffectiveTimeout","remaining","throwIfTotalTimeoutExhausted","TimeoutError","innerPromise","maxSafeTimeout","inputStr","resolveInput","url","appendSearchParams","_prefix","_baseUrl","fetchFn","requestInit","doFetch","effectiveTimeout","managed","fetchRequest","supportsRequestStreams","streamRequest","error","isNetworkError","NetworkError","attemptRetry","delayMs","safeDelay","delayOptions","delay","stop","getRetryDelayForFetchError","getRetryDelayForHttpError","retryAfter","parseRetryAfter","retryDelay","retryResult","responseFromHook","clonedResponse","hookResult","RetryMarker","ForceRetryError","shouldThrow","HTTPError","processedError","supportsResponseStreams","streamResponse","responsePromise","type","mimeType","responseTypes","createInstance","defaults","fn","input","options","merged","mergeOptions","normalized","normalizeOptions","createResponsePromise","method","HTTP_METHODS","newDefaults","RetryMarker","neta","index_default"]}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
var V=["get","post","put","patch","delete","head","options"],M={limit:2,methods:["get","put","head","delete","options"],statusCodes:[408,413,429,500,502,503,504],afterStatusCodes:[413,429,503],maxRetryAfter:1/0,backoffLimit:1/0,delay:t=>300*2**(t-1),jitter:!1,retryOnTimeout:!1,shouldRetry:void 0},Y=1e4,E=2147483647,W={json:"application/json",text:"text/*",formData:"multipart/form-data",arrayBuffer:"*/*",blob:"*/*",bytes:"*/*"},Ne=typeof globalThis.AbortController=="function",Oe=typeof globalThis.AbortSignal<"u",Ue=typeof globalThis.FormData=="function",G=(()=>{try{return typeof globalThis.ReadableStream=="function"}catch{return!1}})(),K=(()=>{try{let t=!1,e=new Request(new URL("https://empty.invalid"),{body:new ReadableStream,method:"POST",get duplex(){return t=!0,"half"}}).headers.has("Content-Type");return t&&!e}catch{return!1}})();var S=class extends Error{constructor(e,r,o){let n=`${e.status}${e.statusText?` ${e.statusText}`:""}`;super(`Request failed with status code ${n}`),this.name="HTTPError",this.response=e,this.request=r,this.options=o,this.data=void 0}},b=class extends Error{constructor(e){super("Request timed out"),this.name="TimeoutError",this.request=e}},k=class extends Error{constructor(e,r){super("Network error",r),this.name="NetworkError",this.request=e}},q=class extends Error{constructor(e){super("Force retry"),this.name="ForceRetryError",this.customDelay=e?.delay,this.customRequest=e?.request}},U=class extends Error{constructor(e){let r=e.map(o=>o.message??"Unknown validation error").join("; ");super(`Schema validation failed: ${r}`),this.name="SchemaValidationError",this.issues=e}};var T=Symbol("neta.stop"),D=class{constructor(e){this.options=e}};function Q(t,e){let r=Number(t.headers.get("content-length"))||0,o=0,n=t.body.getReader(),i=new ReadableStream({async pull(p){let{done:y,value:d}=await n.read();if(y){e({percent:1,transferredBytes:o,totalBytes:r||o}),p.close();return}o+=d.byteLength;let R=r?o/r:0;e({percent:R,transferredBytes:o,totalBytes:r}),p.enqueue(d)},cancel(p){return n.cancel(p)}});return new Response(i,{status:t.status,statusText:t.statusText,headers:t.headers})}function X(t,e,r){let o=Number(t.headers.get("content-length"))||0,n=0,i=t.body.getReader(),p=new ReadableStream({async pull(y){let{done:d,value:R}=await i.read();if(d){e({percent:1,transferredBytes:n,totalBytes:o||n}),y.close();return}n+=R.byteLength;let u=o?n/o:0;e({percent:u,transferredBytes:n,totalBytes:o}),y.enqueue(R)},cancel(y){return i.cancel(y)}});return new Request(t.url,{method:t.method,headers:t.headers,body:p,duplex:"half",signal:t.signal})}function me(t){return typeof t=="number"?{...M,limit:t}:{...M,...t}}function he(t){return{init:[...t?.init??[]],beforeRequest:[...t?.beforeRequest??[]],afterResponse:[...t?.afterResponse??[]],beforeError:[...t?.beforeError??[]],beforeRetry:[...t?.beforeRetry??[]]}}function pe(t,e){let r=new Headers(t);return e&&new Headers(e).forEach((n,i)=>{r.set(i,n)}),r}function ye(t,e){return{init:[...t?.init??[],...e?.init??[]],beforeRequest:[...t?.beforeRequest??[],...e?.beforeRequest??[]],afterResponse:[...t?.afterResponse??[],...e?.afterResponse??[]],beforeError:[...t?.beforeError??[],...e?.beforeError??[]],beforeRetry:[...t?.beforeRetry??[],...e?.beforeRetry??[]]}}function _(t,e){if(!t)return e??{};if(!e)return t;let r={...t,...e};return(t.headers||e.headers)&&(r.headers=pe(t.headers,e.headers)),(t.hooks||e.hooks)&&(r.hooks=ye(t.hooks,e.hooks)),r}function Z(t,e){let r=t instanceof Request?t.url:String(t);if(e?.prefix){let o=String(e.prefix).replace(/\/+$/,""),n=r.replace(/^\/+/,"");r=`${o}/${n}`}if(e?.baseUrl)try{new URL(r)}catch{r=new URL(r,new Request(String(e.baseUrl)).url).href}return r}function ee(t,e){if(!e)return t;if(typeof e=="string"){let o=e.replace(/^\?/,"");return o&&(t.search=t.search?`${t.search}&${o}`:`?${o}`),t}let r;if(e instanceof URLSearchParams)r=e;else if(Array.isArray(e))r=new URLSearchParams(e);else{r=new URLSearchParams;for(let[o,n]of Object.entries(e))n!==void 0&&r.set(o,String(n))}return r.forEach((o,n)=>{t.searchParams.append(n,o)}),t}function te(t){let e={...t,method:we(t.method??"get"),retry:me(t.retry),timeout:t.timeout??Y,totalTimeout:t.totalTimeout??!1,hooks:he(t.hooks),throwHttpErrors:t.throwHttpErrors??!0,fetch:t.fetch??globalThis.fetch.bind(globalThis),context:t.context??{},prefix:t.prefix?String(t.prefix):""};if(t.json!==void 0){e.body=e.stringifyJson?e.stringifyJson(t.json):JSON.stringify(t.json);let r=new Headers(e.headers);r.has("content-type")||r.set("content-type","application/json"),e.headers=r}return e}function we(t){return(t??"get").toLowerCase()}function $(t,e){return new Promise((r,o)=>{let n=e?.signal;if(n?.aborted){o(n.reason??new DOMException("Aborted","AbortError"));return}let i=setTimeout(r,t);n&&n.addEventListener("abort",()=>{clearTimeout(i),o(n.reason??new DOMException("Aborted","AbortError"))},{once:!0})})}function re(t){let e=t.headers.get("retry-after")??t.headers.get("ratelimit-reset")??t.headers.get("x-ratelimit-retry-after")??t.headers.get("x-ratelimit-reset")??t.headers.get("x-rate-limit-reset");if(!e)return;let r=Number(e);if(!Number.isNaN(r))return r>=Date.parse("2024-01-01")/1e3?Math.max(0,r*1e3-Date.now()):r*1e3;let o=Date.parse(e);if(!Number.isNaN(o))return Math.max(0,o-Date.now())}function ne(t,e){if(e===!0)return Math.random()*t;if(typeof e=="function"){let r=e(t);return Number.isFinite(r)&&r>=0?r:t}return t}function oe(t){return t instanceof TypeError&&(t.message==="Failed to fetch"||t.message==="fetch failed"||t.message==="NetworkError when attempting to fetch resource."||t.message.includes("network")||t.message.includes("ECONNREFUSED")||t.message.includes("ENOTFOUND")||t.message.includes("ETIMEDOUT")||t.message.includes("ECONNRESET"))}var se="The `schema` argument must follow the Standard Schema specification";async function ae(t,e){if(typeof e!="object"&&typeof e!="function"||e===null)throw new TypeError(se);let r=e["~standard"];if(typeof r!="object"||r===null||typeof r.validate!="function")throw new TypeError(se);let o=await r.validate(t);if(o.issues)throw new U(o.issues);return o.value}function Re(t,e){let r=new AbortController,o;return t!==!1&&typeof t=="number"&&(o=setTimeout(()=>r.abort("timeout"),t)),e&&(e.aborted?r.abort(e.reason):e.addEventListener("abort",()=>r.abort(e.reason),{once:!0})),{signal:r.signal,cleanup:()=>{o&&clearTimeout(o)}}}async function ge(t,e){let{body:r}=t;if(!r)try{return await t.text()}catch{return}let o;try{o=r.getReader()}catch{return}let n=new TextDecoder,i=[],p=0,y=10*1024*1024,d=(async()=>{try{for(;;){let{done:g,value:w}=await o.read();if(g)break;if(p+=w.byteLength,p>y){o.cancel().catch(()=>{});return}i.push(n.decode(w,{stream:!0}))}}catch{return}return i.push(n.decode()),i.join("")})(),R=new Promise(g=>{let w=setTimeout(()=>g(void 0),e);d.finally(()=>clearTimeout(w))}),u=await Promise.race([d,R]);return u===void 0&&o.cancel().catch(()=>{}),u}async function be(t,e,r,o){let n=await ge(t,e);if(!n)return;let i=(t.headers.get("content-type")??"").split(";",1)[0].trim().toLowerCase();if(!/\/(?:.*[.+-])?json$/.test(i))return n;try{return r.parseJson?await r.parseJson(n,{request:o,response:t}):JSON.parse(n)}catch{return}}function A(t){let{hooks:e,json:r,parseJson:o,stringifyJson:n,searchParams:i,timeout:p,totalTimeout:y,throwHttpErrors:d,fetch:R,context:u,_userSignal:g,prefix:w,baseUrl:O,onDownloadProgress:c,onUploadProgress:J,...v}=t;return Object.freeze(v)}function N(t,e){let r=t.delay(e),o=ne(r,t.jitter);return Math.min(t.backoffLimit,o)}function ie(t,e){for(let u of e.hooks.init)u(e);let r=0,o=typeof e.totalTimeout=="number"?performance.now():void 0,n,i=()=>{if(o===void 0)return;let u=performance.now()-o;return Math.max(0,e.totalTimeout-u)},p=()=>{let u=i();return e.timeout===!1?u:u===void 0?e.timeout:Math.min(e.timeout,u)},y=()=>{let u=i();if(u!==void 0&&u<=0)throw new b(n)},d=(async()=>{if(typeof e.timeout=="number"&&e.timeout>E)throw new RangeError(`The \`timeout\` option cannot be greater than ${E}`);if(typeof e.totalTimeout=="number"&&e.totalTimeout>E)throw new RangeError(`The \`totalTimeout\` option cannot be greater than ${E}`);let u=Z(t,{prefix:e.prefix,baseUrl:e.baseUrl}),g=new URL(u);g=ee(g,e.searchParams);let{prefix:w,baseUrl:O,retry:c,timeout:J,totalTimeout:v,hooks:H,searchParams:Te,json:Ee,throwHttpErrors:P,fetch:ue,parseJson:Se,stringifyJson:ke,context:qe,_userSignal:De,onDownloadProgress:C,onUploadProgress:z,...fe}=e;n=new Request(g.href,{...fe,method:e.method.toUpperCase()}),await Promise.resolve();for(let a of H.beforeRequest){let s=await a({request:n,options:A(e),retryCount:0});if(s instanceof Response)return s;s instanceof Request&&(n=s)}let I=e._userSignal,B=async()=>{let a=p(),s=i();if(s!==void 0&&s<=0)throw new b(n);let l=Re(a,I),f=n.clone();z&&f.body&&K&&(f=X(f,z,e.body));try{let h=await ue(f,{signal:l.signal});return l.cleanup(),h}catch(h){throw l.cleanup(),l.signal.aborted&&l.signal.reason==="timeout"?new b(n):oe(h)?new k(n,{cause:h}):h}},L=async(a,s)=>{let l=Math.min(s,E),f={signal:I},h=i();if(h!==void 0){if(h<=0)throw new b(n);if(l>=h)throw await $(h,f),new b(n)}await $(l,f),y();for(let F of H.beforeRetry){let x=await F({request:n,options:A(e),error:a,retryCount:r+1});if(x instanceof Request){n=x;break}if(x instanceof Response)return r++,x;if(x===T)return T}return y(),r++,B()},le=async a=>{if(r>=c.limit||!c.methods.includes(e.method))throw a;if(c.shouldRetry!==void 0){let s=await c.shouldRetry({error:a,retryCount:r+1});if(s===!1)throw a;if(s===!0)return N(c,r+1)}if(a instanceof b){if(!c.retryOnTimeout)throw a;return N(c,r+1)}if(a instanceof k)return N(c,r+1);throw a},de=async a=>{if(r>=c.limit||!c.methods.includes(e.method))throw a;if(c.shouldRetry!==void 0){let s=await c.shouldRetry({error:a,retryCount:r+1});if(s===!1)throw a;if(s===!0)return N(c,r+1)}if(!c.statusCodes.includes(a.response.status))throw a;if(c.afterStatusCodes.includes(a.response.status)){let s=re(a.response);if(s!==void 0)return Math.min(c.maxRetryAfter,Math.max(0,s))}if(a.response.status===413)throw a;return N(c,r+1)},m;for(;;)try{m=await B();break}catch(a){let s=await le(a),l=await L(a,s);if(l===T)return;if(l instanceof Response){m=l;break}}let j=!1;for(;;){try{for(let s of H.afterResponse){let l=m.clone(),f=await s({request:n,options:A(e),response:l,retryCount:r});if(f instanceof D)throw new q(f.options);f instanceof Response&&(m=f)}}catch(s){if(!(s instanceof q))throw s;let l=s.customDelay??N(c,r+1);s.customRequest&&(n=s.customRequest);let f=await L(s,l);if(f===T)return;if(f instanceof Response){m=f,j=!0;continue}continue}let a=typeof P=="function"?P(m.status):P;if(!m.ok&&m.type!=="opaque"&&a){let s=new S(m,n,A(e));if(s.data=await be(m,e.timeout===!1?1e4:e.timeout,e,n),j)throw s;let l;try{l=await de(s)}catch{let h=s;for(let F of H.beforeError){let x=await F({request:n,options:A(e),error:h,retryCount:r});x instanceof Error&&(h=x)}throw h}let f=await L(s,l);if(f===T)return;if(f instanceof Response){m=f,j=!1;continue}continue}break}if(e.parseJson&&(m.json=async()=>{let a=await m.clone().text();return a===""?JSON.parse(a):e.parseJson(a,{request:n,response:m})}),C){if(typeof C!="function")throw new TypeError("The `onDownloadProgress` option must be a function");if(!G)throw new Error("Streams are not supported. `ReadableStream` is missing.");return Q(m,C)}return m})(),R={then:d.then.bind(d),catch:d.catch.bind(d),finally:d.finally.bind(d),[Symbol.toStringTag]:"ResponsePromise"};for(let[u,g]of Object.entries(W))R[u]=async w=>{n&&n.headers.set("accept",n.headers.get("accept")||g);let O=await d;if(u!=="json")return O[u]();let c=await O.text();if(c==="")return w!==void 0?ae(void 0,w):JSON.parse(c);let J=e.parseJson?await e.parseJson(c,{request:n,response:O}):JSON.parse(c);return w===void 0?J:ae(J,w)};return R}function ce(t){let e=(r,o)=>{let n=_(t,o),i=te(n);return i._userSignal=i.signal??void 0,ie(r,i)};for(let r of V)e[r]=(o,n)=>e(o,{...n,method:r});return e.create=r=>ce(_(t,r)),e.extend=e.create,e.retry=r=>new D(r),e}var xe=ce(),We=xe;export{q as ForceRetryError,S as HTTPError,ce as NetaClient,S as NetaError,k as NetworkError,U as SchemaValidationError,b as TimeoutError,ce as createInstance,We as default,xe as neta,T as stop};
|
|
2
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/constants.js","../src/errors.js","../src/types.js","../src/stream.js","../src/utils.js","../src/core.js","../src/index.js"],"sourcesContent":["/** @type {import('../types/types.js').HttpMethod[]} */\nexport const HTTP_METHODS = [\n 'get', 'post', 'put', 'patch', 'delete', 'head', 'options',\n];\n\n/** @type {import('../types/types.js').RetryOptions} */\nexport const DEFAULT_RETRY = {\n limit: 2,\n methods: ['get', 'put', 'head', 'delete', 'options'],\n statusCodes: [408, 413, 429, 500, 502, 503, 504],\n afterStatusCodes: [413, 429, 503],\n maxRetryAfter: Infinity,\n backoffLimit: Infinity,\n delay: (attemptCount) => 300 * 2 ** (attemptCount - 1),\n jitter: false,\n retryOnTimeout: false,\n shouldRetry: undefined,\n};\n\nexport const DEFAULT_TIMEOUT = 10_000;\n\nexport const maxSafeTimeout = 2_147_483_647; // 2^31 - 1\n\n/** @type {Record<string, string>} */\nexport const responseTypes = {\n json: 'application/json',\n text: 'text/*',\n formData: 'multipart/form-data',\n arrayBuffer: '*/*',\n blob: '*/*',\n bytes: '*/*',\n};\n\nexport const supportsAbortController = typeof globalThis.AbortController === 'function';\nexport const supportsAbortSignal = typeof globalThis.AbortSignal !== 'undefined';\nexport const supportsFormData = typeof globalThis.FormData === 'function';\n\nexport const supportsResponseStreams = (() => {\n try {\n return typeof globalThis.ReadableStream === 'function';\n } catch {\n return false;\n }\n})();\n\nexport const supportsRequestStreams = (() => {\n try {\n let duplexAccessed = false;\n const hasContentType = new Request(\n new URL('https://empty.invalid'),\n {\n body: new ReadableStream(),\n method: 'POST',\n get duplex() {\n duplexAccessed = true;\n return 'half';\n },\n },\n ).headers.has('Content-Type');\n return duplexAccessed && !hasContentType;\n } catch {\n return false;\n }\n})();\n","export class HTTPError extends Error {\n /**\n * @param {Response} response\n * @param {Request} request\n * @param {import('./types.js').NormalizedOptions} options\n */\n constructor(response, request, options) {\n const status = `${response.status}${response.statusText ? ` ${response.statusText}` : ''}`;\n super(`Request failed with status code ${status}`);\n this.name = 'HTTPError';\n this.response = response;\n this.request = request;\n this.options = options;\n /** @type {unknown} */\n this.data = undefined;\n }\n}\n\nexport class TimeoutError extends Error {\n /**\n * @param {Request} request\n */\n constructor(request) {\n super('Request timed out');\n this.name = 'TimeoutError';\n this.request = request;\n }\n}\n\nexport class NetworkError extends Error {\n /**\n * @param {Request} request\n * @param {{ cause?: Error }} [options]\n */\n constructor(request, options) {\n super('Network error', options);\n this.name = 'NetworkError';\n this.request = request;\n }\n}\n\nexport class ForceRetryError extends Error {\n /**\n * @param {{ delay?: number, request?: Request }} [options]\n */\n constructor(options) {\n super('Force retry');\n this.name = 'ForceRetryError';\n this.customDelay = options?.delay;\n this.customRequest = options?.request;\n }\n}\n\nexport class SchemaValidationError extends Error {\n /**\n * @param {Array<{ message?: string, path?: Array<string | number | symbol> }>} issues\n */\n constructor(issues) {\n const message = issues.map((i) => i.message ?? 'Unknown validation error').join('; ');\n super(`Schema validation failed: ${message}`);\n this.name = 'SchemaValidationError';\n this.issues = issues;\n }\n}\n","/** @type {unique symbol} */\nexport const stop = Symbol('neta.stop');\n\nexport class RetryMarker {\n /**\n * @param {{ delay?: number, request?: Request }} [options]\n */\n constructor(options) {\n this.options = options;\n }\n}\n","/**\n * @param {Response} response\n * @param {(progress: { percent: number, transferredBytes: number, totalBytes: number }) => void} onDownloadProgress\n * @returns {Response}\n */\nexport function streamResponse(response, onDownloadProgress) {\n const totalBytes = Number(response.headers.get('content-length')) || 0;\n let transferredBytes = 0;\n\n const reader = response.body.getReader();\n\n const stream = new ReadableStream({\n async pull(controller) {\n const { done, value } = await reader.read();\n if (done) {\n onDownloadProgress({\n percent: 1,\n transferredBytes,\n totalBytes: totalBytes || transferredBytes,\n });\n controller.close();\n return;\n }\n\n transferredBytes += value.byteLength;\n const percent = totalBytes ? transferredBytes / totalBytes : 0;\n onDownloadProgress({ percent, transferredBytes, totalBytes });\n controller.enqueue(value);\n },\n cancel(reason) {\n return reader.cancel(reason);\n },\n });\n\n return new Response(stream, {\n status: response.status,\n statusText: response.statusText,\n headers: response.headers,\n });\n}\n\n/**\n * @param {Request} request\n * @param {(progress: { percent: number, transferredBytes: number, totalBytes: number }) => void} onUploadProgress\n * @param {BodyInit} [originalBody]\n * @returns {Request}\n */\nexport function streamRequest(request, onUploadProgress, originalBody) {\n const totalBytes = Number(request.headers.get('content-length')) || 0;\n let transferredBytes = 0;\n\n const reader = request.body.getReader();\n\n const stream = new ReadableStream({\n async pull(controller) {\n const { done, value } = await reader.read();\n if (done) {\n onUploadProgress({\n percent: 1,\n transferredBytes,\n totalBytes: totalBytes || transferredBytes,\n });\n controller.close();\n return;\n }\n\n transferredBytes += value.byteLength;\n const percent = totalBytes ? transferredBytes / totalBytes : 0;\n onUploadProgress({ percent, transferredBytes, totalBytes });\n controller.enqueue(value);\n },\n cancel(reason) {\n return reader.cancel(reason);\n },\n });\n\n return new Request(request.url, {\n method: request.method,\n headers: request.headers,\n body: stream,\n duplex: 'half',\n signal: request.signal,\n });\n}\n","import { DEFAULT_RETRY, DEFAULT_TIMEOUT } from './constants.js';\n\n/**\n * @param {import('../types/types.js').Options['retry']} retry\n * @returns {import('../types/types.js').RetryOptions}\n */\nexport function normalizeRetry(retry) {\n if (typeof retry === 'number') {\n return { ...DEFAULT_RETRY, limit: retry };\n }\n return { ...DEFAULT_RETRY, ...retry };\n}\n\n/**\n * @param {import('../types/types.js').Hooks} [hooks]\n * @returns {import('../types/types.js').NormalizedHooks}\n */\nexport function normalizeHooks(hooks) {\n return {\n init: [...(hooks?.init ?? [])],\n beforeRequest: [...(hooks?.beforeRequest ?? [])],\n afterResponse: [...(hooks?.afterResponse ?? [])],\n beforeError: [...(hooks?.beforeError ?? [])],\n beforeRetry: [...(hooks?.beforeRetry ?? [])],\n };\n}\n\n/**\n * @param {HeadersInit} [target]\n * @param {HeadersInit} [source]\n * @returns {Headers}\n */\nexport function mergeHeaders(target, source) {\n const result = new Headers(target);\n if (source) {\n const sourceHeaders = new Headers(source);\n sourceHeaders.forEach((value, key) => {\n result.set(key, value);\n });\n }\n return result;\n}\n\n/**\n * Merge hooks by concatenating arrays.\n * @param {import('../types/types.js').Hooks} [base]\n * @param {import('../types/types.js').Hooks} [override]\n * @returns {import('../types/types.js').Hooks}\n */\nexport function mergeHooks(base, override) {\n return {\n init: [...(base?.init ?? []), ...(override?.init ?? [])],\n beforeRequest: [...(base?.beforeRequest ?? []), ...(override?.beforeRequest ?? [])],\n afterResponse: [...(base?.afterResponse ?? []), ...(override?.afterResponse ?? [])],\n beforeError: [...(base?.beforeError ?? []), ...(override?.beforeError ?? [])],\n beforeRetry: [...(base?.beforeRetry ?? []), ...(override?.beforeRetry ?? [])],\n };\n}\n\n/**\n * @param {import('../types/types.js').Options} [defaults]\n * @param {import('../types/types.js').Options} [overrides]\n * @returns {import('../types/types.js').Options}\n */\nexport function mergeOptions(defaults, overrides) {\n if (!defaults) return overrides ?? {};\n if (!overrides) return defaults;\n\n const merged = { ...defaults, ...overrides };\n\n if (defaults.headers || overrides.headers) {\n merged.headers = mergeHeaders(defaults.headers, overrides.headers);\n }\n\n if (defaults.hooks || overrides.hooks) {\n merged.hooks = mergeHooks(defaults.hooks, overrides.hooks);\n }\n\n return merged;\n}\n\n/**\n * @param {string | URL | Request} input\n * @param {{ prefix?: string, baseUrl?: string | URL }} [options]\n * @returns {string}\n */\nexport function resolveInput(input, options) {\n let inputStr = input instanceof Request ? input.url : String(input);\n\n if (options?.prefix) {\n const prefix = String(options.prefix).replace(/\\/+$/, '');\n const path = inputStr.replace(/^\\/+/, '');\n inputStr = `${prefix}/${path}`;\n }\n\n if (options?.baseUrl) {\n try {\n // If already absolute, keep as-is\n new URL(inputStr);\n } catch {\n // Relative — resolve against baseUrl\n inputStr = new URL(inputStr, new Request(String(options.baseUrl)).url).href;\n }\n }\n\n return inputStr;\n}\n\n/**\n * @param {URL} url\n * @param {import('../types/types.js').SearchParamsInit} [searchParams]\n * @returns {URL}\n */\nexport function appendSearchParams(url, searchParams) {\n if (!searchParams) return url;\n\n if (typeof searchParams === 'string') {\n const cleaned = searchParams.replace(/^\\?/, '');\n if (cleaned) {\n url.search = url.search ? `${url.search}&${cleaned}` : `?${cleaned}`;\n }\n return url;\n }\n\n /** @type {URLSearchParams} */\n let params;\n\n if (searchParams instanceof URLSearchParams) {\n params = searchParams;\n } else if (Array.isArray(searchParams)) {\n params = new URLSearchParams(searchParams);\n } else {\n params = new URLSearchParams();\n for (const [key, value] of Object.entries(searchParams)) {\n if (value !== undefined) {\n params.set(key, String(value));\n }\n }\n }\n\n params.forEach((value, key) => {\n url.searchParams.append(key, value);\n });\n\n return url;\n}\n\n/**\n * @param {import('../types/types.js').Options} options\n * @returns {import('../types/types.js').InternalOptions}\n */\nexport function normalizeOptions(options) {\n const normalized = {\n ...options,\n method: normalizeRequestMethod(options.method ?? 'get'),\n retry: normalizeRetry(options.retry),\n timeout: options.timeout ?? DEFAULT_TIMEOUT,\n totalTimeout: options.totalTimeout ?? false,\n hooks: normalizeHooks(options.hooks),\n throwHttpErrors: options.throwHttpErrors ?? true,\n fetch: options.fetch ?? globalThis.fetch.bind(globalThis),\n context: options.context ?? {},\n prefix: options.prefix ? String(options.prefix) : '',\n };\n\n if (options.json !== undefined) {\n normalized.body = normalized.stringifyJson\n ? normalized.stringifyJson(options.json)\n : JSON.stringify(options.json);\n const headers = new Headers(normalized.headers);\n if (!headers.has('content-type')) {\n headers.set('content-type', 'application/json');\n }\n normalized.headers = headers;\n }\n\n return normalized;\n}\n\n/**\n * @param {string} [method]\n * @returns {string}\n */\nexport function normalizeRequestMethod(method) {\n return (method ?? 'get').toLowerCase();\n}\n\n/**\n * @param {number} ms\n * @param {{ signal?: AbortSignal }} [options]\n * @returns {Promise<void>}\n */\nexport function delay(ms, options) {\n return new Promise((resolve, reject) => {\n const signal = options?.signal;\n\n if (signal?.aborted) {\n reject(signal.reason ?? new DOMException('Aborted', 'AbortError'));\n return;\n }\n\n const timer = setTimeout(resolve, ms);\n\n if (signal) {\n signal.addEventListener(\n 'abort',\n () => {\n clearTimeout(timer);\n reject(signal.reason ?? new DOMException('Aborted', 'AbortError'));\n },\n { once: true },\n );\n }\n });\n}\n\n/**\n * Parse Retry-After from multiple common header formats.\n * @param {Response} response\n * @returns {number | undefined}\n */\nexport function parseRetryAfter(response) {\n const header =\n response.headers.get('retry-after') ??\n response.headers.get('ratelimit-reset') ??\n response.headers.get('x-ratelimit-retry-after') ??\n response.headers.get('x-ratelimit-reset') ??\n response.headers.get('x-rate-limit-reset');\n\n if (!header) return undefined;\n\n const seconds = Number(header);\n if (!Number.isNaN(seconds)) {\n // Large numbers are treated as timestamps (threshold: 2024-01-01 epoch)\n if (seconds >= Date.parse('2024-01-01') / 1000) {\n return Math.max(0, seconds * 1000 - Date.now());\n }\n return seconds * 1000;\n }\n\n const date = Date.parse(header);\n if (!Number.isNaN(date)) {\n return Math.max(0, date - Date.now());\n }\n\n return undefined;\n}\n\n/**\n * Apply jitter to a delay value.\n * @param {number} delayMs\n * @param {boolean | ((delay: number) => number)} jitter\n * @returns {number}\n */\nexport function applyJitter(delayMs, jitter) {\n if (jitter === true) {\n return Math.random() * delayMs;\n }\n if (typeof jitter === 'function') {\n const result = jitter(delayMs);\n return Number.isFinite(result) && result >= 0 ? result : delayMs;\n }\n return delayMs;\n}\n\n/**\n * Check if error is a raw network error (TypeError from fetch).\n * @param {unknown} error\n * @returns {boolean}\n */\nexport function isNetworkError(error) {\n return (\n error instanceof TypeError &&\n (error.message === 'Failed to fetch' ||\n error.message === 'fetch failed' ||\n error.message === 'NetworkError when attempting to fetch resource.' ||\n error.message.includes('network') ||\n error.message.includes('ECONNREFUSED') ||\n error.message.includes('ENOTFOUND') ||\n error.message.includes('ETIMEDOUT') ||\n error.message.includes('ECONNRESET'))\n );\n}\n","import { HTTPError, TimeoutError, NetworkError, ForceRetryError, SchemaValidationError } from './errors.js';\nimport { RetryMarker } from './types.js';\nimport { stop } from './types.js';\nimport { streamResponse, streamRequest } from './stream.js';\nimport {\n appendSearchParams,\n resolveInput,\n delay,\n isNetworkError,\n applyJitter,\n parseRetryAfter,\n} from './utils.js';\nimport {\n maxSafeTimeout,\n responseTypes,\n supportsResponseStreams,\n supportsRequestStreams,\n} from './constants.js';\n\nconst invalidSchemaMessage = 'The `schema` argument must follow the Standard Schema specification';\n\n/**\n * @param {unknown} jsonValue\n * @param {any} schema\n * @returns {Promise<unknown>}\n */\nasync function validateJsonWithSchema(jsonValue, schema) {\n if ((typeof schema !== 'object' && typeof schema !== 'function') || schema === null) {\n throw new TypeError(invalidSchemaMessage);\n }\n\n const standardSchema = schema['~standard'];\n if (\n typeof standardSchema !== 'object' ||\n standardSchema === null ||\n typeof standardSchema.validate !== 'function'\n ) {\n throw new TypeError(invalidSchemaMessage);\n }\n\n const result = await standardSchema.validate(jsonValue);\n if (result.issues) {\n throw new SchemaValidationError(result.issues);\n }\n\n return result.value;\n}\n\n/**\n * @param {number | false} timeout\n * @param {AbortSignal} [userSignal]\n * @returns {{ signal: AbortSignal, cleanup: () => void }}\n */\nfunction createManagedSignal(timeout, userSignal) {\n const controller = new AbortController();\n /** @type {ReturnType<typeof setTimeout> | undefined} */\n let timer;\n\n if (timeout !== false && typeof timeout === 'number') {\n timer = setTimeout(() => controller.abort('timeout'), timeout);\n }\n\n if (userSignal) {\n if (userSignal.aborted) {\n controller.abort(userSignal.reason);\n } else {\n userSignal.addEventListener('abort', () => controller.abort(userSignal.reason), { once: true });\n }\n }\n\n return {\n signal: controller.signal,\n cleanup: () => {\n if (timer) clearTimeout(timer);\n },\n };\n}\n\n/**\n * Read response text with timeout and size limit.\n * @param {Response} response\n * @param {number} timeoutMs\n * @returns {Promise<string | undefined>}\n */\nasync function readResponseText(response, timeoutMs) {\n const { body } = response;\n if (!body) {\n try {\n return await response.text();\n } catch {\n return undefined;\n }\n }\n\n /** @type {ReadableStreamDefaultReader<Uint8Array>} */\n let reader;\n try {\n reader = body.getReader();\n } catch {\n return undefined;\n }\n\n const decoder = new TextDecoder();\n const chunks = [];\n let totalBytes = 0;\n const maxSize = 10 * 1024 * 1024;\n\n const readAll = (async () => {\n try {\n for (;;) {\n const { done, value } = await reader.read();\n if (done) break;\n totalBytes += value.byteLength;\n if (totalBytes > maxSize) {\n void reader.cancel().catch(() => {});\n return undefined;\n }\n chunks.push(decoder.decode(value, { stream: true }));\n }\n } catch {\n return undefined;\n }\n chunks.push(decoder.decode());\n return chunks.join('');\n })();\n\n const timeoutPromise = new Promise((resolve) => {\n const id = setTimeout(() => resolve(undefined), timeoutMs);\n void readAll.finally(() => clearTimeout(id));\n });\n\n const result = await Promise.race([readAll, timeoutPromise]);\n if (result === undefined) void reader.cancel().catch(() => {});\n return result;\n}\n\n/**\n * @param {Response} response\n * @param {number} timeoutMs\n * @param {any} options\n * @param {Request} request\n * @returns {Promise<unknown>}\n */\nasync function getResponseData(response, timeoutMs, options, request) {\n const text = await readResponseText(response, timeoutMs);\n if (!text) return undefined;\n\n const contentType = (response.headers.get('content-type') ?? '').split(';', 1)[0].trim().toLowerCase();\n const isJson = /\\/(?:.*[.+-])?json$/.test(contentType);\n if (!isJson) return text;\n\n try {\n return options.parseJson\n ? await options.parseJson(text, { request, response })\n : JSON.parse(text);\n } catch {\n return undefined;\n }\n}\n\n/**\n * Strip internal-only options.\n * @param {any} options\n * @returns {any}\n */\nfunction getNormalizedOptions(options) {\n const {\n hooks, json, parseJson, stringifyJson, searchParams,\n timeout, totalTimeout, throwHttpErrors, fetch,\n context, _userSignal, prefix, baseUrl,\n onDownloadProgress, onUploadProgress,\n ...rest\n } = options;\n return Object.freeze(rest);\n}\n\n/**\n * Calculate retry delay.\n * @param {any} retry\n * @param {number} retryCount\n * @returns {number}\n */\nfunction calculateDelay(retry, retryCount) {\n const base = retry.delay(retryCount);\n const jittered = applyJitter(base, retry.jitter);\n return Math.min(retry.backoffLimit, jittered);\n}\n\n/**\n * @param {string | URL | Request} input\n * @param {any} options\n * @returns {any}\n */\nexport function createResponsePromise(input, options) {\n // Run init hooks (synchronous, mutate options clone)\n for (const hook of options.hooks.init) {\n hook(options);\n }\n\n let retryCount = 0;\n const startTime = typeof options.totalTimeout === 'number' ? performance.now() : undefined;\n /** @type {Request} */\n let currentRequest;\n\n const getRemainingTotalTimeout = () => {\n if (startTime === undefined) return undefined;\n const elapsed = performance.now() - startTime;\n return Math.max(0, options.totalTimeout - elapsed);\n };\n\n const getEffectiveTimeout = () => {\n const remaining = getRemainingTotalTimeout();\n if (options.timeout === false) return remaining;\n if (remaining === undefined) return options.timeout;\n return Math.min(options.timeout, remaining);\n };\n\n const throwIfTotalTimeoutExhausted = () => {\n const remaining = getRemainingTotalTimeout();\n if (remaining !== undefined && remaining <= 0) {\n throw new TimeoutError(currentRequest);\n }\n };\n\n const innerPromise = (async () => {\n if (typeof options.timeout === 'number' && options.timeout > maxSafeTimeout) {\n throw new RangeError(`The \\`timeout\\` option cannot be greater than ${maxSafeTimeout}`);\n }\n if (typeof options.totalTimeout === 'number' && options.totalTimeout > maxSafeTimeout) {\n throw new RangeError(`The \\`totalTimeout\\` option cannot be greater than ${maxSafeTimeout}`);\n }\n\n // Resolve URL\n const inputStr = resolveInput(input, { prefix: options.prefix, baseUrl: options.baseUrl });\n let url = new URL(inputStr);\n url = appendSearchParams(url, options.searchParams);\n\n // Build request init (strip neta-specific options)\n const {\n prefix: _prefix, baseUrl: _baseUrl, retry, timeout, totalTimeout,\n hooks, searchParams, json, throwHttpErrors, fetch: fetchFn,\n parseJson, stringifyJson, context, _userSignal,\n onDownloadProgress, onUploadProgress,\n ...requestInit\n } = options;\n\n currentRequest = new Request(url.href, {\n ...requestInit,\n method: options.method.toUpperCase(),\n });\n\n // Defer so body shortcuts can set Accept header\n await Promise.resolve();\n\n // beforeRequest hooks\n for (const hook of hooks.beforeRequest) {\n const result = await hook({\n request: currentRequest,\n options: getNormalizedOptions(options),\n retryCount: 0,\n });\n\n if (result instanceof Response) return result;\n if (result instanceof Request) currentRequest = result;\n }\n\n const userSignal = options._userSignal;\n\n /**\n * Perform a single fetch attempt with timeout.\n * @returns {Promise<Response>}\n */\n const doFetch = async () => {\n const effectiveTimeout = getEffectiveTimeout();\n const remaining = getRemainingTotalTimeout();\n if (remaining !== undefined && remaining <= 0) throw new TimeoutError(currentRequest);\n\n const managed = createManagedSignal(effectiveTimeout, userSignal);\n\n let fetchRequest = currentRequest.clone();\n if (onUploadProgress && fetchRequest.body && supportsRequestStreams) {\n fetchRequest = streamRequest(fetchRequest, onUploadProgress, options.body);\n }\n\n try {\n const response = await fetchFn(fetchRequest, { signal: managed.signal });\n managed.cleanup();\n return response;\n } catch (error) {\n managed.cleanup();\n if (managed.signal.aborted && managed.signal.reason === 'timeout') {\n throw new TimeoutError(currentRequest);\n }\n if (isNetworkError(error)) {\n throw new NetworkError(currentRequest, { cause: error });\n }\n throw error;\n }\n };\n\n /**\n * Attempt retry: wait, run beforeRetry hooks, then fetch again.\n * @param {Error} error\n * @param {number} delayMs\n * @returns {Promise<Response | typeof stop>}\n */\n const attemptRetry = async (error, delayMs) => {\n const safeDelay = Math.min(delayMs, maxSafeTimeout);\n const delayOptions = { signal: userSignal };\n\n const remaining = getRemainingTotalTimeout();\n if (remaining !== undefined) {\n if (remaining <= 0) throw new TimeoutError(currentRequest);\n if (safeDelay >= remaining) {\n await delay(remaining, delayOptions);\n throw new TimeoutError(currentRequest);\n }\n }\n\n await delay(safeDelay, delayOptions);\n throwIfTotalTimeoutExhausted();\n\n // Run beforeRetry hooks\n for (const hook of hooks.beforeRetry) {\n const result = await hook({\n request: currentRequest,\n options: getNormalizedOptions(options),\n error,\n retryCount: retryCount + 1,\n });\n\n if (result instanceof Request) { currentRequest = result; break; }\n if (result instanceof Response) { retryCount++; return result; }\n if (result === stop) return stop;\n }\n\n throwIfTotalTimeoutExhausted();\n retryCount++;\n return doFetch();\n };\n\n /**\n * Check if we should retry a fetch-level error (timeout, network).\n * @param {Error} error\n * @returns {Promise<number>} delay in ms, or throws if no retry\n */\n const getRetryDelayForFetchError = async (error) => {\n if (retryCount >= retry.limit) throw error;\n if (!retry.methods.includes(options.method)) throw error;\n\n if (retry.shouldRetry !== undefined) {\n const result = await retry.shouldRetry({ error, retryCount: retryCount + 1 });\n if (result === false) throw error;\n if (result === true) return calculateDelay(retry, retryCount + 1);\n }\n\n if (error instanceof TimeoutError) {\n if (!retry.retryOnTimeout) throw error;\n return calculateDelay(retry, retryCount + 1);\n }\n\n if (error instanceof NetworkError) {\n return calculateDelay(retry, retryCount + 1);\n }\n\n throw error;\n };\n\n /**\n * Check if we should retry an HTTP error.\n * @param {HTTPError} error\n * @returns {Promise<number>} delay in ms, or throws if no retry\n */\n const getRetryDelayForHttpError = async (error) => {\n if (retryCount >= retry.limit) throw error;\n if (!retry.methods.includes(options.method)) throw error;\n\n if (retry.shouldRetry !== undefined) {\n const result = await retry.shouldRetry({ error, retryCount: retryCount + 1 });\n if (result === false) throw error;\n if (result === true) return calculateDelay(retry, retryCount + 1);\n }\n\n if (!retry.statusCodes.includes(error.response.status)) throw error;\n\n // Handle Retry-After\n if (retry.afterStatusCodes.includes(error.response.status)) {\n const retryAfter = parseRetryAfter(error.response);\n if (retryAfter !== undefined) {\n return Math.min(retry.maxRetryAfter, Math.max(0, retryAfter));\n }\n }\n\n if (error.response.status === 413) throw error;\n\n return calculateDelay(retry, retryCount + 1);\n };\n\n // === Main request loop ===\n /** @type {Response} */\n let response;\n\n // Initial fetch with fetch-error retry loop\n for (;;) {\n try {\n response = await doFetch();\n break;\n } catch (error) {\n const retryDelay = await getRetryDelayForFetchError(error);\n const retryResult = await attemptRetry(error, retryDelay);\n if (retryResult === stop) return undefined;\n if (retryResult instanceof Response) { response = retryResult; break; }\n }\n }\n\n // afterResponse hooks + HTTP error retry loop\n let responseFromHook = false;\n\n for (;;) {\n // Run afterResponse hooks\n try {\n for (const hook of hooks.afterResponse) {\n const clonedResponse = response.clone();\n const hookResult = await hook({\n request: currentRequest,\n options: getNormalizedOptions(options),\n response: clonedResponse,\n retryCount,\n });\n\n if (hookResult instanceof RetryMarker) {\n throw new ForceRetryError(hookResult.options);\n }\n\n if (hookResult instanceof Response) {\n response = hookResult;\n }\n }\n } catch (error) {\n if (!(error instanceof ForceRetryError)) throw error;\n\n // Forced retry from afterResponse hook\n const retryDelay = error.customDelay ?? calculateDelay(retry, retryCount + 1);\n if (error.customRequest) currentRequest = error.customRequest;\n const retryResult = await attemptRetry(error, retryDelay);\n if (retryResult === stop) return undefined;\n if (retryResult instanceof Response) {\n response = retryResult;\n responseFromHook = true;\n continue;\n }\n continue;\n }\n\n // Check HTTP errors\n const shouldThrow = typeof throwHttpErrors === 'function'\n ? throwHttpErrors(response.status)\n : throwHttpErrors;\n\n if (!response.ok && response.type !== 'opaque' && shouldThrow) {\n const error = new HTTPError(response, currentRequest, getNormalizedOptions(options));\n error.data = await getResponseData(\n response,\n options.timeout === false ? 10_000 : options.timeout,\n options,\n currentRequest,\n );\n\n if (responseFromHook) throw error;\n\n // Try to retry\n let retryDelay;\n try {\n retryDelay = await getRetryDelayForHttpError(error);\n } catch {\n // Run beforeError hooks, then throw\n let processedError = error;\n for (const hook of hooks.beforeError) {\n const hookResult = await hook({\n request: currentRequest,\n options: getNormalizedOptions(options),\n error: processedError,\n retryCount,\n });\n if (hookResult instanceof Error) processedError = hookResult;\n }\n throw processedError;\n }\n\n const retryResult = await attemptRetry(error, retryDelay);\n if (retryResult === stop) return undefined;\n if (retryResult instanceof Response) {\n response = retryResult;\n responseFromHook = false; // Allow further retries\n continue;\n }\n continue;\n }\n\n break;\n }\n\n // Decorate response with custom parseJson\n if (options.parseJson) {\n response.json = async () => {\n const text = await response.clone().text();\n if (text === '') return JSON.parse(text);\n return options.parseJson(text, { request: currentRequest, response });\n };\n }\n\n // Handle download progress\n if (onDownloadProgress) {\n if (typeof onDownloadProgress !== 'function') {\n throw new TypeError('The `onDownloadProgress` option must be a function');\n }\n if (!supportsResponseStreams) {\n throw new Error('Streams are not supported. `ReadableStream` is missing.');\n }\n return streamResponse(response, onDownloadProgress);\n }\n\n return response;\n })();\n\n // Build ResponsePromise thenable\n /** @type {any} */\n const responsePromise = {\n then: innerPromise.then.bind(innerPromise),\n catch: innerPromise.catch.bind(innerPromise),\n finally: innerPromise.finally.bind(innerPromise),\n [Symbol.toStringTag]: 'ResponsePromise',\n };\n\n for (const [type, mimeType] of Object.entries(responseTypes)) {\n responsePromise[type] = async (schema) => {\n if (currentRequest) {\n currentRequest.headers.set('accept', currentRequest.headers.get('accept') || mimeType);\n }\n\n const response = await innerPromise;\n if (type !== 'json') return response[type]();\n\n const text = await response.text();\n if (text === '') {\n if (schema !== undefined) return validateJsonWithSchema(undefined, schema);\n return JSON.parse(text);\n }\n\n const jsonValue = options.parseJson\n ? await options.parseJson(text, { request: currentRequest, response })\n : JSON.parse(text);\n\n return schema === undefined ? jsonValue : validateJsonWithSchema(jsonValue, schema);\n };\n }\n\n return responsePromise;\n}\n","import { HTTP_METHODS } from './constants.js';\nimport { createResponsePromise } from './core.js';\nimport { RetryMarker } from './types.js';\nimport { mergeOptions, normalizeOptions } from './utils.js';\n\n/**\n * @param {import('../types/types.js').Options} [defaults]\n * @returns {import('../types/types.js').NetaInstance}\n */\nexport function createInstance(defaults) {\n const fn = (input, options) => {\n const merged = mergeOptions(defaults, options);\n const normalized = normalizeOptions(merged);\n // Stash user signal before we override it internally\n normalized._userSignal = normalized.signal ?? undefined;\n return createResponsePromise(input, normalized);\n };\n\n for (const method of HTTP_METHODS) {\n fn[method] = (input, options) => fn(input, { ...options, method });\n }\n\n fn.create = (newDefaults) => createInstance(mergeOptions(defaults, newDefaults));\n fn.extend = fn.create;\n\n /**\n * Signal forced retry from an afterResponse hook.\n * @param {{ delay?: number, request?: Request }} [options]\n * @returns {RetryMarker}\n */\n fn.retry = (options) => new RetryMarker(options);\n\n return fn;\n}\n\nconst neta = createInstance();\n\nexport default neta;\nexport { neta };\nexport { HTTPError, TimeoutError, NetworkError, ForceRetryError, SchemaValidationError } from './errors.js';\nexport { HTTPError as NetaError } from './errors.js';\nexport { createInstance as NetaClient };\nexport { stop } from './types.js';\n"],"mappings":"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","names":["HTTP_METHODS","DEFAULT_RETRY","attemptCount","DEFAULT_TIMEOUT","maxSafeTimeout","responseTypes","supportsAbortController","supportsAbortSignal","supportsFormData","supportsResponseStreams","supportsRequestStreams","duplexAccessed","hasContentType","HTTPError","response","request","options","status","TimeoutError","NetworkError","ForceRetryError","SchemaValidationError","issues","message","i","stop","RetryMarker","options","streamResponse","response","onDownloadProgress","totalBytes","transferredBytes","reader","stream","controller","done","value","percent","reason","streamRequest","request","onUploadProgress","originalBody","normalizeRetry","retry","DEFAULT_RETRY","normalizeHooks","hooks","mergeHeaders","target","source","result","value","key","mergeHooks","base","override","mergeOptions","defaults","overrides","merged","resolveInput","input","options","inputStr","prefix","path","appendSearchParams","url","searchParams","cleaned","params","normalizeOptions","normalized","normalizeRequestMethod","DEFAULT_TIMEOUT","headers","method","delay","ms","resolve","reject","signal","timer","parseRetryAfter","response","header","seconds","date","applyJitter","delayMs","jitter","isNetworkError","error","invalidSchemaMessage","validateJsonWithSchema","jsonValue","schema","standardSchema","result","SchemaValidationError","createManagedSignal","timeout","userSignal","controller","timer","readResponseText","response","timeoutMs","body","reader","decoder","chunks","totalBytes","maxSize","readAll","done","value","timeoutPromise","resolve","id","getResponseData","options","request","text","contentType","getNormalizedOptions","hooks","json","parseJson","stringifyJson","searchParams","totalTimeout","throwHttpErrors","fetch","context","_userSignal","prefix","baseUrl","onDownloadProgress","onUploadProgress","rest","calculateDelay","retry","retryCount","base","jittered","applyJitter","createResponsePromise","input","hook","startTime","currentRequest","getRemainingTotalTimeout","elapsed","getEffectiveTimeout","remaining","throwIfTotalTimeoutExhausted","TimeoutError","innerPromise","maxSafeTimeout","inputStr","resolveInput","url","appendSearchParams","_prefix","_baseUrl","fetchFn","requestInit","doFetch","effectiveTimeout","managed","fetchRequest","supportsRequestStreams","streamRequest","error","isNetworkError","NetworkError","attemptRetry","delayMs","safeDelay","delayOptions","delay","stop","getRetryDelayForFetchError","getRetryDelayForHttpError","retryAfter","parseRetryAfter","retryDelay","retryResult","responseFromHook","clonedResponse","hookResult","RetryMarker","ForceRetryError","shouldThrow","HTTPError","processedError","supportsResponseStreams","streamResponse","responsePromise","type","mimeType","responseTypes","createInstance","defaults","fn","input","options","merged","mergeOptions","normalized","normalizeOptions","createResponsePromise","method","HTTP_METHODS","newDefaults","RetryMarker","neta","index_default"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@anmetric/neta",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Tiny, elegant HTTP client built on fetch for browser and Node.js",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"exports": {
|
|
7
|
+
".": {
|
|
8
|
+
"types": "./types/index.d.ts",
|
|
9
|
+
"import": "./dist/index.js",
|
|
10
|
+
"require": "./dist/index.cjs"
|
|
11
|
+
}
|
|
12
|
+
},
|
|
13
|
+
"sideEffects": false,
|
|
14
|
+
"main": "./dist/index.cjs",
|
|
15
|
+
"module": "./dist/index.js",
|
|
16
|
+
"types": "./types/index.d.ts",
|
|
17
|
+
"files": [
|
|
18
|
+
"dist",
|
|
19
|
+
"types"
|
|
20
|
+
],
|
|
21
|
+
"scripts": {
|
|
22
|
+
"build": "tsup",
|
|
23
|
+
"dev": "tsup --watch",
|
|
24
|
+
"test": "vitest run",
|
|
25
|
+
"test:watch": "vitest",
|
|
26
|
+
"typecheck": "tsc --noEmit",
|
|
27
|
+
"prepublishOnly": "npm run build"
|
|
28
|
+
},
|
|
29
|
+
"engines": {
|
|
30
|
+
"node": ">=18"
|
|
31
|
+
},
|
|
32
|
+
"devDependencies": {
|
|
33
|
+
"tsup": "^8.0.0",
|
|
34
|
+
"typescript": "^5.4.0",
|
|
35
|
+
"vitest": "^2.0.0"
|
|
36
|
+
},
|
|
37
|
+
"license": "MIT"
|
|
38
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { RetryOptions } from './types.js';
|
|
2
|
+
|
|
3
|
+
export declare const HTTP_METHODS: string[];
|
|
4
|
+
export declare const DEFAULT_RETRY: RetryOptions;
|
|
5
|
+
export declare const DEFAULT_TIMEOUT: number;
|
|
6
|
+
export declare const maxSafeTimeout: number;
|
|
7
|
+
export declare const responseTypes: Record<string, string>;
|
|
8
|
+
export declare const supportsAbortController: boolean;
|
|
9
|
+
export declare const supportsAbortSignal: boolean;
|
|
10
|
+
export declare const supportsFormData: boolean;
|
|
11
|
+
export declare const supportsResponseStreams: boolean;
|
|
12
|
+
export declare const supportsRequestStreams: boolean;
|
package/types/core.d.ts
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { NormalizedOptions } from './types.js';
|
|
2
|
+
|
|
3
|
+
export declare class HTTPError extends Error {
|
|
4
|
+
response: Response;
|
|
5
|
+
request: Request;
|
|
6
|
+
options: NormalizedOptions;
|
|
7
|
+
data: unknown;
|
|
8
|
+
constructor(response: Response, request: Request, options: NormalizedOptions);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export declare class TimeoutError extends Error {
|
|
12
|
+
request: Request;
|
|
13
|
+
constructor(request: Request);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export declare class NetworkError extends Error {
|
|
17
|
+
request: Request;
|
|
18
|
+
constructor(request: Request, options?: { cause?: Error });
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export declare class ForceRetryError extends Error {
|
|
22
|
+
customDelay?: number;
|
|
23
|
+
customRequest?: Request;
|
|
24
|
+
constructor(options?: { delay?: number; request?: Request });
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export declare class SchemaValidationError extends Error {
|
|
28
|
+
issues: Array<{ message?: string; path?: Array<string | number | symbol> }>;
|
|
29
|
+
constructor(issues: Array<{ message?: string; path?: Array<string | number | symbol> }>);
|
|
30
|
+
}
|
package/types/index.d.ts
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { NetaInstance, Options } from './types.js';
|
|
2
|
+
|
|
3
|
+
export declare function createInstance(defaults?: Options): NetaInstance;
|
|
4
|
+
|
|
5
|
+
declare const neta: NetaInstance;
|
|
6
|
+
export default neta;
|
|
7
|
+
export { neta };
|
|
8
|
+
|
|
9
|
+
export { HTTPError, TimeoutError, NetworkError, ForceRetryError, SchemaValidationError } from './errors.js';
|
|
10
|
+
export { HTTPError as NetaError } from './errors.js';
|
|
11
|
+
export declare const NetaClient: new (defaults?: Options) => NetaInstance;
|
|
12
|
+
export type NetaClient = NetaInstance;
|
|
13
|
+
export { stop } from './types.js';
|
|
14
|
+
export type {
|
|
15
|
+
AfterResponseHook,
|
|
16
|
+
BeforeErrorHook,
|
|
17
|
+
BeforeRequestHook,
|
|
18
|
+
BeforeRetryHook,
|
|
19
|
+
DownloadProgress,
|
|
20
|
+
Hooks,
|
|
21
|
+
HttpMethod,
|
|
22
|
+
InitHook,
|
|
23
|
+
InternalOptions,
|
|
24
|
+
NetaInstance,
|
|
25
|
+
NetaResponse,
|
|
26
|
+
NormalizedHooks,
|
|
27
|
+
NormalizedOptions,
|
|
28
|
+
Options,
|
|
29
|
+
ResponsePromise,
|
|
30
|
+
RetryMarker,
|
|
31
|
+
RetryOptions,
|
|
32
|
+
SchemaValidationError as SchemaValidationErrorType,
|
|
33
|
+
SearchParamsInit,
|
|
34
|
+
StandardSchema,
|
|
35
|
+
UploadProgress,
|
|
36
|
+
} from './types.js';
|
package/types/retry.d.ts
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { InternalOptions } from './types.js';
|
|
2
|
+
|
|
3
|
+
interface RetryState {
|
|
4
|
+
getRetryCount: () => number;
|
|
5
|
+
setRetryCount: (n: number) => void;
|
|
6
|
+
getStartTime: () => number | undefined;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export declare function executeWithRetry(
|
|
10
|
+
makeFetch: () => Promise<Response>,
|
|
11
|
+
request: Request,
|
|
12
|
+
options: InternalOptions,
|
|
13
|
+
state: RetryState,
|
|
14
|
+
): Promise<Response>;
|
|
15
|
+
|
|
16
|
+
export declare function retryFromError(
|
|
17
|
+
error: unknown,
|
|
18
|
+
makeFetch: () => Promise<Response>,
|
|
19
|
+
request: Request,
|
|
20
|
+
options: InternalOptions,
|
|
21
|
+
state: RetryState,
|
|
22
|
+
): Promise<Response | void>;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { DownloadProgress, UploadProgress } from './types.js';
|
|
2
|
+
|
|
3
|
+
export declare function streamResponse(
|
|
4
|
+
response: Response,
|
|
5
|
+
onDownloadProgress: (progress: DownloadProgress) => void,
|
|
6
|
+
): Response;
|
|
7
|
+
|
|
8
|
+
export declare function streamRequest(
|
|
9
|
+
request: Request,
|
|
10
|
+
onUploadProgress: (progress: UploadProgress) => void,
|
|
11
|
+
originalBody?: BodyInit,
|
|
12
|
+
): Request;
|
package/types/types.d.ts
ADDED
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
export type HttpMethod = 'get' | 'post' | 'put' | 'patch' | 'delete' | 'head' | 'options';
|
|
2
|
+
|
|
3
|
+
export type SearchParamsInit =
|
|
4
|
+
| string
|
|
5
|
+
| Record<string, string | number | boolean | undefined>
|
|
6
|
+
| URLSearchParams
|
|
7
|
+
| Array<[string, string]>;
|
|
8
|
+
|
|
9
|
+
export interface RetryOptions {
|
|
10
|
+
limit: number;
|
|
11
|
+
methods: string[];
|
|
12
|
+
statusCodes: number[];
|
|
13
|
+
afterStatusCodes: number[];
|
|
14
|
+
maxRetryAfter: number;
|
|
15
|
+
backoffLimit: number;
|
|
16
|
+
delay: (attemptCount: number) => number;
|
|
17
|
+
jitter: boolean | ((delay: number) => number);
|
|
18
|
+
retryOnTimeout: boolean;
|
|
19
|
+
shouldRetry?: (info: { error: Error; retryCount: number }) => boolean | undefined | Promise<boolean | undefined>;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export interface DownloadProgress {
|
|
23
|
+
percent: number;
|
|
24
|
+
transferredBytes: number;
|
|
25
|
+
totalBytes: number;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface UploadProgress {
|
|
29
|
+
percent: number;
|
|
30
|
+
transferredBytes: number;
|
|
31
|
+
totalBytes: number;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// Standard Schema v1 (https://github.com/standard-schema/standard-schema)
|
|
35
|
+
export interface StandardSchema {
|
|
36
|
+
'~standard': {
|
|
37
|
+
validate: (value: unknown) => { value?: unknown; issues?: Array<{ message?: string; path?: Array<string | number | symbol> }> } | Promise<{ value?: unknown; issues?: Array<{ message?: string; path?: Array<string | number | symbol> }> }>;
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export interface InitHook {
|
|
42
|
+
(options: Options): void;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export interface BeforeRequestHook {
|
|
46
|
+
(info: {
|
|
47
|
+
request: Request;
|
|
48
|
+
options: NormalizedOptions;
|
|
49
|
+
retryCount: number;
|
|
50
|
+
}): Request | Response | void | Promise<Request | Response | void>;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export interface AfterResponseHook {
|
|
54
|
+
(info: {
|
|
55
|
+
request: Request;
|
|
56
|
+
options: NormalizedOptions;
|
|
57
|
+
response: Response;
|
|
58
|
+
retryCount: number;
|
|
59
|
+
}): Response | import('./types.js').RetryMarker | void | Promise<Response | import('./types.js').RetryMarker | void>;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export interface BeforeErrorHook {
|
|
63
|
+
(info: {
|
|
64
|
+
request: Request;
|
|
65
|
+
options: NormalizedOptions;
|
|
66
|
+
error: Error;
|
|
67
|
+
retryCount: number;
|
|
68
|
+
}): Error | void | Promise<Error | void>;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export interface BeforeRetryHook {
|
|
72
|
+
(info: {
|
|
73
|
+
request: Request;
|
|
74
|
+
options: NormalizedOptions;
|
|
75
|
+
error: Error;
|
|
76
|
+
retryCount: number;
|
|
77
|
+
}): Request | Response | typeof stop | void | Promise<Request | Response | typeof stop | void>;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export interface Hooks {
|
|
81
|
+
init?: InitHook[];
|
|
82
|
+
beforeRequest?: BeforeRequestHook[];
|
|
83
|
+
afterResponse?: AfterResponseHook[];
|
|
84
|
+
beforeError?: BeforeErrorHook[];
|
|
85
|
+
beforeRetry?: BeforeRetryHook[];
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export interface NormalizedHooks {
|
|
89
|
+
init: InitHook[];
|
|
90
|
+
beforeRequest: BeforeRequestHook[];
|
|
91
|
+
afterResponse: AfterResponseHook[];
|
|
92
|
+
beforeError: BeforeErrorHook[];
|
|
93
|
+
beforeRetry: BeforeRetryHook[];
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export interface Options extends Omit<RequestInit, 'method'> {
|
|
97
|
+
method?: HttpMethod | string;
|
|
98
|
+
prefix?: string | URL;
|
|
99
|
+
baseUrl?: string | URL;
|
|
100
|
+
retry?: number | Partial<RetryOptions>;
|
|
101
|
+
timeout?: number | false;
|
|
102
|
+
totalTimeout?: number | false;
|
|
103
|
+
hooks?: Hooks;
|
|
104
|
+
searchParams?: SearchParamsInit;
|
|
105
|
+
json?: unknown;
|
|
106
|
+
parseJson?: (text: string, context: { request: Request; response: Response }) => unknown;
|
|
107
|
+
stringifyJson?: (value: unknown) => string;
|
|
108
|
+
throwHttpErrors?: boolean | ((status: number) => boolean);
|
|
109
|
+
fetch?: typeof globalThis.fetch;
|
|
110
|
+
context?: Record<string, unknown>;
|
|
111
|
+
onDownloadProgress?: (progress: DownloadProgress) => void;
|
|
112
|
+
onUploadProgress?: (progress: UploadProgress) => void;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
export interface InternalOptions extends Omit<Options, 'retry' | 'timeout' | 'totalTimeout' | 'hooks' | 'throwHttpErrors' | 'fetch' | 'context' | 'prefix'> {
|
|
116
|
+
method: string;
|
|
117
|
+
retry: RetryOptions;
|
|
118
|
+
timeout: number | false;
|
|
119
|
+
totalTimeout: number | false;
|
|
120
|
+
hooks: NormalizedHooks;
|
|
121
|
+
throwHttpErrors: boolean | ((status: number) => boolean);
|
|
122
|
+
fetch: typeof globalThis.fetch;
|
|
123
|
+
context: Record<string, unknown>;
|
|
124
|
+
prefix: string;
|
|
125
|
+
_userSignal?: AbortSignal;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
export interface NormalizedOptions extends Omit<RequestInit, 'method'> {
|
|
129
|
+
method: string;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
export interface NetaResponse extends Response {
|
|
133
|
+
json<T = unknown>(): Promise<T>;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
export interface ResponsePromise extends Promise<NetaResponse> {
|
|
137
|
+
json<T = unknown>(schema?: StandardSchema): Promise<T>;
|
|
138
|
+
text(): Promise<string>;
|
|
139
|
+
blob(): Promise<Blob>;
|
|
140
|
+
arrayBuffer(): Promise<ArrayBuffer>;
|
|
141
|
+
formData(): Promise<FormData>;
|
|
142
|
+
bytes(): Promise<Uint8Array>;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
export interface NetaInstance {
|
|
146
|
+
(input: string | URL | Request, options?: Options): ResponsePromise;
|
|
147
|
+
get(input: string | URL | Request, options?: Options): ResponsePromise;
|
|
148
|
+
post(input: string | URL | Request, options?: Options): ResponsePromise;
|
|
149
|
+
put(input: string | URL | Request, options?: Options): ResponsePromise;
|
|
150
|
+
patch(input: string | URL | Request, options?: Options): ResponsePromise;
|
|
151
|
+
delete(input: string | URL | Request, options?: Options): ResponsePromise;
|
|
152
|
+
head(input: string | URL | Request, options?: Options): ResponsePromise;
|
|
153
|
+
options(input: string | URL | Request, options?: Options): ResponsePromise;
|
|
154
|
+
create(defaults?: Options): NetaInstance;
|
|
155
|
+
extend(defaults?: Options): NetaInstance;
|
|
156
|
+
retry(options?: { delay?: number; request?: Request }): import('./types.js').RetryMarker;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
export declare const stop: unique symbol;
|
|
160
|
+
|
|
161
|
+
export declare class RetryMarker {
|
|
162
|
+
options?: { delay?: number; request?: Request };
|
|
163
|
+
constructor(options?: { delay?: number; request?: Request });
|
|
164
|
+
}
|
package/types/utils.d.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { Hooks, InternalOptions, NormalizedHooks, Options, RetryOptions, SearchParamsInit } from './types.js';
|
|
2
|
+
|
|
3
|
+
export declare function normalizeRetry(retry: Options['retry']): RetryOptions;
|
|
4
|
+
export declare function normalizeHooks(hooks?: Options['hooks']): NormalizedHooks;
|
|
5
|
+
export declare function mergeHeaders(target?: HeadersInit, source?: HeadersInit): Headers;
|
|
6
|
+
export declare function mergeHooks(base?: Hooks, override?: Hooks): Hooks;
|
|
7
|
+
export declare function mergeOptions(defaults?: Options, overrides?: Options): Options;
|
|
8
|
+
export declare function resolveInput(input: string | URL | Request, options?: { prefix?: string; baseUrl?: string | URL }): string;
|
|
9
|
+
export declare function appendSearchParams(url: URL, searchParams?: SearchParamsInit): URL;
|
|
10
|
+
export declare function normalizeOptions(options: Options): InternalOptions;
|
|
11
|
+
export declare function normalizeRequestMethod(method?: string): string;
|
|
12
|
+
export declare function delay(ms: number, options?: { signal?: AbortSignal }): Promise<void>;
|
|
13
|
+
export declare function parseRetryAfter(response: Response): number | undefined;
|
|
14
|
+
export declare function applyJitter(delayMs: number, jitter: boolean | ((delay: number) => number)): number;
|
|
15
|
+
export declare function isNetworkError(error: unknown): boolean;
|