rezo 1.0.107 → 1.0.108

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 (52) hide show
  1. package/dist/adapters/curl.cjs +138 -53
  2. package/dist/adapters/curl.js +138 -53
  3. package/dist/adapters/entries/curl.d.ts +82 -1
  4. package/dist/adapters/entries/fetch.d.ts +82 -1
  5. package/dist/adapters/entries/http.d.ts +82 -1
  6. package/dist/adapters/entries/http2.d.ts +82 -1
  7. package/dist/adapters/entries/react-native.d.ts +82 -1
  8. package/dist/adapters/entries/xhr.d.ts +82 -1
  9. package/dist/adapters/fetch.cjs +2 -1
  10. package/dist/adapters/fetch.js +2 -1
  11. package/dist/adapters/http.cjs +5 -2
  12. package/dist/adapters/http.js +5 -2
  13. package/dist/adapters/http2.cjs +2 -1
  14. package/dist/adapters/http2.js +2 -1
  15. package/dist/adapters/index.cjs +6 -6
  16. package/dist/adapters/react-native.cjs +2 -1
  17. package/dist/adapters/react-native.js +2 -1
  18. package/dist/adapters/xhr.cjs +2 -1
  19. package/dist/adapters/xhr.js +2 -1
  20. package/dist/cache/index.cjs +9 -9
  21. package/dist/cookies/cookie-jar.cjs +6 -5
  22. package/dist/cookies/cookie-jar.js +2 -1
  23. package/dist/cookies/index.cjs +10 -10
  24. package/dist/crawler/index.cjs +42 -42
  25. package/dist/crawler/plugin/index.cjs +1 -1
  26. package/dist/crawler.d.ts +81 -0
  27. package/dist/entries/crawler.cjs +6 -6
  28. package/dist/index.cjs +44 -44
  29. package/dist/index.d.ts +82 -1
  30. package/dist/internal/agents/index.cjs +14 -14
  31. package/dist/platform/browser.d.ts +82 -1
  32. package/dist/platform/bun.d.ts +82 -1
  33. package/dist/platform/deno.d.ts +82 -1
  34. package/dist/platform/node.d.ts +82 -1
  35. package/dist/platform/react-native.d.ts +82 -1
  36. package/dist/platform/worker.d.ts +82 -1
  37. package/dist/proxy/index.cjs +4 -4
  38. package/dist/queue/index.cjs +9 -9
  39. package/dist/responses/buildResponse.cjs +22 -8
  40. package/dist/responses/buildResponse.js +22 -8
  41. package/dist/responses/universal/index.cjs +11 -11
  42. package/dist/stealth/index.cjs +17 -17
  43. package/dist/stealth/profiles/index.cjs +10 -10
  44. package/dist/utils/headers.cjs +27 -2
  45. package/dist/utils/headers.js +27 -2
  46. package/dist/utils/http-config.cjs +28 -7
  47. package/dist/utils/http-config.js +28 -7
  48. package/dist/version.cjs +1 -1
  49. package/dist/version.js +1 -1
  50. package/dist/wget/index.cjs +51 -51
  51. package/dist/wget/index.d.ts +81 -0
  52. package/package.json +1 -1
@@ -10,7 +10,8 @@ const { buildSmartError, builErrorFromResponse } = require('../responses/buildEr
10
10
  const { RezoCookieJar, Cookie } = require('../cookies/cookie-jar.cjs');
11
11
  const RezoFormData = require('../utils/form-data.cjs');
12
12
  const { existsSync } = require("node:fs");
13
- const { getDefaultConfig, prepareHTTPOptions, calculateRetryDelay } = require('../utils/http-config.cjs');
13
+ const { getDefaultConfig, prepareHTTPOptions, calculateRetryDelay, shouldRetry } = require('../utils/http-config.cjs');
14
+ const { handleRateLimitWait, shouldWaitOnStatus } = require('../utils/rate-limit-wait.cjs');
14
15
  const { RezoHeaders } = require('../utils/headers.cjs');
15
16
  const { StreamResponse } = require('../responses/stream.cjs');
16
17
  const { DownloadResponse } = require('../responses/download.cjs');
@@ -2288,9 +2289,20 @@ async function executeRequest(options, defaultOptions, jar) {
2288
2289
  }
2289
2290
  }
2290
2291
  const executor = new CurlExecutor;
