@aranova/tracking-next 0.17.2 → 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 {
@@ -1412,6 +1412,14 @@ var pageExitMetadataSchema = import_zod7.z.object({
1412
1412
  // null = left without any scroll signal; floor is 0 so a valid 0% is never
1413
1413
  // rejected (a single bad field 422s the whole keepalive beacon batch).
1414
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(),
1415
1423
  page: import_zod7.z.object({
1416
1424
  path: import_zod7.z.string()
1417
1425
  }).strict()
@@ -1636,6 +1644,97 @@ function attachTimeOnSite(client, config) {
1636
1644
  };
1637
1645
  }
1638
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
+
1639
1738
  // ../tracking-core/src/triggers/scroll-depth.ts
1640
1739
  function attachScrollDepth(client, config) {
1641
1740
  if (typeof window === "undefined" || typeof document === "undefined") {
@@ -1646,17 +1745,14 @@ function attachScrollDepth(client, config) {
1646
1745
  let firedForPath = /* @__PURE__ */ new Set();
1647
1746
  let currentPath2 = window.location.pathname;
1648
1747
  let rafId = null;
1649
- function getScrollPercent() {
1650
- const doc = document.documentElement;
1651
- const scrollTop = window.scrollY || doc.scrollTop;
1652
- const scrollHeight = doc.scrollHeight;
1653
- const clientHeight = doc.clientHeight;
1654
- if (scrollHeight <= clientHeight) return 100;
1655
- return Math.round((scrollTop + clientHeight) / scrollHeight * 100);
1656
- }
1748
+ const gate = createBaselineGate();
1657
1749
  function checkThresholds() {
1658
- const percent = getScrollPercent();
1750
+ const percent = gate.sample();
1751
+ if (percent === null) return;
1752
+ const baseline = gate.baseline();
1753
+ if (baseline === null) return;
1659
1754
  for (const threshold of thresholds) {
1755
+ if (threshold <= baseline) continue;
1660
1756
  if (percent >= threshold && !firedForPath.has(threshold)) {
1661
1757
  firedForPath.add(threshold);
1662
1758
  client.trackEvent({
@@ -1683,28 +1779,15 @@ function attachScrollDepth(client, config) {
1683
1779
  if (newPath === currentPath2) return;
1684
1780
  currentPath2 = newPath;
1685
1781
  firedForPath = /* @__PURE__ */ new Set();
1686
- setTimeout(checkThresholds, 0);
1782
+ gate.rebaseline();
1687
1783
  }
1688
- const originalPushState = history.pushState.bind(history);
1689
- const originalReplaceState = history.replaceState.bind(history);
1690
- function patchedPushState(...args) {
1691
- originalPushState(...args);
1692
- setTimeout(resetIfPathChanged, 0);
1693
- }
1694
- function patchedReplaceState(...args) {
1695
- originalReplaceState(...args);
1696
- setTimeout(resetIfPathChanged, 0);
1697
- }
1698
- history.pushState = patchedPushState;
1699
- history.replaceState = patchedReplaceState;
1700
- window.addEventListener("popstate", resetIfPathChanged);
1784
+ const unsubscribeNav = onHistoryChange(resetIfPathChanged);
1701
1785
  window.addEventListener("scroll", onScroll, { passive: true });
1702
- setTimeout(checkThresholds, 0);
1786
+ gate.rebaseline();
1703
1787
  return () => {
1704
1788
  if (rafId !== null) cancelAnimationFrame(rafId);
1705
- history.pushState = originalPushState;
1706
- history.replaceState = originalReplaceState;
1707
- window.removeEventListener("popstate", resetIfPathChanged);
1789
+ gate.cancel();
1790
+ unsubscribeNav();
1708
1791
  window.removeEventListener("scroll", onScroll);
1709
1792
  };
1710
1793
  }
@@ -1786,21 +1869,14 @@ function attachPageExit(client) {
1786
1869
  let accumulatedMs = 0;
1787
1870
  let maxScrollPercent = null;
1788
1871
  let rafId = null;
1789
- function getScrollPercent() {
1790
- const doc = document.documentElement;
1791
- const scrollTop = window.scrollY || doc.scrollTop;
1792
- const scrollHeight = doc.scrollHeight;
1793
- const clientHeight = doc.clientHeight;
1794
- if (scrollHeight <= clientHeight) return 100;
1795
- return Math.round((scrollTop + clientHeight) / scrollHeight * 100);
1796
- }
1872
+ const gate = createBaselineGate();
1797
1873
  function onScroll() {
1798
1874
  if (rafId !== null) return;
1799
1875
  rafId = requestAnimationFrame(() => {
1800
1876
  rafId = null;
1801
- const percent = getScrollPercent();
1802
- if (percent >= 1 && (maxScrollPercent === null || percent > maxScrollPercent)) {
1803
- maxScrollPercent = Math.min(percent, 100);
1877
+ const percent = gate.sample();
1878
+ if (percent !== null && (maxScrollPercent === null || percent > maxScrollPercent)) {
1879
+ maxScrollPercent = percent;
1804
1880
  }
1805
1881
  });
1806
1882
  }
@@ -1819,6 +1895,12 @@ function attachPageExit(client) {
1819
1895
  metadata: {
1820
1896
  dwell_ms: dwell,
1821
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(),
1822
1904
  page: { path }
1823
1905
  },
1824
1906
  pageUrl: window.location.href,
@@ -1836,6 +1918,7 @@ function attachPageExit(client) {
1836
1918
  emitSegment(currentPath2, false);
1837
1919
  currentPath2 = newPath;
1838
1920
  maxScrollPercent = null;
1921
+ gate.rebaseline();
1839
1922
  accumulatedMs = 0;
1840
1923
  activeSince = document.visibilityState === "visible" ? Date.now() : null;
1841
1924
  }
@@ -1849,27 +1932,15 @@ function attachPageExit(client) {
1849
1932
  function onPageHide() {
1850
1933
  emitSegment(currentPath2, true);
1851
1934
  }
1852
- const originalPushState = history.pushState.bind(history);
1853
- const originalReplaceState = history.replaceState.bind(history);
1854
- function patchedPushState(...args) {
1855
- originalPushState(...args);
1856
- setTimeout(onNavigate, 0);
1857
- }
1858
- function patchedReplaceState(...args) {
1859
- originalReplaceState(...args);
1860
- setTimeout(onNavigate, 0);
1861
- }
1862
- history.pushState = patchedPushState;
1863
- history.replaceState = patchedReplaceState;
1864
- window.addEventListener("popstate", onNavigate);
1935
+ const unsubscribeNav = onHistoryChange(onNavigate);
1865
1936
  window.addEventListener("scroll", onScroll, { passive: true });
1866
1937
  document.addEventListener("visibilitychange", onVisibilityChange);
1867
1938
  window.addEventListener("pagehide", onPageHide);
1939
+ gate.rebaseline();
1868
1940
  return () => {
1869
1941
  if (rafId !== null) cancelAnimationFrame(rafId);
1870
- history.pushState = originalPushState;
1871
- history.replaceState = originalReplaceState;
1872
- window.removeEventListener("popstate", onNavigate);
1942
+ gate.cancel();
1943
+ unsubscribeNav();
1873
1944
  window.removeEventListener("scroll", onScroll);
1874
1945
  document.removeEventListener("visibilitychange", onVisibilityChange);
1875
1946
  window.removeEventListener("pagehide", onPageHide);
@@ -2685,10 +2756,10 @@ function GoogleAdsTracking(props) {
2685
2756
  }
2686
2757
 
2687
2758
  // src/factory.tsx
2688
- var import_navigation = require("next/navigation");
2759
+ var import_navigation3 = require("next/navigation");
2689
2760
 
2690
2761
  // package.json
2691
- var version = "0.17.2";
2762
+ var version = "0.17.3";
2692
2763
 
2693
2764
  // src/factory.tsx
2694
2765
  var import_react4 = require("react");
@@ -2782,8 +2853,8 @@ var PhoneField = (0, import_react3.forwardRef)(function PhoneField2({ country, v
2782
2853
  // src/factory.tsx
2783
2854
  var import_jsx_runtime5 = require("react/jsx-runtime");
2784
2855
  function PageViewTracker({ fire }) {
2785
- const pathname = (0, import_navigation.usePathname)();
2786
- const searchParams = (0, import_navigation.useSearchParams)();
2856
+ const pathname = (0, import_navigation3.usePathname)();
2857
+ const searchParams = (0, import_navigation3.useSearchParams)();
2787
2858
  (0, import_react4.useEffect)(() => {
2788
2859
  fire();
2789
2860
  }, [pathname, searchParams, fire]);
@@ -2814,7 +2885,7 @@ function createTracking(options) {
2814
2885
  const specificPageVisitConfig = triggers.automatic.specific_page_visit ?? null;
2815
2886
  const timeOnSiteConfig = triggers.automatic.time_on_site ?? null;
2816
2887
  function TrackingProvider({ children }) {
2817
- const pathname = (0, import_navigation.usePathname)();
2888
+ const pathname = (0, import_navigation3.usePathname)();
2818
2889
  const client = (0, import_react4.useMemo)(
2819
2890
  () => createTypedClient(
2820
2891
  getOrCreateTrackingClient({