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
@@ -200,6 +200,30 @@ class Http2SessionPool {
200
200
  this.closeAllSessions();
201
201
  }
202
202
  }
203
+ function updateTiming(config, timing, contentLengthCounter) {
204
+ const now = performance.now();
205
+ config.timing.domainLookupStart = timing.dnsStart || config.timing.startTime;
206
+ config.timing.domainLookupEnd = timing.dnsEnd || timing.dnsStart || config.timing.startTime;
207
+ config.timing.connectStart = timing.tcpStart || timing.dnsEnd || config.timing.startTime;
208
+ config.timing.secureConnectionStart = timing.tlsStart || 0;
209
+ config.timing.connectEnd = timing.tcpEnd || timing.tlsEnd || timing.tcpStart || config.timing.startTime;
210
+ config.timing.requestStart = timing.tcpEnd || config.timing.startTime;
211
+ config.timing.responseStart = timing.firstByteTime || config.timing.requestStart;
212
+ config.timing.responseEnd = now;
213
+ config.transfer.bodySize = contentLengthCounter;
214
+ config.transfer.responseSize = contentLengthCounter;
215
+ }
216
+ function getTimingDurations(config) {
217
+ const t = config.timing;
218
+ return {
219
+ total: t.responseEnd - t.startTime,
220
+ dns: t.domainLookupEnd - t.domainLookupStart,
221
+ tcp: t.secureConnectionStart > 0 ? t.secureConnectionStart - t.connectStart : t.connectEnd - t.connectStart,
222
+ tls: t.secureConnectionStart > 0 ? t.connectEnd - t.secureConnectionStart : undefined,
223
+ firstByte: t.responseStart - t.startTime,
224
+ download: t.responseEnd - t.responseStart
225
+ };
226
+ }
203
227
  const responseCacheInstances = new Map;
