@grabbit-labs/dynafetch 0.2.4 → 0.2.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -119,7 +119,7 @@ const page = await dynafetch({
119
119
  |-|-|-|
120
120
  | `minWaitMs` | `75` | Minimum ms before checking idle state |
121
121
  | `idleWaitMs` | `100` | Ms of zero pending requests to consider settled |
122
- | `maxWaitMs` | `2000` | Hard cap on wait time |
122
+ | `maxWaitMs` | `3000` | Hard cap on wait time |
123
123
  | `moduleWaitMs` | `6000` | Max wait for ES module bundling |
124
124
  | `timeoutMs` | none | Overall operation timeout |
125
125
 
Binary file
Binary file
Binary file
Binary file
Binary file
package/dist/index.js CHANGED
@@ -764,8 +764,19 @@ var TELEMETRY_INLINE_PATTERNS = [
764
764
  /\bhj\s*\(/i,
765
765
  /\bnewrelic\b/i,
766
766
  /\bdatadog\b/i,
767
- /\bSentry\b/i,
768
- /\bLogRocket\b/i
767
+ /\bSentry\.(?:init|capture|captureException|configureScope|withScope)\b/i,
768
+ /\bLogRocket\.(?:init|identify|track)\b/i
769
+ ];
770
+ var INLINE_APPLICATION_PATTERNS = [
771
+ /\bwebpackChunk\w*/i,
772
+ /\b__webpack_require__\b/i,
773
+ /\b__SCRIPTS_LOADED__\b/i,
774
+ /\b__LOADABLE_LOADED_CHUNKS__\b/i,
775
+ /\bparcelRequire\b/i,
776
+ /\bwindow\.__[A-Z0-9_]{3,}\s*=/i,
777
+ /\bglobalThis\.__[A-Z0-9_]{3,}\s*=/i,
778
+ /\bself\.__[A-Z0-9_]{3,}\s*=/i,
779
+ /\bperformance\.mark\s*\(/i
769
780
  ];
770
781
  function siteKey(hostname) {
771
782
  const parts = hostname.split(".").filter(Boolean);
@@ -799,6 +810,7 @@ function classifyScriptAsset(script, pageUrl) {
799
810
  if (script.scriptKind === "module") return "application";
800
811
  return "unknown";
801
812
  }
813
+ if (matchesAny(content, INLINE_APPLICATION_PATTERNS)) return "application";
802
814
  if (matchesAny(content, TELEMETRY_INLINE_PATTERNS)) return "telemetry";
803
815
  return "application";
804
816
  }
@@ -878,6 +890,8 @@ var Executor = class {
878
890
  this.originalGlobalMessageChannel = void 0;
879
891
  this.originalGlobalMessagePort = void 0;
880
892
  this.activeTimerHandles = /* @__PURE__ */ new Set();
893
+ this.domMutationVersion = 0;
894
+ this.domMutationObserver = null;
881
895
  // Simple telemetry counters (useful for debugging).
882
896
  this.telemetry_stubbed = 0;
883
897
  this.telemetry_proxy = 0;
@@ -996,11 +1010,87 @@ var Executor = class {
996
1010
  }
997
1011
  this.activeTimerHandles.clear();
998
1012
  }
1013
+ noteDomMutation() {
1014
+ this.domMutationVersion++;
1015
+ }
1016
+ async waitForDomSettle(window) {
1017
+ const quietMs = this.clampMs(
1018
+ Number(process.env.PHANTOM_DOM_SETTLE_MS ?? Math.max(this.quiescenceOptions.idleWaitMs * 4, 400)),
1019
+ 50,
1020
+ 5e3
1021
+ );
1022
+ const maxMs = this.clampMs(
1023
+ Number(process.env.PHANTOM_DOM_SETTLE_MAX_MS ?? Math.max(quietMs * 4, 2e3)),
1024
+ quietMs,
1025
+ 1e4
1026
+ );
1027
+ const start = Date.now();
1028
+ let seenVersion = this.domMutationVersion;
1029
+ while (!this.windowClosed) {
1030
+ await new Promise((resolve) => setTimeout(resolve, quietMs));
1031
+ if (this.domMutationVersion === seenVersion) return;
1032
+ seenVersion = this.domMutationVersion;
1033
+ if (Date.now() - start >= maxMs) return;
1034
+ }
1035
+ }
1036
+ async performAutoScroll(window) {
1037
+ const steps = this.clampMs(Number(process.env.PHANTOM_AUTO_SCROLL_STEPS ?? 3), 0, 12);
1038
+ if (steps === 0) return false;
1039
+ const stepPx = this.clampMs(
1040
+ Number(process.env.PHANTOM_AUTO_SCROLL_STEP_PX ?? Math.max(Number(window.innerHeight) || 0, 900)),
1041
+ 100,
1042
+ 1e4
1043
+ );
1044
+ const delayMs = this.clampMs(Number(process.env.PHANTOM_AUTO_SCROLL_DELAY_MS ?? 150), 0, 5e3);
1045
+ const dispatchScroll = () => {
1046
+ try {
1047
+ window.dispatchEvent(new window.Event("scroll"));
1048
+ } catch {
1049
+ }
1050
+ try {
1051
+ window.document?.dispatchEvent?.(new window.Event("scroll"));
1052
+ } catch {
1053
+ }
1054
+ try {
1055
+ window.document?.documentElement?.dispatchEvent?.(new window.Event("scroll"));
1056
+ } catch {
1057
+ }
1058
+ };
1059
+ let didScroll = false;
1060
+ for (let step = 0; step < steps && !this.windowClosed; step++) {
1061
+ const nextY = (Number(window.scrollY) || 0) + stepPx;
1062
+ try {
1063
+ window.scrollTo(0, nextY);
1064
+ } catch {
1065
+ try {
1066
+ window.scrollY = nextY;
1067
+ } catch {
1068
+ }
1069
+ }
1070
+ dispatchScroll();
1071
+ didScroll = true;
1072
+ if (delayMs > 0) {
1073
+ await new Promise((resolve) => window.setTimeout(resolve, delayMs));
1074
+ }
1075
+ }
1076
+ if (didScroll) {
1077
+ try {
1078
+ window.scrollTo(0, 0);
1079
+ } catch {
1080
+ try {
1081
+ window.scrollY = 0;
1082
+ } catch {
1083
+ }
1084
+ }
1085
+ dispatchScroll();
1086
+ }
1087
+ return didScroll;
1088
+ }
999
1089
  applyDefaults(quiescence, moduleWaitMsOverride) {
1000
1090
  const hardMaxCap = this.clampMs(Number(process.env.PHANTOM_QUIESCENCE_MAX_CAP_MS ?? 8e3), 500, 6e4);
1001
1091
  const minWaitMs = this.clampMs(quiescence?.minWaitMs ?? 75, 0, 1e4);
1002
1092
  const idleWaitMs = this.clampMs(quiescence?.idleWaitMs ?? 100, 0, 1e4);
1003
- const maxWaitMs = this.clampMs(quiescence?.maxWaitMs ?? 2e3, 0, hardMaxCap);
1093
+ const maxWaitMs = this.clampMs(quiescence?.maxWaitMs ?? 3e3, 0, hardMaxCap);
1004
1094
  const hardModuleCap = this.clampMs(Number(process.env.PHANTOM_MODULE_WAIT_MAX_CAP_MS ?? 3e4), 1e3, 12e4);
1005
1095
  this.moduleWaitMs = this.clampMs(Number(process.env.PHANTOM_MODULE_WAIT_MS ?? moduleWaitMsOverride ?? 6e3), 1e3, hardModuleCap);
1006
1096
  this.quiescenceOptions = { minWaitMs, idleWaitMs, maxWaitMs };
@@ -1539,9 +1629,10 @@ var Executor = class {
1539
1629
  };
1540
1630
  }
1541
1631
  if (!window.crypto) window.crypto = {};
1632
+ const nodeWebCrypto = g.crypto || nodeCrypto.webcrypto;
1542
1633
  if (!window.crypto.getRandomValues) {
1543
- if (g.crypto && typeof g.crypto.getRandomValues === "function") {
1544
- window.crypto.getRandomValues = g.crypto.getRandomValues.bind(g.crypto);
1634
+ if (nodeWebCrypto && typeof nodeWebCrypto.getRandomValues === "function") {
1635
+ window.crypto.getRandomValues = nodeWebCrypto.getRandomValues.bind(nodeWebCrypto);
1545
1636
  } else {
1546
1637
  window.crypto.getRandomValues = (arr) => {
1547
1638
  const buf = nodeCrypto.randomBytes(arr.length);
@@ -1550,6 +1641,22 @@ var Executor = class {
1550
1641
  };
1551
1642
  }
1552
1643
  }
1644
+ if (!window.crypto.subtle && nodeWebCrypto?.subtle) {
1645
+ window.crypto.subtle = nodeWebCrypto.subtle;
1646
+ }
1647
+ if (!window.crypto.randomUUID) {
1648
+ if (typeof nodeWebCrypto?.randomUUID === "function") {
1649
+ window.crypto.randomUUID = nodeWebCrypto.randomUUID.bind(nodeWebCrypto);
1650
+ } else {
1651
+ window.crypto.randomUUID = () => nodeCrypto.randomUUID();
1652
+ }
1653
+ }
1654
+ if (!window.CryptoKey && g.CryptoKey) {
1655
+ window.CryptoKey = g.CryptoKey;
1656
+ }
1657
+ if (!window.SubtleCrypto && g.SubtleCrypto) {
1658
+ window.SubtleCrypto = g.SubtleCrypto;
1659
+ }
1553
1660
  {
1554
1661
  const _g = globalThis;
1555
1662
  if (_g.MessageChannel) {
@@ -1629,6 +1736,81 @@ var Executor = class {
1629
1736
  availHeight: 900
1630
1737
  };
1631
1738
  }
1739
+ const parsePx = (value) => {
1740
+ if (typeof value === "number" && Number.isFinite(value)) return value;
1741
+ if (typeof value === "string") {
1742
+ const parsed = Number.parseFloat(value.replace(/px$/i, ""));
1743
+ if (Number.isFinite(parsed)) return parsed;
1744
+ }
1745
+ return 0;
1746
+ };
1747
+ const fallbackViewportHeight = Math.max(Number(window.innerHeight) || 0, 900);
1748
+ const fallbackViewportWidth = Math.max(Number(window.innerWidth) || 0, 1440);
1749
+ const fallbackDocumentHeight = Math.max(fallbackViewportHeight * 6, 5400);
1750
+ const makeRect = (width, height) => ({
1751
+ x: 0,
1752
+ y: 0,
1753
+ top: 0,
1754
+ left: 0,
1755
+ bottom: height,
1756
+ right: width,
1757
+ width,
1758
+ height,
1759
+ toJSON() {
1760
+ return { x: 0, y: 0, top: 0, left: 0, bottom: height, right: width, width, height };
1761
+ }
1762
+ });
1763
+ const computeElementBox = (el) => {
1764
+ const tag = String(el?.tagName || "").toLowerCase();
1765
+ if (tag === "html" || tag === "body") {
1766
+ return {
1767
+ width: fallbackViewportWidth,
1768
+ height: fallbackDocumentHeight
1769
+ };
1770
+ }
1771
+ const width = parsePx(el?.style?.width) || parsePx(el?.getAttribute?.("width")) || fallbackViewportWidth;
1772
+ const height = parsePx(el?.style?.height) || parsePx(el?.getAttribute?.("height")) || 240;
1773
+ return { width, height };
1774
+ };
1775
+ try {
1776
+ const elementProto = window.Element?.prototype;
1777
+ if (elementProto) {
1778
+ const originalGetBoundingClientRect = elementProto.getBoundingClientRect;
1779
+ elementProto.getBoundingClientRect = function() {
1780
+ try {
1781
+ const rect = originalGetBoundingClientRect?.call(this);
1782
+ if (rect && (rect.width > 0 || rect.height > 0)) return rect;
1783
+ } catch {
1784
+ }
1785
+ const { width, height } = computeElementBox(this);
1786
+ return makeRect(width, height);
1787
+ };
1788
+ }
1789
+ const htmlEl = window.document?.documentElement;
1790
+ const bodyEl = window.document?.body;
1791
+ const defineSize = (target, name, value) => {
1792
+ try {
1793
+ Object.defineProperty(target, name, {
1794
+ configurable: true,
1795
+ get: () => value
1796
+ });
1797
+ } catch {
1798
+ }
1799
+ };
1800
+ if (htmlEl) {
1801
+ defineSize(htmlEl, "clientWidth", fallbackViewportWidth);
1802
+ defineSize(htmlEl, "clientHeight", fallbackViewportHeight);
1803
+ defineSize(htmlEl, "scrollWidth", fallbackViewportWidth);
1804
+ defineSize(htmlEl, "scrollHeight", fallbackDocumentHeight);
1805
+ }
1806
+ if (bodyEl) {
1807
+ defineSize(bodyEl, "clientWidth", fallbackViewportWidth);
1808
+ defineSize(bodyEl, "clientHeight", fallbackViewportHeight);
1809
+ defineSize(bodyEl, "scrollWidth", fallbackViewportWidth);
1810
+ defineSize(bodyEl, "scrollHeight", fallbackDocumentHeight);
1811
+ }
1812
+ } catch {
1813
+ }
1632
1814
  if (!window.visualViewport) {
1633
1815
  window.visualViewport = {
1634
1816
  width: window.innerWidth,
@@ -2115,6 +2297,23 @@ var Executor = class {
2115
2297
  }
2116
2298
  });
2117
2299
  const { window } = dom;
2300
+ this.domMutationVersion = 0;
2301
+ try {
2302
+ const MutationObserverCtor = window.MutationObserver;
2303
+ const targetNode = window.document?.documentElement || window.document?.body;
2304
+ if (MutationObserverCtor && targetNode) {
2305
+ this.domMutationObserver = new MutationObserverCtor(() => {
2306
+ this.noteDomMutation();
2307
+ });
2308
+ this.domMutationObserver.observe(targetNode, {
2309
+ subtree: true,
2310
+ childList: true,
2311
+ characterData: true,
2312
+ attributes: true
2313
+ });
2314
+ }
2315
+ } catch {
2316
+ }
2118
2317
  let readyStateValue = "loading";
2119
2318
  try {
2120
2319
  Object.defineProperty(window.document, "readyState", {
@@ -2241,6 +2440,27 @@ var Executor = class {
2241
2440
  this.timings.quiescence_ms = Date.now() - quiescenceStart;
2242
2441
  const reason = this.matchFound && !this.findAll ? "(early exit on match)" : "";
2243
2442
  log(`[Executor] Quiescence reached in ${Date.now() - quiescenceStart}ms ${reason}`);
2443
+ if (!this.matchFound || this.findAll) {
2444
+ const didScroll = await this.performAutoScroll(window);
2445
+ if (didScroll) {
2446
+ if (this.moduleInFlight.size > 0) {
2447
+ await this.waitForModuleWork(this.moduleWaitMs);
2448
+ }
2449
+ log("[Executor] Waiting for post-scroll quiescence...");
2450
+ const postScrollStart = Date.now();
2451
+ try {
2452
+ await this.waitForQuiescence();
2453
+ } catch (e) {
2454
+ warn("[Executor] Post-scroll quiescence wait failed:", e);
2455
+ }
2456
+ this.timings.quiescence_ms += Date.now() - postScrollStart;
2457
+ }
2458
+ }
2459
+ try {
2460
+ await this.waitForDomSettle(window);
2461
+ } catch (e) {
2462
+ warn("[Executor] DOM settle wait failed:", e);
2463
+ }
2244
2464
  const renderedHtml = this.serializeDocument(window);
2245
2465
  this.windowClosed = true;
2246
2466
  const result = {
@@ -2257,6 +2477,11 @@ var Executor = class {
2257
2477
  this.unrefNewMessagePorts(initialActiveHandles);
2258
2478
  return result;
2259
2479
  } finally {
2480
+ try {
2481
+ this.domMutationObserver?.disconnect?.();
2482
+ } catch {
2483
+ }
2484
+ this.domMutationObserver = null;
2260
2485
  const g = globalThis;
2261
2486
  if (this.originalGlobalMessageChannel !== void 0) {
2262
2487
  g.MessageChannel = this.originalGlobalMessageChannel;