@aranova/tracking-next 0.17.1 → 0.17.3

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/dist/index.d.mts CHANGED
@@ -302,6 +302,7 @@ type MultiPageSessionConfig = z.infer<typeof multiPageSessionConfigSchema>;
302
302
  declare const pageExitMetadataSchema: z.ZodObject<{
303
303
  dwell_ms: z.ZodNumber;
304
304
  max_scroll_percent: z.ZodNullable<z.ZodNumber>;
305
+ scroll_baseline_percent: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
305
306
  page: z.ZodObject<{
306
307
  path: z.ZodString;
307
308
  }, "strict", z.ZodTypeAny, {
@@ -315,12 +316,14 @@ declare const pageExitMetadataSchema: z.ZodObject<{
315
316
  };
316
317
  dwell_ms: number;
317
318
  max_scroll_percent: number | null;
319
+ scroll_baseline_percent?: number | null | undefined;
318
320
  }, {
319
321
  page: {
320
322
  path: string;
321
323
  };
322
324
  dwell_ms: number;
323
325
  max_scroll_percent: number | null;
326
+ scroll_baseline_percent?: number | null | undefined;
324
327
  }>;
325
328
  type PageExitMetadata = z.infer<typeof pageExitMetadataSchema>;
326
329
  /**
@@ -887,6 +890,7 @@ declare const EVENT_REGISTRY: {
887
890
  readonly metadataSchema: z.ZodObject<{
888
891
  dwell_ms: z.ZodNumber;
889
892
  max_scroll_percent: z.ZodNullable<z.ZodNumber>;
893
+ scroll_baseline_percent: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
890
894
  page: z.ZodObject<{
891
895
  path: z.ZodString;
892
896
  }, "strict", z.ZodTypeAny, {
@@ -900,12 +904,14 @@ declare const EVENT_REGISTRY: {
900
904
  };
901
905
  dwell_ms: number;
902
906
  max_scroll_percent: number | null;
907
+ scroll_baseline_percent?: number | null | undefined;
903
908
  }, {
904
909
  page: {
905
910
  path: string;
906
911
  };
907
912
  dwell_ms: number;
908
913
  max_scroll_percent: number | null;
914
+ scroll_baseline_percent?: number | null | undefined;
909
915
  }>;
910
916
  readonly configSchema: z.ZodObject<{}, "strict", z.ZodTypeAny, {}, {}>;
911
917
  };
package/dist/index.d.ts CHANGED
@@ -302,6 +302,7 @@ type MultiPageSessionConfig = z.infer<typeof multiPageSessionConfigSchema>;
302
302
  declare const pageExitMetadataSchema: z.ZodObject<{
303
303
  dwell_ms: z.ZodNumber;
304
304
  max_scroll_percent: z.ZodNullable<z.ZodNumber>;
305
+ scroll_baseline_percent: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
305
306
  page: z.ZodObject<{
306
307
  path: z.ZodString;
307
308
  }, "strict", z.ZodTypeAny, {
@@ -315,12 +316,14 @@ declare const pageExitMetadataSchema: z.ZodObject<{
315
316
  };
316
317
  dwell_ms: number;
317
318
  max_scroll_percent: number | null;
319
+ scroll_baseline_percent?: number | null | undefined;
318
320
  }, {
319
321
  page: {
320
322
  path: string;
321
323
  };
322
324
  dwell_ms: number;
323
325
  max_scroll_percent: number | null;
326
+ scroll_baseline_percent?: number | null | undefined;
324
327
  }>;
325
328
  type PageExitMetadata = z.infer<typeof pageExitMetadataSchema>;
326
329
  /**
@@ -887,6 +890,7 @@ declare const EVENT_REGISTRY: {
887
890
  readonly metadataSchema: z.ZodObject<{
888
891
  dwell_ms: z.ZodNumber;
889
892
  max_scroll_percent: z.ZodNullable<z.ZodNumber>;
893
+ scroll_baseline_percent: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
890
894
  page: z.ZodObject<{
891
895
  path: z.ZodString;
892
896
  }, "strict", z.ZodTypeAny, {
@@ -900,12 +904,14 @@ declare const EVENT_REGISTRY: {
900
904
  };
901
905
  dwell_ms: number;
902
906
  max_scroll_percent: number | null;
907
+ scroll_baseline_percent?: number | null | undefined;
903
908
  }, {
904
909
  page: {
905
910
  path: string;
906
911
  };
907
912
  dwell_ms: number;
908
913
  max_scroll_percent: number | null;
914
+ scroll_baseline_percent?: number | null | undefined;
909
915
  }>;
910
916
  readonly configSchema: z.ZodObject<{}, "strict", z.ZodTypeAny, {}, {}>;
911
917
  };
package/dist/index.js CHANGED
@@ -680,9 +680,9 @@ function resolveConversionConfig(options) {
680
680
  }
681
681
  }
682
682
  function notifyResolved() {
683
- const listeners = [...resolveListeners];
683
+ const listeners2 = [...resolveListeners];
684
684
  resolveListeners.clear();
685
- for (const listener of listeners) {
685
+ for (const listener of listeners2) {
686
686
  try {
687
687
  listener();
688
688
  } catch {
@@ -1122,6 +1122,27 @@ function getOrCreateTrackingClient(config) {
1122
1122
  globalClientKey = key;
1123
1123
  return globalClient;
1124
1124
  }
1125
+ var clientCaptureRegistry = /* @__PURE__ */ new WeakMap();
1126
+ function attachClientCapturesOnce(client, build) {
1127
+ let entry = clientCaptureRegistry.get(client);
1128
+ if (entry === void 0) {
1129
+ entry = { detach: build(), refCount: 0 };
1130
+ clientCaptureRegistry.set(client, entry);
1131
+ }
1132
+ entry.refCount += 1;
1133
+ let released = false;
1134
+ return () => {
1135
+ if (released) return;
1136
+ released = true;
1137
+ const current = clientCaptureRegistry.get(client);
1138
+ if (current === void 0) return;
1139
+ current.refCount -= 1;
1140
+ if (current.refCount <= 0) {
1141
+ current.detach();
1142
+ clientCaptureRegistry.delete(client);
1143
+ }
1144
+ };
1145
+ }
1125
1146
  function createTrackingClient(config) {
1126
1147
  const flushIntervalMs = config.flushIntervalMs ?? DEFAULT_FLUSH_INTERVAL_MS;
1127
1148
  const maxQueueSize = Math.min(config.maxQueueSize ?? DEFAULT_MAX_QUEUE_SIZE, HARD_MAX_BATCH);
@@ -1391,6 +1412,14 @@ var pageExitMetadataSchema = import_zod7.z.object({
1391
1412
  // null = left without any scroll signal; floor is 0 so a valid 0% is never
1392
1413
  // rejected (a single bad field 422s the whole keepalive beacon batch).
1393
1414
  max_scroll_percent: import_zod7.z.number().int().min(0).max(100).nullable(),
1415
+ // The gating baseline: the fraction of the page visible at load with
1416
+ // zero scrolling — or, for pages that grew after the post-paint snapshot
1417
+ // (skeleton/streaming renders), the first-scroll position that
1418
+ // established it. null = page had no scrollable range, or the segment
1419
+ // ended before the snapshot. `.optional()` is load-bearing: SDK builds
1420
+ // predating this field keep POSTing page_exit without the key —
1421
+ // requiring it would 422 whole keepalive beacon batches.
1422
+ scroll_baseline_percent: import_zod7.z.number().int().min(0).max(100).nullable().optional(),
1394
1423
  page: import_zod7.z.object({
1395
1424
  path: import_zod7.z.string()
1396
1425
  }).strict()
@@ -1615,6 +1644,97 @@ function attachTimeOnSite(client, config) {
1615
1644
  };
1616
1645
  }
1617
1646
 
1647
+ // ../tracking-core/src/triggers/navigation.ts
1648
+ var listeners = /* @__PURE__ */ new Set();
1649
+ var restorePatch = null;
1650
+ function notify() {
1651
+ for (const listener of listeners) listener();
1652
+ }
1653
+ function notifyDeferred() {
1654
+ setTimeout(notify, 0);
1655
+ }
1656
+ function installPatch() {
1657
+ const originalPushState = history.pushState;
1658
+ const originalReplaceState = history.replaceState;
1659
+ function patchedPushState(...args) {
1660
+ originalPushState.apply(this, args);
1661
+ notifyDeferred();
1662
+ }
1663
+ function patchedReplaceState(...args) {
1664
+ originalReplaceState.apply(this, args);
1665
+ notifyDeferred();
1666
+ }
1667
+ history.pushState = patchedPushState;
1668
+ history.replaceState = patchedReplaceState;
1669
+ window.addEventListener("popstate", notify);
1670
+ restorePatch = () => {
1671
+ history.pushState = originalPushState;
1672
+ history.replaceState = originalReplaceState;
1673
+ window.removeEventListener("popstate", notify);
1674
+ restorePatch = null;
1675
+ };
1676
+ }
1677
+ function onHistoryChange(listener) {
1678
+ if (listeners.size === 0) installPatch();
1679
+ listeners.add(listener);
1680
+ return () => {
1681
+ if (!listeners.delete(listener)) return;
1682
+ if (listeners.size === 0) restorePatch?.();
1683
+ };
1684
+ }
1685
+
1686
+ // ../tracking-core/src/triggers/scroll-measurement.ts
1687
+ var BOTTOM_EPSILON_PX = 2;
1688
+ function measureScrollPercent() {
1689
+ const root = document.scrollingElement ?? document.documentElement;
1690
+ const scrollHeight = root.scrollHeight;
1691
+ const clientHeight = root.clientHeight;
1692
+ if (scrollHeight <= 0 || clientHeight <= 0) return null;
1693
+ if (scrollHeight <= clientHeight + BOTTOM_EPSILON_PX) return null;
1694
+ const maxTop = scrollHeight - clientHeight;
1695
+ const scrollTop = Math.min(Math.max(root.scrollTop, 0), maxTop);
1696
+ if (scrollTop + clientHeight >= scrollHeight - BOTTOM_EPSILON_PX) return 100;
1697
+ return Math.max(1, Math.min(100, Math.round((scrollTop + clientHeight) / scrollHeight * 100)));
1698
+ }
1699
+ function scheduleBaselineSnapshot(onSnapshot) {
1700
+ let rafId = requestAnimationFrame(() => {
1701
+ rafId = requestAnimationFrame(() => {
1702
+ onSnapshot(measureScrollPercent());
1703
+ });
1704
+ });
1705
+ return () => cancelAnimationFrame(rafId);
1706
+ }
1707
+ function createBaselineGate() {
1708
+ let baselinePercent = null;
1709
+ let ready = false;
1710
+ let cancelSnapshot = null;
1711
+ return {
1712
+ rebaseline() {
1713
+ cancelSnapshot?.();
1714
+ ready = false;
1715
+ baselinePercent = null;
1716
+ cancelSnapshot = scheduleBaselineSnapshot((b) => {
1717
+ baselinePercent = b;
1718
+ ready = true;
1719
+ });
1720
+ },
1721
+ cancel() {
1722
+ cancelSnapshot?.();
1723
+ },
1724
+ baseline() {
1725
+ return ready ? baselinePercent : null;
1726
+ },
1727
+ sample() {
1728
+ if (!ready) return null;
1729
+ if (baselinePercent === null) {
1730
+ baselinePercent = measureScrollPercent();
1731
+ return null;
1732
+ }
1733
+ return measureScrollPercent();
1734
+ }
1735
+ };
1736
+ }
1737
+
1618
1738
  // ../tracking-core/src/triggers/scroll-depth.ts
1619
1739
  function attachScrollDepth(client, config) {
1620
1740
  if (typeof window === "undefined" || typeof document === "undefined") {
@@ -1625,17 +1745,14 @@ function attachScrollDepth(client, config) {
1625
1745
  let firedForPath = /* @__PURE__ */ new Set();
1626
1746
  let currentPath2 = window.location.pathname;
1627
1747
  let rafId = null;
1628
- function getScrollPercent() {
1629
- const doc = document.documentElement;
1630
- const scrollTop = window.scrollY || doc.scrollTop;
1631
- const scrollHeight = doc.scrollHeight;
1632
- const clientHeight = doc.clientHeight;
1633
- if (scrollHeight <= clientHeight) return 100;
1634
- return Math.round((scrollTop + clientHeight) / scrollHeight * 100);
1635
- }
1748
+ const gate = createBaselineGate();
1636
1749
  function checkThresholds() {
1637
- const percent = getScrollPercent();
1750
+ const percent = gate.sample();
1751
+ if (percent === null) return;
1752
+ const baseline = gate.baseline();
1753
+ if (baseline === null) return;
1638
1754
  for (const threshold of thresholds) {
1755
+ if (threshold <= baseline) continue;
1639
1756
  if (percent >= threshold && !firedForPath.has(threshold)) {
1640
1757
  firedForPath.add(threshold);
1641
1758
  client.trackEvent({
@@ -1662,28 +1779,15 @@ function attachScrollDepth(client, config) {
1662
1779
  if (newPath === currentPath2) return;
1663
1780
  currentPath2 = newPath;
1664
1781
  firedForPath = /* @__PURE__ */ new Set();
1665
- setTimeout(checkThresholds, 0);
1782
+ gate.rebaseline();
1666
1783
  }
1667
- const originalPushState = history.pushState.bind(history);
1668
- const originalReplaceState = history.replaceState.bind(history);
1669
- function patchedPushState(...args) {
1670
- originalPushState(...args);
1671
- setTimeout(resetIfPathChanged, 0);
1672
- }
1673
- function patchedReplaceState(...args) {
1674
- originalReplaceState(...args);
1675
- setTimeout(resetIfPathChanged, 0);
1676
- }
1677
- history.pushState = patchedPushState;
1678
- history.replaceState = patchedReplaceState;
1679
- window.addEventListener("popstate", resetIfPathChanged);
1784
+ const unsubscribeNav = onHistoryChange(resetIfPathChanged);
1680
1785
  window.addEventListener("scroll", onScroll, { passive: true });
1681
- setTimeout(checkThresholds, 0);
1786
+ gate.rebaseline();
1682
1787
  return () => {
1683
1788
  if (rafId !== null) cancelAnimationFrame(rafId);
1684
- history.pushState = originalPushState;
1685
- history.replaceState = originalReplaceState;
1686
- window.removeEventListener("popstate", resetIfPathChanged);
1789
+ gate.cancel();
1790
+ unsubscribeNav();
1687
1791
  window.removeEventListener("scroll", onScroll);
1688
1792
  };
1689
1793
  }
@@ -1765,21 +1869,14 @@ function attachPageExit(client) {
1765
1869
  let accumulatedMs = 0;
1766
1870
  let maxScrollPercent = null;
1767
1871
  let rafId = null;
1768
- function getScrollPercent() {
1769
- const doc = document.documentElement;
1770
- const scrollTop = window.scrollY || doc.scrollTop;
1771
- const scrollHeight = doc.scrollHeight;
1772
- const clientHeight = doc.clientHeight;
1773
- if (scrollHeight <= clientHeight) return 100;
1774
- return Math.round((scrollTop + clientHeight) / scrollHeight * 100);
1775
- }
1872
+ const gate = createBaselineGate();
1776
1873
  function onScroll() {
1777
1874
  if (rafId !== null) return;
1778
1875
  rafId = requestAnimationFrame(() => {
1779
1876
  rafId = null;
1780
- const percent = getScrollPercent();
1781
- if (percent >= 1 && (maxScrollPercent === null || percent > maxScrollPercent)) {
1782
- maxScrollPercent = Math.min(percent, 100);
1877
+ const percent = gate.sample();
1878
+ if (percent !== null && (maxScrollPercent === null || percent > maxScrollPercent)) {
1879
+ maxScrollPercent = percent;
1783
1880
  }
1784
1881
  });
1785
1882
  }
@@ -1798,6 +1895,12 @@ function attachPageExit(client) {
1798
1895
  metadata: {
1799
1896
  dwell_ms: dwell,
1800
1897
  max_scroll_percent: maxScrollPercent,
1898
+ // Lets the backend tell "scrolled to the bottom" apart from "the page
1899
+ // was barely scrollable". null = unscrollable page or the segment
1900
+ // ended before the post-paint snapshot landed. For pages that grew
1901
+ // after the snapshot this is the first-scroll position, not the
1902
+ // at-load fraction (see BaselineGate.baseline).
1903
+ scroll_baseline_percent: gate.baseline(),
1801
1904
  page: { path }
1802
1905
  },
1803
1906
  pageUrl: window.location.href,
@@ -1815,6 +1918,7 @@ function attachPageExit(client) {
1815
1918
  emitSegment(currentPath2, false);
1816
1919
  currentPath2 = newPath;
1817
1920
  maxScrollPercent = null;
1921
+ gate.rebaseline();
1818
1922
  accumulatedMs = 0;
1819
1923
  activeSince = document.visibilityState === "visible" ? Date.now() : null;
1820
1924
  }
@@ -1828,27 +1932,15 @@ function attachPageExit(client) {
1828
1932
  function onPageHide() {
1829
1933
  emitSegment(currentPath2, true);
1830
1934
  }
1831
- const originalPushState = history.pushState.bind(history);
1832
- const originalReplaceState = history.replaceState.bind(history);
1833
- function patchedPushState(...args) {
1834
- originalPushState(...args);
1835
- setTimeout(onNavigate, 0);
1836
- }
1837
- function patchedReplaceState(...args) {
1838
- originalReplaceState(...args);
1839
- setTimeout(onNavigate, 0);
1840
- }
1841
- history.pushState = patchedPushState;
1842
- history.replaceState = patchedReplaceState;
1843
- window.addEventListener("popstate", onNavigate);
1935
+ const unsubscribeNav = onHistoryChange(onNavigate);
1844
1936
  window.addEventListener("scroll", onScroll, { passive: true });
1845
1937
  document.addEventListener("visibilitychange", onVisibilityChange);
1846
1938
  window.addEventListener("pagehide", onPageHide);
1939
+ gate.rebaseline();
1847
1940
  return () => {
1848
1941
  if (rafId !== null) cancelAnimationFrame(rafId);
1849
- history.pushState = originalPushState;
1850
- history.replaceState = originalReplaceState;
1851
- window.removeEventListener("popstate", onNavigate);
1942
+ gate.cancel();
1943
+ unsubscribeNav();
1852
1944
  window.removeEventListener("scroll", onScroll);
1853
1945
  document.removeEventListener("visibilitychange", onVisibilityChange);
1854
1946
  window.removeEventListener("pagehide", onPageHide);
@@ -2664,10 +2756,10 @@ function GoogleAdsTracking(props) {
2664
2756
  }
2665
2757
 
2666
2758
  // src/factory.tsx
2667
- var import_navigation = require("next/navigation");
2759
+ var import_navigation3 = require("next/navigation");
2668
2760
 
2669
2761
  // package.json
2670
- var version = "0.17.1";
2762
+ var version = "0.17.3";
2671
2763
 
2672
2764
  // src/factory.tsx
2673
2765
  var import_react4 = require("react");
@@ -2761,8 +2853,8 @@ var PhoneField = (0, import_react3.forwardRef)(function PhoneField2({ country, v
2761
2853
  // src/factory.tsx
2762
2854
  var import_jsx_runtime5 = require("react/jsx-runtime");
2763
2855
  function PageViewTracker({ fire }) {
2764
- const pathname = (0, import_navigation.usePathname)();
2765
- const searchParams = (0, import_navigation.useSearchParams)();
2856
+ const pathname = (0, import_navigation3.usePathname)();
2857
+ const searchParams = (0, import_navigation3.useSearchParams)();
2766
2858
  (0, import_react4.useEffect)(() => {
2767
2859
  fire();
2768
2860
  }, [pathname, searchParams, fire]);
@@ -2793,7 +2885,7 @@ function createTracking(options) {
2793
2885
  const specificPageVisitConfig = triggers.automatic.specific_page_visit ?? null;
2794
2886
  const timeOnSiteConfig = triggers.automatic.time_on_site ?? null;
2795
2887
  function TrackingProvider({ children }) {
2796
- const pathname = (0, import_navigation.usePathname)();
2888
+ const pathname = (0, import_navigation3.usePathname)();
2797
2889
  const client = (0, import_react4.useMemo)(
2798
2890
  () => createTypedClient(
2799
2891
  getOrCreateTrackingClient({
@@ -2866,33 +2958,6 @@ function createTracking(options) {
2866
2958
  });
2867
2959
  }
2868
2960
  }, [pathname]);
2869
- (0, import_react4.useEffect)(() => {
2870
- const rawClient = getOrCreateTrackingClient({
2871
- apiKey,
2872
- endpoint,
2873
- surface: "next",
2874
- packageName: "@aranova/tracking-next",
2875
- triggers,
2876
- environment,
2877
- activeGtagIds: gtagIds,
2878
- debug
2879
- });
2880
- return attachBfcacheRestore(wrapClient(rawClient));
2881
- }, []);
2882
- (0, import_react4.useEffect)(() => {
2883
- if (!timeOnSiteConfig) return;
2884
- const rawClient = getOrCreateTrackingClient({
2885
- apiKey,
2886
- endpoint,
2887
- surface: "next",
2888
- packageName: "@aranova/tracking-next",
2889
- triggers,
2890
- environment,
2891
- activeGtagIds: gtagIds,
2892
- debug
2893
- });
2894
- return attachTimeOnSite(wrapClient(rawClient), timeOnSiteConfig);
2895
- }, []);
2896
2961
  const multiPageSessionConfig = triggers.automatic.multi_page_session ?? null;
2897
2962
  const mpsLastPathRef = (0, import_react4.useRef)(null);
2898
2963
  (0, import_react4.useEffect)(() => {
@@ -2943,7 +3008,6 @@ function createTracking(options) {
2943
3008
  }
2944
3009
  }, [pathname]);
2945
3010
  (0, import_react4.useEffect)(() => {
2946
- const detachers = [];
2947
3011
  const rawClient = getOrCreateTrackingClient({
2948
3012
  apiKey,
2949
3013
  endpoint,
@@ -2954,29 +3018,36 @@ function createTracking(options) {
2954
3018
  activeGtagIds: gtagIds,
2955
3019
  debug
2956
3020
  });
2957
- const detectorClient = wrapClient(rawClient);
2958
- detachers.push(attachPageExit(detectorClient));
2959
- const scrollDepth = triggers.automatic.scroll_depth;
2960
- if (scrollDepth) {
2961
- detachers.push(attachScrollDepth(detectorClient, scrollDepth));
2962
- }
2963
- const formStart = triggers.automatic.form_start;
2964
- if (formStart) {
2965
- detachers.push(attachFormStart(detectorClient, formStart));
2966
- }
2967
- const ctaClick = triggers.manual?.cta_click;
2968
- if (ctaClick) {
2969
- detachers.push(attachCtaClickCapture(detectorClient, ctaClick));
2970
- }
2971
- const phoneClick = triggers.manual?.phone_click;
2972
- if (phoneClick) {
2973
- detachers.push(attachPhoneClickCapture(detectorClient, phoneClick));
2974
- }
2975
- return () => {
2976
- for (let i = detachers.length - 1; i >= 0; i--) {
2977
- detachers[i]();
3021
+ return attachClientCapturesOnce(rawClient, () => {
3022
+ const detachers = [];
3023
+ const detectorClient = wrapClient(rawClient);
3024
+ detachers.push(attachPageExit(detectorClient));
3025
+ detachers.push(attachBfcacheRestore(detectorClient));
3026
+ if (timeOnSiteConfig) {
3027
+ detachers.push(attachTimeOnSite(detectorClient, timeOnSiteConfig));
3028
+ }
3029
+ const scrollDepth = triggers.automatic.scroll_depth;
3030
+ if (scrollDepth) {
3031
+ detachers.push(attachScrollDepth(detectorClient, scrollDepth));
2978
3032
  }
2979
- };
3033
+ const formStart = triggers.automatic.form_start;
3034
+ if (formStart) {
3035
+ detachers.push(attachFormStart(detectorClient, formStart));
3036
+ }
3037
+ const ctaClick = triggers.manual?.cta_click;
3038
+ if (ctaClick) {
3039
+ detachers.push(attachCtaClickCapture(detectorClient, ctaClick));
3040
+ }
3041
+ const phoneClick = triggers.manual?.phone_click;
3042
+ if (phoneClick) {
3043
+ detachers.push(attachPhoneClickCapture(detectorClient, phoneClick));
3044
+ }
3045
+ return () => {
3046
+ for (let i = detachers.length - 1; i >= 0; i--) {
3047
+ detachers[i]();
3048
+ }
3049
+ };
3050
+ });
2980
3051
  }, []);
2981
3052
  return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(TrackingContext.Provider, { value: client, children: [
2982
3053
  /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_react4.Suspense, { fallback: null, children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(PageViewTracker, { fire: firePageView }) }),