rezo 1.0.18 → 1.0.20

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 (38) hide show
  1. package/dist/adapters/curl.cjs +39 -34
  2. package/dist/adapters/curl.js +39 -34
  3. package/dist/adapters/entries/curl.d.ts +29 -17
  4. package/dist/adapters/entries/fetch.d.ts +29 -17
  5. package/dist/adapters/entries/http.d.ts +29 -17
  6. package/dist/adapters/entries/http2.d.ts +29 -17
  7. package/dist/adapters/entries/react-native.d.ts +29 -17
  8. package/dist/adapters/entries/xhr.d.ts +29 -17
  9. package/dist/adapters/fetch.cjs +42 -41
  10. package/dist/adapters/fetch.js +42 -41
  11. package/dist/adapters/http.cjs +184 -73
  12. package/dist/adapters/http.js +184 -73
  13. package/dist/adapters/http2.cjs +41 -36
  14. package/dist/adapters/http2.js +41 -36
  15. package/dist/adapters/index.cjs +6 -6
  16. package/dist/adapters/react-native.cjs +41 -27
  17. package/dist/adapters/react-native.js +41 -27
  18. package/dist/adapters/xhr.cjs +43 -38
  19. package/dist/adapters/xhr.js +43 -38
  20. package/dist/cache/index.cjs +13 -13
  21. package/dist/crawler.d.ts +29 -17
  22. package/dist/entries/crawler.cjs +5 -5
  23. package/dist/index.cjs +24 -24
  24. package/dist/index.d.ts +29 -17
  25. package/dist/platform/browser.d.ts +29 -17
  26. package/dist/platform/bun.d.ts +29 -17
  27. package/dist/platform/deno.d.ts +29 -17
  28. package/dist/platform/node.d.ts +29 -17
  29. package/dist/platform/react-native.d.ts +29 -17
  30. package/dist/platform/worker.d.ts +29 -17
  31. package/dist/plugin/index.cjs +36 -36
  32. package/dist/proxy/index.cjs +2 -2
  33. package/dist/queue/index.cjs +8 -8
  34. package/dist/utils/http-config.cjs +8 -4
  35. package/dist/utils/http-config.js +8 -4
  36. package/dist/utils/timing.cjs +90 -0
  37. package/dist/utils/timing.js +78 -0
  38. package/package.json +1 -1
