rezo 1.0.44 → 1.0.46

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.
Files changed (37) hide show
  1. package/dist/adapters/entries/curl.d.ts +115 -0
  2. package/dist/adapters/entries/fetch.d.ts +115 -0
  3. package/dist/adapters/entries/http.d.ts +115 -0
  4. package/dist/adapters/entries/http2.d.ts +115 -0
  5. package/dist/adapters/entries/react-native.d.ts +115 -0
  6. package/dist/adapters/entries/xhr.d.ts +115 -0
  7. package/dist/adapters/fetch.cjs +18 -0
  8. package/dist/adapters/fetch.js +18 -0
  9. package/dist/adapters/http.cjs +22 -4
  10. package/dist/adapters/http.js +22 -4
  11. package/dist/adapters/http2.cjs +21 -0
  12. package/dist/adapters/http2.js +21 -0
  13. package/dist/adapters/index.cjs +6 -6
  14. package/dist/adapters/xhr.cjs +19 -0
  15. package/dist/adapters/xhr.js +19 -0
  16. package/dist/cache/index.cjs +9 -9
  17. package/dist/core/hooks.cjs +4 -2
  18. package/dist/core/hooks.js +4 -2
  19. package/dist/crawler/index.cjs +40 -40
  20. package/dist/crawler.d.ts +115 -0
  21. package/dist/entries/crawler.cjs +5 -5
  22. package/dist/index.cjs +27 -27
  23. package/dist/index.d.ts +115 -0
  24. package/dist/internal/agents/index.cjs +10 -10
  25. package/dist/platform/browser.d.ts +115 -0
  26. package/dist/platform/bun.d.ts +115 -0
  27. package/dist/platform/deno.d.ts +115 -0
  28. package/dist/platform/node.d.ts +115 -0
  29. package/dist/platform/react-native.d.ts +115 -0
  30. package/dist/platform/worker.d.ts +115 -0
  31. package/dist/proxy/index.cjs +5 -5
  32. package/dist/proxy/index.js +1 -1
  33. package/dist/queue/index.cjs +8 -8
  34. package/dist/responses/universal/index.cjs +11 -11
  35. package/dist/utils/rate-limit-wait.cjs +217 -0
  36. package/dist/utils/rate-limit-wait.js +208 -0
  37. package/package.json +1 -1
@@ -1416,6 +1416,35 @@ export type OnTimeoutHook = (event: TimeoutEvent, config: RezoConfig) => void;
1416
1416
  * Use for cleanup, logging
1417
1417
  */
1418
1418
  export type OnAbortHook = (event: AbortEvent, config: RezoConfig) => void;
1419
+ /**
1420
+ * Rate limit wait event data - fired when waiting due to rate limiting
1421
+ */
1422
+ export interface RateLimitWaitEvent {
1423
+ /** HTTP status code that triggered the wait (e.g., 429, 503) */
1424
+ status: number;
1425
+ /** Time to wait in milliseconds */
1426
+ waitTime: number;
1427
+ /** Current wait attempt number (1-indexed) */
1428
+ attempt: number;
1429
+ /** Maximum wait attempts configured */
1430
+ maxAttempts: number;
1431
+ /** Where the wait time was extracted from */
1432
+ source: "header" | "body" | "function" | "default";
1433
+ /** The header or body path used (if applicable) */
1434
+ sourcePath?: string;
1435
+ /** URL being requested */
1436
+ url: string;
1437
+ /** HTTP method of the request */
1438
+ method: string;
1439
+ /** Timestamp when the wait started */
1440
+ timestamp: number;
1441
+ }
1442
+ /**
1443
+ * Hook called when rate limit wait occurs
1444
+ * Informational only - cannot abort the wait
1445
+ * Use for logging, monitoring, alerting
1446
+ */
1447
+ export type OnRateLimitWaitHook = (event: RateLimitWaitEvent, config: RezoConfig) => void | Promise<void>;
1419
1448
  /**
1420
1449
  * Hook called before a proxy is selected
1421
1450
  * Can return a specific proxy to override selection
@@ -1496,6 +1525,7 @@ export interface RezoHooks {
1496
1525
  onTls: OnTlsHook[];
1497
1526
  onTimeout: OnTimeoutHook[];
1498
1527
  onAbort: OnAbortHook[];
1528
+ onRateLimitWait: OnRateLimitWaitHook[];
1499
1529
  }
1500
1530
  /**
1501
1531
  * Create empty hooks object with all arrays initialized
@@ -2426,6 +2456,91 @@ export interface RezoRequestConfig<D = any> {
2426
2456
  /** Weather to stop or continue retry when certain condition is met*/