2291
- try {
2292
- const result = await executor.execute(config, originalRequest, streamResponse, downloadResponse, uploadResponse);
2293
- if (!streamResponse && !downloadResponse && !uploadResponse) {
2292
+ const retryConfig = config.retry;
2293
+ let retryAttempt = 0;
2294
+ const ABSOLUTE_MAX_ATTEMPTS = 50;
2295
+ let totalAttempts = 0;
2296
+ while (true) {
2297
+ totalAttempts++;
2298
+ if (totalAttempts > ABSOLUTE_MAX_ATTEMPTS) {
2299
+ throw new RezoError(`Absolute maximum attempts (${ABSOLUTE_MAX_ATTEMPTS}) exceeded. This prevents infinite loops from retries and redirects.`, config, "ERR_MAX_ATTEMPTS", originalRequest);
2300
+ }
2301
+ try {
2302
+ const result = await executor.execute(config, originalRequest, streamResponse, downloadResponse, uploadResponse);
2303
+ if (streamResponse || downloadResponse || uploadResponse) {
2304
+ return result;
2305
+ }
2294
2306
  const response = result;
2295
2307
  if (proxyManager && selectedProxy) {
2296
2308
  proxyManager.reportSuccess(selectedProxy);
@@ -2317,67 +2329,140 @@ async function executeRequest(options, defaultOptions, jar) {
2317
2329
  }
2318
2330
  }
2319
2331
  debugLog.complete(config, response.finalUrl || requestUrl, config.redirectHistory?.length || 0, duration);
2320
- if (response.status >= 400) {
2332
+ const _validateStatus = originalRequest.validateStatus ?? ((s) => s >= 200 && s < 300);
2333
+ if (originalRequest.validateStatus !== null && !_validateStatus(response.status)) {
2334
+ if (shouldWaitOnStatus(response.status, options.waitOnStatus)) {
2335
+ const rateLimitWaitAttempt = config._rateLimitWaitAttempt || 0;
2336
+ const waitResult = await handleRateLimitWait({
2337
+ status: response.status,
2338
+ headers: response.headers,
2339
+ data: response.data,
2340
+ url: requestUrl,
2341
+ method,
2342
+ config,
2343
+ options,
2344
+ currentWaitAttempt: rateLimitWaitAttempt
2345
+ });
2346
+ if (waitResult.shouldRetry) {
2347
+ config._rateLimitWaitAttempt = waitResult.waitAttempt;
2348
+ continue;
2349
+ }
2350
+ }
2321
2351
  const httpError = builErrorFromResponse(`Request failed with status code ${response.status}`, response, config, originalRequest);
2322
- if (config.retry) {
2323
- const maxRetries = config.retry.maxRetries || 0;
2324
- const statusCodes = config.retry.statusCodes || [408, 429, 500, 502, 503, 504];
2325
- if (statusCodes.includes(response.status)) {
2326
- if (config.retryAttempts < maxRetries) {
2327
- config.retryAttempts++;
2328
- if (!config.errors)
2329
- config.errors = [];
2330
- config.errors.push({
2331
- attempt: config.retryAttempts,
2332
- error: httpError,
2333
- duration: perform.now()
2334
- });
2335
- perform.reset();
2336
- const delay = calculateRetryDelay(config.retryAttempts, config.retry.retryDelay, config.retry.backoff, config.retry.maxDelay);
2337
- debugLog.retry(config, config.retryAttempts, maxRetries, response.status, delay);
2338
- if (config.hooks?.beforeRetry && config.hooks.beforeRetry.length > 0) {
2339
- for (const hook of config.hooks.beforeRetry) {
2340
- await hook(config, httpError, config.retryAttempts);
2341
- }
2352
+ if (retryConfig) {
2353
+ retryAttempt++;
2354
+ if (retryConfig.condition) {
2355
+ const shouldContinue = await retryConfig.condition(httpError, retryAttempt);
2356
+ if (shouldContinue === false) {
2357
+ if (retryConfig.onRetryExhausted) {
2358
+ await retryConfig.onRetryExhausted(httpError, retryAttempt);
2342
2359
  }
2343
- if (delay > 0) {
2344
- await new Promise((resolve) => setTimeout(resolve, delay));
2360
+ throw httpError;
2361
+ }
2362
+ } else {
2363
+ const canRetry = shouldRetry(httpError, retryAttempt, method, retryConfig);
2364
+ if (!canRetry) {
2365
+ if (retryAttempt > retryConfig.maxRetries) {
2366
+ debugLog.maxRetries(config, retryConfig.maxRetries);
2367
+ if (retryConfig.onRetryExhausted) {
2368
+ await retryConfig.onRetryExhausted(httpError, retryAttempt);
2369
+ }
2345
2370
  }
2346
- return executeRequest(options, defaultOptions, jar);
2371
+ throw httpError;
2372
+ }
2373
+ }
2374
+ if (!config.errors)
2375
+ config.errors = [];
2376
+ config.errors.push({
2377
+ attempt: retryAttempt,
2378
+ error: httpError,
2379
+ duration: perform.now()
2380
+ });
2381
+ perform.reset();
2382
+ const currentDelay = calculateRetryDelay(retryAttempt, retryConfig.retryDelay, retryConfig.backoff, retryConfig.maxDelay);
2383
+ debugLog.retry(config, retryAttempt, retryConfig.maxRetries, response.status, currentDelay);
2384
+ if (retryConfig.onRetry) {
2385
+ const shouldProceed = await retryConfig.onRetry(httpError, retryAttempt, currentDelay);
2386
+ if (shouldProceed === false) {
2387
+ throw httpError;
2347
2388
  }
2348
2389
  }
2349
- debugLog.maxRetries(config, maxRetries);
2390
+ if (config.hooks?.beforeRetry && config.hooks.beforeRetry.length > 0) {
2391
+ for (const hook of config.hooks.beforeRetry) {
2392
+ await hook(config, httpError, retryAttempt);
2393
+ }
2394
+ }
2395
+ if (currentDelay > 0) {
2396
+ await new Promise((resolve) => setTimeout(resolve, currentDelay));
2397
+ }
2398
+ config.retryAttempts++;
2399
+ continue;
2350
2400
  }
2351
2401
  throw httpError;
2352
2402
  }
2353
- }
2354
- return result;
2355
- } catch (error) {
2356
- if (proxyManager && selectedProxy) {
2357
- proxyManager.reportFailure(selectedProxy, error);
2358
- if (proxyManager.config.retryWithNextProxy) {
2359
- const maxRetries = proxyManager.config.maxProxyRetries ?? 3;
2360
- const attempt = (config._proxyRetryCount ?? 0) + 1;
2361
- if (attempt <= maxRetries) {
2362
- config._proxyRetryCount = attempt;
2363
- const retryUrl = typeof originalRequest.url === "string" ? originalRequest.url : originalRequest.url?.toString() || "";
2364
- const nextProxy = proxyManager.next(retryUrl);
2365
- if (nextProxy) {
2366
- options.proxy = {
2367
- protocol: nextProxy.protocol,
2368
- host: nextProxy.host,
2369
- port: nextProxy.port,
2370
- auth: nextProxy.auth
2371
- };
2372
- return executeRequest(options, defaultOptions, jar);
2403
+ return result;
2404
+ } catch (error) {
2405
+ if (error instanceof RezoError) {
2406
+ if (retryConfig && !retryConfig.condition) {
2407
+ const errorCode = error.code ?? error.cause?.code;
2408
+ const isRetryableError = errorCode && shouldRetry(error, retryAttempt + 1, method, retryConfig);
2409
+ if (isRetryableError) {
2410
+ retryAttempt++;
2411
+ if (!config.errors)
2412
+ config.errors = [];
2413
+ config.errors.push({
2414
+ attempt: retryAttempt,
2415
+ error,
2416
+ duration: perform.now()
2417
+ });
2418
+ perform.reset();
2419
+ const currentDelay = calculateRetryDelay(retryAttempt, retryConfig.retryDelay, retryConfig.backoff, retryConfig.maxDelay);
2420
+ debugLog.retry(config, retryAttempt, retryConfig.maxRetries, 0, currentDelay);
2421
+ if (retryConfig.onRetry) {
2422
+ const shouldProceed = await retryConfig.onRetry(error, retryAttempt, currentDelay);
2423
+ if (shouldProceed === false)
2424
+ throw error;
2425
+ }
2426
+ if (config.hooks?.beforeRetry && config.hooks.beforeRetry.length > 0) {
2427
+ for (const hook of config.hooks.beforeRetry) {
2428
+ await hook(config, error, retryAttempt);
2429
+ }
2430
+ }
2431
+ if (currentDelay > 0) {
2432
+ await new Promise((resolve) => setTimeout(resolve, currentDelay));
2433
+ }
2434
+ config.retryAttempts++;
2435
+ continue;
2373
2436
  }
2374
2437
  }
2438
+ if (proxyManager && selectedProxy) {
2439
+ proxyManager.reportFailure(selectedProxy, error);
2440
+ if (proxyManager.config.retryWithNextProxy) {
2441
+ const maxProxyRetries = proxyManager.config.maxProxyRetries ?? 3;
2442
+ const proxyAttempt = (config._proxyRetryCount ?? 0) + 1;
2443
+ if (proxyAttempt <= maxProxyRetries) {
2444
+ config._proxyRetryCount = proxyAttempt;
2445
+ const retryUrl = typeof originalRequest.url === "string" ? originalRequest.url : originalRequest.url?.toString() || "";
2446
+ const nextProxy = proxyManager.next(retryUrl);
2447
+ if (nextProxy) {
2448
+ originalRequest.proxy = {
2449
+ protocol: nextProxy.protocol,
2450
+ host: nextProxy.host,
2451
+ port: nextProxy.port,
2452
+ auth: nextProxy.auth
2453
+ };
2454
+ continue;
2455
+ }
2456
+ }
2457
+ }
2458
+ }
2459
+ throw error;
2375
2460
  }
2461
+ if (proxyManager && selectedProxy) {
2462
+ proxyManager.reportFailure(selectedProxy, error);
2463
+ }
2464
+ throw buildSmartError(config, originalRequest, error);
2376
2465
  }
2377
- if (error instanceof RezoError) {
2378
- throw error;
2379
- }
2380
- throw buildSmartError(config, originalRequest, error);
2381
2466
  }
2382
2467
  }
2383
2468
 
@@ -10,7 +10,8 @@ import { buildSmartError, builErrorFromResponse } from '../responses/buildError.
10
10
  import { RezoCookieJar, Cookie } from '../cookies/cookie-jar.js';
11
11
  import RezoFormData from '../utils/form-data.js';
12
12
  import { existsSync } from "node:fs";
13
- import { getDefaultConfig, prepareHTTPOptions, calculateRetryDelay } from '../utils/http-config.js';
13
+ import { getDefaultConfig, prepareHTTPOptions, calculateRetryDelay, shouldRetry } from '../utils/http-config.js';
14
+ import { handleRateLimitWait, shouldWaitOnStatus } from '../utils/rate-limit-wait.js';
14
15
  import { RezoHeaders } from '../utils/headers.js';
15
16
  import { StreamResponse } from '../responses/stream.js';
16
17
  import { DownloadResponse } from '../responses/download.js';
@@ -2288,9 +2289,20 @@ export async function executeRequest(options, defaultOptions, jar) {
2288
2289
  }
2289
2290
  }
2290
2291
  const executor = new CurlExecutor;
2291
- try {
2292
- const result = await executor.execute(config, originalRequest, streamResponse, downloadResponse, uploadResponse);
2293
- if (!streamResponse && !downloadResponse && !uploadResponse) {
2292
+ const retryConfig = config.retry;
2293
+ let retryAttempt = 0;
2294
+ const ABSOLUTE_MAX_ATTEMPTS = 50;
2295
+ let totalAttempts = 0;
2296
+ while (true) {
2297
+ totalAttempts++;
2298
+ if (totalAttempts > ABSOLUTE_MAX_ATTEMPTS) {
2299
+ throw new RezoError(`Absolute maximum attempts (${ABSOLUTE_MAX_ATTEMPTS}) exceeded. This prevents infinite loops from retries and redirects.`, config, "ERR_MAX_ATTEMPTS", originalRequest);
2300
+ }
2301
+ try {
2302
+ const result = await executor.execute(config, originalRequest, streamResponse, downloadResponse, uploadResponse);
2303
+ if (streamResponse || downloadResponse || uploadResponse) {
2304
+ return result;
2305
+ }
2294
2306
  const response = result;
2295
2307
  if (proxyManager && selectedProxy) {
2296
2308
  proxyManager.reportSuccess(selectedProxy);
@@ -2317,67 +2329,140 @@ export async function executeRequest(options, defaultOptions, jar) {
2317
2329
  }
2318
2330
  }
2319
2331
  debugLog.complete(config, response.finalUrl || requestUrl, config.redirectHistory?.length || 0, duration);
2320
- if (response.status >= 400) {
2332
+ const _validateStatus = originalRequest.validateStatus ?? ((s) => s >= 200 && s < 300);
2333
+ if (originalRequest.validateStatus !== null && !_validateStatus(response.status)) {
2334
+ if (shouldWaitOnStatus(response.status, options.waitOnStatus)) {
2335
+ const rateLimitWaitAttempt = config._rateLimitWaitAttempt || 0;
2336
+ const waitResult = await handleRateLimitWait({
2337
+ status: response.status,
2338
+ headers: response.headers,
2339
+ data: response.data,
2340
+ url: requestUrl,
2341
+ method,
2342
+ config,
2343
+ options,
2344
+ currentWaitAttempt: rateLimitWaitAttempt
2345
+ });
2346
+ if (waitResult.shouldRetry) {
2347
+ config._rateLimitWaitAttempt = waitResult.waitAttempt;
2348
+ continue;
2349
+ }
2350
+ }
2321
2351
  const httpError = builErrorFromResponse(`Request failed with status code ${response.status}`, response, config, originalRequest);
2322
- if (config.retry) {
2323
- const maxRetries = config.retry.maxRetries || 0;
2324
- const statusCodes = config.retry.statusCodes || [408, 429, 500, 502, 503, 504];
2325
- if (statusCodes.includes(response.status)) {
2326
- if (config.retryAttempts < maxRetries) {
2327
- config.retryAttempts++;
2328
- if (!config.errors)
2329
- config.errors = [];
2330
- config.errors.push({
2331
- attempt: config.retryAttempts,
2332
- error: httpError,
2333
- duration: perform.now()
2334
- });
2335
- perform.reset();
2336
- const delay = calculateRetryDelay(config.retryAttempts, config.retry.retryDelay, config.retry.backoff, config.retry.maxDelay);
2337
- debugLog.retry(config, config.retryAttempts, maxRetries, response.status, delay);
2338
- if (config.hooks?.beforeRetry && config.hooks.beforeRetry.length > 0) {
2339
- for (const hook of config.hooks.beforeRetry) {
2340
- await hook(config, httpError, config.retryAttempts);
2341
- }
2352
+ if (retryConfig) {
2353
+ retryAttempt++;
2354
+ if (retryConfig.condition) {
2355
+ const shouldContinue = await retryConfig.condition(httpError, retryAttempt);
2356
+ if (shouldContinue === false) {
2357
+ if (retryConfig.onRetryExhausted) {
2358
+ await retryConfig.onRetryExhausted(httpError, retryAttempt);
2342
2359
  }
2343
- if (delay > 0) {
2344
- await new Promise((resolve) => setTimeout(resolve, delay));
2360
+ throw httpError;
2361
+ }
2362
+ } else {
2363
+ const canRetry = shouldRetry(httpError, retryAttempt, method, retryConfig);
2364
+ if (!canRetry) {
2365
+ if (retryAttempt > retryConfig.maxRetries) {
2366
+ debugLog.maxRetries(config, retryConfig.maxRetries);
2367
+ if (retryConfig.onRetryExhausted) {
2368
+ await retryConfig.onRetryExhausted(httpError, retryAttempt);
2369
+ }
2345
2370
  }
2346
- return executeRequest(options, defaultOptions, jar);
2371
+ throw httpError;
2372
+ }
2373
+ }
2374
+ if (!config.errors)
2375
+ config.errors = [];
2376
+ config.errors.push({
2377
+ attempt: retryAttempt,
2378
+ error: httpError,
2379
+ duration: perform.now()
2380
+ });
2381
+ perform.reset();
2382
+ const currentDelay = calculateRetryDelay(retryAttempt, retryConfig.retryDelay, retryConfig.backoff, retryConfig.maxDelay);
2383
+ debugLog.retry(config, retryAttempt, retryConfig.maxRetries, response.status, currentDelay);
2384
+ if (retryConfig.onRetry) {
2385
+ const shouldProceed = await retryConfig.onRetry(httpError, retryAttempt, currentDelay);
2386
+ if (shouldProceed === false) {
2387
+ throw httpError;
2347
2388
  }
2348
2389
  }
2349
- debugLog.maxRetries(config, maxRetries);
2390
+ if (config.hooks?.beforeRetry && config.hooks.beforeRetry.length > 0) {
2391
+ for (const hook of config.hooks.beforeRetry) {
2392
+ await hook(config, httpError, retryAttempt);
2393
+ }
2394
+ }
2395
+ if (currentDelay > 0) {
2396
+ await new Promise((resolve) => setTimeout(resolve, currentDelay));
2397
+ }
2398
+ config.retryAttempts++;
2399
+ continue;
2350
2400
  }
2351
2401
  throw httpError;
2352
2402
  }
2353
- }
2354
- return result;
2355
- } catch (error) {
2356
- if (proxyManager && selectedProxy) {
2357
- proxyManager.reportFailure(selectedProxy, error);
2358
- if (proxyManager.config.retryWithNextProxy) {
2359
- const maxRetries = proxyManager.config.maxProxyRetries ?? 3;
2360
- const attempt = (config._proxyRetryCount ?? 0) + 1;
2361
- if (attempt <= maxRetries) {
2362
- config._proxyRetryCount = attempt;
2363
- const retryUrl = typeof originalRequest.url === "string" ? originalRequest.url : originalRequest.url?.toString() || "";
2364
- const nextProxy = proxyManager.next(retryUrl);
2365
- if (nextProxy) {
2366
- options.proxy = {
2367
- protocol: nextProxy.protocol,
2368
- host: nextProxy.host,
2369
- port: nextProxy.port,
2370
- auth: nextProxy.auth
2371
- };
2372
- return executeRequest(options, defaultOptions, jar);
2403
+ return result;
2404
+ } catch (error) {
2405
+ if (error instanceof RezoError) {
2406
+ if (retryConfig && !retryConfig.condition) {
2407
+ const errorCode = error.code ?? error.cause?.code;
2408
+ const isRetryableError = errorCode && shouldRetry(error, retryAttempt + 1, method, retryConfig);
2409
+ if (isRetryableError) {
2410
+ retryAttempt++;
2411
+ if (!config.errors)
2412
+ config.errors = [];
2413
+ config.errors.push({
2414
+ attempt: retryAttempt,
2415
+ error,
2416
+ duration: perform.now()
2417
+ });
2418
+ perform.reset();
2419
+ const currentDelay = calculateRetryDelay(retryAttempt, retryConfig.retryDelay, retryConfig.backoff, retryConfig.maxDelay);
2420
+ debugLog.retry(config, retryAttempt, retryConfig.maxRetries, 0, currentDelay);
2421
+ if (retryConfig.onRetry) {
2422
+ const shouldProceed = await retryConfig.onRetry(error, retryAttempt, currentDelay);
2423
+ if (shouldProceed === false)
2424
+ throw error;
2425
+ }
2426
+ if (config.hooks?.beforeRetry && config.hooks.beforeRetry.length > 0) {
2427
+ for (const hook of config.hooks.beforeRetry) {
2428
+ await hook(config, error, retryAttempt);
2429
+ }
2430
+ }
2431
+ if (currentDelay > 0) {
2432
+ await new Promise((resolve) => setTimeout(resolve, currentDelay));
2433
+ }
2434
+ config.retryAttempts++;
2435
+ continue;
2373
2436
  }
2374
2437
  }
2438
+ if (proxyManager && selectedProxy) {
2439
+ proxyManager.reportFailure(selectedProxy, error);
2440
+ if (proxyManager.config.retryWithNextProxy) {
2441
+ const maxProxyRetries = proxyManager.config.maxProxyRetries ?? 3;
2442
+ const proxyAttempt = (config._proxyRetryCount ?? 0) + 1;
2443
+ if (proxyAttempt <= maxProxyRetries) {
2444
+ config._proxyRetryCount = proxyAttempt;
2445
+ const retryUrl = typeof originalRequest.url === "string" ? originalRequest.url : originalRequest.url?.toString() || "";
2446
+ const nextProxy = proxyManager.next(retryUrl);
2447
+ if (nextProxy) {
2448
+ originalRequest.proxy = {
2449
+ protocol: nextProxy.protocol,
2450
+ host: nextProxy.host,
2451
+ port: nextProxy.port,
2452
+ auth: nextProxy.auth
2453
+ };
2454
+ continue;
2455
+ }
2456
+ }
2457
+ }
2458
+ }
2459
+ throw error;
2375
2460
  }
2461
+ if (proxyManager && selectedProxy) {
2462
+ proxyManager.reportFailure(selectedProxy, error);
2463
+ }
2464
+ throw buildSmartError(config, originalRequest, error);
2376
2465
  }
2377
- if (error instanceof RezoError) {
2378
- throw error;
2379
- }
2380
- throw buildSmartError(config, originalRequest, error);
2381
2466
  }
2382
2467
  }
2383
2468
 
@@ -2921,6 +2921,23 @@ export interface RezoDefaultOptions {
2921
2921
  * ```
2922
2922
  */
2923
2923
  proxyManager?: ProxyManager | ProxyManagerConfig;
2924
+ /**
2925
+ * Determines whether a given HTTP status code should be treated as successful.
2926
+ * When a status code fails validation, the request throws an error.
2927
+ *
2928
+ * @default (status) => status >= 200 && status < 300
2929
+ */
2930
+ validateStatus?: ((status: number) => boolean) | null;
2931
+ /**
2932
+ * Custom function to serialize URL query parameters.
2933
+ * Replaces the default serialization logic.
2934
+ */
2935
+ paramsSerializer?: (params: Record<string, any>) => string;
2936
+ /**
2937
+ * Custom DNS lookup function for hostname resolution.
2938
+ * Replaces the default `dns.lookup` used by Node.js.
2939
+ */
2940
+ dnsLookup?: RezoHttpRequest["dnsLookup"];
2924
2941
  /** Browser fingerprint stealth configuration (instance-level only) */
2925
2942
  stealth?: RezoStealth;
2926
2943
  }
@@ -3302,6 +3319,16 @@ export interface RezoConfig {
3302
3319
  onRedirect?: RezoRequestConfig["beforeRedirect"];
3303
3320
  /** Character encoding for request body and response data */
3304
3321
  encoding?: BufferEncoding;
3322
+ /**
3323
+ * Determines whether a given HTTP status code should be treated as successful.
3324
+ * Used by adapters to decide if a response is an error.
3325
+ * @default (status) => status >= 200 && status < 300
3326
+ */
3327
+ validateStatus?: ((status: number) => boolean) | null;
3328
+ /**
3329
+ * Custom DNS lookup function for hostname resolution.
3330
+ */
3331
+ dnsLookup?: (hostname: string, options: any, callback: (err: Error | null, address: string, family: number) => void) => void;
3305
3332
  /**
3306
3333
  * Whether to use cookies for the request
3307
3334
  */
@@ -4117,6 +4144,60 @@ export interface RezoRequestConfig<D = any> {
4117
4144
  };
4118
4145
  /** Character encoding for request body and response data */
4119
4146
  encoding?: BufferEncoding;
4147
+ /**
4148
+ * Determines whether a given HTTP status code should be treated as a successful response.
4149
+ * When a status code fails validation, the request will throw an error.
4150
+ *
4151
+ * @param status - The HTTP response status code
4152
+ * @returns true if the status code should be considered successful
4153
+ *
4154
+ * @default (status) => status >= 200 && status < 300
4155
+ *
4156
+ * @example
4157
+ * ```typescript
4158
+ * // Accept all 2xx and 3xx as success
4159
+ * validateStatus: (status) => status >= 200 && status < 400
4160
+ *
4161
+ * // Never throw on any status code
4162
+ * validateStatus: () => true
4163
+ *
4164
+ * // Only accept 200
4165
+ * validateStatus: (status) => status === 200
4166
+ * ```
4167
+ */
4168
+ validateStatus?: ((status: number) => boolean) | null;
4169
+ /**
4170
+ * Custom function to serialize URL query parameters.
4171
+ * When provided, this replaces the default serialization logic.
4172
+ *
4173
+ * @param params - The params object to serialize
4174
+ * @returns The serialized query string (without leading '?')
4175
+ *
4176
+ * @example
4177
+ * ```typescript
4178
+ * // Custom array serialization
4179
+ * paramsSerializer: (params) => qs.stringify(params, { arrayFormat: 'brackets' })
4180
+ *
4181
+ * // Simple key=value pairs
4182
+ * paramsSerializer: (params) =>
4183
+ * Object.entries(params).map(([k, v]) => `${k}=${v}`).join('&')
4184
+ * ```
4185
+ */
4186
+ paramsSerializer?: (params: Record<string, any>) => string;
4187
+ /**
4188
+ * Custom DNS lookup function for hostname resolution.
4189
+ * Replaces the default `dns.lookup` used by Node.js for this request.
4190
+ *
4191
+ * @example
4192
+ * ```typescript
4193
+ * import { lookup } from 'node:dns/promises';
4194
+ * dnsLookup: (hostname, options, callback) => {
4195
+ * // Custom DNS resolution logic
4196
+ * lookup(hostname).then(result => callback(null, result.address, result.family));
4197
+ * }
4198
+ * ```
4199
+ */
4200
+ dnsLookup?: (hostname: string, options: any, callback: (err: Error | null, address: string, family: number) => void) => void;
4120
4201
  /**
4121
4202
  * Request lifecycle hooks for intercepting and modifying request/response behavior.
4122
4203
  * Optional hooks that will be merged with default hooks during request processing.
@@ -5591,7 +5672,7 @@ export interface RezoInstance extends Rezo, RezoCallable {
5591
5672
  *
5592
5673
  * IMPORTANT: Update these values when bumping package version.
5593
5674
  */
5594
- export declare const VERSION = "1.0.107";
5675
+ export declare const VERSION = "1.0.108";
5595
5676
  /**
5596
5677
  * cURL Options Configuration
5597
5678
  *
@@ -2921,6 +2921,23 @@ export interface RezoDefaultOptions {
2921
2921
  * ```
2922
2922
  */
2923
2923
  proxyManager?: ProxyManager | ProxyManagerConfig;
2924
+ /**
2925
+ * Determines whether a given HTTP status code should be treated as successful.
2926
+ * When a status code fails validation, the request throws an error.
2927
+ *
2928
+ * @default (status) => status >= 200 && status < 300
2929
+ */
2930
+ validateStatus?: ((status: number) => boolean) | null;
2931
+ /**
2932
+ * Custom function to serialize URL query parameters.
2933
+ * Replaces the default serialization logic.
2934
+ */
2935
+ paramsSerializer?: (params: Record<string, any>) => string;
2936
+ /**
2937
+ * Custom DNS lookup function for hostname resolution.
2938
+ * Replaces the default `dns.lookup` used by Node.js.
2939
+ */
2940
+ dnsLookup?: RezoHttpRequest["dnsLookup"];
2924
2941
  /** Browser fingerprint stealth configuration (instance-level only) */
2925
2942
  stealth?: RezoStealth;
2926
2943
  }
@@ -3302,6 +3319,16 @@ export interface RezoConfig {
3302
3319
  onRedirect?: RezoRequestConfig["beforeRedirect"];
3303
3320
  /** Character encoding for request body and response data */
3304
3321
  encoding?: BufferEncoding;
3322
+ /**
3323
+ * Determines whether a given HTTP status code should be treated as successful.
3324
+ * Used by adapters to decide if a response is an error.
3325
+ * @default (status) => status >= 200 && status < 300
3326
+ */
3327
+ validateStatus?: ((status: number) => boolean) | null;
3328
+ /**
3329
+ * Custom DNS lookup function for hostname resolution.
3330
+ */
3331
+ dnsLookup?: (hostname: string, options: any, callback: (err: Error | null, address: string, family: number) => void) => void;
3305
3332
  /**
3306
3333
  * Whether to use cookies for the request
3307
3334
  */
@@ -4117,6 +4144,60 @@ export interface RezoRequestConfig<D = any> {
4117
4144
  };
4118
4145
  /** Character encoding for request body and response data */
4119
4146
  encoding?: BufferEncoding;
4147
+ /**
4148
+ * Determines whether a given HTTP status code should be treated as a successful response.
4149
+ * When a status code fails validation, the request will throw an error.
4150
+ *
4151
+ * @param status - The HTTP response status code
4152
+ * @returns true if the status code should be considered successful
4153
+ *
4154
+ * @default (status) => status >= 200 && status < 300
4155
+ *
4156
+ * @example
4157
+ * ```typescript
4158
+ * // Accept all 2xx and 3xx as success
4159
+ * validateStatus: (status) => status >= 200 && status < 400
4160
+ *
4161
+ * // Never throw on any status code
4162
+ * validateStatus: () => true
4163
+ *
4164
+ * // Only accept 200
4165
+ * validateStatus: (status) => status === 200
4166
+ * ```
4167
+ */
4168
+ validateStatus?: ((status: number) => boolean) | null;
4169
+ /**
4170
+ * Custom function to serialize URL query parameters.
4171
+ * When provided, this replaces the default serialization logic.
4172
+ *
4173
+ * @param params - The params object to serialize
4174
+ * @returns The serialized query string (without leading '?')
4175
+ *
4176
+ * @example
4177
+ * ```typescript
4178
+ * // Custom array serialization
4179
+ * paramsSerializer: (params) => qs.stringify(params, { arrayFormat: 'brackets' })
4180
+ *
4181
+ * // Simple key=value pairs
4182
+ * paramsSerializer: (params) =>
4183
+ * Object.entries(params).map(([k, v]) => `${k}=${v}`).join('&')
4184
+ * ```
4185
+ */
4186
+ paramsSerializer?: (params: Record<string, any>) => string;
4187
+ /**
4188
+ * Custom DNS lookup function for hostname resolution.
4189
+ * Replaces the default `dns.lookup` used by Node.js for this request.
4190
+ *
4191
+ * @example
4192
+ * ```typescript
4193
+ * import { lookup } from 'node:dns/promises';
4194
+ * dnsLookup: (hostname, options, callback) => {
4195
+ * // Custom DNS resolution logic
4196
+ * lookup(hostname).then(result => callback(null, result.address, result.family));
4197
+ * }
4198
+ * ```
4199
+ */
4200
+ dnsLookup?: (hostname: string, options: any, callback: (err: Error | null, address: string, family: number) => void) => void;
4120
4201
  /**
4121
4202
  * Request lifecycle hooks for intercepting and modifying request/response behavior.
4122
4203
  * Optional hooks that will be merged with default hooks during request processing.
@@ -5591,7 +5672,7 @@ export interface RezoInstance extends Rezo, RezoCallable {
5591
5672
  *
5592
5673
  * IMPORTANT: Update these values when bumping package version.
5593
5674
  */
5594
- export declare const VERSION = "1.0.107";
5675
+ export declare const VERSION = "1.0.108";
5595
5676
  export declare const isRezoError: typeof RezoError.isRezoError;
5596
5677
  export declare const Cancel: typeof RezoError;
5597
5678
  export declare const CancelToken: {