@@ -1815,24 +1815,26 @@ export interface RezoConfig {
1815
1815
  * - removeAllCookiesSync(): Clear all cookies
1816
1816
  */
1817
1817
  cookieJar: RezoCookieJar;
1818
- /** @description Comprehensive timing information */
1818
+ /** @description Comprehensive timing information (matches PerformanceResourceTiming API) */
1819
1819
  timing: {
1820
- /** @description Request start timestamp (absolute performance.now() value) */
1821
- startTimestamp: number;
1822
- /** @description Request end timestamp (absolute performance.now() value) */
1823
- endTimestamp: number;
1824
- /** @description DNS lookup duration in milliseconds */
1825
- dnsMs?: number;
1826
- /** @description TCP connection duration in milliseconds */
1827
- tcpMs?: number;
1828
- /** @description TLS handshake duration in milliseconds */
1829
- tlsMs?: number;
1830
- /** @description Time to first byte in milliseconds (from start to first response byte) */
1831
- ttfbMs?: number;
1832
- /** @description Content transfer duration in milliseconds */
1833
- transferMs?: number;
1834
- /** @description Total request duration in milliseconds (endTimestamp - startTimestamp) */
1835
- durationMs: number;
1820
+ /** @description Request start timestamp (performance.now() value when request began) */
1821
+ startTime: number;
1822
+ /** @description Timestamp when DNS lookup started */
1823
+ domainLookupStart: number;
1824
+ /** @description Timestamp when DNS lookup ended */
1825
+ domainLookupEnd: number;
1826
+ /** @description Timestamp when connection started */
1827
+ connectStart: number;
1828
+ /** @description Timestamp when TLS handshake started (0 for HTTP) */
1829
+ secureConnectionStart: number;
1830
+ /** @description Timestamp when connection completed */
1831
+ connectEnd: number;
1832
+ /** @description Timestamp when request was sent */
1833
+ requestStart: number;
1834
+ /** @description Timestamp when first byte of response received */
1835
+ responseStart: number;
1836
+ /** @description Timestamp when response completed */
1837
+ responseEnd: number;
1836
1838
  };
1837
1839
  /** @description Network connection information */
1838
1840
  network: {
@@ -1894,6 +1896,8 @@ export interface RezoConfig {
1894
1896
  };
1895
1897
  /** @description Debug mode flag */
1896
1898
  debug?: boolean;
1899
+ /** @description URL tracking mode flag - logs redirect chain and retries */
1900
+ trackUrl?: boolean;
1897
1901
  /** @description Request tracking identifier */
1898
1902
  requestId: string;
1899
1903
  /** @description Session identifier */
@@ -2753,6 +2757,12 @@ export interface RezoRequestConfig<D = any> {
2753
2757
  debug?: boolean;
2754
2758
  /** Enable verbose logging with detailed information */
2755
2759
  verbose?: boolean;
2760
+ /**
2761
+ * Enable URL tracking to log the complete redirect chain with status codes.
2762
+ * When enabled, logs: initial URL -> redirect URL (status code) -> final URL
2763
+ * Also logs retry attempts with their status codes.
2764
+ */
2765
+ trackUrl?: boolean;
2756
2766
  /** Name of the cookie containing XSRF token */
2757
2767
  xsrfCookieName?: string;
2758
2768
  /** Name of the header to send XSRF token in */
@@ -3234,6 +3244,8 @@ export interface RezoDefaultOptions {
3234
3244
  debug?: RezoHttpRequest["debug"];
3235
3245
  /** Enable verbose logging with detailed information */
3236
3246
  verbose?: RezoHttpRequest["verbose"];
3247
+ /** Enable URL tracking to log redirect chain and retry attempts */
3248
+ trackUrl?: RezoHttpRequest["trackUrl"];
3237
3249
  /** HTTP agent for HTTP requests */
3238
3250
  httpAgent?: RezoHttpRequest["httpAgent"];
3239
3251
  /** HTTPS agent for HTTPS requests */
@@ -28,6 +28,30 @@ const Environment = {
28
28
  return typeof AbortController !== "undefined";
29
29
  }
30
30
  };
31
+ function updateTiming(config, timing, bodySize) {
32
+ const now = performance.now();
33
+ config.timing.domainLookupStart = config.timing.startTime;
34
+ config.timing.domainLookupEnd = config.timing.startTime;
35
+ config.timing.connectStart = config.timing.startTime;
36
+ config.timing.secureConnectionStart = 0;
37
+ config.timing.connectEnd = config.timing.startTime;
38
+ config.timing.requestStart = config.timing.startTime;
39
+ config.timing.responseStart = timing.firstByteTime || config.timing.startTime;
40
+ config.timing.responseEnd = now;
41
+ config.transfer.bodySize = bodySize;
42
+ config.transfer.responseSize = bodySize;
43
+ }
44
+ function getTimingDurations(config) {
45
+ const t = config.timing;
46
+ return {
47
+ total: t.responseEnd - t.startTime,
48
+ dns: t.domainLookupEnd - t.domainLookupStart,
49
+ tcp: t.secureConnectionStart > 0 ? t.secureConnectionStart - t.connectStart : t.connectEnd - t.connectStart,
50
+ tls: t.secureConnectionStart > 0 ? t.connectEnd - t.secureConnectionStart : undefined,
51
+ firstByte: t.responseStart - t.startTime,
52
+ download: t.responseEnd - t.responseStart
53
+ };
54
+ }
31
55
  const responseCacheInstances = new Map;
32
56
  function getCacheConfigKey(option) {
33
57
  if (option === true)
@@ -314,10 +338,11 @@ async function executeFetchRequest(fetchOptions, config, options, perform, strea
314
338
  const maxRetries = config?.retry?.maxRetries || 0;
315
339
  const incrementDelay = config?.retry?.incrementDelay || false;
316
340
  const statusCodes = config?.retry?.statusCodes;
341
+ const startTime = performance.now();
317
342
  const timing = {
318
- startTime: performance.now(),
319
- startTimestamp: Date.now()
343
+ startTime
320
344
  };
345
+ config.timing.startTime = startTime;
321
346
  const ABSOLUTE_MAX_ATTEMPTS = 50;
322
347
  const visitedUrls = new Set;
323
348
  let totalAttempts = 0;
@@ -471,7 +496,6 @@ async function executeSingleFetchRequest(config, fetchOptions, requestCount, tim
471
496
  config.finalUrl = url.href;
472
497
  config.network.protocol = isSecure ? "https" : "http";
473
498
  config.network.httpVersion = undefined;
474
- config.timing.startTimestamp = timing.startTimestamp;
475
499
  if (!config.transfer) {
476
500
  config.transfer = { requestSize: 0, responseSize: 0, headerSize: 0, bodySize: 0 };
477
501
  } else if (config.transfer.requestSize === undefined) {
@@ -550,9 +574,9 @@ async function executeSingleFetchRequest(config, fetchOptions, requestCount, tim
550
574
  if (timeoutId)
551
575
  clearTimeout(timeoutId);
552
576
  }
553
- if (!config.timing.ttfbMs) {
577
+ if (!timing.firstByteTime) {
554
578
  timing.firstByteTime = performance.now();
555
- config.timing.ttfbMs = timing.firstByteTime - timing.startTime;
579
+ config.timing.responseStart = timing.firstByteTime;
556
580
  }
557
581
  const status = response.status;
558
582
  const statusText = response.statusText;
@@ -589,8 +613,8 @@ async function executeSingleFetchRequest(config, fetchOptions, requestCount, tim
589
613
  contentLength: contentLength ? parseInt(contentLength, 10) : undefined,
590
614
  cookies: cookies.array,
591
615
  timing: {
592
- firstByte: config.timing.ttfbMs,
593
- total: performance.now() - timing.startTime
616
+ firstByte: config.timing.responseStart - config.timing.startTime,
617
+ total: performance.now() - config.timing.startTime
594
618
  }
595
619
  };
596
620
  eventEmitter.emit("headers", headersEvent);
@@ -635,12 +659,8 @@ async function executeSingleFetchRequest(config, fetchOptions, requestCount, tim
635
659
  responseData = await response.text();
636
660
  }
637
661
  }
638
- config.timing.endTimestamp = Date.now();
639
- config.timing.durationMs = performance.now() - timing.startTime;
640
- config.timing.transferMs = timing.firstByteTime ? performance.now() - timing.firstByteTime : config.timing.durationMs;
641
662
  const bodySize = bodyBuffer?.byteLength || (typeof responseData === "string" ? responseData.length : 0);
642
- config.transfer.bodySize = bodySize;
643
- config.transfer.responseSize = bodySize;
663
+ updateTiming(config, timing, bodySize);
644
664
  config.status = status;
645
665
  config.statusText = statusText;
646
666
  _stats.statusOnNext = status >= 400 ? "error" : "success";
@@ -674,14 +694,10 @@ async function executeSingleFetchRequest(config, fetchOptions, requestCount, tim
674
694
  fileName: config.fileName,
675
695
  fileSize: buffer.length,
676
696
  timing: {
677
- total: config.timing.durationMs || 0,
678
- dns: config.timing.dnsMs,
679
- tcp: config.timing.tcpMs,
680
- tls: config.timing.tlsMs,
681
- firstByte: config.timing.ttfbMs,
682
- download: config.timing.transferMs || 0
697
+ ...getTimingDurations(config),
698
+ download: getTimingDurations(config).download || 0
683
699
  },
684
- averageSpeed: config.timing.transferMs ? buffer.length / config.timing.transferMs * 1000 : 0,
700
+ averageSpeed: getTimingDurations(config).download ? buffer.length / getTimingDurations(config).download * 1000 : 0,
685
701
  config: sanitizeConfig(config)
686
702
  };
687
703
  downloadResult.emit("finish", downloadFinishEvent);
@@ -720,16 +736,12 @@ async function executeSingleFetchRequest(config, fetchOptions, requestCount, tim
720
736
  urls: buildUrlTree(config, url.href),
721
737
  uploadSize: config.transfer.requestSize || 0,
722
738
  timing: {
723
- total: config.timing.durationMs || 0,
724
- dns: config.timing.dnsMs,
725
- tcp: config.timing.tcpMs,
726
- tls: config.timing.tlsMs,
727
- upload: config.timing.transferMs || 0,
728
- waiting: config.timing.ttfbMs || 0,
729
- download: config.timing.transferMs
739
+ ...getTimingDurations(config),
740
+ upload: getTimingDurations(config).firstByte || 0,
741
+ waiting: getTimingDurations(config).download > 0 && getTimingDurations(config).firstByte > 0 ? getTimingDurations(config).download - getTimingDurations(config).firstByte : 0
730
742
  },
731
- averageUploadSpeed: config.timing.transferMs ? (config.transfer.requestSize || 0) / config.timing.transferMs * 1000 : 0,
732
- averageDownloadSpeed: config.timing.transferMs ? bodySize / config.timing.transferMs * 1000 : 0,
743
+ averageUploadSpeed: getTimingDurations(config).firstByte && config.transfer.requestSize ? config.transfer.requestSize / getTimingDurations(config).firstByte * 1000 : 0,
744
+ averageDownloadSpeed: getTimingDurations(config).download ? bodySize / getTimingDurations(config).download * 1000 : 0,
733
745
  config: sanitizeConfig(config)
734
746
  };
735
747
  uploadResult.emit("finish", uploadFinishEvent);
@@ -777,11 +789,7 @@ async function handleStreamingResponse(response, config, timing, streamResult, u
777
789
  streamResult.emit("download-progress", progressEvent);
778
790
  }
779
791
  }
780
- config.timing.endTimestamp = Date.now();
781
- config.timing.durationMs = performance.now() - timing.startTime;
782
- config.timing.transferMs = timing.firstByteTime ? performance.now() - timing.firstByteTime : config.timing.durationMs;
783
- config.transfer.bodySize = bytesReceived;
784
- config.transfer.responseSize = bytesReceived;
792
+ updateTiming(config, timing, bytesReceived);
785
793
  const streamFinishEvent = {
786
794
  status,
787
795
  statusText,
@@ -791,14 +799,7 @@ async function handleStreamingResponse(response, config, timing, streamResult, u
791
799
  finalUrl: url.href,
792
800
  cookies,
793
801
  urls: buildUrlTree(config, url.href),
794
- timing: {
795
- total: config.timing.durationMs || 0,
796
- dns: config.timing.dnsMs,
797
- tcp: config.timing.tcpMs,
798
- tls: config.timing.tlsMs,
799
- firstByte: config.timing.ttfbMs,
800
- download: config.timing.transferMs
801
- },
802
+ timing: getTimingDurations(config),
802
803
  config: sanitizeConfig(config)
803
804
  };
804
805
  streamResult.emit("finish", streamFinishEvent);
@@ -28,6 +28,30 @@ const Environment = {
28
28
  return typeof AbortController !== "undefined";
29
29
  }
30
30
  };
31
+ function updateTiming(config, timing, bodySize) {
32
+ const now = performance.now();
33
+ config.timing.domainLookupStart = config.timing.startTime;
34
+ config.timing.domainLookupEnd = config.timing.startTime;
35
+ config.timing.connectStart = config.timing.startTime;
36
+ config.timing.secureConnectionStart = 0;
37
+ config.timing.connectEnd = config.timing.startTime;
38
+ config.timing.requestStart = config.timing.startTime;
39
+ config.timing.responseStart = timing.firstByteTime || config.timing.startTime;
40
+ config.timing.responseEnd = now;
41
+ config.transfer.bodySize = bodySize;
42
+ config.transfer.responseSize = bodySize;
43
+ }
44
+ function getTimingDurations(config) {
45
+ const t = config.timing;
46
+ return {
47
+ total: t.responseEnd - t.startTime,
48
+ dns: t.domainLookupEnd - t.domainLookupStart,
49
+ tcp: t.secureConnectionStart > 0 ? t.secureConnectionStart - t.connectStart : t.connectEnd - t.connectStart,
50
+ tls: t.secureConnectionStart > 0 ? t.connectEnd - t.secureConnectionStart : undefined,
51
+ firstByte: t.responseStart - t.startTime,
52
+ download: t.responseEnd - t.responseStart
53
+ };
54
+ }
31
55
  const responseCacheInstances = new Map;
32
56
  function getCacheConfigKey(option) {
33
57
  if (option === true)
@@ -314,10 +338,11 @@ async function executeFetchRequest(fetchOptions, config, options, perform, strea
314
338
  const maxRetries = config?.retry?.maxRetries || 0;
315
339
  const incrementDelay = config?.retry?.incrementDelay || false;
316
340
  const statusCodes = config?.retry?.statusCodes;
341
+ const startTime = performance.now();
317
342
  const timing = {
318
- startTime: performance.now(),
319
- startTimestamp: Date.now()
343
+ startTime
320
344
  };
345
+ config.timing.startTime = startTime;
321
346
  const ABSOLUTE_MAX_ATTEMPTS = 50;
322
347
  const visitedUrls = new Set;
323
348
  let totalAttempts = 0;
@@ -471,7 +496,6 @@ async function executeSingleFetchRequest(config, fetchOptions, requestCount, tim
471
496
  config.finalUrl = url.href;
472
497
  config.network.protocol = isSecure ? "https" : "http";
473
498
  config.network.httpVersion = undefined;
474
- config.timing.startTimestamp = timing.startTimestamp;
475
499
  if (!config.transfer) {
476
500
  config.transfer = { requestSize: 0, responseSize: 0, headerSize: 0, bodySize: 0 };
477
501
  } else if (config.transfer.requestSize === undefined) {
@@ -550,9 +574,9 @@ async function executeSingleFetchRequest(config, fetchOptions, requestCount, tim
550
574
  if (timeoutId)
551
575
  clearTimeout(timeoutId);
552
576
  }
553
- if (!config.timing.ttfbMs) {
577
+ if (!timing.firstByteTime) {
554
578
  timing.firstByteTime = performance.now();
555
- config.timing.ttfbMs = timing.firstByteTime - timing.startTime;
579
+ config.timing.responseStart = timing.firstByteTime;
556
580
  }
557
581
  const status = response.status;
558
582
  const statusText = response.statusText;
@@ -589,8 +613,8 @@ async function executeSingleFetchRequest(config, fetchOptions, requestCount, tim
589
613
  contentLength: contentLength ? parseInt(contentLength, 10) : undefined,
590
614
  cookies: cookies.array,
591
615
  timing: {
592
- firstByte: config.timing.ttfbMs,
593
- total: performance.now() - timing.startTime
616
+ firstByte: config.timing.responseStart - config.timing.startTime,
617
+ total: performance.now() - config.timing.startTime
594
618
  }
595
619
  };
596
620
  eventEmitter.emit("headers", headersEvent);
@@ -635,12 +659,8 @@ async function executeSingleFetchRequest(config, fetchOptions, requestCount, tim
635
659
  responseData = await response.text();
636
660
  }
637
661
  }
638
- config.timing.endTimestamp = Date.now();
639
- config.timing.durationMs = performance.now() - timing.startTime;
640
- config.timing.transferMs = timing.firstByteTime ? performance.now() - timing.firstByteTime : config.timing.durationMs;
641
662
  const bodySize = bodyBuffer?.byteLength || (typeof responseData === "string" ? responseData.length : 0);
642
- config.transfer.bodySize = bodySize;
643
- config.transfer.responseSize = bodySize;
663
+ updateTiming(config, timing, bodySize);
644
664
  config.status = status;
645
665
  config.statusText = statusText;
646
666
  _stats.statusOnNext = status >= 400 ? "error" : "success";
@@ -674,14 +694,10 @@ async function executeSingleFetchRequest(config, fetchOptions, requestCount, tim
674
694
  fileName: config.fileName,
675
695
  fileSize: buffer.length,
676
696
  timing: {
677
- total: config.timing.durationMs || 0,
678
- dns: config.timing.dnsMs,
679
- tcp: config.timing.tcpMs,
680
- tls: config.timing.tlsMs,
681
- firstByte: config.timing.ttfbMs,
682
- download: config.timing.transferMs || 0
697
+ ...getTimingDurations(config),
698
+ download: getTimingDurations(config).download || 0
683
699
  },
684
- averageSpeed: config.timing.transferMs ? buffer.length / config.timing.transferMs * 1000 : 0,
700
+ averageSpeed: getTimingDurations(config).download ? buffer.length / getTimingDurations(config).download * 1000 : 0,
685
701
  config: sanitizeConfig(config)
686
702
  };
687
703
  downloadResult.emit("finish", downloadFinishEvent);
@@ -720,16 +736,12 @@ async function executeSingleFetchRequest(config, fetchOptions, requestCount, tim
720
736
  urls: buildUrlTree(config, url.href),
721
737
  uploadSize: config.transfer.requestSize || 0,
722
738
  timing: {
723
- total: config.timing.durationMs || 0,
724
- dns: config.timing.dnsMs,
725
- tcp: config.timing.tcpMs,
726
- tls: config.timing.tlsMs,
727
- upload: config.timing.transferMs || 0,
728
- waiting: config.timing.ttfbMs || 0,
729
- download: config.timing.transferMs
739
+ ...getTimingDurations(config),
740
+ upload: getTimingDurations(config).firstByte || 0,
741
+ waiting: getTimingDurations(config).download > 0 && getTimingDurations(config).firstByte > 0 ? getTimingDurations(config).download - getTimingDurations(config).firstByte : 0
730
742
  },
731
- averageUploadSpeed: config.timing.transferMs ? (config.transfer.requestSize || 0) / config.timing.transferMs * 1000 : 0,
732
- averageDownloadSpeed: config.timing.transferMs ? bodySize / config.timing.transferMs * 1000 : 0,
743
+ averageUploadSpeed: getTimingDurations(config).firstByte && config.transfer.requestSize ? config.transfer.requestSize / getTimingDurations(config).firstByte * 1000 : 0,
744
+ averageDownloadSpeed: getTimingDurations(config).download ? bodySize / getTimingDurations(config).download * 1000 : 0,
733
745
  config: sanitizeConfig(config)
734
746
  };
735
747
  uploadResult.emit("finish", uploadFinishEvent);
@@ -777,11 +789,7 @@ async function handleStreamingResponse(response, config, timing, streamResult, u
777
789
  streamResult.emit("download-progress", progressEvent);
778
790
  }
779
791
  }
780
- config.timing.endTimestamp = Date.now();
781
- config.timing.durationMs = performance.now() - timing.startTime;
782
- config.timing.transferMs = timing.firstByteTime ? performance.now() - timing.firstByteTime : config.timing.durationMs;
783
- config.transfer.bodySize = bytesReceived;
784
- config.transfer.responseSize = bytesReceived;
792
+ updateTiming(config, timing, bytesReceived);
785
793
  const streamFinishEvent = {
786
794
  status,
787
795
  statusText,
@@ -791,14 +799,7 @@ async function handleStreamingResponse(response, config, timing, streamResult, u
791
799
  finalUrl: url.href,
792
800
  cookies,
793
801
  urls: buildUrlTree(config, url.href),
794
- timing: {
795
- total: config.timing.durationMs || 0,
796
- dns: config.timing.dnsMs,
797
- tcp: config.timing.tcpMs,
798
- tls: config.timing.tlsMs,
799
- firstByte: config.timing.ttfbMs,
800
- download: config.timing.transferMs
801
- },
802
+ timing: getTimingDurations(config),
802
803
  config: sanitizeConfig(config)
803
804
  };
804
805
  streamResult.emit("finish", streamFinishEvent);