2427
2457
  condition?: (error: RezoError) => boolean | Promise<boolean>;
2428
2458
  };
2459
+ /**
2460
+ * Rate limit wait configuration - wait and retry when receiving rate limit responses.
2461
+ *
2462
+ * This feature runs BEFORE the retry system. When a rate-limiting status code is received,
2463
+ * the client will wait for the specified time and automatically retry the request.
2464
+ *
2465
+ * **Basic Usage:**
2466
+ * - `waitOnStatus: true` - Enable waiting on 429 status (default behavior)
2467
+ * - `waitOnStatus: [429, 503]` - Enable waiting on specific status codes
2468
+ *
2469
+ * **Wait Time Sources:**
2470
+ * - `'retry-after'` - Use standard Retry-After header (default)
2471
+ * - `{ header: 'X-RateLimit-Reset' }` - Use custom header
2472
+ * - `{ body: 'retry_after' }` - Extract from JSON response body
2473
+ * - Custom function for complex logic
2474
+ *
2475
+ * @example
2476
+ * ```typescript
2477
+ * // Wait on 429 using Retry-After header
2478
+ * await rezo.get(url, { waitOnStatus: true });
2479
+ *
2480
+ * // Wait on 429 using custom header
2481
+ * await rezo.get(url, {
2482
+ * waitOnStatus: true,
2483
+ * waitTimeSource: { header: 'X-RateLimit-Reset' }
2484
+ * });
2485
+ *
2486
+ * // Wait on 429 extracting time from JSON body
2487
+ * await rezo.get(url, {
2488
+ * waitOnStatus: true,
2489
+ * waitTimeSource: { body: 'data.retry_after' }
2490
+ * });
2491
+ *
2492
+ * // Custom function for complex APIs
2493
+ * await rezo.get(url, {
2494
+ * waitOnStatus: [429, 503],
2495
+ * waitTimeSource: (response) => {
2496
+ * const reset = response.headers.get('x-ratelimit-reset');
2497
+ * return reset ? parseInt(reset) - Math.floor(Date.now() / 1000) : null;
2498
+ * }
2499
+ * });
2500
+ * ```
2501
+ */
2502
+ waitOnStatus?: boolean | number[];
2503
+ /**
2504
+ * Where to extract the wait time from when rate-limited.
2505
+ *
2506
+ * - `'retry-after'` - Standard Retry-After header (default)
2507
+ * - `{ header: string }` - Custom header name (e.g., 'X-RateLimit-Reset')
2508
+ * - `{ body: string }` - JSON path in response body (e.g., 'data.retry_after', 'wait_seconds')
2509
+ * - Function - Custom logic receiving the response, return seconds to wait or null
2510
+ *
2511
+ * @default 'retry-after'
2512
+ */
2513
+ waitTimeSource?: "retry-after" | {
2514
+ header: string;
2515
+ } | {
2516
+ body: string;
2517
+ } | ((response: {
2518
+ status: number;
2519
+ headers: RezoHeaders;
2520
+ data?: any;
2521
+ }) => number | null);
2522
+ /**
2523
+ * Maximum time to wait for rate limit in milliseconds.
2524
+ * If the extracted wait time exceeds this, the request will fail instead of waiting.
2525
+ * Set to 0 for unlimited wait time.
2526
+ *
2527
+ * @default 60000 (60 seconds)
2528
+ */
2529
+ maxWaitTime?: number;
2530
+ /**
2531
+ * Default wait time in milliseconds if the wait time source returns nothing.
2532
+ * Used as fallback when Retry-After header or body path is not present.
2533
+ *
2534
+ * @default 1000 (1 second)
2535
+ */
2536
+ defaultWaitTime?: number;
2537
+ /**
2538
+ * Maximum number of wait attempts before giving up.
2539
+ * After this many waits, the request will proceed to retry logic or fail.
2540
+ *
2541
+ * @default 3
2542
+ */
2543
+ maxWaitAttempts?: number;
2429
2544
  /** Whether to use a secure context for HTTPS requests */
2430
2545
  useSecureContext?: boolean;
2431
2546
  /** Custom secure context for TLS connections */