204
228
  function getCacheConfigKey(option) {
205
229
  if (option === true)
@@ -489,10 +513,11 @@ async function executeHttp2Request(fetchOptions, config, options, perform, fs, s
489
513
  const maxRetries = config?.retry?.maxRetries || 0;
490
514
  const incrementDelay = config?.retry?.incrementDelay || false;
491
515
  const statusCodes = config?.retry?.statusCodes;
516
+ const startTime = performance.now();
492
517
  const timing = {
493
- startTime: performance.now(),
494
- startTimestamp: Date.now()
518
+ startTime
495
519
  };
520
+ config.timing.startTime = startTime;
496
521
  const ABSOLUTE_MAX_ATTEMPTS = 50;
497
522
  const visitedUrls = new Set;
498
523
  let totalAttempts = 0;
@@ -662,7 +687,6 @@ async function executeHttp2Stream(config, fetchOptions, requestCount, timing, _s
662
687
  config.isSecure = isSecure;
663
688
  config.finalUrl = url.href;
664
689
  config.network.protocol = "h2";
665
- config.timing.startTimestamp = timing.startTimestamp;
666
690
  }
667
691
  const headers = {
668
692
  [http2.constants.HTTP2_HEADER_METHOD]: fetchOptions.method.toUpperCase(),
@@ -739,9 +763,9 @@ async function executeHttp2Stream(config, fetchOptions, requestCount, timing, _s
739
763
  responseHeaders = headers;
740
764
  status = Number(headers[http2.constants.HTTP2_HEADER_STATUS]) || 200;
741
765
  statusText = getStatusText(status);
742
- if (!config.timing.ttfbMs) {
766
+ if (!timing.firstByteTime) {
743
767
  timing.firstByteTime = performance.now();
744
- config.timing.ttfbMs = timing.firstByteTime - timing.startTime;
768
+ config.timing.responseStart = timing.firstByteTime;
745
769
  }
746
770
  const location = headers["location"];
747
771
  const isRedirect = status >= 300 && status < 400 && location;
@@ -760,8 +784,8 @@ async function executeHttp2Stream(config, fetchOptions, requestCount, timing, _s
760
784
  contentLength: headers["content-length"] ? parseInt(headers["content-length"], 10) : undefined,
761
785
  cookies: config.responseCookies?.array || [],
762
786
  timing: {
763
- firstByte: config.timing.ttfbMs,
764
- total: performance.now() - timing.startTime
787
+ firstByte: config.timing.responseStart - config.timing.startTime,
788
+ total: performance.now() - config.timing.startTime
765
789
  }
766
790
  };
767
791
  eventEmitter.emit("headers", headersEvent);
@@ -798,9 +822,7 @@ async function executeHttp2Stream(config, fetchOptions, requestCount, timing, _s
798
822
  }
799
823
  });
800
824
  req.on("end", async () => {
801
- config.timing.endTimestamp = Date.now();
802
- config.timing.durationMs = performance.now() - timing.startTime;
803
- config.timing.transferMs = timing.firstByteTime ? performance.now() - timing.firstByteTime : config.timing.durationMs;
825
+ updateTiming(config, timing, contentLengthCounter);
804
826
  if (!config.transfer) {
805
827
  config.transfer = { requestSize: 0, responseSize: 0, headerSize: 0, bodySize: 0 };
806
828
  }
@@ -820,8 +842,6 @@ async function executeHttp2Stream(config, fetchOptions, requestCount, timing, _s
820
842
  config.transfer.requestSize = Buffer.byteLength(JSON.stringify(body), "utf8");
821
843
  }
822
844
  }
823
- config.transfer.bodySize = contentLengthCounter;
824
- config.transfer.responseSize = contentLengthCounter;
825
845
  (sessionPool || Http2SessionPool.getInstance()).releaseSession(url);
826
846
  if (_stats.statusOnNext === "redirect") {
827
847
  const partialResponse = {
@@ -920,14 +940,10 @@ async function executeHttp2Stream(config, fetchOptions, requestCount, timing, _s
920
940
  fileName: config.fileName,
921
941
  fileSize: responseBody.length,
922
942
  timing: {
923
- total: config.timing.durationMs || 0,
924
- dns: config.timing.dnsMs,
925
- tcp: config.timing.tcpMs,
926
- tls: config.timing.tlsMs,
927
- firstByte: config.timing.ttfbMs,
928
- download: config.timing.transferMs || 0
943
+ ...getTimingDurations(config),
944
+ download: getTimingDurations(config).download || 0
929
945
  },
930
- averageSpeed: config.timing.transferMs ? responseBody.length / config.timing.transferMs * 1000 : 0,
946
+ averageSpeed: getTimingDurations(config).download ? responseBody.length / getTimingDurations(config).download * 1000 : 0,
931
947
  config: sanitizeConfig(config)
932
948
  };
933
949
  downloadResult.emit("finish", downloadFinishEvent);
@@ -962,14 +978,7 @@ async function executeHttp2Stream(config, fetchOptions, requestCount, timing, _s
962
978
  finalUrl: url.href,
963
979
  cookies: config.responseCookies || { array: [], serialized: [], netscape: "", string: "", setCookiesString: [] },
964
980
  urls: buildUrlTree(config, url.href),
965
- timing: {
966
- total: config.timing.durationMs || 0,
967
- dns: config.timing.dnsMs,
968
- tcp: config.timing.tcpMs,
969
- tls: config.timing.tlsMs,
970
- firstByte: config.timing.ttfbMs,
971
- download: config.timing.transferMs
972
- },
981
+ timing: getTimingDurations(config),
973
982
  config: sanitizeConfig(config)
974
983
  };
975
984
  streamResult.emit("finish", streamFinishEvent);
@@ -992,16 +1001,12 @@ async function executeHttp2Stream(config, fetchOptions, requestCount, timing, _s
992
1001
  urls: buildUrlTree(config, url.href),
993
1002
  uploadSize: config.transfer.requestSize || 0,
994
1003
  timing: {
995
- total: config.timing.durationMs || 0,
996
- dns: config.timing.dnsMs,
997
- tcp: config.timing.tcpMs,
998
- tls: config.timing.tlsMs,
999
- upload: config.timing.transferMs || 0,
1000
- waiting: config.timing.ttfbMs || 0,
1001
- download: config.timing.transferMs
1004
+ ...getTimingDurations(config),
1005
+ upload: getTimingDurations(config).firstByte || 0,
1006
+ waiting: getTimingDurations(config).download > 0 && getTimingDurations(config).firstByte > 0 ? getTimingDurations(config).download - getTimingDurations(config).firstByte : 0
1002
1007
  },
1003
- averageUploadSpeed: config.timing.transferMs ? (config.transfer.requestSize || 0) / config.timing.transferMs * 1000 : 0,
1004
- averageDownloadSpeed: config.timing.transferMs ? contentLengthCounter / config.timing.transferMs * 1000 : 0,
1008
+ averageUploadSpeed: getTimingDurations(config).firstByte && config.transfer.requestSize ? config.transfer.requestSize / getTimingDurations(config).firstByte * 1000 : 0,
1009
+ averageDownloadSpeed: getTimingDurations(config).download ? contentLengthCounter / getTimingDurations(config).download * 1000 : 0,
1005
1010
  config: sanitizeConfig(config)
1006
1011
  };
1007
1012
  uploadResult.emit("finish", uploadFinishEvent);
@@ -1,6 +1,6 @@
1
- const _mod_vl2qhv = require('./picker.cjs');
2
- exports.detectRuntime = _mod_vl2qhv.detectRuntime;
3
- exports.getAdapterCapabilities = _mod_vl2qhv.getAdapterCapabilities;
4
- exports.buildAdapterContext = _mod_vl2qhv.buildAdapterContext;
5
- exports.getAvailableAdapters = _mod_vl2qhv.getAvailableAdapters;
6
- exports.selectAdapter = _mod_vl2qhv.selectAdapter;;
1
+ const _mod_dy07ge = require('./picker.cjs');
2
+ exports.detectRuntime = _mod_dy07ge.detectRuntime;
3
+ exports.getAdapterCapabilities = _mod_dy07ge.getAdapterCapabilities;
4
+ exports.buildAdapterContext = _mod_dy07ge.buildAdapterContext;
5
+ exports.getAvailableAdapters = _mod_dy07ge.getAvailableAdapters;
6
+ exports.selectAdapter = _mod_dy07ge.selectAdapter;;
@@ -17,6 +17,30 @@ const Environment = {
17
17
  hasFormData: typeof FormData !== "undefined",
18
18
  hasAbortController: typeof AbortController !== "undefined"
19
19
  };
20
+ function updateTiming(config, timing, bodySize) {
21
+ const now = performance.now();
22
+ config.timing.domainLookupStart = config.timing.startTime;
23
+ config.timing.domainLookupEnd = config.timing.startTime;
24
+ config.timing.connectStart = config.timing.startTime;
25
+ config.timing.secureConnectionStart = 0;
26
+ config.timing.connectEnd = config.timing.startTime;
27
+ config.timing.requestStart = config.timing.startTime;
28
+ config.timing.responseStart = timing.firstByteTime || config.timing.startTime;
29
+ config.timing.responseEnd = now;
30
+ config.transfer.bodySize = bodySize;
31
+ config.transfer.responseSize = bodySize;
32
+ }
33
+ function getTimingDurations(config) {
34
+ const t = config.timing;
35
+ return {
36
+ total: t.responseEnd - t.startTime,
37
+ dns: t.domainLookupEnd - t.domainLookupStart,
38
+ tcp: t.secureConnectionStart > 0 ? t.secureConnectionStart - t.connectStart : t.connectEnd - t.connectStart,
39
+ tls: t.secureConnectionStart > 0 ? t.connectEnd - t.secureConnectionStart : undefined,
40
+ firstByte: t.responseStart - t.startTime,
41
+ download: t.responseEnd - t.responseStart
42
+ };
43
+ }
20
44
  const responseCacheInstances = new Map;
21
45
  function getCacheConfigKey(option) {
22
46
  if (option === true)
@@ -227,10 +251,11 @@ async function executeFetchRequest(fetchOptions, config, options, perform, strea
227
251
  const maxRetries = config?.retry?.maxRetries || 0;
228
252
  const incrementDelay = config?.retry?.incrementDelay || false;
229
253
  const statusCodes = config?.retry?.statusCodes;
254
+ const startTime = performance.now();
230
255
  const timing = {
231
- startTime: performance.now(),
232
- startTimestamp: Date.now()
256
+ startTime
233
257
  };
258
+ config.timing.startTime = startTime;
234
259
  const ABSOLUTE_MAX_ATTEMPTS = 50;
235
260
  let totalAttempts = 0;
236
261
  config.setSignal();
@@ -294,7 +319,6 @@ async function executeSingleRequest(config, fetchOptions, timing, streamResult,
294
319
  config.isSecure = isSecure;
295
320
  config.finalUrl = url;
296
321
  config.network.protocol = isSecure ? "https" : "http";
297
- config.timing.startTimestamp = timing.startTimestamp;
298
322
  const reqHeaders = fetchOptions.headers instanceof RezoHeaders ? fetchOptions.headers.toObject() : fetchOptions.headers || {};
299
323
  const headers = toFetchHeaders(reqHeaders);
300
324
  const eventEmitter = streamResult || downloadResult || uploadResult;
@@ -347,9 +371,9 @@ async function executeSingleRequest(config, fetchOptions, timing, streamResult,
347
371
  if (timeoutId)
348
372
  clearTimeout(timeoutId);
349
373
  }
350
- if (!config.timing.ttfbMs) {
374
+ if (!timing.firstByteTime) {
351
375
  timing.firstByteTime = performance.now();
352
- config.timing.ttfbMs = timing.firstByteTime - timing.startTime;
376
+ config.timing.responseStart = timing.firstByteTime;
353
377
  }
354
378
  const status = response.status;
355
379
  const statusText = response.statusText;
@@ -373,8 +397,8 @@ async function executeSingleRequest(config, fetchOptions, timing, streamResult,
373
397
  contentLength: contentLength ? parseInt(contentLength, 10) : undefined,
374
398
  cookies: cookies.array,
375
399
  timing: {
376
- firstByte: config.timing.ttfbMs,
377
- total: performance.now() - timing.startTime
400
+ firstByte: config.timing.responseStart - config.timing.startTime,
401
+ total: performance.now() - config.timing.startTime
378
402
  }
379
403
  };
380
404
  eventEmitter.emit("headers", headersEvent);
@@ -428,11 +452,7 @@ async function executeSingleRequest(config, fetchOptions, timing, streamResult,
428
452
  bodySize = text.length;
429
453
  }
430
454
  }
431
- config.timing.endTimestamp = Date.now();
432
- config.timing.durationMs = performance.now() - timing.startTime;
433
- config.timing.transferMs = timing.firstByteTime ? performance.now() - timing.firstByteTime : config.timing.durationMs;
434
- config.transfer.bodySize = bodySize;
435
- config.transfer.responseSize = bodySize;
455
+ updateTiming(config, timing, bodySize);
436
456
  if (status >= 400) {
437
457
  const error = builErrorFromResponse(`HTTP Error ${status}: ${statusText}`, {
438
458
  status,
@@ -466,11 +486,7 @@ async function executeSingleRequest(config, fetchOptions, timing, streamResult,
466
486
  finalUrl: url,
467
487
  cookies,
468
488
  urls: buildUrlTree(config, url),
469
- timing: {
470
- total: config.timing.durationMs || 0,
471
- firstByte: config.timing.ttfbMs,
472
- download: config.timing.transferMs
473
- },
489
+ timing: getTimingDurations(config),
474
490
  config: sanitizeConfig(config)
475
491
  };
476
492
  streamResult.emit("finish", streamFinishEvent);
@@ -491,11 +507,10 @@ async function executeSingleRequest(config, fetchOptions, timing, streamResult,
491
507
  fileName: config.fileName || "",
492
508
  fileSize: bodySize,
493
509
  timing: {
494
- total: config.timing.durationMs || 0,
495
- firstByte: config.timing.ttfbMs,
496
- download: config.timing.transferMs || 0
510
+ ...getTimingDurations(config),
511
+ download: getTimingDurations(config).download || 0
497
512
  },
498
- averageSpeed: config.timing.transferMs ? bodySize / config.timing.transferMs * 1000 : 0,
513
+ averageSpeed: getTimingDurations(config).download ? bodySize / getTimingDurations(config).download * 1000 : 0,
499
514
  config: sanitizeConfig(config)
500
515
  };
501
516
  downloadResult.emit("finish", downloadFinishEvent);
@@ -517,13 +532,12 @@ async function executeSingleRequest(config, fetchOptions, timing, streamResult,
517
532
  urls: buildUrlTree(config, url),
518
533
  uploadSize: config.transfer.requestSize || 0,
519
534
  timing: {
520
- total: config.timing.durationMs || 0,
521
- upload: config.timing.transferMs || 0,
522
- waiting: config.timing.ttfbMs || 0,
523
- download: config.timing.transferMs
535
+ ...getTimingDurations(config),
536
+ upload: getTimingDurations(config).firstByte || 0,
537
+ waiting: getTimingDurations(config).download > 0 && getTimingDurations(config).firstByte > 0 ? getTimingDurations(config).download - getTimingDurations(config).firstByte : 0
524
538
  },
525
- averageUploadSpeed: config.timing.transferMs ? (config.transfer.requestSize || 0) / config.timing.transferMs * 1000 : 0,
526
- averageDownloadSpeed: config.timing.transferMs ? bodySize / config.timing.transferMs * 1000 : 0,
539
+ averageUploadSpeed: getTimingDurations(config).firstByte && config.transfer.requestSize ? config.transfer.requestSize / getTimingDurations(config).firstByte * 1000 : 0,
540
+ averageDownloadSpeed: getTimingDurations(config).download ? bodySize / getTimingDurations(config).download * 1000 : 0,
527
541
  config: sanitizeConfig(config)
528
542
  };
529
543
  uploadResult.emit("finish", uploadFinishEvent);
@@ -17,6 +17,30 @@ const Environment = {
17
17
  hasFormData: typeof FormData !== "undefined",
18
18
  hasAbortController: typeof AbortController !== "undefined"
19
19
  };
20
+ function updateTiming(config, timing, bodySize) {
21
+ const now = performance.now();
22
+ config.timing.domainLookupStart = config.timing.startTime;
23
+ config.timing.domainLookupEnd = config.timing.startTime;
24
+ config.timing.connectStart = config.timing.startTime;
25
+ config.timing.secureConnectionStart = 0;
26
+ config.timing.connectEnd = config.timing.startTime;
27
+ config.timing.requestStart = config.timing.startTime;
28
+ config.timing.responseStart = timing.firstByteTime || config.timing.startTime;
29
+ config.timing.responseEnd = now;
30
+ config.transfer.bodySize = bodySize;
31
+ config.transfer.responseSize = bodySize;
32
+ }
33
+ function getTimingDurations(config) {
34
+ const t = config.timing;
35
+ return {
36
+ total: t.responseEnd - t.startTime,
37
+ dns: t.domainLookupEnd - t.domainLookupStart,
38
+ tcp: t.secureConnectionStart > 0 ? t.secureConnectionStart - t.connectStart : t.connectEnd - t.connectStart,
39
+ tls: t.secureConnectionStart > 0 ? t.connectEnd - t.secureConnectionStart : undefined,
40
+ firstByte: t.responseStart - t.startTime,
41
+ download: t.responseEnd - t.responseStart
42
+ };
43
+ }
20
44
  const responseCacheInstances = new Map;
21
45
  function getCacheConfigKey(option) {
22
46
  if (option === true)
@@ -227,10 +251,11 @@ async function executeFetchRequest(fetchOptions, config, options, perform, strea
227
251
  const maxRetries = config?.retry?.maxRetries || 0;
228
252
  const incrementDelay = config?.retry?.incrementDelay || false;
229
253
  const statusCodes = config?.retry?.statusCodes;
254
+ const startTime = performance.now();
230
255
  const timing = {
231
- startTime: performance.now(),
232
- startTimestamp: Date.now()
256
+ startTime
233
257
  };
258
+ config.timing.startTime = startTime;
234
259
  const ABSOLUTE_MAX_ATTEMPTS = 50;
235
260
  let totalAttempts = 0;
236
261
  config.setSignal();
@@ -294,7 +319,6 @@ async function executeSingleRequest(config, fetchOptions, timing, streamResult,
294
319
  config.isSecure = isSecure;
295
320
  config.finalUrl = url;
296
321
  config.network.protocol = isSecure ? "https" : "http";
297
- config.timing.startTimestamp = timing.startTimestamp;
298
322
  const reqHeaders = fetchOptions.headers instanceof RezoHeaders ? fetchOptions.headers.toObject() : fetchOptions.headers || {};
299
323
  const headers = toFetchHeaders(reqHeaders);
300
324
  const eventEmitter = streamResult || downloadResult || uploadResult;
@@ -347,9 +371,9 @@ async function executeSingleRequest(config, fetchOptions, timing, streamResult,
347
371
  if (timeoutId)
348
372
  clearTimeout(timeoutId);
349
373
  }
350
- if (!config.timing.ttfbMs) {
374
+ if (!timing.firstByteTime) {
351
375
  timing.firstByteTime = performance.now();
352
- config.timing.ttfbMs = timing.firstByteTime - timing.startTime;
376
+ config.timing.responseStart = timing.firstByteTime;
353
377
  }
354
378
  const status = response.status;
355
379
  const statusText = response.statusText;
@@ -373,8 +397,8 @@ async function executeSingleRequest(config, fetchOptions, timing, streamResult,
373
397
  contentLength: contentLength ? parseInt(contentLength, 10) : undefined,
374
398
  cookies: cookies.array,
375
399
  timing: {
376
- firstByte: config.timing.ttfbMs,
377
- total: performance.now() - timing.startTime
400
+ firstByte: config.timing.responseStart - config.timing.startTime,
401
+ total: performance.now() - config.timing.startTime
378
402
  }
379
403
  };
380
404
  eventEmitter.emit("headers", headersEvent);
@@ -428,11 +452,7 @@ async function executeSingleRequest(config, fetchOptions, timing, streamResult,
428
452
  bodySize = text.length;
429
453
  }
430
454
  }
431
- config.timing.endTimestamp = Date.now();
432
- config.timing.durationMs = performance.now() - timing.startTime;
433
- config.timing.transferMs = timing.firstByteTime ? performance.now() - timing.firstByteTime : config.timing.durationMs;
434
- config.transfer.bodySize = bodySize;
435
- config.transfer.responseSize = bodySize;
455
+ updateTiming(config, timing, bodySize);
436
456
  if (status >= 400) {
437
457
  const error = builErrorFromResponse(`HTTP Error ${status}: ${statusText}`, {
438
458
  status,
@@ -466,11 +486,7 @@ async function executeSingleRequest(config, fetchOptions, timing, streamResult,
466
486
  finalUrl: url,
467
487
  cookies,
468
488
  urls: buildUrlTree(config, url),
469
- timing: {
470
- total: config.timing.durationMs || 0,
471
- firstByte: config.timing.ttfbMs,
472
- download: config.timing.transferMs
473
- },
489
+ timing: getTimingDurations(config),
474
490
  config: sanitizeConfig(config)
475
491
  };
476
492
  streamResult.emit("finish", streamFinishEvent);
@@ -491,11 +507,10 @@ async function executeSingleRequest(config, fetchOptions, timing, streamResult,
491
507
  fileName: config.fileName || "",
492
508
  fileSize: bodySize,
493
509
  timing: {
494
- total: config.timing.durationMs || 0,
495
- firstByte: config.timing.ttfbMs,
496
- download: config.timing.transferMs || 0
510
+ ...getTimingDurations(config),
511
+ download: getTimingDurations(config).download || 0
497
512
  },
498
- averageSpeed: config.timing.transferMs ? bodySize / config.timing.transferMs * 1000 : 0,
513
+ averageSpeed: getTimingDurations(config).download ? bodySize / getTimingDurations(config).download * 1000 : 0,
499
514
  config: sanitizeConfig(config)
500
515
  };
501
516
  downloadResult.emit("finish", downloadFinishEvent);
@@ -517,13 +532,12 @@ async function executeSingleRequest(config, fetchOptions, timing, streamResult,
517
532
  urls: buildUrlTree(config, url),
518
533
  uploadSize: config.transfer.requestSize || 0,
519
534
  timing: {
520
- total: config.timing.durationMs || 0,
521
- upload: config.timing.transferMs || 0,
522
- waiting: config.timing.ttfbMs || 0,
523
- download: config.timing.transferMs
535
+ ...getTimingDurations(config),
536
+ upload: getTimingDurations(config).firstByte || 0,
537
+ waiting: getTimingDurations(config).download > 0 && getTimingDurations(config).firstByte > 0 ? getTimingDurations(config).download - getTimingDurations(config).firstByte : 0
524
538
  },
525
- averageUploadSpeed: config.timing.transferMs ? (config.transfer.requestSize || 0) / config.timing.transferMs * 1000 : 0,
526
- averageDownloadSpeed: config.timing.transferMs ? bodySize / config.timing.transferMs * 1000 : 0,
539
+ averageUploadSpeed: getTimingDurations(config).firstByte && config.transfer.requestSize ? config.transfer.requestSize / getTimingDurations(config).firstByte * 1000 : 0,
540
+ averageDownloadSpeed: getTimingDurations(config).download ? bodySize / getTimingDurations(config).download * 1000 : 0,
527
541
  config: sanitizeConfig(config)
528
542
  };
529
543
  uploadResult.emit("finish", uploadFinishEvent);
@@ -16,6 +16,30 @@ const Environment = {
16
16
  hasFormData: typeof FormData !== "undefined",
17
17
  hasBlob: typeof Blob !== "undefined"
18
18
  };
19
+ function updateTiming(config, timing, bodySize) {
20
+ const now = performance.now();
21
+ config.timing.domainLookupStart = config.timing.startTime;
22
+ config.timing.domainLookupEnd = config.timing.startTime;
23
+ config.timing.connectStart = config.timing.startTime;
24
+ config.timing.secureConnectionStart = 0;
25
+ config.timing.connectEnd = config.timing.startTime;
26
+ config.timing.requestStart = config.timing.startTime;
27
+ config.timing.responseStart = timing.firstByteTime || config.timing.startTime;
28
+ config.timing.responseEnd = now;
29
+ config.transfer.bodySize = bodySize;
30
+ config.transfer.responseSize = bodySize;
31
+ }
32
+ function getTimingDurations(config) {
33
+ const t = config.timing;
34
+ return {
35
+ total: t.responseEnd - t.startTime,
36
+ dns: t.domainLookupEnd - t.domainLookupStart,
37
+ tcp: t.secureConnectionStart > 0 ? t.secureConnectionStart - t.connectStart : t.connectEnd - t.connectStart,
38
+ tls: t.secureConnectionStart > 0 ? t.connectEnd - t.secureConnectionStart : undefined,
39
+ firstByte: t.responseStart - t.startTime,
40
+ download: t.responseEnd - t.responseStart
41
+ };
42
+ }
19
43
  const responseCacheInstances = new Map;
20
44
  function getCacheConfigKey(option) {
21
45
  if (option === true)
@@ -258,10 +282,11 @@ async function executeXHRRequest(fetchOptions, config, options, perform, streamR
258
282
  const maxRetries = config?.retry?.maxRetries || 0;
259
283
  const incrementDelay = config?.retry?.incrementDelay || false;
260
284
  const statusCodes = config?.retry?.statusCodes;
285
+ const startTime = performance.now();
261
286
  const timing = {
262
- startTime: performance.now(),
263
- startTimestamp: Date.now()
287
+ startTime
264
288
  };
289
+ config.timing.startTime = startTime;
265
290
  const ABSOLUTE_MAX_ATTEMPTS = 50;
266
291
  let totalAttempts = 0;
267
292
  config.setSignal();
@@ -326,7 +351,6 @@ function executeSingleXHRRequest(config, fetchOptions, timing, streamResult, dow
326
351
  config.isSecure = isSecure;
327
352
  config.finalUrl = url;
328
353
  config.network.protocol = isSecure ? "https" : "http";
329
- config.timing.startTimestamp = timing.startTimestamp;
330
354
  const xhr = new XMLHttpRequest;
331
355
  xhr.open(fetchOptions.method.toUpperCase(), url, true);
332
356
  const headers = toXHRHeaders(fetchOptions.headers);
@@ -370,9 +394,9 @@ function executeSingleXHRRequest(config, fetchOptions, timing, streamResult, dow
370
394
  });
371
395
  }
372
396
  xhr.onprogress = (event) => {
373
- if (!config.timing.ttfbMs) {
397
+ if (!timing.firstByteTime) {
374
398
  timing.firstByteTime = performance.now();
375
- config.timing.ttfbMs = timing.firstByteTime - timing.startTime;
399
+ config.timing.responseStart = timing.firstByteTime;
376
400
  }
377
401
  if (eventEmitter) {
378
402
  const progressEvent = {
@@ -404,13 +428,10 @@ function executeSingleXHRRequest(config, fetchOptions, timing, streamResult, dow
404
428
  };
405
429
  }
406
430
  xhr.onload = () => {
407
- if (!config.timing.ttfbMs) {
431
+ if (!timing.firstByteTime) {
408
432
  timing.firstByteTime = performance.now();
409
- config.timing.ttfbMs = timing.firstByteTime - timing.startTime;
433
+ config.timing.responseStart = timing.firstByteTime;
410
434
  }
411
- config.timing.endTimestamp = Date.now();
412
- config.timing.durationMs = performance.now() - timing.startTime;
413
- config.timing.transferMs = timing.firstByteTime ? performance.now() - timing.firstByteTime : config.timing.durationMs;
414
435
  const status = xhr.status;
415
436
  const statusText = xhr.statusText;
416
437
  const responseHeaders = parseXHRHeaders(xhr);
@@ -427,8 +448,8 @@ function executeSingleXHRRequest(config, fetchOptions, timing, streamResult, dow
427
448
  contentLength: contentLength ? parseInt(contentLength, 10) : undefined,
428
449
  cookies: cookies.array,
429
450
  timing: {
430
- firstByte: config.timing.ttfbMs,
431
- total: performance.now() - timing.startTime
451
+ firstByte: config.timing.responseStart - config.timing.startTime,
452
+ total: performance.now() - config.timing.startTime
432
453
  }
433
454
  };
434
455
  eventEmitter.emit("headers", headersEvent);
@@ -472,8 +493,7 @@ function executeSingleXHRRequest(config, fetchOptions, timing, streamResult, dow
472
493
  responseData = text;
473
494
  }
474
495
  }
475
- config.transfer.bodySize = bodySize;
476
- config.transfer.responseSize = bodySize;
496
+ updateTiming(config, timing, bodySize);
477
497
  if (status >= 400) {
478
498
  const error = builErrorFromResponse(`HTTP Error ${status}: ${statusText}`, {
479
499
  status,
@@ -509,14 +529,7 @@ function executeSingleXHRRequest(config, fetchOptions, timing, streamResult, dow
509
529
  finalUrl: xhr.responseURL || url,
510
530
  cookies,
511
531
  urls: buildUrlTree(config, xhr.responseURL || url),
512
- timing: {
513
- total: config.timing.durationMs || 0,
514
- dns: config.timing.dnsMs,
515
- tcp: config.timing.tcpMs,
516
- tls: config.timing.tlsMs,
517
- firstByte: config.timing.ttfbMs,
518
- download: config.timing.transferMs
519
- },
532
+ timing: getTimingDurations(config),
520
533
  config: sanitizeConfig(config)
521
534
  };
522
535
  streamResult.emit("finish", streamFinishEvent);
@@ -537,14 +550,10 @@ function executeSingleXHRRequest(config, fetchOptions, timing, streamResult, dow
537
550
  fileName: config.fileName || "",
538
551
  fileSize: bodySize,
539
552
  timing: {
540
- total: config.timing.durationMs || 0,
541
- dns: config.timing.dnsMs,
542
- tcp: config.timing.tcpMs,
543
- tls: config.timing.tlsMs,
544
- firstByte: config.timing.ttfbMs,
545
- download: config.timing.transferMs || 0
553
+ ...getTimingDurations(config),
554
+ download: getTimingDurations(config).download || 0
546
555
  },
547
- averageSpeed: config.timing.transferMs ? bodySize / config.timing.transferMs * 1000 : 0,
556
+ averageSpeed: getTimingDurations(config).download ? bodySize / getTimingDurations(config).download * 1000 : 0,
548
557
  config: sanitizeConfig(config)
549
558
  };
550
559
  downloadResult.emit("finish", downloadFinishEvent);
@@ -566,16 +575,12 @@ function executeSingleXHRRequest(config, fetchOptions, timing, streamResult, dow
566
575
  urls: buildUrlTree(config, xhr.responseURL || url),
567
576
  uploadSize: config.transfer.requestSize || 0,
568
577
  timing: {
569
- total: config.timing.durationMs || 0,
570
- dns: config.timing.dnsMs,
571
- tcp: config.timing.tcpMs,
572
- tls: config.timing.tlsMs,
573
- upload: config.timing.transferMs || 0,
574
- waiting: config.timing.ttfbMs || 0,
575
- download: config.timing.transferMs
578
+ ...getTimingDurations(config),
579
+ upload: getTimingDurations(config).firstByte || 0,
580
+ waiting: getTimingDurations(config).download > 0 && getTimingDurations(config).firstByte > 0 ? getTimingDurations(config).download - getTimingDurations(config).firstByte : 0
576
581
  },
577
- averageUploadSpeed: config.timing.transferMs ? (config.transfer.requestSize || 0) / config.timing.transferMs * 1000 : 0,
578
- averageDownloadSpeed: config.timing.transferMs ? bodySize / config.timing.transferMs * 1000 : 0,
582
+ averageUploadSpeed: getTimingDurations(config).firstByte && config.transfer.requestSize ? config.transfer.requestSize / getTimingDurations(config).firstByte * 1000 : 0,
583
+ averageDownloadSpeed: getTimingDurations(config).download ? bodySize / getTimingDurations(config).download * 1000 : 0,
579
584
  config: sanitizeConfig(config)
580
585
  };
581
586
  uploadResult.emit("finish", uploadFinishEvent);