@naturalcycles/js-lib 14.147.0 → 14.148.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/dist/http/fetcher.d.ts +1 -0
- package/dist/http/fetcher.js +33 -4
- package/dist-esm/http/fetcher.js +33 -4
- package/package.json +1 -1
- package/src/http/fetcher.ts +39 -4
package/dist/http/fetcher.d.ts
CHANGED
|
@@ -51,6 +51,7 @@ export declare class Fetcher {
|
|
|
51
51
|
callNativeFetch(url: string, init: RequestInit): Promise<Response>;
|
|
52
52
|
private onNotOkResponse;
|
|
53
53
|
private processRetry;
|
|
54
|
+
private getRetryTimeout;
|
|
54
55
|
/**
|
|
55
56
|
* Default is yes,
|
|
56
57
|
* unless there's reason not to (e.g method is POST).
|
package/dist/http/fetcher.js
CHANGED
|
@@ -151,6 +151,8 @@ class Fetcher {
|
|
|
151
151
|
try {
|
|
152
152
|
res.fetchResponse = await this.callNativeFetch(req.fullUrl, req.init);
|
|
153
153
|
res.ok = res.fetchResponse.ok;
|
|
154
|
+
// important to set it to undefined, otherwise it can keep the previous value (from previous try)
|
|
155
|
+
res.err = undefined;
|
|
154
156
|
}
|
|
155
157
|
catch (err) {
|
|
156
158
|
// For example, CORS error would result in "TypeError: failed to fetch" here
|
|
@@ -308,12 +310,11 @@ class Fetcher {
|
|
|
308
310
|
// but we should log all previous errors, otherwise they are lost.
|
|
309
311
|
// Here is the right place where we know it's not the "last error"
|
|
310
312
|
if (res.err) {
|
|
311
|
-
const { retryAttempt } = retryStatus;
|
|
312
313
|
this.cfg.logger.error([
|
|
313
314
|
' <<',
|
|
314
315
|
res.fetchResponse?.status || 0,
|
|
315
316
|
res.signature,
|
|
316
|
-
`try#${retryAttempt + 1}/${count + 1}`,
|
|
317
|
+
`try#${retryStatus.retryAttempt + 1}/${count + 1}`,
|
|
317
318
|
(0, time_util_1._since)(res.req.started),
|
|
318
319
|
]
|
|
319
320
|
.filter(Boolean)
|
|
@@ -321,8 +322,36 @@ class Fetcher {
|
|
|
321
322
|
}
|
|
322
323
|
retryStatus.retryAttempt++;
|
|
323
324
|
retryStatus.retryTimeout = (0, number_util_1._clamp)(retryStatus.retryTimeout * timeoutMultiplier, 0, timeoutMax);
|
|
324
|
-
|
|
325
|
-
|
|
325
|
+
await (0, pDelay_1.pDelay)(this.getRetryTimeout(res));
|
|
326
|
+
}
|
|
327
|
+
getRetryTimeout(res) {
|
|
328
|
+
let timeout = 0;
|
|
329
|
+
// Handling http 429 with specific retry headers
|
|
330
|
+
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Retry-After
|
|
331
|
+
if (res.fetchResponse && [429, 503].includes(res.fetchResponse.status)) {
|
|
332
|
+
const retryAfterStr = res.fetchResponse.headers.get('retry-after') ??
|
|
333
|
+
res.fetchResponse.headers.get('x-ratelimit-reset');
|
|
334
|
+
if (retryAfterStr) {
|
|
335
|
+
if (Number(retryAfterStr)) {
|
|
336
|
+
timeout = Number(retryAfterStr) * 1000;
|
|
337
|
+
}
|
|
338
|
+
else {
|
|
339
|
+
const date = new Date(retryAfterStr);
|
|
340
|
+
if (!isNaN(date)) {
|
|
341
|
+
timeout = Number(date) - Date.now();
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
this.cfg.logger.log(`retry-after: ${retryAfterStr}`);
|
|
345
|
+
if (!timeout) {
|
|
346
|
+
this.cfg.logger.warn(`retry-after could not be parsed`);
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
if (!timeout) {
|
|
351
|
+
const noise = Math.random() * 500;
|
|
352
|
+
timeout = res.retryStatus.retryTimeout + noise;
|
|
353
|
+
}
|
|
354
|
+
return timeout;
|
|
326
355
|
}
|
|
327
356
|
/**
|
|
328
357
|
* Default is yes,
|
package/dist-esm/http/fetcher.js
CHANGED
|
@@ -136,6 +136,8 @@ export class Fetcher {
|
|
|
136
136
|
try {
|
|
137
137
|
res.fetchResponse = await this.callNativeFetch(req.fullUrl, req.init);
|
|
138
138
|
res.ok = res.fetchResponse.ok;
|
|
139
|
+
// important to set it to undefined, otherwise it can keep the previous value (from previous try)
|
|
140
|
+
res.err = undefined;
|
|
139
141
|
}
|
|
140
142
|
catch (err) {
|
|
141
143
|
// For example, CORS error would result in "TypeError: failed to fetch" here
|
|
@@ -295,12 +297,11 @@ export class Fetcher {
|
|
|
295
297
|
// but we should log all previous errors, otherwise they are lost.
|
|
296
298
|
// Here is the right place where we know it's not the "last error"
|
|
297
299
|
if (res.err) {
|
|
298
|
-
const { retryAttempt } = retryStatus;
|
|
299
300
|
this.cfg.logger.error([
|
|
300
301
|
' <<',
|
|
301
302
|
((_a = res.fetchResponse) === null || _a === void 0 ? void 0 : _a.status) || 0,
|
|
302
303
|
res.signature,
|
|
303
|
-
`try#${retryAttempt + 1}/${count + 1}`,
|
|
304
|
+
`try#${retryStatus.retryAttempt + 1}/${count + 1}`,
|
|
304
305
|
_since(res.req.started),
|
|
305
306
|
]
|
|
306
307
|
.filter(Boolean)
|
|
@@ -308,8 +309,36 @@ export class Fetcher {
|
|
|
308
309
|
}
|
|
309
310
|
retryStatus.retryAttempt++;
|
|
310
311
|
retryStatus.retryTimeout = _clamp(retryStatus.retryTimeout * timeoutMultiplier, 0, timeoutMax);
|
|
311
|
-
|
|
312
|
-
|
|
312
|
+
await pDelay(this.getRetryTimeout(res));
|
|
313
|
+
}
|
|
314
|
+
getRetryTimeout(res) {
|
|
315
|
+
var _a;
|
|
316
|
+
let timeout = 0;
|
|
317
|
+
// Handling http 429 with specific retry headers
|
|
318
|
+
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Retry-After
|
|
319
|
+
if (res.fetchResponse && [429, 503].includes(res.fetchResponse.status)) {
|
|
320
|
+
const retryAfterStr = (_a = res.fetchResponse.headers.get('retry-after')) !== null && _a !== void 0 ? _a : res.fetchResponse.headers.get('x-ratelimit-reset');
|
|
321
|
+
if (retryAfterStr) {
|
|
322
|
+
if (Number(retryAfterStr)) {
|
|
323
|
+
timeout = Number(retryAfterStr) * 1000;
|
|
324
|
+
}
|
|
325
|
+
else {
|
|
326
|
+
const date = new Date(retryAfterStr);
|
|
327
|
+
if (!isNaN(date)) {
|
|
328
|
+
timeout = Number(date) - Date.now();
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
this.cfg.logger.log(`retry-after: ${retryAfterStr}`);
|
|
332
|
+
if (!timeout) {
|
|
333
|
+
this.cfg.logger.warn(`retry-after could not be parsed`);
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
if (!timeout) {
|
|
338
|
+
const noise = Math.random() * 500;
|
|
339
|
+
timeout = res.retryStatus.retryTimeout + noise;
|
|
340
|
+
}
|
|
341
|
+
return timeout;
|
|
313
342
|
}
|
|
314
343
|
/**
|
|
315
344
|
* Default is yes,
|
package/package.json
CHANGED
package/src/http/fetcher.ts
CHANGED
|
@@ -15,6 +15,7 @@ import {
|
|
|
15
15
|
import { pDelay } from '../promise/pDelay'
|
|
16
16
|
import { _jsonParse, _jsonParseIfPossible } from '../string/json.util'
|
|
17
17
|
import { _since } from '../time/time.util'
|
|
18
|
+
import { NumberOfMilliseconds } from '../types'
|
|
18
19
|
import type {
|
|
19
20
|
FetcherAfterResponseHook,
|
|
20
21
|
FetcherBeforeRequestHook,
|
|
@@ -224,6 +225,8 @@ export class Fetcher {
|
|
|
224
225
|
try {
|
|
225
226
|
res.fetchResponse = await this.callNativeFetch(req.fullUrl, req.init)
|
|
226
227
|
res.ok = res.fetchResponse.ok
|
|
228
|
+
// important to set it to undefined, otherwise it can keep the previous value (from previous try)
|
|
229
|
+
res.err = undefined
|
|
227
230
|
} catch (err) {
|
|
228
231
|
// For example, CORS error would result in "TypeError: failed to fetch" here
|
|
229
232
|
res.err = err as Error
|
|
@@ -405,13 +408,12 @@ export class Fetcher {
|
|
|
405
408
|
// but we should log all previous errors, otherwise they are lost.
|
|
406
409
|
// Here is the right place where we know it's not the "last error"
|
|
407
410
|
if (res.err) {
|
|
408
|
-
const { retryAttempt } = retryStatus
|
|
409
411
|
this.cfg.logger.error(
|
|
410
412
|
[
|
|
411
413
|
' <<',
|
|
412
414
|
res.fetchResponse?.status || 0,
|
|
413
415
|
res.signature,
|
|
414
|
-
`try#${retryAttempt + 1}/${count + 1}`,
|
|
416
|
+
`try#${retryStatus.retryAttempt + 1}/${count + 1}`,
|
|
415
417
|
_since(res.req.started),
|
|
416
418
|
]
|
|
417
419
|
.filter(Boolean)
|
|
@@ -423,8 +425,41 @@ export class Fetcher {
|
|
|
423
425
|
retryStatus.retryAttempt++
|
|
424
426
|
retryStatus.retryTimeout = _clamp(retryStatus.retryTimeout * timeoutMultiplier, 0, timeoutMax)
|
|
425
427
|
|
|
426
|
-
|
|
427
|
-
|
|
428
|
+
await pDelay(this.getRetryTimeout(res))
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
private getRetryTimeout(res: FetcherResponse): NumberOfMilliseconds {
|
|
432
|
+
let timeout: NumberOfMilliseconds = 0
|
|
433
|
+
|
|
434
|
+
// Handling http 429 with specific retry headers
|
|
435
|
+
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Retry-After
|
|
436
|
+
if (res.fetchResponse && [429, 503].includes(res.fetchResponse.status)) {
|
|
437
|
+
const retryAfterStr =
|
|
438
|
+
res.fetchResponse.headers.get('retry-after') ??
|
|
439
|
+
res.fetchResponse.headers.get('x-ratelimit-reset')
|
|
440
|
+
if (retryAfterStr) {
|
|
441
|
+
if (Number(retryAfterStr)) {
|
|
442
|
+
timeout = Number(retryAfterStr) * 1000
|
|
443
|
+
} else {
|
|
444
|
+
const date = new Date(retryAfterStr)
|
|
445
|
+
if (!isNaN(date as any)) {
|
|
446
|
+
timeout = Number(date) - Date.now()
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
this.cfg.logger.log(`retry-after: ${retryAfterStr}`)
|
|
451
|
+
if (!timeout) {
|
|
452
|
+
this.cfg.logger.warn(`retry-after could not be parsed`)
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
if (!timeout) {
|
|
458
|
+
const noise = Math.random() * 500
|
|
459
|
+
timeout = res.retryStatus.retryTimeout + noise
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
return timeout
|
|
428
463
|
}
|
|
429
464
|
|
|
430
465
|
/**
|