@@ -10,6 +10,7 @@ const { DownloadResponse } = require('../responses/universal/download.cjs');
10
10
  const { UploadResponse } = require('../responses/universal/upload.cjs');
11
11
  const { isSameDomain, RezoPerformance } = require('../utils/tools.cjs');
12
12
  const { ResponseCache } = require('../cache/universal-response-cache.cjs');
13
+ const { handleRateLimitWait, shouldWaitOnStatus } = require('../utils/rate-limit-wait.cjs');
13
14
  const Environment = {
14
15
  isNode: typeof process !== "undefined" && process.versions?.node,
15
16
  isBrowser: typeof window !== "undefined" && typeof document !== "undefined",
@@ -555,6 +556,23 @@ async function executeFetchRequest(fetchOptions, config, options, perform, strea
555
556
  return response;
556
557
  }
557
558
  if (statusOnNext === "error") {
559
+ if (shouldWaitOnStatus(response.status, options.waitOnStatus)) {
560
+ const rateLimitWaitAttempt = config._rateLimitWaitAttempt || 0;
561
+ const waitResult = await handleRateLimitWait({
562
+ status: response.status,
563
+ headers: response.headers,
564
+ data: response.data,
565
+ url: fetchOptions.fullUrl || fetchOptions.url?.toString() || "",
566
+ method: fetchOptions.method || "GET",
567
+ config,
568
+ options,
569
+ currentWaitAttempt: rateLimitWaitAttempt
570
+ });
571
+ if (waitResult.shouldRetry) {
572
+ config._rateLimitWaitAttempt = waitResult.waitAttempt;
573
+ continue;
574
+ }
575
+ }
558
576
  const httpError = builErrorFromResponse(`Request failed with status code ${response.status}`, response, config, fetchOptions);
559
577
  if (config.retry && statusCodes?.includes(response.status)) {
560
578
  if (maxRetries > retries) {
@@ -10,6 +10,7 @@ import { DownloadResponse } from '../responses/universal/download.js';
10
10
  import { UploadResponse } from '../responses/universal/upload.js';
11
11
  import { isSameDomain, RezoPerformance } from '../utils/tools.js';
12
12
  import { ResponseCache } from '../cache/universal-response-cache.js';
13
+ import { handleRateLimitWait, shouldWaitOnStatus } from '../utils/rate-limit-wait.js';
13
14
  const Environment = {
14
15
  isNode: typeof process !== "undefined" && process.versions?.node,
15
16
  isBrowser: typeof window !== "undefined" && typeof document !== "undefined",
@@ -555,6 +556,23 @@ async function executeFetchRequest(fetchOptions, config, options, perform, strea
555
556
  return response;
556
557
  }
557
558
  if (statusOnNext === "error") {
559
+ if (shouldWaitOnStatus(response.status, options.waitOnStatus)) {
560
+ const rateLimitWaitAttempt = config._rateLimitWaitAttempt || 0;
561
+ const waitResult = await handleRateLimitWait({
562
+ status: response.status,
563
+ headers: response.headers,
564
+ data: response.data,
565
+ url: fetchOptions.fullUrl || fetchOptions.url?.toString() || "",
566
+ method: fetchOptions.method || "GET",
567
+ config,
568
+ options,
569
+ currentWaitAttempt: rateLimitWaitAttempt
570
+ });
571
+ if (waitResult.shouldRetry) {
572
+ config._rateLimitWaitAttempt = waitResult.waitAttempt;
573
+ continue;
574
+ }
575
+ }
558
576
  const httpError = builErrorFromResponse(`Request failed with status code ${response.status}`, response, config, fetchOptions);
559
577
  if (config.retry && statusCodes?.includes(response.status)) {
560
578
  if (maxRetries > retries) {
@@ -21,6 +21,7 @@ const { getGlobalDNSCache } = require('../cache/dns-cache.cjs');
21
21
  const { ResponseCache } = require('../cache/response-cache.cjs');
22
22
  const { getGlobalAgentPool } = require('../utils/agent-pool.cjs');
23
23
  const { StagedTimeoutManager, parseStagedTimeouts } = require('../utils/staged-timeout.cjs');
24
+ const { handleRateLimitWait, shouldWaitOnStatus } = require('../utils/rate-limit-wait.cjs');
24
25
  const dns = require("node:dns");
25
26
  const debugLog = {
26
27
  requestStart: (config, url, method) => {
@@ -604,6 +605,23 @@ async function executeHttp1Request(fetchOptions, config, options, perform, fs, s
604
605
  continue;
605
606
  }
606
607
  if (statusOnNext === "error") {
608
+ if (shouldWaitOnStatus(response.status, options.waitOnStatus)) {
609
+ const rateLimitWaitAttempt = config._rateLimitWaitAttempt || 0;
610
+ const waitResult = await handleRateLimitWait({
611
+ status: response.status,
612
+ headers: response.headers,
613
+ data: response.data,
614
+ url: fetchOptions.fullUrl || fetchOptions.url?.toString() || "",
615
+ method: fetchOptions.method || "GET",
616
+ config,
617
+ options,
618
+ currentWaitAttempt: rateLimitWaitAttempt
619
+ });
620
+ if (waitResult.shouldRetry) {
621
+ config._rateLimitWaitAttempt = waitResult.waitAttempt;
622
+ continue;
623
+ }
624
+ }
607
625
  const httpError = builErrorFromResponse(`Request failed with status code ${response.status}`, response, config, fetchOptions);
608
626
  if (config.retry && statusCodes?.includes(response.status)) {
609
627
  if (maxRetries > retries) {
@@ -1022,7 +1040,7 @@ async function request(config, fetchOptions, requestCount, timing, _stats, respo
1022
1040
  timeoutManager.startPhase("connect");
1023
1041
  }
1024
1042
  }
1025
- socket.on("error", (err) => {
1043
+ socket.once("error", (err) => {
1026
1044
  timeoutManager.clearAll();
1027
1045
  _stats.statusOnNext = "error";
1028
1046
  const error = buildSmartError(config, fetchOptions, err);
@@ -1030,7 +1048,7 @@ async function request(config, fetchOptions, requestCount, timing, _stats, respo
1030
1048
  });
1031
1049
  timing.dnsStart = performance.now();
1032
1050
  config.timing.domainLookupStart = timing.dnsStart;
1033
- socket.on("lookup", (err, address, family) => {
1051
+ socket.once("lookup", (err, address, family) => {
1034
1052
  if (!timing.dnsEnd) {
1035
1053
  timing.dnsEnd = performance.now();
1036
1054
  config.timing.domainLookupEnd = timing.dnsEnd;
@@ -1056,7 +1074,7 @@ async function request(config, fetchOptions, requestCount, timing, _stats, respo
1056
1074
  }
1057
1075
  }
1058
1076
  });
1059
- socket.on("secureConnect", () => {
1077
+ socket.once("secureConnect", () => {
1060
1078
  timeoutManager.clearPhase("connect");
1061
1079
  if (timeoutManager.hasPhase("headers")) {
1062
1080
  timeoutManager.startPhase("headers");
@@ -1112,7 +1130,7 @@ async function request(config, fetchOptions, requestCount, timing, _stats, respo
1112
1130
  }
1113
1131
  }
1114
1132
  });
1115
- socket.on("connect", () => {
1133
+ socket.once("connect", () => {
1116
1134
  if (!isSecure) {
1117
1135
  timeoutManager.clearPhase("connect");
1118
1136
  if (timeoutManager.hasPhase("headers")) {
@@ -21,6 +21,7 @@ import { getGlobalDNSCache } from '../cache/dns-cache.js';
21
21
  import { ResponseCache } from '../cache/response-cache.js';
22
22
  import { getGlobalAgentPool } from '../utils/agent-pool.js';
23
23
  import { StagedTimeoutManager, parseStagedTimeouts } from '../utils/staged-timeout.js';
24
+ import { handleRateLimitWait, shouldWaitOnStatus } from '../utils/rate-limit-wait.js';
24
25
  import dns from "node:dns";
25
26
  const debugLog = {
26
27
  requestStart: (config, url, method) => {
@@ -604,6 +605,23 @@ async function executeHttp1Request(fetchOptions, config, options, perform, fs, s
604
605
  continue;
605
606
  }
606
607
  if (statusOnNext === "error") {
608
+ if (shouldWaitOnStatus(response.status, options.waitOnStatus)) {
609
+ const rateLimitWaitAttempt = config._rateLimitWaitAttempt || 0;
610
+ const waitResult = await handleRateLimitWait({
611
+ status: response.status,
612
+ headers: response.headers,
613
+ data: response.data,
614
+ url: fetchOptions.fullUrl || fetchOptions.url?.toString() || "",
615
+ method: fetchOptions.method || "GET",
616
+ config,
617
+ options,
618
+ currentWaitAttempt: rateLimitWaitAttempt
619
+ });
620
+ if (waitResult.shouldRetry) {
621
+ config._rateLimitWaitAttempt = waitResult.waitAttempt;
622
+ continue;
623
+ }
624
+ }
607
625
  const httpError = builErrorFromResponse(`Request failed with status code ${response.status}`, response, config, fetchOptions);
608
626
  if (config.retry && statusCodes?.includes(response.status)) {
609
627
  if (maxRetries > retries) {
@@ -1022,7 +1040,7 @@ async function request(config, fetchOptions, requestCount, timing, _stats, respo
1022
1040
  timeoutManager.startPhase("connect");
1023
1041
  }
1024
1042
  }
1025
- socket.on("error", (err) => {
1043
+ socket.once("error", (err) => {
1026
1044
  timeoutManager.clearAll();
1027
1045
  _stats.statusOnNext = "error";
1028
1046
  const error = buildSmartError(config, fetchOptions, err);
@@ -1030,7 +1048,7 @@ async function request(config, fetchOptions, requestCount, timing, _stats, respo
1030
1048
  });
1031
1049
  timing.dnsStart = performance.now();
1032
1050
  config.timing.domainLookupStart = timing.dnsStart;
1033
- socket.on("lookup", (err, address, family) => {
1051
+ socket.once("lookup", (err, address, family) => {
1034
1052
  if (!timing.dnsEnd) {
1035
1053
  timing.dnsEnd = performance.now();
1036
1054
  config.timing.domainLookupEnd = timing.dnsEnd;
@@ -1056,7 +1074,7 @@ async function request(config, fetchOptions, requestCount, timing, _stats, respo
1056
1074
  }
1057
1075
  }
1058
1076
  });
1059
- socket.on("secureConnect", () => {
1077
+ socket.once("secureConnect", () => {
1060
1078
  timeoutManager.clearPhase("connect");
1061
1079
  if (timeoutManager.hasPhase("headers")) {
1062
1080
  timeoutManager.startPhase("headers");
@@ -1112,7 +1130,7 @@ async function request(config, fetchOptions, requestCount, timing, _stats, respo
1112
1130
  }
1113
1131
  }
1114
1132
  });
1115
- socket.on("connect", () => {
1133
+ socket.once("connect", () => {
1116
1134
  if (!isSecure) {
1117
1135
  timeoutManager.clearPhase("connect");
1118
1136
  if (timeoutManager.hasPhase("headers")) {
@@ -18,6 +18,7 @@ const { isSameDomain, RezoPerformance } = require('../utils/tools.cjs');
18
18
  const { SocksClient } = require('../internal/agents/socks-client.cjs');
19
19
  const net = require("node:net");
20
20
  const { ResponseCache } = require('../cache/response-cache.cjs');
21
+ const { handleRateLimitWait, shouldWaitOnStatus } = require('../utils/rate-limit-wait.cjs');
21
22
  let zstdDecompressSync = null;
22
23
  let zstdChecked = false;
23
24
  const debugLog = {
@@ -259,6 +260,7 @@ class Http2SessionPool {
259
260
  }
260
261
  return new Promise((resolve, reject) => {
261
262
  const session = http2.connect(authority, sessionOptions);
263
+ session.setMaxListeners(20);
262
264
  let settled = false;
263
265
  const timeoutId = timeout ? setTimeout(() => {
264
266
  if (!settled) {
@@ -336,6 +338,7 @@ class Http2SessionPool {
336
338
  rejectUnauthorized: rejectUnauthorized !== false,
337
339
  ALPNProtocols: ["h2", "http/1.1"]
338
340
  });
341
+ tlsSocket.setMaxListeners(20);
339
342
  const tlsTimeoutId = timeout ? setTimeout(() => {
340
343
  tlsSocket.destroy();
341
344
  reject(new Error(`TLS handshake timeout after ${timeout}ms`));
@@ -433,6 +436,7 @@ class Http2SessionPool {
433
436
  rejectUnauthorized: rejectUnauthorized !== false,
434
437
  ALPNProtocols: ["h2", "http/1.1"]
435
438
  });
439
+ tlsSocket.setMaxListeners(20);
436
440
  const tlsTimeoutId = timeout ? setTimeout(() => {
437
441
  tlsSocket.destroy();
438
442
  reject(new Error(`TLS handshake timeout after ${timeout}ms`));
@@ -960,6 +964,23 @@ async function executeHttp2Request(fetchOptions, config, options, perform, fs, s
960
964
  return response;
961
965
  }
962
966
  if (statusOnNext === "error") {
967
+ if (shouldWaitOnStatus(response.status, options.waitOnStatus)) {
968
+ const rateLimitWaitAttempt = config._rateLimitWaitAttempt || 0;
969
+ const waitResult = await handleRateLimitWait({
970
+ status: response.status,
971
+ headers: response.headers,
972
+ data: response.data,
973
+ url: fetchOptions.fullUrl || fetchOptions.url?.toString() || "",
974
+ method: fetchOptions.method || "GET",
975
+ config,
976
+ options,
977
+ currentWaitAttempt: rateLimitWaitAttempt
978
+ });
979
+ if (waitResult.shouldRetry) {
980
+ config._rateLimitWaitAttempt = waitResult.waitAttempt;
981
+ continue;
982
+ }
983
+ }
963
984
  const httpError = builErrorFromResponse(`Request failed with status code ${response.status}`, response, config, fetchOptions);
964
985
  if (config.retry && statusCodes?.includes(response.status)) {
965
986
  if (maxRetries > retries) {
@@ -18,6 +18,7 @@ import { isSameDomain, RezoPerformance } from '../utils/tools.js';
18
18
  import { SocksClient } from '../internal/agents/socks-client.js';
19
19
  import * as net from "node:net";
20
20
  import { ResponseCache } from '../cache/response-cache.js';
21
+ import { handleRateLimitWait, shouldWaitOnStatus } from '../utils/rate-limit-wait.js';
21
22
  let zstdDecompressSync = null;
22
23
  let zstdChecked = false;
23
24
  const debugLog = {
@@ -259,6 +260,7 @@ class Http2SessionPool {
259
260
  }
260
261
  return new Promise((resolve, reject) => {
261
262
  const session = http2.connect(authority, sessionOptions);
263
+ session.setMaxListeners(20);
262
264
  let settled = false;
263
265
  const timeoutId = timeout ? setTimeout(() => {
264
266
  if (!settled) {
@@ -336,6 +338,7 @@ class Http2SessionPool {
336
338
  rejectUnauthorized: rejectUnauthorized !== false,
337
339
  ALPNProtocols: ["h2", "http/1.1"]
338
340
  });
341
+ tlsSocket.setMaxListeners(20);
339
342
  const tlsTimeoutId = timeout ? setTimeout(() => {
340
343
  tlsSocket.destroy();
341
344
  reject(new Error(`TLS handshake timeout after ${timeout}ms`));
@@ -433,6 +436,7 @@ class Http2SessionPool {
433
436
  rejectUnauthorized: rejectUnauthorized !== false,
434
437
  ALPNProtocols: ["h2", "http/1.1"]
435
438
  });
439
+ tlsSocket.setMaxListeners(20);
436
440
  const tlsTimeoutId = timeout ? setTimeout(() => {
437
441
  tlsSocket.destroy();
438
442
  reject(new Error(`TLS handshake timeout after ${timeout}ms`));
@@ -960,6 +964,23 @@ async function executeHttp2Request(fetchOptions, config, options, perform, fs, s
960
964
  return response;
961
965
  }
962
966
  if (statusOnNext === "error") {
967
+ if (shouldWaitOnStatus(response.status, options.waitOnStatus)) {
968
+ const rateLimitWaitAttempt = config._rateLimitWaitAttempt || 0;
969
+ const waitResult = await handleRateLimitWait({
970
+ status: response.status,
971
+ headers: response.headers,
972
+ data: response.data,
973
+ url: fetchOptions.fullUrl || fetchOptions.url?.toString() || "",
974
+ method: fetchOptions.method || "GET",
975
+ config,
976
+ options,
977
+ currentWaitAttempt: rateLimitWaitAttempt
978
+ });
979
+ if (waitResult.shouldRetry) {
980
+ config._rateLimitWaitAttempt = waitResult.waitAttempt;
981
+ continue;
982
+ }
983
+ }
963
984
  const httpError = builErrorFromResponse(`Request failed with status code ${response.status}`, response, config, fetchOptions);
964
985
  if (config.retry && statusCodes?.includes(response.status)) {
965
986
  if (maxRetries > retries) {
@@ -1,6 +1,6 @@
1
- const _mod_ggr948 = require('./picker.cjs');
2
- exports.detectRuntime = _mod_ggr948.detectRuntime;
3
- exports.getAdapterCapabilities = _mod_ggr948.getAdapterCapabilities;
4
- exports.buildAdapterContext = _mod_ggr948.buildAdapterContext;
5
- exports.getAvailableAdapters = _mod_ggr948.getAvailableAdapters;
6
- exports.selectAdapter = _mod_ggr948.selectAdapter;;
1
+ const _mod_hql2dt = require('./picker.cjs');
2
+ exports.detectRuntime = _mod_hql2dt.detectRuntime;
3
+ exports.getAdapterCapabilities = _mod_hql2dt.getAdapterCapabilities;
4
+ exports.buildAdapterContext = _mod_hql2dt.buildAdapterContext;
5
+ exports.getAvailableAdapters = _mod_hql2dt.getAvailableAdapters;
6
+ exports.selectAdapter = _mod_hql2dt.selectAdapter;;
@@ -10,6 +10,7 @@ const { DownloadResponse } = require('../responses/universal/download.cjs');
10
10
  const { UploadResponse } = require('../responses/universal/upload.cjs');
11
11
  const { RezoPerformance } = require('../utils/tools.cjs');
12
12
  const { ResponseCache } = require('../cache/universal-response-cache.cjs');
13
+ const { handleRateLimitWait, shouldWaitOnStatus } = require('../utils/rate-limit-wait.cjs');
13
14
  const Environment = {
14
15
  isBrowser: typeof window !== "undefined" && typeof document !== "undefined",
15
16
  hasXHR: typeof XMLHttpRequest !== "undefined",
@@ -390,6 +391,24 @@ async function executeXHRRequest(fetchOptions, config, options, perform, streamR
390
391
  try {
391
392
  const response = await executeSingleXHRRequest(config, fetchOptions, timing, streamResult, downloadResult, uploadResult);
392
393
  if (response instanceof RezoError) {
394
+ const errorStatus = response.status || 0;
395
+ if (shouldWaitOnStatus(errorStatus, options.waitOnStatus)) {
396
+ const rateLimitWaitAttempt = config._rateLimitWaitAttempt || 0;
397
+ const waitResult = await handleRateLimitWait({
398
+ status: errorStatus,
399
+ headers: response.response?.headers || new RezoHeaders,
400
+ data: response.response?.data,
401
+ url: fetchOptions.fullUrl || fetchOptions.url?.toString() || "",
402
+ method: fetchOptions.method || "GET",
403
+ config,
404
+ options,
405
+ currentWaitAttempt: rateLimitWaitAttempt
406
+ });
407
+ if (waitResult.shouldRetry) {
408
+ config._rateLimitWaitAttempt = waitResult.waitAttempt;
409
+ continue;
410
+ }
411
+ }
393
412
  config.errors.push({
394
413
  attempt: config.retryAttempts + 1,
395
414
  error: response,
@@ -10,6 +10,7 @@ import { DownloadResponse } from '../responses/universal/download.js';
10
10
  import { UploadResponse } from '../responses/universal/upload.js';
11
11
  import { RezoPerformance } from '../utils/tools.js';
12
12
  import { ResponseCache } from '../cache/universal-response-cache.js';
13
+ import { handleRateLimitWait, shouldWaitOnStatus } from '../utils/rate-limit-wait.js';
13
14
  const Environment = {
14
15
  isBrowser: typeof window !== "undefined" && typeof document !== "undefined",
15
16
  hasXHR: typeof XMLHttpRequest !== "undefined",
@@ -390,6 +391,24 @@ async function executeXHRRequest(fetchOptions, config, options, perform, streamR
390
391
  try {
391
392
  const response = await executeSingleXHRRequest(config, fetchOptions, timing, streamResult, downloadResult, uploadResult);
392
393
  if (response instanceof RezoError) {
394
+ const errorStatus = response.status || 0;
395
+ if (shouldWaitOnStatus(errorStatus, options.waitOnStatus)) {
396
+ const rateLimitWaitAttempt = config._rateLimitWaitAttempt || 0;
397
+ const waitResult = await handleRateLimitWait({
398
+ status: errorStatus,
399
+ headers: response.response?.headers || new RezoHeaders,
400
+ data: response.response?.data,
401
+ url: fetchOptions.fullUrl || fetchOptions.url?.toString() || "",
402
+ method: fetchOptions.method || "GET",
403
+ config,
404
+ options,
405
+ currentWaitAttempt: rateLimitWaitAttempt
406
+ });
407
+ if (waitResult.shouldRetry) {
408
+ config._rateLimitWaitAttempt = waitResult.waitAttempt;
409
+ continue;
410
+ }
411
+ }
393
412
  config.errors.push({
394
413
  attempt: config.retryAttempts + 1,
395
414
  error: response,
@@ -1,9 +1,9 @@
1
- const _mod_dh1qvy = require('./lru-cache.cjs');
2
- exports.LRUCache = _mod_dh1qvy.LRUCache;;
3
- const _mod_2s112k = require('./dns-cache.cjs');
4
- exports.DNSCache = _mod_2s112k.DNSCache;
5
- exports.getGlobalDNSCache = _mod_2s112k.getGlobalDNSCache;
6
- exports.resetGlobalDNSCache = _mod_2s112k.resetGlobalDNSCache;;
7
- const _mod_xow5u3 = require('./response-cache.cjs');
8
- exports.ResponseCache = _mod_xow5u3.ResponseCache;
9
- exports.normalizeResponseCacheConfig = _mod_xow5u3.normalizeResponseCacheConfig;;
1
+ const _mod_7sa5is = require('./lru-cache.cjs');
2
+ exports.LRUCache = _mod_7sa5is.LRUCache;;
3
+ const _mod_7a00cs = require('./dns-cache.cjs');
4
+ exports.DNSCache = _mod_7a00cs.DNSCache;
5
+ exports.getGlobalDNSCache = _mod_7a00cs.getGlobalDNSCache;
6
+ exports.resetGlobalDNSCache = _mod_7a00cs.resetGlobalDNSCache;;
7
+ const _mod_6w1tvn = require('./response-cache.cjs');
8
+ exports.ResponseCache = _mod_6w1tvn.ResponseCache;
9
+ exports.normalizeResponseCacheConfig = _mod_6w1tvn.normalizeResponseCacheConfig;;
@@ -24,7 +24,8 @@ function createDefaultHooks() {
24
24
  onDns: [],
25
25
  onTls: [],
26
26
  onTimeout: [],
27
- onAbort: []
27
+ onAbort: [],
28
+ onRateLimitWait: []
28
29
  };
29
30
  }
30
31
  function mergeHooks(base, overrides) {
@@ -55,7 +56,8 @@ function mergeHooks(base, overrides) {
55
56
  onDns: [...base.onDns, ...overrides.onDns || []],
56
57
  onTls: [...base.onTls, ...overrides.onTls || []],
57
58
  onTimeout: [...base.onTimeout, ...overrides.onTimeout || []],
58
- onAbort: [...base.onAbort, ...overrides.onAbort || []]
59
+ onAbort: [...base.onAbort, ...overrides.onAbort || []],
60
+ onRateLimitWait: [...base.onRateLimitWait, ...overrides.onRateLimitWait || []]
59
61
  };
60
62
  }
61
63
  function serializeHooks(hooks) {
@@ -24,7 +24,8 @@ export function createDefaultHooks() {
24
24
  onDns: [],
25
25
  onTls: [],
26
26
  onTimeout: [],
27
- onAbort: []
27
+ onAbort: [],
28
+ onRateLimitWait: []
28
29
  };
29
30
  }
30
31
  export function mergeHooks(base, overrides) {
@@ -55,7 +56,8 @@ export function mergeHooks(base, overrides) {
55
56
  onDns: [...base.onDns, ...overrides.onDns || []],
56
57
  onTls: [...base.onTls, ...overrides.onTls || []],
57
58
  onTimeout: [...base.onTimeout, ...overrides.onTimeout || []],
58
- onAbort: [...base.onAbort, ...overrides.onAbort || []]
59
+ onAbort: [...base.onAbort, ...overrides.onAbort || []],
60
+ onRateLimitWait: [...base.onRateLimitWait, ...overrides.onRateLimitWait || []]
59
61
  };
60
62
  }
61
63
  export function serializeHooks(hooks) {