@aranova/tracking-react 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
@@ -349,6 +349,7 @@ type MultiPageSessionConfig = z.infer<typeof multiPageSessionConfigSchema>;
349
349
  declare const pageExitMetadataSchema: z.ZodObject<{
350
350
  dwell_ms: z.ZodNumber;
351
351
  max_scroll_percent: z.ZodNullable<z.ZodNumber>;
352
+ scroll_baseline_percent: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
352
353
  page: z.ZodObject<{
353
354
  path: z.ZodString;
354
355
  }, "strict", z.ZodTypeAny, {
@@ -362,12 +363,14 @@ declare const pageExitMetadataSchema: z.ZodObject<{
362
363
  };
363
364
  dwell_ms: number;
364
365
  max_scroll_percent: number | null;
366
+ scroll_baseline_percent?: number | null | undefined;
365
367
  }, {
366
368
  page: {
367
369
  path: string;
368
370
  };
369
371
  dwell_ms: number;
370
372
  max_scroll_percent: number | null;
373
+ scroll_baseline_percent?: number | null | undefined;
371
374
  }>;
372
375
  type PageExitMetadata = z.infer<typeof pageExitMetadataSchema>;
373
376
  /**
@@ -934,6 +937,7 @@ declare const EVENT_REGISTRY: {
934
937
  readonly metadataSchema: z.ZodObject<{
935
938
  dwell_ms: z.ZodNumber;
936
939
  max_scroll_percent: z.ZodNullable<z.ZodNumber>;
940
+ scroll_baseline_percent: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
937
941
  page: z.ZodObject<{
938
942
  path: z.ZodString;
939
943
  }, "strict", z.ZodTypeAny, {
@@ -947,12 +951,14 @@ declare const EVENT_REGISTRY: {
947
951
  };
948
952
  dwell_ms: number;
949
953
  max_scroll_percent: number | null;
954
+ scroll_baseline_percent?: number | null | undefined;
950
955
  }, {
951
956
  page: {
952
957
  path: string;
953
958
  };
954
959
  dwell_ms: number;
955
960
  max_scroll_percent: number | null;
961
+ scroll_baseline_percent?: number | null | undefined;
956
962
  }>;
957
963
  readonly configSchema: z.ZodObject<{}, "strict", z.ZodTypeAny, {}, {}>;
958
964
  };
package/dist/index.d.ts CHANGED
@@ -349,6 +349,7 @@ type MultiPageSessionConfig = z.infer<typeof multiPageSessionConfigSchema>;
349
349
  declare const pageExitMetadataSchema: z.ZodObject<{
350
350
  dwell_ms: z.ZodNumber;
351
351
  max_scroll_percent: z.ZodNullable<z.ZodNumber>;
352
+ scroll_baseline_percent: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
352
353
  page: z.ZodObject<{
353
354
  path: z.ZodString;
354
355
  }, "strict", z.ZodTypeAny, {
@@ -362,12 +363,14 @@ declare const pageExitMetadataSchema: z.ZodObject<{
362
363
  };
363
364
  dwell_ms: number;
364
365
  max_scroll_percent: number | null;
366
+ scroll_baseline_percent?: number | null | undefined;
365
367
  }, {
366
368
  page: {
367
369
  path: string;
368
370
  };
369
371
  dwell_ms: number;
370
372
  max_scroll_percent: number | null;
373
+ scroll_baseline_percent?: number | null | undefined;
371
374
  }>;
372
375
  type PageExitMetadata = z.infer<typeof pageExitMetadataSchema>;
373
376
  /**
@@ -934,6 +937,7 @@ declare const EVENT_REGISTRY: {
934
937
  readonly metadataSchema: z.ZodObject<{
935
938
  dwell_ms: z.ZodNumber;
936
939
  max_scroll_percent: z.ZodNullable<z.ZodNumber>;
940
+ scroll_baseline_percent: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
937
941
  page: z.ZodObject<{
938
942
  path: z.ZodString;
939
943
  }, "strict", z.ZodTypeAny, {
@@ -947,12 +951,14 @@ declare const EVENT_REGISTRY: {
947
951
  };
948
952
  dwell_ms: number;
949
953
  max_scroll_percent: number | null;
954
+ scroll_baseline_percent?: number | null | undefined;
950
955
  }, {
951
956
  page: {
952
957
  path: string;
953
958
  };
954
959
  dwell_ms: number;
955
960
  max_scroll_percent: number | null;
961
+ scroll_baseline_percent?: number | null | undefined;
956
962
  }>;
957
963
  readonly configSchema: z.ZodObject<{}, "strict", z.ZodTypeAny, {}, {}>;
958
964
  };
package/dist/index.js CHANGED
@@ -726,9 +726,9 @@ function resolveConversionConfig(options) {
726
726
  }
727
727
  }
728
728
  function notifyResolved() {
729
- const listeners = [...resolveListeners];
729
+ const listeners2 = [...resolveListeners];
730
730
  resolveListeners.clear();
731
- for (const listener of listeners) {
731
+ for (const listener of listeners2) {
732
732
  try {
733
733
  listener();
734
734
  } catch {
@@ -1496,6 +1496,14 @@ var pageExitMetadataSchema = import_zod7.z.object({
1496
1496
  // null = left without any scroll signal; floor is 0 so a valid 0% is never
1497
1497
  // rejected (a single bad field 422s the whole keepalive beacon batch).
1498
1498
  max_scroll_percent: import_zod7.z.number().int().min(0).max(100).nullable(),
1499
+ // The gating baseline: the fraction of the page visible at load with
1500
+ // zero scrolling — or, for pages that grew after the post-paint snapshot
1501
+ // (skeleton/streaming renders), the first-scroll position that
1502
+ // established it. null = page had no scrollable range, or the segment
1503
+ // ended before the snapshot. `.optional()` is load-bearing: SDK builds
1504
+ // predating this field keep POSTing page_exit without the key —
1505
+ // requiring it would 422 whole keepalive beacon batches.
1506
+ scroll_baseline_percent: import_zod7.z.number().int().min(0).max(100).nullable().optional(),
1499
1507
  page: import_zod7.z.object({
1500
1508
  path: import_zod7.z.string()
1501
1509
  }).strict()
@@ -1765,6 +1773,97 @@ function attachSpecificPageVisit(client, config) {
1765
1773
  };
1766
1774
  }
1767
1775
 
1776
+ // ../tracking-core/src/triggers/navigation.ts
1777
+ var listeners = /* @__PURE__ */ new Set();
1778
+ var restorePatch = null;
1779
+ function notify() {
1780
+ for (const listener of listeners) listener();
1781
+ }
1782
+ function notifyDeferred() {
1783
+ setTimeout(notify, 0);
1784
+ }
1785
+ function installPatch() {
1786
+ const originalPushState = history.pushState;
1787
+ const originalReplaceState = history.replaceState;
1788
+ function patchedPushState(...args) {
1789
+ originalPushState.apply(this, args);
1790
+ notifyDeferred();
1791
+ }
1792
+ function patchedReplaceState(...args) {
1793
+ originalReplaceState.apply(this, args);
1794
+ notifyDeferred();
1795
+ }
1796
+ history.pushState = patchedPushState;
1797
+ history.replaceState = patchedReplaceState;
1798
+ window.addEventListener("popstate", notify);
1799
+ restorePatch = () => {
1800
+ history.pushState = originalPushState;
1801
+ history.replaceState = originalReplaceState;
1802
+ window.removeEventListener("popstate", notify);
1803
+ restorePatch = null;
1804
+ };
1805
+ }
1806
+ function onHistoryChange(listener) {
1807
+ if (listeners.size === 0) installPatch();
1808
+ listeners.add(listener);
1809
+ return () => {
1810
+ if (!listeners.delete(listener)) return;
1811
+ if (listeners.size === 0) restorePatch?.();
1812
+ };
1813
+ }
1814
+
1815
+ // ../tracking-core/src/triggers/scroll-measurement.ts
1816
+ var BOTTOM_EPSILON_PX = 2;
1817
+ function measureScrollPercent() {
1818
+ const root = document.scrollingElement ?? document.documentElement;
1819
+ const scrollHeight = root.scrollHeight;
1820
+ const clientHeight = root.clientHeight;
1821
+ if (scrollHeight <= 0 || clientHeight <= 0) return null;
1822
+ if (scrollHeight <= clientHeight + BOTTOM_EPSILON_PX) return null;
1823
+ const maxTop = scrollHeight - clientHeight;
1824
+ const scrollTop = Math.min(Math.max(root.scrollTop, 0), maxTop);
1825
+ if (scrollTop + clientHeight >= scrollHeight - BOTTOM_EPSILON_PX) return 100;
1826
+ return Math.max(1, Math.min(100, Math.round((scrollTop + clientHeight) / scrollHeight * 100)));
1827
+ }
1828
+ function scheduleBaselineSnapshot(onSnapshot) {
1829
+ let rafId = requestAnimationFrame(() => {
1830
+ rafId = requestAnimationFrame(() => {
1831
+ onSnapshot(measureScrollPercent());
1832
+ });
1833
+ });
1834
+ return () => cancelAnimationFrame(rafId);
1835
+ }
1836
+ function createBaselineGate() {
1837
+ let baselinePercent = null;
1838
+ let ready = false;
1839
+ let cancelSnapshot = null;
1840
+ return {
1841
+ rebaseline() {
1842
+ cancelSnapshot?.();
1843
+ ready = false;
1844
+ baselinePercent = null;
1845
+ cancelSnapshot = scheduleBaselineSnapshot((b) => {
1846
+ baselinePercent = b;
1847
+ ready = true;
1848
+ });
1849
+ },
1850
+ cancel() {
1851
+ cancelSnapshot?.();
1852
+ },
1853
+ baseline() {
1854
+ return ready ? baselinePercent : null;
1855
+ },
1856
+ sample() {
1857
+ if (!ready) return null;
1858
+ if (baselinePercent === null) {
1859
+ baselinePercent = measureScrollPercent();
1860
+ return null;
1861
+ }
1862
+ return measureScrollPercent();
1863
+ }
1864
+ };
1865
+ }
1866
+
1768
1867
  // ../tracking-core/src/triggers/scroll-depth.ts
1769
1868
  function attachScrollDepth(client, config) {
1770
1869
  if (typeof window === "undefined" || typeof document === "undefined") {
@@ -1775,17 +1874,14 @@ function attachScrollDepth(client, config) {
1775
1874
  let firedForPath = /* @__PURE__ */ new Set();
1776
1875
  let currentPath2 = window.location.pathname;
1777
1876
  let rafId = null;
1778
- function getScrollPercent() {
1779
- const doc = document.documentElement;
1780
- const scrollTop = window.scrollY || doc.scrollTop;
1781
- const scrollHeight = doc.scrollHeight;
1782
- const clientHeight = doc.clientHeight;
1783
- if (scrollHeight <= clientHeight) return 100;
1784
- return Math.round((scrollTop + clientHeight) / scrollHeight * 100);
1785
- }
1877
+ const gate = createBaselineGate();
1786
1878
  function checkThresholds() {
1787
- const percent = getScrollPercent();
1879
+ const percent = gate.sample();
1880
+ if (percent === null) return;
1881
+ const baseline = gate.baseline();
1882
+ if (baseline === null) return;
1788
1883
  for (const threshold of thresholds) {
1884
+ if (threshold <= baseline) continue;
1789
1885
  if (percent >= threshold && !firedForPath.has(threshold)) {
1790
1886
  firedForPath.add(threshold);
1791
1887
  client.trackEvent({
@@ -1812,28 +1908,15 @@ function attachScrollDepth(client, config) {
1812
1908
  if (newPath === currentPath2) return;
1813
1909
  currentPath2 = newPath;
1814
1910
  firedForPath = /* @__PURE__ */ new Set();
1815
- setTimeout(checkThresholds, 0);
1911
+ gate.rebaseline();
1816
1912
  }
1817
- const originalPushState = history.pushState.bind(history);
1818
- const originalReplaceState = history.replaceState.bind(history);
1819
- function patchedPushState(...args) {
1820
- originalPushState(...args);
1821
- setTimeout(resetIfPathChanged, 0);
1822
- }
1823
- function patchedReplaceState(...args) {
1824
- originalReplaceState(...args);
1825
- setTimeout(resetIfPathChanged, 0);
1826
- }
1827
- history.pushState = patchedPushState;
1828
- history.replaceState = patchedReplaceState;
1829
- window.addEventListener("popstate", resetIfPathChanged);
1913
+ const unsubscribeNav = onHistoryChange(resetIfPathChanged);
1830
1914
  window.addEventListener("scroll", onScroll, { passive: true });
1831
- setTimeout(checkThresholds, 0);
1915
+ gate.rebaseline();
1832
1916
  return () => {
1833
1917
  if (rafId !== null) cancelAnimationFrame(rafId);
1834
- history.pushState = originalPushState;
1835
- history.replaceState = originalReplaceState;
1836
- window.removeEventListener("popstate", resetIfPathChanged);
1918
+ gate.cancel();
1919
+ unsubscribeNav();
1837
1920
  window.removeEventListener("scroll", onScroll);
1838
1921
  };
1839
1922
  }
@@ -2005,21 +2088,14 @@ function attachPageExit(client) {
2005
2088
  let accumulatedMs = 0;
2006
2089
  let maxScrollPercent = null;
2007
2090
  let rafId = null;
2008
- function getScrollPercent() {
2009
- const doc = document.documentElement;
2010
- const scrollTop = window.scrollY || doc.scrollTop;
2011
- const scrollHeight = doc.scrollHeight;
2012
- const clientHeight = doc.clientHeight;
2013
- if (scrollHeight <= clientHeight) return 100;
2014
- return Math.round((scrollTop + clientHeight) / scrollHeight * 100);
2015
- }
2091
+ const gate = createBaselineGate();
2016
2092
  function onScroll() {
2017
2093
  if (rafId !== null) return;
2018
2094
  rafId = requestAnimationFrame(() => {
2019
2095
  rafId = null;
2020
- const percent = getScrollPercent();
2021
- if (percent >= 1 && (maxScrollPercent === null || percent > maxScrollPercent)) {
2022
- maxScrollPercent = Math.min(percent, 100);
2096
+ const percent = gate.sample();
2097
+ if (percent !== null && (maxScrollPercent === null || percent > maxScrollPercent)) {
2098
+ maxScrollPercent = percent;
2023
2099
  }
2024
2100
  });
2025
2101
  }
@@ -2038,6 +2114,12 @@ function attachPageExit(client) {
2038
2114
  metadata: {
2039
2115
  dwell_ms: dwell,
2040
2116
  max_scroll_percent: maxScrollPercent,
2117
+ // Lets the backend tell "scrolled to the bottom" apart from "the page
2118
+ // was barely scrollable". null = unscrollable page or the segment
2119
+ // ended before the post-paint snapshot landed. For pages that grew
2120
+ // after the snapshot this is the first-scroll position, not the
2121
+ // at-load fraction (see BaselineGate.baseline).
2122
+ scroll_baseline_percent: gate.baseline(),
2041
2123
  page: { path }
2042
2124
  },
2043
2125
  pageUrl: window.location.href,
@@ -2055,6 +2137,7 @@ function attachPageExit(client) {
2055
2137
  emitSegment(currentPath2, false);
2056
2138
  currentPath2 = newPath;
2057
2139
  maxScrollPercent = null;
2140
+ gate.rebaseline();
2058
2141
  accumulatedMs = 0;
2059
2142
  activeSince = document.visibilityState === "visible" ? Date.now() : null;
2060
2143
  }
@@ -2068,27 +2151,15 @@ function attachPageExit(client) {
2068
2151
  function onPageHide() {
2069
2152
  emitSegment(currentPath2, true);
2070
2153
  }
2071
- const originalPushState = history.pushState.bind(history);
2072
- const originalReplaceState = history.replaceState.bind(history);
2073
- function patchedPushState(...args) {
2074
- originalPushState(...args);
2075
- setTimeout(onNavigate, 0);
2076
- }
2077
- function patchedReplaceState(...args) {
2078
- originalReplaceState(...args);
2079
- setTimeout(onNavigate, 0);
2080
- }
2081
- history.pushState = patchedPushState;
2082
- history.replaceState = patchedReplaceState;
2083
- window.addEventListener("popstate", onNavigate);
2154
+ const unsubscribeNav = onHistoryChange(onNavigate);
2084
2155
  window.addEventListener("scroll", onScroll, { passive: true });
2085
2156
  document.addEventListener("visibilitychange", onVisibilityChange);
2086
2157
  window.addEventListener("pagehide", onPageHide);
2158
+ gate.rebaseline();
2087
2159
  return () => {
2088
2160
  if (rafId !== null) cancelAnimationFrame(rafId);
2089
- history.pushState = originalPushState;
2090
- history.replaceState = originalReplaceState;
2091
- window.removeEventListener("popstate", onNavigate);
2161
+ gate.cancel();
2162
+ unsubscribeNav();
2092
2163
  window.removeEventListener("scroll", onScroll);
2093
2164
  document.removeEventListener("visibilitychange", onVisibilityChange);
2094
2165
  window.removeEventListener("pagehide", onPageHide);
@@ -2893,7 +2964,7 @@ function GoogleAdsTracking(props) {
2893
2964
  var import_react6 = require("react");
2894
2965
 
2895
2966
  // package.json
2896
- var version = "0.17.2";
2967
+ var version = "0.17.3";
2897
2968
 
2898
2969
  // ../tracking-core/src/phone-react.tsx
2899
2970
  var import_react5 = require("react");