@aranova/tracking-next 0.15.0 → 0.16.1

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.js CHANGED
@@ -1273,6 +1273,7 @@ function createTrackingClient(config) {
1273
1273
  return {
1274
1274
  trackEvent,
1275
1275
  flush,
1276
+ flushBeacon: flushOnUnload,
1276
1277
  getSessionId: () => sessionId,
1277
1278
  getVisitorId: () => visitorId,
1278
1279
  destroy: () => {
@@ -1297,9 +1298,17 @@ var ctaClickMetadataSchema = import_zod2.z.object({
1297
1298
  path: import_zod2.z.string()
1298
1299
  }).strict(),
1299
1300
  section: import_zod2.z.string().nullable().optional(),
1300
- destination_url: import_zod2.z.string().nullable().optional()
1301
+ destination_url: import_zod2.z.string().nullable().optional(),
1302
+ // Set by auto-capture (and available to manual callers): the link target
1303
+ // and a short element descriptor (tag#id) for tying clicks to specific UI.
1304
+ href: import_zod2.z.string().nullable().optional(),
1305
+ element: import_zod2.z.string().nullable().optional()
1306
+ }).strict();
1307
+ var ctaClickConfigSchema = import_zod2.z.object({
1308
+ autoCapture: import_zod2.z.object({
1309
+ selector: import_zod2.z.string().optional()
1310
+ }).strict().optional()
1301
1311
  }).strict();
1302
- var ctaClickConfigSchema = import_zod2.z.object({}).strict();
1303
1312
 
1304
1313
  // ../tracking-core/src/events/sdk-heartbeat.ts
1305
1314
  var import_zod3 = require("zod");
@@ -1375,31 +1384,44 @@ var multiPageSessionConfigSchema = import_zod6.z.object({
1375
1384
  pageThreshold: import_zod6.z.number().int().min(2)
1376
1385
  }).strict();
1377
1386
 
1378
- // ../tracking-core/src/events/phone-click.ts
1387
+ // ../tracking-core/src/events/page-exit.ts
1379
1388
  var import_zod7 = require("zod");
1380
- var phoneClickMetadataSchema = import_zod7.z.object({
1381
- phone_number: import_zod7.z.string(),
1389
+ var pageExitMetadataSchema = import_zod7.z.object({
1390
+ dwell_ms: import_zod7.z.number().int().min(0),
1391
+ // null = left without any scroll signal; floor is 0 so a valid 0% is never
1392
+ // rejected (a single bad field 422s the whole keepalive beacon batch).
1393
+ max_scroll_percent: import_zod7.z.number().int().min(0).max(100).nullable(),
1382
1394
  page: import_zod7.z.object({
1383
1395
  path: import_zod7.z.string()
1384
- }).strict(),
1385
- section: import_zod7.z.string().nullable().optional()
1396
+ }).strict()
1386
1397
  }).strict();
1387
- var phoneClickConfigSchema = import_zod7.z.object({}).strict();
1398
+ var pageExitConfigSchema = import_zod7.z.object({}).strict();
1388
1399
 
1389
- // ../tracking-core/src/events/scroll-depth.ts
1400
+ // ../tracking-core/src/events/phone-click.ts
1390
1401
  var import_zod8 = require("zod");
1391
- var scrollDepthMetadataSchema = import_zod8.z.object({
1392
- depth_percent: import_zod8.z.number().int().min(1).max(100),
1402
+ var phoneClickMetadataSchema = import_zod8.z.object({
1403
+ phone_number: import_zod8.z.string(),
1393
1404
  page: import_zod8.z.object({
1394
1405
  path: import_zod8.z.string()
1406
+ }).strict(),
1407
+ section: import_zod8.z.string().nullable().optional()
1408
+ }).strict();
1409
+ var phoneClickConfigSchema = import_zod8.z.object({}).strict();
1410
+
1411
+ // ../tracking-core/src/events/scroll-depth.ts
1412
+ var import_zod9 = require("zod");
1413
+ var scrollDepthMetadataSchema = import_zod9.z.object({
1414
+ depth_percent: import_zod9.z.number().int().min(1).max(100),
1415
+ page: import_zod9.z.object({
1416
+ path: import_zod9.z.string()
1395
1417
  }).strict()
1396
1418
  }).strict();
1397
- var scrollDepthConfigSchema = import_zod8.z.object({
1398
- thresholds: import_zod8.z.array(import_zod8.z.number().int().min(1).max(100)).min(1)
1419
+ var scrollDepthConfigSchema = import_zod9.z.object({
1420
+ thresholds: import_zod9.z.array(import_zod9.z.number().int().min(1).max(100)).min(1)
1399
1421
  }).strict();
1400
1422
 
1401
1423
  // ../tracking-core/src/events/specific-page-visit.ts
1402
- var import_zod9 = require("zod");
1424
+ var import_zod10 = require("zod");
1403
1425
  var SPECIFIC_PAGE_NAMES = [
1404
1426
  "contact_page",
1405
1427
  "about_page",
@@ -1410,18 +1432,18 @@ var SPECIFIC_PAGE_NAMES = [
1410
1432
  "faq_page",
1411
1433
  "testimonials_page"
1412
1434
  ];
1413
- var specificPageNameSchema = import_zod9.z.enum(SPECIFIC_PAGE_NAMES);
1414
- var specificPageVisitMetadataSchema = import_zod9.z.object({
1435
+ var specificPageNameSchema = import_zod10.z.enum(SPECIFIC_PAGE_NAMES);
1436
+ var specificPageVisitMetadataSchema = import_zod10.z.object({
1415
1437
  page_name: specificPageNameSchema,
1416
- page: import_zod9.z.object({
1417
- path: import_zod9.z.string()
1438
+ page: import_zod10.z.object({
1439
+ path: import_zod10.z.string()
1418
1440
  }).strict()
1419
1441
  }).strict();
1420
- var specificPageVisitConfigSchema = import_zod9.z.object({
1421
- pages: import_zod9.z.array(
1422
- import_zod9.z.object({
1442
+ var specificPageVisitConfigSchema = import_zod10.z.object({
1443
+ pages: import_zod10.z.array(
1444
+ import_zod10.z.object({
1423
1445
  name: specificPageNameSchema,
1424
- pathPattern: import_zod9.z.custom((value) => value instanceof RegExp, {
1446
+ pathPattern: import_zod10.z.custom((value) => value instanceof RegExp, {
1425
1447
  message: "pathPattern must be a RegExp"
1426
1448
  })
1427
1449
  }).strict()
@@ -1429,15 +1451,15 @@ var specificPageVisitConfigSchema = import_zod9.z.object({
1429
1451
  }).strict();
1430
1452
 
1431
1453
  // ../tracking-core/src/events/time-on-site.ts
1432
- var import_zod10 = require("zod");
1433
- var timeOnSiteMetadataSchema = import_zod10.z.object({
1434
- duration_ms: import_zod10.z.number().int().nonnegative(),
1435
- page: import_zod10.z.object({
1436
- path: import_zod10.z.string()
1454
+ var import_zod11 = require("zod");
1455
+ var timeOnSiteMetadataSchema = import_zod11.z.object({
1456
+ duration_ms: import_zod11.z.number().int().nonnegative(),
1457
+ page: import_zod11.z.object({
1458
+ path: import_zod11.z.string()
1437
1459
  }).strict()
1438
1460
  }).strict();
1439
- var timeOnSiteConfigSchema = import_zod10.z.object({
1440
- thresholdSeconds: import_zod10.z.number().int().positive()
1461
+ var timeOnSiteConfigSchema = import_zod11.z.object({
1462
+ thresholdSeconds: import_zod11.z.number().int().positive()
1441
1463
  }).strict();
1442
1464
 
1443
1465
  // ../tracking-core/src/events/registry.ts
@@ -1479,6 +1501,11 @@ var EVENT_REGISTRY = {
1479
1501
  metadataSchema: sdkHeartbeatMetadataSchema,
1480
1502
  configSchema: sdkHeartbeatConfigSchema
1481
1503
  },
1504
+ page_exit: {
1505
+ kind: "automatic",
1506
+ metadataSchema: pageExitMetadataSchema,
1507
+ configSchema: pageExitConfigSchema
1508
+ },
1482
1509
  // --- manual triggers ---
1483
1510
  form_submit: {
1484
1511
  kind: "manual",
@@ -1722,6 +1749,163 @@ function attachFormStart(client, config) {
1722
1749
  };
1723
1750
  }
1724
1751
 
1752
+ // ../tracking-core/src/triggers/page-exit.ts
1753
+ function attachPageExit(client) {
1754
+ if (typeof window === "undefined" || typeof document === "undefined") {
1755
+ return () => {
1756
+ };
1757
+ }
1758
+ let currentPath2 = window.location.pathname;
1759
+ let activeSince = document.visibilityState === "visible" ? Date.now() : null;
1760
+ let accumulatedMs = 0;
1761
+ let maxScrollPercent = null;
1762
+ let rafId = null;
1763
+ function getScrollPercent() {
1764
+ const doc = document.documentElement;
1765
+ const scrollTop = window.scrollY || doc.scrollTop;
1766
+ const scrollHeight = doc.scrollHeight;
1767
+ const clientHeight = doc.clientHeight;
1768
+ if (scrollHeight <= clientHeight) return 100;
1769
+ return Math.round((scrollTop + clientHeight) / scrollHeight * 100);
1770
+ }
1771
+ function onScroll() {
1772
+ if (rafId !== null) return;
1773
+ rafId = requestAnimationFrame(() => {
1774
+ rafId = null;
1775
+ const percent = getScrollPercent();
1776
+ if (percent >= 1 && (maxScrollPercent === null || percent > maxScrollPercent)) {
1777
+ maxScrollPercent = Math.min(percent, 100);
1778
+ }
1779
+ });
1780
+ }
1781
+ function settledDwellMs() {
1782
+ let total = accumulatedMs;
1783
+ if (activeSince !== null) {
1784
+ total += Date.now() - activeSince;
1785
+ }
1786
+ return Math.max(0, Math.round(total));
1787
+ }
1788
+ function emitSegment(path, flush) {
1789
+ const dwell = settledDwellMs();
1790
+ if (dwell === 0) return;
1791
+ client.trackEvent({
1792
+ eventType: "page_exit",
1793
+ metadata: {
1794
+ dwell_ms: dwell,
1795
+ max_scroll_percent: maxScrollPercent,
1796
+ page: { path }
1797
+ },
1798
+ pageUrl: window.location.href,
1799
+ occurredAt: null
1800
+ });
1801
+ if (flush) {
1802
+ client.flushBeacon();
1803
+ }
1804
+ accumulatedMs = 0;
1805
+ activeSince = document.visibilityState === "visible" ? Date.now() : null;
1806
+ }
1807
+ function onNavigate() {
1808
+ const newPath = window.location.pathname;
1809
+ if (newPath === currentPath2) return;
1810
+ emitSegment(currentPath2, false);
1811
+ currentPath2 = newPath;
1812
+ maxScrollPercent = null;
1813
+ accumulatedMs = 0;
1814
+ activeSince = document.visibilityState === "visible" ? Date.now() : null;
1815
+ }
1816
+ function onVisibilityChange() {
1817
+ if (document.visibilityState === "hidden") {
1818
+ emitSegment(currentPath2, true);
1819
+ } else {
1820
+ activeSince = Date.now();
1821
+ }
1822
+ }
1823
+ function onPageHide() {
1824
+ emitSegment(currentPath2, true);
1825
+ }
1826
+ const originalPushState = history.pushState.bind(history);
1827
+ const originalReplaceState = history.replaceState.bind(history);
1828
+ function patchedPushState(...args) {
1829
+ originalPushState(...args);
1830
+ setTimeout(onNavigate, 0);
1831
+ }
1832
+ function patchedReplaceState(...args) {
1833
+ originalReplaceState(...args);
1834
+ setTimeout(onNavigate, 0);
1835
+ }
1836
+ history.pushState = patchedPushState;
1837
+ history.replaceState = patchedReplaceState;
1838
+ window.addEventListener("popstate", onNavigate);
1839
+ window.addEventListener("scroll", onScroll, { passive: true });
1840
+ document.addEventListener("visibilitychange", onVisibilityChange);
1841
+ window.addEventListener("pagehide", onPageHide);
1842
+ return () => {
1843
+ if (rafId !== null) cancelAnimationFrame(rafId);
1844
+ history.pushState = originalPushState;
1845
+ history.replaceState = originalReplaceState;
1846
+ window.removeEventListener("popstate", onNavigate);
1847
+ window.removeEventListener("scroll", onScroll);
1848
+ document.removeEventListener("visibilitychange", onVisibilityChange);
1849
+ window.removeEventListener("pagehide", onPageHide);
1850
+ };
1851
+ }
1852
+
1853
+ // ../tracking-core/src/triggers/cta-click-capture.ts
1854
+ var DEFAULT_CTA_SELECTOR = "[data-aranova-cta]";
1855
+ var CTA_NAME_MAX_LENGTH = 120;
1856
+ function describeElement(el) {
1857
+ const tag = el.tagName.toLowerCase();
1858
+ return el.id ? `${tag}#${el.id}` : tag;
1859
+ }
1860
+ function resolveCtaName(el) {
1861
+ const explicit = el.getAttribute("data-aranova-cta");
1862
+ if (explicit && explicit.trim().length > 0) return explicit.trim();
1863
+ const text = (el.textContent ?? "").trim().replace(/\s+/g, " ");
1864
+ if (text.length > 0) return text.slice(0, CTA_NAME_MAX_LENGTH);
1865
+ return describeElement(el);
1866
+ }
1867
+ function attachCtaClickCapture(client, config) {
1868
+ if (typeof window === "undefined" || typeof document === "undefined") {
1869
+ return () => {
1870
+ };
1871
+ }
1872
+ const autoCapture = config.autoCapture;
1873
+ if (!autoCapture) {
1874
+ return () => {
1875
+ };
1876
+ }
1877
+ const selector = autoCapture.selector ?? DEFAULT_CTA_SELECTOR;
1878
+ function onClick(event) {
1879
+ const target = event.target;
1880
+ if (!(target instanceof Element)) return;
1881
+ let matched = null;
1882
+ try {
1883
+ matched = target.closest(selector);
1884
+ } catch {
1885
+ return;
1886
+ }
1887
+ if (matched === null) return;
1888
+ const href = matched instanceof HTMLAnchorElement ? matched.href || null : matched.getAttribute("href");
1889
+ client.trackEvent({
1890
+ eventType: "cta_click",
1891
+ metadata: {
1892
+ cta_name: resolveCtaName(matched),
1893
+ page: { path: window.location.pathname },
1894
+ section: matched.getAttribute("data-aranova-section"),
1895
+ destination_url: href,
1896
+ href,
1897
+ element: describeElement(matched)
1898
+ },
1899
+ pageUrl: window.location.href,
1900
+ occurredAt: null
1901
+ });
1902
+ }
1903
+ document.addEventListener("click", onClick, true);
1904
+ return () => {
1905
+ document.removeEventListener("click", onClick, true);
1906
+ };
1907
+ }
1908
+
1725
1909
  // ../tracking-core/src/resources/sales/errors.ts
1726
1910
  var AranovaApiError = class extends Error {
1727
1911
  constructor(message, options) {
@@ -1928,42 +2112,42 @@ function createSalesClient(config) {
1928
2112
  }
1929
2113
 
1930
2114
  // ../tracking-core/src/resources/sales/schema.ts
1931
- var import_zod11 = require("zod");
2115
+ var import_zod12 = require("zod");
1932
2116
  var SUPPORTED_CURRENCIES = ["USD", "CAD"];
1933
2117
  var TRACKING_ENVIRONMENTS = ["production", "development"];
1934
- var currencySchema = import_zod11.z.enum(SUPPORTED_CURRENCIES);
1935
- var centsSchema = import_zod11.z.number().int().nonnegative();
1936
- var quantitySchema = import_zod11.z.string().regex(/^\d+(\.\d{1,3})?$/);
1937
- var metadataSchema = import_zod11.z.record(import_zod11.z.unknown());
1938
- var saleItemSchema = import_zod11.z.object({
1939
- external_item_id: import_zod11.z.string().nullable().optional(),
1940
- name: import_zod11.z.string().nullable().optional(),
1941
- category: import_zod11.z.string().nullable().optional(),
2118
+ var currencySchema = import_zod12.z.enum(SUPPORTED_CURRENCIES);
2119
+ var centsSchema = import_zod12.z.number().int().nonnegative();
2120
+ var quantitySchema = import_zod12.z.string().regex(/^\d+(\.\d{1,3})?$/);
2121
+ var metadataSchema = import_zod12.z.record(import_zod12.z.unknown());
2122
+ var saleItemSchema = import_zod12.z.object({
2123
+ external_item_id: import_zod12.z.string().nullable().optional(),
2124
+ name: import_zod12.z.string().nullable().optional(),
2125
+ category: import_zod12.z.string().nullable().optional(),
1942
2126
  quantity: quantitySchema,
1943
2127
  unit_price_cents: centsSchema,
1944
2128
  // Non-negativity validated on the wire — same contract as the other cents
1945
2129
  // fields — and backstopped by the DB CHECK.
1946
2130
  unit_cost_cents: centsSchema.nullable().optional()
1947
2131
  }).strict();
1948
- var saleServiceSchema = import_zod11.z.object({
1949
- service: import_zod11.z.string(),
2132
+ var saleServiceSchema = import_zod12.z.object({
2133
+ service: import_zod12.z.string(),
1950
2134
  amount_cents: centsSchema
1951
2135
  }).strict();
1952
- var customerNameSchema = import_zod11.z.string().max(200);
1953
- var customerPhoneSchema = import_zod11.z.string().max(64);
1954
- var customerEmailSchema = import_zod11.z.string().max(320).email();
2136
+ var customerNameSchema = import_zod12.z.string().max(200);
2137
+ var customerPhoneSchema = import_zod12.z.string().max(64);
2138
+ var customerEmailSchema = import_zod12.z.string().max(320).email();
1955
2139
  function refineServiceXor(val, ctx, { requireAmount }) {
1956
2140
  if (val.services != null) {
1957
2141
  if (val.service != null) {
1958
2142
  ctx.addIssue({
1959
- code: import_zod11.z.ZodIssueCode.custom,
2143
+ code: import_zod12.z.ZodIssueCode.custom,
1960
2144
  message: "pass either `service` or `services`, not both",
1961
2145
  path: ["services"]
1962
2146
  });
1963
2147
  }
1964
2148
  if (val.services.length === 0) {
1965
2149
  ctx.addIssue({
1966
- code: import_zod11.z.ZodIssueCode.custom,
2150
+ code: import_zod12.z.ZodIssueCode.custom,
1967
2151
  message: "`services` must not be empty",
1968
2152
  path: ["services"]
1969
2153
  });
@@ -1971,7 +2155,7 @@ function refineServiceXor(val, ctx, { requireAmount }) {
1971
2155
  const keys = val.services.map((s) => s.service);
1972
2156
  if (new Set(keys).size !== keys.length) {
1973
2157
  ctx.addIssue({
1974
- code: import_zod11.z.ZodIssueCode.custom,
2158
+ code: import_zod12.z.ZodIssueCode.custom,
1975
2159
  message: "`services` must not list the same service more than once",
1976
2160
  path: ["services"]
1977
2161
  });
@@ -1980,7 +2164,7 @@ function refineServiceXor(val, ctx, { requireAmount }) {
1980
2164
  const sum = val.services.reduce((acc, s) => acc + s.amount_cents, 0);
1981
2165
  if (val.amount_total_cents !== sum) {
1982
2166
  ctx.addIssue({
1983
- code: import_zod11.z.ZodIssueCode.custom,
2167
+ code: import_zod12.z.ZodIssueCode.custom,
1984
2168
  message: "amount_total_cents must equal the sum of the services amounts (omit it to derive it automatically)",
1985
2169
  path: ["amount_total_cents"]
1986
2170
  });
@@ -1988,37 +2172,37 @@ function refineServiceXor(val, ctx, { requireAmount }) {
1988
2172
  }
1989
2173
  } else if (requireAmount && val.amount_total_cents == null) {
1990
2174
  ctx.addIssue({
1991
- code: import_zod11.z.ZodIssueCode.custom,
2175
+ code: import_zod12.z.ZodIssueCode.custom,
1992
2176
  message: "amount_total_cents is required unless `services` is provided",
1993
2177
  path: ["amount_total_cents"]
1994
2178
  });
1995
2179
  }
1996
2180
  }
1997
- var saleCreateSchema = import_zod11.z.object({
1998
- external_id: import_zod11.z.string().nullable().optional(),
1999
- description: import_zod11.z.string().nullable().optional(),
2000
- service: import_zod11.z.string().nullable().optional(),
2001
- services: import_zod11.z.array(saleServiceSchema).nullable().optional(),
2181
+ var saleCreateSchema = import_zod12.z.object({
2182
+ external_id: import_zod12.z.string().nullable().optional(),
2183
+ description: import_zod12.z.string().nullable().optional(),
2184
+ service: import_zod12.z.string().nullable().optional(),
2185
+ services: import_zod12.z.array(saleServiceSchema).nullable().optional(),
2002
2186
  currency: currencySchema,
2003
2187
  // Optional only because the plural `services` form derives it from the sum
2004
2188
  // (see refineServiceXor); the singular/serviceless path still requires it.
2005
2189
  amount_total_cents: centsSchema.nullable().optional(),
2006
- occurred_at: import_zod11.z.string().datetime(),
2007
- environment: import_zod11.z.enum(TRACKING_ENVIRONMENTS).default("production"),
2008
- items: import_zod11.z.array(saleItemSchema).default([]),
2190
+ occurred_at: import_zod12.z.string().datetime(),
2191
+ environment: import_zod12.z.enum(TRACKING_ENVIRONMENTS).default("production"),
2192
+ items: import_zod12.z.array(saleItemSchema).default([]),
2009
2193
  metadata: metadataSchema.nullable().optional(),
2010
2194
  customer_name: customerNameSchema.nullable().optional(),
2011
2195
  customer_phone: customerPhoneSchema.nullable().optional(),
2012
2196
  customer_email: customerEmailSchema.nullable().optional()
2013
2197
  }).strict().superRefine((val, ctx) => refineServiceXor(val, ctx, { requireAmount: true }));
2014
- var saleUpdateSchema = import_zod11.z.object({
2015
- description: import_zod11.z.string().nullable().optional(),
2016
- service: import_zod11.z.string().nullable().optional(),
2017
- services: import_zod11.z.array(saleServiceSchema).nullable().optional(),
2198
+ var saleUpdateSchema = import_zod12.z.object({
2199
+ description: import_zod12.z.string().nullable().optional(),
2200
+ service: import_zod12.z.string().nullable().optional(),
2201
+ services: import_zod12.z.array(saleServiceSchema).nullable().optional(),
2018
2202
  currency: currencySchema.optional(),
2019
2203
  amount_total_cents: centsSchema.optional(),
2020
- occurred_at: import_zod11.z.string().datetime().optional(),
2021
- items: import_zod11.z.array(saleItemSchema).optional(),
2204
+ occurred_at: import_zod12.z.string().datetime().optional(),
2205
+ items: import_zod12.z.array(saleItemSchema).optional(),
2022
2206
  metadata: metadataSchema.nullable().optional(),
2023
2207
  customer_name: customerNameSchema.nullable().optional(),
2024
2208
  customer_phone: customerPhoneSchema.nullable().optional(),
@@ -2420,7 +2604,7 @@ function GoogleAdsTracking(props) {
2420
2604
  var import_navigation = require("next/navigation");
2421
2605
 
2422
2606
  // package.json
2423
- var version = "0.15.0";
2607
+ var version = "0.16.1";
2424
2608
 
2425
2609
  // src/factory.tsx
2426
2610
  var import_react4 = require("react");
@@ -2513,6 +2697,14 @@ var PhoneField = (0, import_react3.forwardRef)(function PhoneField2({ country, v
2513
2697
 
2514
2698
  // src/factory.tsx
2515
2699
  var import_jsx_runtime5 = require("react/jsx-runtime");
2700
+ function PageViewTracker({ fire }) {
2701
+ const pathname = (0, import_navigation.usePathname)();
2702
+ const searchParams = (0, import_navigation.useSearchParams)();
2703
+ (0, import_react4.useEffect)(() => {
2704
+ fire();
2705
+ }, [pathname, searchParams, fire]);
2706
+ return null;
2707
+ }
2516
2708
  function createTracking(options) {
2517
2709
  const { apiKey, endpoint, triggers, environment, gtagIds, debug, phone, conversionConfig } = options;
2518
2710
  if (!apiKey || !endpoint) {
@@ -2539,7 +2731,6 @@ function createTracking(options) {
2539
2731
  const timeOnSiteConfig = triggers.automatic.time_on_site ?? null;
2540
2732
  function TrackingProvider({ children }) {
2541
2733
  const pathname = (0, import_navigation.usePathname)();
2542
- const searchParams = (0, import_navigation.useSearchParams)();
2543
2734
  const client = (0, import_react4.useMemo)(
2544
2735
  () => createTypedClient(
2545
2736
  getOrCreateTrackingClient({
@@ -2569,7 +2760,7 @@ function createTracking(options) {
2569
2760
  const autoFire = conversionStore ? createConversionAutoFire(conversionStore) : null;
2570
2761
  return (c) => autoFire ? withConversionAutoFire(c, autoFire) : c;
2571
2762
  }, [conversionStore]);
2572
- (0, import_react4.useEffect)(() => {
2763
+ const firePageView = (0, import_react4.useCallback)(() => {
2573
2764
  const rawClient = getOrCreateTrackingClient({
2574
2765
  apiKey,
2575
2766
  endpoint,
@@ -2581,7 +2772,7 @@ function createTracking(options) {
2581
2772
  debug
2582
2773
  });
2583
2774
  fireManualPageView(wrapClient(rawClient));
2584
- }, [pathname, searchParams]);
2775
+ }, [wrapClient]);
2585
2776
  const firedPagesRef = (0, import_react4.useRef)(/* @__PURE__ */ new Set());
2586
2777
  (0, import_react4.useEffect)(() => {
2587
2778
  if (!specificPageVisitConfig || pathname === null) return;
@@ -2701,6 +2892,7 @@ function createTracking(options) {
2701
2892
  debug
2702
2893
  });
2703
2894
  const detectorClient = wrapClient(rawClient);
2895
+ detachers.push(attachPageExit(detectorClient));
2704
2896
  const scrollDepth = triggers.automatic.scroll_depth;
2705
2897
  if (scrollDepth) {
2706
2898
  detachers.push(attachScrollDepth(detectorClient, scrollDepth));
@@ -2709,13 +2901,20 @@ function createTracking(options) {
2709
2901
  if (formStart) {
2710
2902
  detachers.push(attachFormStart(detectorClient, formStart));
2711
2903
  }
2904
+ const ctaClick = triggers.manual?.cta_click;
2905
+ if (ctaClick) {
2906
+ detachers.push(attachCtaClickCapture(detectorClient, ctaClick));
2907
+ }
2712
2908
  return () => {
2713
2909
  for (let i = detachers.length - 1; i >= 0; i--) {
2714
2910
  detachers[i]();
2715
2911
  }
2716
2912
  };
2717
2913
  }, []);
2718
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(TrackingContext.Provider, { value: client, children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(PhoneConfigProvider, { value: phone ?? null, children }) });
2914
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(TrackingContext.Provider, { value: client, children: [
2915
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_react4.Suspense, { fallback: null, children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(PageViewTracker, { fire: firePageView }) }),
2916
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(PhoneConfigProvider, { value: phone ?? null, children })
2917
+ ] });
2719
2918
  }
2720
2919
  function useTracking() {
2721
2920
  const client = (0, import_react4.useContext)(TrackingContext);