@aranova/tracking-next 0.17.2 → 0.18.0

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
@@ -35,6 +35,7 @@ __export(src_exports, {
35
35
  ConsentBanner: () => ConsentBanner,
36
36
  DEFAULT_DECLINE_TTL_DAYS: () => DEFAULT_DECLINE_TTL_DAYS,
37
37
  DEFAULT_PHONE_COUNTRY: () => DEFAULT_PHONE_COUNTRY,
38
+ EVENT_SEMANTICS: () => EVENT_SEMANTICS,
38
39
  GoogleAdsTracking: () => GoogleAdsTracking,
39
40
  NAMED_RANGES: () => NAMED_RANGES,
40
41
  PhoneField: () => PhoneField,
@@ -55,6 +56,8 @@ __export(src_exports, {
55
56
  fromMinor: () => fromMinor,
56
57
  getConsentChoice: () => getConsentChoice,
57
58
  getConsentState: () => getConsentState,
59
+ getEventSemantics: () => getEventSemantics,
60
+ getTrackingConfigRuntime: () => getTrackingConfigRuntime,
58
61
  onConsentChange: () => onConsentChange,
59
62
  optIn: () => optIn,
60
63
  optOut: () => optOut,
@@ -302,6 +305,11 @@ function captureTrackingParamsFromLocation(url = typeof window === "undefined" ?
302
305
  }
303
306
 
304
307
  // ../tracking-core/src/gtag.ts
308
+ var GTAG_SCRIPT_HOST = "https://www.googletagmanager.com/gtag/js";
309
+ var TRACKING_SCRIPT_ATTRIBUTE = "data-aranova-tracking";
310
+ function getScriptMarker(id) {
311
+ return `aranova-${id}`;
312
+ }
305
313
  var GTAG_ID_PATTERN = /^[A-Z]{1,3}-[A-Za-z0-9_-]+$/;
306
314
  function isValidGtagId(id) {
307
315
  return GTAG_ID_PATTERN.test(id);
@@ -330,6 +338,15 @@ gtag('js', new Date());
330
338
  gtag('config', '${gtagId}');
331
339
  `.trim();
332
340
  }
341
+ function ensureGtagFunction() {
342
+ window.dataLayer = window.dataLayer || [];
343
+ if (typeof window.gtag === "function") return window.gtag;
344
+ function gtag() {
345
+ window.dataLayer?.push(arguments);
346
+ }
347
+ window.gtag = gtag;
348
+ return window.gtag;
349
+ }
333
350
  var SEND_TO_RE = /^AW-[A-Za-z0-9]+\/[A-Za-z0-9_-]+$/;
334
351
  function isValidSendTo(sendTo) {
335
352
  return SEND_TO_RE.test(sendTo);
@@ -348,6 +365,27 @@ function fireGtagConversion(input) {
348
365
  return false;
349
366
  }
350
367
  }
368
+ function applyDefaultConsentState() {
369
+ const gtag = ensureGtagFunction();
370
+ gtag(
371
+ "consent",
372
+ "default",
373
+ buildConsentPayload(getConsentState() === "denied" ? "denied" : "granted")
374
+ );
375
+ }
376
+ function loadGtagScript(gtagId) {
377
+ if (typeof document === "undefined") return;
378
+ const marker = getScriptMarker("gtag-loader");
379
+ const existingScript = document.querySelector(
380
+ `script[${TRACKING_SCRIPT_ATTRIBUTE}="${marker}"]`
381
+ );
382
+ if (existingScript) return;
383
+ const script = document.createElement("script");
384
+ script.async = true;
385
+ script.src = `${GTAG_SCRIPT_HOST}?id=${encodeURIComponent(gtagId)}`;
386
+ script.setAttribute(TRACKING_SCRIPT_ATTRIBUTE, marker);
387
+ document.head.append(script);
388
+ }
351
389
  function createGtagInitScriptMulti(gtagIds) {
352
390
  const safeIds = gtagIds.filter(isValidGtagId);
353
391
  const configs = safeIds.map((id) => `gtag('config', '${id}');`).join("\n");
@@ -634,6 +672,7 @@ function parseConversionConfig(raw) {
634
672
  business_id: typeof obj.business_id === "string" ? obj.business_id : void 0,
635
673
  customer_id: typeof obj.customer_id === "string" ? obj.customer_id : null,
636
674
  environment: typeof obj.environment === "string" ? obj.environment : void 0,
675
+ google_tracking_state: obj.google_tracking_state === "active" ? "active" : "disabled",
637
676
  gtag_ids: isStringMap(obj.gtag_ids) ? obj.gtag_ids : {},
638
677
  meta_pixel_ids: isStringMap(obj.meta_pixel_ids) ? obj.meta_pixel_ids : {},
639
678
  services,
@@ -680,9 +719,9 @@ function resolveConversionConfig(options) {
680
719
  }
681
720
  }
682
721
  function notifyResolved() {
683
- const listeners = [...resolveListeners];
722
+ const listeners2 = [...resolveListeners];
684
723
  resolveListeners.clear();
685
- for (const listener of listeners) {
724
+ for (const listener of listeners2) {
686
725
  try {
687
726
  listener();
688
727
  } catch {
@@ -769,6 +808,283 @@ function formatDateInTz(iso, timeZone, opts, locale) {
769
808
  }).format(date);
770
809
  }
771
810
 
811
+ // ../tracking-core/src/resources/tracking-config-runtime.ts
812
+ var CACHE_PREFIX2 = "_aranova_cfg_runtime_";
813
+ var AUTHORITY_TTL_MS = 6e4;
814
+ var runtimes = /* @__PURE__ */ new Map();
815
+ var configuredIds = /* @__PURE__ */ new Set();
816
+ var scriptLoad = null;
817
+ var jsInitialized = false;
818
+ function cacheKey2(url) {
819
+ return `${CACHE_PREFIX2}${url}`;
820
+ }
821
+ function readCache2(url) {
822
+ if (typeof window === "undefined") return null;
823
+ try {
824
+ const raw = window.sessionStorage.getItem(cacheKey2(url));
825
+ if (!raw) return null;
826
+ const parsed = JSON.parse(raw);
827
+ const config = parseConversionConfig(parsed.config);
828
+ if (!config) return null;
829
+ return {
830
+ etag: typeof parsed.etag === "string" ? parsed.etag : null,
831
+ config
832
+ };
833
+ } catch {
834
+ return null;
835
+ }
836
+ }
837
+ function writeCache2(url, entry) {
838
+ if (typeof window === "undefined") return;
839
+ try {
840
+ window.sessionStorage.setItem(cacheKey2(url), JSON.stringify(entry));
841
+ } catch {
842
+ }
843
+ }
844
+ function isTombstone(config) {
845
+ return config.google_tracking_state === "disabled" || Object.keys(config.gtag_ids).length === 0;
846
+ }
847
+ function validateConfig(config, ref) {
848
+ return config.business_id === ref.businessId && config.environment === ref.environment && (config.google_tracking_state === "active" || config.google_tracking_state === "disabled");
849
+ }
850
+ function pageViewSnapshot() {
851
+ if (typeof window === "undefined" || typeof document === "undefined") return null;
852
+ return {
853
+ href: window.location.href,
854
+ title: document.title || null,
855
+ referrer: document.referrer || null
856
+ };
857
+ }
858
+ function ensureScript(gtagId) {
859
+ if (typeof window === "undefined" || typeof document === "undefined") return Promise.resolve();
860
+ applyDefaultConsentState();
861
+ loadGtagScript(gtagId);
862
+ if (scriptLoad) return scriptLoad;
863
+ scriptLoad = new Promise((resolve) => {
864
+ const script = document.querySelector(
865
+ 'script[data-aranova-tracking="aranova-gtag-loader"]'
866
+ );
867
+ if (!script) {
868
+ resolve();
869
+ return;
870
+ }
871
+ if (script.dataset.loaded === "true") {
872
+ resolve();
873
+ return;
874
+ }
875
+ window.setTimeout(resolve, 0);
876
+ script.addEventListener(
877
+ "load",
878
+ () => {
879
+ script.dataset.loaded = "true";
880
+ resolve();
881
+ },
882
+ { once: true }
883
+ );
884
+ script.addEventListener("error", () => resolve(), { once: true });
885
+ });
886
+ return scriptLoad;
887
+ }
888
+ var TrackingConfigRuntime = class {
889
+ constructor(ref, fetchImpl = globalThis.fetch) {
890
+ this.ref = ref;
891
+ this.fetchImpl = fetchImpl;
892
+ this.current = null;
893
+ this.etag = null;
894
+ this.stateValue = "unconfirmed";
895
+ this.confirmedAt = 0;
896
+ this.inFlight = null;
897
+ this.conversionQueue = [];
898
+ this.automaticQueue = [];
899
+ this.pageQueue = [];
900
+ this.listeners = /* @__PURE__ */ new Set();
901
+ const cached = readCache2(ref.cdnUrl);
902
+ this.current = cached?.config ?? null;
903
+ this.etag = cached?.etag ?? null;
904
+ void this.revalidate();
905
+ if (typeof window !== "undefined") {
906
+ window.addEventListener("visibilitychange", () => {
907
+ if (document.visibilityState === "visible") void this.revalidate();
908
+ });
909
+ window.addEventListener("focus", () => void this.revalidate());
910
+ }
911
+ }
912
+ state() {
913
+ return this.stateValue;
914
+ }
915
+ config() {
916
+ return this.stateValue === "active" || this.stateValue === "tombstone" ? this.current : null;
917
+ }
918
+ __unsafeExpireAuthorityForTests() {
919
+ this.confirmedAt = 0;
920
+ }
921
+ subscribe(listener) {
922
+ this.listeners.add(listener);
923
+ return () => this.listeners.delete(listener);
924
+ }
925
+ async ensureAuthority() {
926
+ if (this.stateValue !== "unconfirmed" && Date.now() - this.confirmedAt < AUTHORITY_TTL_MS) {
927
+ return true;
928
+ }
929
+ await this.revalidate();
930
+ return this.stateValue !== "unconfirmed" && Date.now() - this.confirmedAt < AUTHORITY_TTL_MS;
931
+ }
932
+ async revalidate() {
933
+ if (typeof window === "undefined" || !this.fetchImpl) return;
934
+ if (this.inFlight) return this.inFlight;
935
+ this.inFlight = this.revalidateNow().finally(() => {
936
+ this.inFlight = null;
937
+ });
938
+ return this.inFlight;
939
+ }
940
+ queuePageView(snapshot = pageViewSnapshot()) {
941
+ if (!snapshot) return;
942
+ this.pageQueue.push(snapshot);
943
+ void this.flush();
944
+ }
945
+ fireConversion(key, options) {
946
+ this.conversionQueue.push({ key, ...options });
947
+ void this.flush();
948
+ }
949
+ queueAutomaticEvent(eventType, metadata, transactionPath) {
950
+ this.automaticQueue.push({ eventType, metadata, transactionPath });
951
+ void this.flush();
952
+ }
953
+ listGoals() {
954
+ return this.current?.goals ?? [];
955
+ }
956
+ async revalidateNow() {
957
+ try {
958
+ const headers = {};
959
+ if (this.etag) headers["If-None-Match"] = this.etag;
960
+ const response = await this.fetchImpl(this.ref.cdnUrl, {
961
+ method: "GET",
962
+ headers,
963
+ cache: "no-cache"
964
+ });
965
+ if (response.status === 304 && this.current && validateConfig(this.current, this.ref)) {
966
+ this.confirm(this.current, this.etag);
967
+ await this.flush();
968
+ return;
969
+ }
970
+ if (!response.ok) {
971
+ this.expireAuthority();
972
+ return;
973
+ }
974
+ const next = parseConversionConfig(await response.json());
975
+ if (!next || !validateConfig(next, this.ref)) {
976
+ this.expireAuthority();
977
+ return;
978
+ }
979
+ if (this.current && next.config_version < this.current.config_version) {
980
+ this.expireAuthority();
981
+ return;
982
+ }
983
+ this.confirm(next, response.headers.get("ETag"));
984
+ await this.flush();
985
+ } catch {
986
+ this.expireAuthority();
987
+ }
988
+ }
989
+ expireAuthority() {
990
+ if (this.stateValue !== "unconfirmed") this.stateValue = "unconfirmed";
991
+ }
992
+ confirm(config, etag) {
993
+ this.current = config;
994
+ this.etag = etag;
995
+ this.confirmedAt = Date.now();
996
+ this.stateValue = isTombstone(config) ? "tombstone" : "active";
997
+ writeCache2(this.ref.cdnUrl, { etag, config });
998
+ if (this.stateValue === "tombstone") {
999
+ this.conversionQueue.length = 0;
1000
+ this.automaticQueue.length = 0;
1001
+ this.pageQueue.length = 0;
1002
+ }
1003
+ for (const listener of this.listeners) listener();
1004
+ }
1005
+ async flush() {
1006
+ if (!await this.ensureAuthority()) return;
1007
+ if (this.stateValue !== "active" || !this.current) return;
1008
+ const ids = Object.values(this.current.gtag_ids).filter(
1009
+ (id) => typeof id === "string" && isValidGtagId(id)
1010
+ );
1011
+ if (ids.length === 0) return;
1012
+ await ensureScript(ids[0]);
1013
+ const gtag = window.gtag;
1014
+ if (typeof gtag !== "function") return;
1015
+ if (!jsInitialized) {
1016
+ gtag("js", /* @__PURE__ */ new Date());
1017
+ jsInitialized = true;
1018
+ }
1019
+ for (const id of ids) {
1020
+ if (configuredIds.has(id)) continue;
1021
+ gtag("config", id, { send_page_view: false });
1022
+ configuredIds.add(id);
1023
+ }
1024
+ while (this.pageQueue.length) {
1025
+ const page = this.pageQueue.shift();
1026
+ gtag("event", "page_view", {
1027
+ page_location: page.href,
1028
+ page_title: page.title ?? void 0,
1029
+ page_referrer: page.referrer ?? void 0
1030
+ });
1031
+ }
1032
+ while (this.automaticQueue.length) {
1033
+ const event = this.automaticQueue.shift();
1034
+ for (const goal of this.current.goals) {
1035
+ if (goal.kind !== "event" || !goal.firing) continue;
1036
+ if (!automaticThresholdMet(goal, event.eventType, event.metadata)) continue;
1037
+ this.conversionQueue.push({
1038
+ key: goal.key,
1039
+ transactionId: `auto:${goal.key}:${event.transactionPath}`
1040
+ });
1041
+ }
1042
+ }
1043
+ while (this.conversionQueue.length) {
1044
+ const item = this.conversionQueue.shift();
1045
+ const goal = this.current.goals.find((g) => g.key === item.key);
1046
+ const firing = goal?.firing;
1047
+ if (!firing || getConsentState() === "denied") continue;
1048
+ const currency = item.currency ?? firing.currency ?? null;
1049
+ const value = item.value ?? (firing.value_cents != null && currency ? fromMinor(firing.value_cents, currency) : null);
1050
+ fireGtagConversion({
1051
+ sendTo: firing.send_to,
1052
+ value,
1053
+ currency,
1054
+ transactionId: item.transactionId ?? null
1055
+ });
1056
+ }
1057
+ }
1058
+ };
1059
+ function automaticThresholdMet(goal, eventType, metadata) {
1060
+ const t = goal.trigger;
1061
+ if (!t || t.event_type !== eventType) return false;
1062
+ switch (eventType) {
1063
+ case "scroll_depth":
1064
+ return typeof metadata.depth_percent === "number" && t.threshold_percent != null && metadata.depth_percent >= t.threshold_percent;
1065
+ case "time_on_site":
1066
+ return typeof metadata.duration_ms === "number" && t.threshold_seconds != null && metadata.duration_ms >= t.threshold_seconds * 1e3;
1067
+ case "multi_page_session":
1068
+ return typeof metadata.page_count === "number" && t.page_threshold != null && metadata.page_count >= t.page_threshold;
1069
+ case "specific_page_visit":
1070
+ return typeof metadata.page_name === "string" && metadata.page_name === t.page_name;
1071
+ case "page_view":
1072
+ case "form_start":
1073
+ case "phone_click":
1074
+ return true;
1075
+ default:
1076
+ return false;
1077
+ }
1078
+ }
1079
+ function getTrackingConfigRuntime(ref, fetchImpl) {
1080
+ const key = `${ref.cdnUrl}|${ref.businessId}|${ref.environment}`;
1081
+ const existing = runtimes.get(key);
1082
+ if (existing) return existing;
1083
+ const runtime = new TrackingConfigRuntime(ref, fetchImpl ?? globalThis.fetch);
1084
+ runtimes.set(key, runtime);
1085
+ return runtime;
1086
+ }
1087
+
772
1088
  // ../tracking-core/src/resources/conversion-autofire.ts
773
1089
  function thresholdMet(goal, eventType, metadata) {
774
1090
  const t = goal.trigger;
@@ -801,6 +1117,12 @@ function createConversionAutoFire(store) {
801
1117
  for (const goal of store.listGoals()) {
802
1118
  if (goal.kind !== "event" || !goal.firing) continue;
803
1119
  if (!thresholdMet(goal, eventType, metadata)) continue;
1120
+ if ("queueAutomaticEvent" in store) {
1121
+ store.fireConversion(goal.key, {
1122
+ transactionId: `auto:${goal.key}:${currentPath()}`
1123
+ });
1124
+ continue;
1125
+ }
804
1126
  const firing = goal.firing;
805
1127
  const cents = firing.value_cents ?? null;
806
1128
  const currency = firing.currency ?? null;
@@ -816,6 +1138,10 @@ function createConversionAutoFire(store) {
816
1138
  }
817
1139
  return {
818
1140
  onAutomaticEvent(eventType, metadata) {
1141
+ if ("queueAutomaticEvent" in store) {
1142
+ store.queueAutomaticEvent(eventType, metadata, currentPath());
1143
+ return;
1144
+ }
819
1145
  if (store.isReady()) {
820
1146
  fireMatching(eventType, metadata);
821
1147
  return;
@@ -1412,6 +1738,14 @@ var pageExitMetadataSchema = import_zod7.z.object({
1412
1738
  // null = left without any scroll signal; floor is 0 so a valid 0% is never
1413
1739
  // rejected (a single bad field 422s the whole keepalive beacon batch).
1414
1740
  max_scroll_percent: import_zod7.z.number().int().min(0).max(100).nullable(),
1741
+ // The gating baseline: the fraction of the page visible at load with
1742
+ // zero scrolling — or, for pages that grew after the post-paint snapshot
1743
+ // (skeleton/streaming renders), the first-scroll position that
1744
+ // established it. null = page had no scrollable range, or the segment
1745
+ // ended before the snapshot. `.optional()` is load-bearing: SDK builds
1746
+ // predating this field keep POSTing page_exit without the key —
1747
+ // requiring it would 422 whole keepalive beacon batches.
1748
+ scroll_baseline_percent: import_zod7.z.number().int().min(0).max(100).nullable().optional(),
1415
1749
  page: import_zod7.z.object({
1416
1750
  path: import_zod7.z.string()
1417
1751
  }).strict()
@@ -1487,69 +1821,154 @@ var timeOnSiteConfigSchema = import_zod11.z.object({
1487
1821
  thresholdSeconds: import_zod11.z.number().int().positive()
1488
1822
  }).strict();
1489
1823
 
1824
+ // ../tracking-core/src/events/semantics.ts
1825
+ var EVENT_SEMANTICS = {
1826
+ page_view: {
1827
+ label: "Page view",
1828
+ category: "page",
1829
+ outcomeRole: "navigation",
1830
+ clientVisibility: "simple"
1831
+ },
1832
+ time_on_site: {
1833
+ label: "Time on site",
1834
+ category: "engagement",
1835
+ outcomeRole: "engagement",
1836
+ clientVisibility: "detailed"
1837
+ },
1838
+ specific_page_visit: {
1839
+ label: "Key page visit",
1840
+ category: "engagement",
1841
+ outcomeRole: "engagement",
1842
+ clientVisibility: "detailed"
1843
+ },
1844
+ scroll_depth: {
1845
+ label: "Scroll depth",
1846
+ category: "engagement",
1847
+ outcomeRole: "engagement",
1848
+ clientVisibility: "detailed"
1849
+ },
1850
+ multi_page_session: {
1851
+ label: "Multi-page session",
1852
+ category: "engagement",
1853
+ outcomeRole: "engagement",
1854
+ clientVisibility: "detailed"
1855
+ },
1856
+ form_start: {
1857
+ label: "Form started",
1858
+ category: "engagement",
1859
+ outcomeRole: "engagement",
1860
+ clientVisibility: "simple"
1861
+ },
1862
+ sdk_heartbeat: {
1863
+ label: "SDK heartbeat",
1864
+ category: "system",
1865
+ outcomeRole: "diagnostic",
1866
+ clientVisibility: "hidden"
1867
+ },
1868
+ page_exit: {
1869
+ label: "Page exit",
1870
+ category: "engagement",
1871
+ outcomeRole: "diagnostic",
1872
+ clientVisibility: "detailed"
1873
+ },
1874
+ form_submit: {
1875
+ label: "Form submitted",
1876
+ category: "lead",
1877
+ outcomeRole: "lead",
1878
+ clientVisibility: "simple"
1879
+ },
1880
+ phone_click: {
1881
+ label: "Phone click",
1882
+ category: "lead",
1883
+ outcomeRole: "lead",
1884
+ clientVisibility: "simple"
1885
+ },
1886
+ cta_click: {
1887
+ label: "CTA click",
1888
+ category: "engagement",
1889
+ outcomeRole: "engagement",
1890
+ clientVisibility: "simple"
1891
+ }
1892
+ };
1893
+ function getEventSemantics(eventName) {
1894
+ return eventName in EVENT_SEMANTICS ? EVENT_SEMANTICS[eventName] : null;
1895
+ }
1896
+
1490
1897
  // ../tracking-core/src/events/registry.ts
1491
1898
  var EVENT_REGISTRY = {
1492
1899
  // --- automatic triggers ---
1493
1900
  page_view: {
1494
1901
  kind: "automatic",
1902
+ semantics: EVENT_SEMANTICS.page_view,
1495
1903
  metadataSchema: pageViewMetadataSchema,
1496
1904
  configSchema: pageViewConfigSchema
1497
1905
  },
1498
1906
  time_on_site: {
1499
1907
  kind: "automatic",
1908
+ semantics: EVENT_SEMANTICS.time_on_site,
1500
1909
  metadataSchema: timeOnSiteMetadataSchema,
1501
1910
  configSchema: timeOnSiteConfigSchema
1502
1911
  },
1503
1912
  specific_page_visit: {
1504
1913
  kind: "automatic",
1914
+ semantics: EVENT_SEMANTICS.specific_page_visit,
1505
1915
  metadataSchema: specificPageVisitMetadataSchema,
1506
1916
  configSchema: specificPageVisitConfigSchema
1507
1917
  },
1508
1918
  scroll_depth: {
1509
1919
  kind: "automatic",
1920
+ semantics: EVENT_SEMANTICS.scroll_depth,
1510
1921
  metadataSchema: scrollDepthMetadataSchema,
1511
1922
  configSchema: scrollDepthConfigSchema
1512
1923
  },
1513
1924
  multi_page_session: {
1514
1925
  kind: "automatic",
1926
+ semantics: EVENT_SEMANTICS.multi_page_session,
1515
1927
  metadataSchema: multiPageSessionMetadataSchema,
1516
1928
  configSchema: multiPageSessionConfigSchema
1517
1929
  },
1518
1930
  form_start: {
1519
1931
  kind: "automatic",
1932
+ semantics: EVENT_SEMANTICS.form_start,
1520
1933
  metadataSchema: formStartMetadataSchema,
1521
1934
  configSchema: formStartConfigSchema
1522
1935
  },
1523
1936
  // --- SDK-internal automatic (not consumer-configurable) ---
1524
1937
  sdk_heartbeat: {
1525
1938
  kind: "automatic",
1939
+ semantics: EVENT_SEMANTICS.sdk_heartbeat,
1526
1940
  metadataSchema: sdkHeartbeatMetadataSchema,
1527
1941
  configSchema: sdkHeartbeatConfigSchema
1528
1942
  },
1529
1943
  page_exit: {
1530
1944
  kind: "automatic",
1945
+ semantics: EVENT_SEMANTICS.page_exit,
1531
1946
  metadataSchema: pageExitMetadataSchema,
1532
1947
  configSchema: pageExitConfigSchema
1533
1948
  },
1534
1949
  // --- manual triggers ---
1535
1950
  form_submit: {
1536
1951
  kind: "manual",
1952
+ semantics: EVENT_SEMANTICS.form_submit,
1537
1953
  metadataSchema: formSubmitMetadataSchema,
1538
1954
  configSchema: formSubmitConfigSchema
1539
1955
  },
1540
1956
  phone_click: {
1541
1957
  kind: "manual",
1958
+ semantics: EVENT_SEMANTICS.phone_click,
1542
1959
  metadataSchema: phoneClickMetadataSchema,
1543
1960
  configSchema: phoneClickConfigSchema
1544
1961
  },
1545
1962
  cta_click: {
1546
1963
  kind: "manual",
1964
+ semantics: EVENT_SEMANTICS.cta_click,
1547
1965
  metadataSchema: ctaClickMetadataSchema,
1548
1966
  configSchema: ctaClickConfigSchema
1549
1967
  }
1550
1968
  };
1551
1969
  var ALL_AUTOMATIC_EVENT_NAMES = Object.entries(EVENT_REGISTRY).filter(([, def]) => def.kind === "automatic").map(([name]) => name);
1552
1970
  var ALL_MANUAL_EVENT_NAMES = Object.entries(EVENT_REGISTRY).filter(([, def]) => def.kind === "manual").map(([name]) => name);
1971
+ var ALL_LEAD_EVENT_NAMES = Object.entries(EVENT_REGISTRY).filter(([, def]) => def.semantics.outcomeRole === "lead").map(([name]) => name);
1553
1972
  function getEventDefinition(name) {
1554
1973
  return EVENT_REGISTRY[name];
1555
1974
  }
@@ -1636,6 +2055,97 @@ function attachTimeOnSite(client, config) {
1636
2055
  };
1637
2056
  }
1638
2057
 
2058
+ // ../tracking-core/src/triggers/navigation.ts
2059
+ var listeners = /* @__PURE__ */ new Set();
2060
+ var restorePatch = null;
2061
+ function notify() {
2062
+ for (const listener of listeners) listener();
2063
+ }
2064
+ function notifyDeferred() {
2065
+ setTimeout(notify, 0);
2066
+ }
2067
+ function installPatch() {
2068
+ const originalPushState = history.pushState;
2069
+ const originalReplaceState = history.replaceState;
2070
+ function patchedPushState(...args) {
2071
+ originalPushState.apply(this, args);
2072
+ notifyDeferred();
2073
+ }
2074
+ function patchedReplaceState(...args) {
2075
+ originalReplaceState.apply(this, args);
2076
+ notifyDeferred();
2077
+ }
2078
+ history.pushState = patchedPushState;
2079
+ history.replaceState = patchedReplaceState;
2080
+ window.addEventListener("popstate", notify);
2081
+ restorePatch = () => {
2082
+ history.pushState = originalPushState;
2083
+ history.replaceState = originalReplaceState;
2084
+ window.removeEventListener("popstate", notify);
2085
+ restorePatch = null;
2086
+ };
2087
+ }
2088
+ function onHistoryChange(listener) {
2089
+ if (listeners.size === 0) installPatch();
2090
+ listeners.add(listener);
2091
+ return () => {
2092
+ if (!listeners.delete(listener)) return;
2093
+ if (listeners.size === 0) restorePatch?.();
2094
+ };
2095
+ }
2096
+
2097
+ // ../tracking-core/src/triggers/scroll-measurement.ts
2098
+ var BOTTOM_EPSILON_PX = 2;
2099
+ function measureScrollPercent() {
2100
+ const root = document.scrollingElement ?? document.documentElement;
2101
+ const scrollHeight = root.scrollHeight;
2102
+ const clientHeight = root.clientHeight;
2103
+ if (scrollHeight <= 0 || clientHeight <= 0) return null;
2104
+ if (scrollHeight <= clientHeight + BOTTOM_EPSILON_PX) return null;
2105
+ const maxTop = scrollHeight - clientHeight;
2106
+ const scrollTop = Math.min(Math.max(root.scrollTop, 0), maxTop);
2107
+ if (scrollTop + clientHeight >= scrollHeight - BOTTOM_EPSILON_PX) return 100;
2108
+ return Math.max(1, Math.min(100, Math.round((scrollTop + clientHeight) / scrollHeight * 100)));
2109
+ }
2110
+ function scheduleBaselineSnapshot(onSnapshot) {
2111
+ let rafId = requestAnimationFrame(() => {
2112
+ rafId = requestAnimationFrame(() => {
2113
+ onSnapshot(measureScrollPercent());
2114
+ });
2115
+ });
2116
+ return () => cancelAnimationFrame(rafId);
2117
+ }
2118
+ function createBaselineGate() {
2119
+ let baselinePercent = null;
2120
+ let ready = false;
2121
+ let cancelSnapshot = null;
2122
+ return {
2123
+ rebaseline() {
2124
+ cancelSnapshot?.();
2125
+ ready = false;
2126
+ baselinePercent = null;
2127
+ cancelSnapshot = scheduleBaselineSnapshot((b) => {
2128
+ baselinePercent = b;
2129
+ ready = true;
2130
+ });
2131
+ },
2132
+ cancel() {
2133
+ cancelSnapshot?.();
2134
+ },
2135
+ baseline() {
2136
+ return ready ? baselinePercent : null;
2137
+ },
2138
+ sample() {
2139
+ if (!ready) return null;
2140
+ if (baselinePercent === null) {
2141
+ baselinePercent = measureScrollPercent();
2142
+ return null;
2143
+ }
2144
+ return measureScrollPercent();
2145
+ }
2146
+ };
2147
+ }
2148
+
1639
2149
  // ../tracking-core/src/triggers/scroll-depth.ts
1640
2150
  function attachScrollDepth(client, config) {
1641
2151
  if (typeof window === "undefined" || typeof document === "undefined") {
@@ -1646,17 +2156,14 @@ function attachScrollDepth(client, config) {
1646
2156
  let firedForPath = /* @__PURE__ */ new Set();
1647
2157
  let currentPath2 = window.location.pathname;
1648
2158
  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
- }
2159
+ const gate = createBaselineGate();
1657
2160
  function checkThresholds() {
1658
- const percent = getScrollPercent();
2161
+ const percent = gate.sample();
2162
+ if (percent === null) return;
2163
+ const baseline = gate.baseline();
2164
+ if (baseline === null) return;
1659
2165
  for (const threshold of thresholds) {
2166
+ if (threshold <= baseline) continue;
1660
2167
  if (percent >= threshold && !firedForPath.has(threshold)) {
1661
2168
  firedForPath.add(threshold);
1662
2169
  client.trackEvent({
@@ -1683,28 +2190,15 @@ function attachScrollDepth(client, config) {
1683
2190
  if (newPath === currentPath2) return;
1684
2191
  currentPath2 = newPath;
1685
2192
  firedForPath = /* @__PURE__ */ new Set();
1686
- setTimeout(checkThresholds, 0);
1687
- }
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);
2193
+ gate.rebaseline();
1697
2194
  }
1698
- history.pushState = patchedPushState;
1699
- history.replaceState = patchedReplaceState;
1700
- window.addEventListener("popstate", resetIfPathChanged);
2195
+ const unsubscribeNav = onHistoryChange(resetIfPathChanged);
1701
2196
  window.addEventListener("scroll", onScroll, { passive: true });
1702
- setTimeout(checkThresholds, 0);
2197
+ gate.rebaseline();
1703
2198
  return () => {
1704
2199
  if (rafId !== null) cancelAnimationFrame(rafId);
1705
- history.pushState = originalPushState;
1706
- history.replaceState = originalReplaceState;
1707
- window.removeEventListener("popstate", resetIfPathChanged);
2200
+ gate.cancel();
2201
+ unsubscribeNav();
1708
2202
  window.removeEventListener("scroll", onScroll);
1709
2203
  };
1710
2204
  }
@@ -1786,21 +2280,14 @@ function attachPageExit(client) {
1786
2280
  let accumulatedMs = 0;
1787
2281
  let maxScrollPercent = null;
1788
2282
  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
- }
2283
+ const gate = createBaselineGate();
1797
2284
  function onScroll() {
1798
2285
  if (rafId !== null) return;
1799
2286
  rafId = requestAnimationFrame(() => {
1800
2287
  rafId = null;
1801
- const percent = getScrollPercent();
1802
- if (percent >= 1 && (maxScrollPercent === null || percent > maxScrollPercent)) {
1803
- maxScrollPercent = Math.min(percent, 100);
2288
+ const percent = gate.sample();
2289
+ if (percent !== null && (maxScrollPercent === null || percent > maxScrollPercent)) {
2290
+ maxScrollPercent = percent;
1804
2291
  }
1805
2292
  });
1806
2293
  }
@@ -1819,6 +2306,12 @@ function attachPageExit(client) {
1819
2306
  metadata: {
1820
2307
  dwell_ms: dwell,
1821
2308
  max_scroll_percent: maxScrollPercent,
2309
+ // Lets the backend tell "scrolled to the bottom" apart from "the page
2310
+ // was barely scrollable". null = unscrollable page or the segment
2311
+ // ended before the post-paint snapshot landed. For pages that grew
2312
+ // after the snapshot this is the first-scroll position, not the
2313
+ // at-load fraction (see BaselineGate.baseline).
2314
+ scroll_baseline_percent: gate.baseline(),
1822
2315
  page: { path }
1823
2316
  },
1824
2317
  pageUrl: window.location.href,
@@ -1836,6 +2329,7 @@ function attachPageExit(client) {
1836
2329
  emitSegment(currentPath2, false);
1837
2330
  currentPath2 = newPath;
1838
2331
  maxScrollPercent = null;
2332
+ gate.rebaseline();
1839
2333
  accumulatedMs = 0;
1840
2334
  activeSince = document.visibilityState === "visible" ? Date.now() : null;
1841
2335
  }
@@ -1849,27 +2343,15 @@ function attachPageExit(client) {
1849
2343
  function onPageHide() {
1850
2344
  emitSegment(currentPath2, true);
1851
2345
  }
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);
2346
+ const unsubscribeNav = onHistoryChange(onNavigate);
1865
2347
  window.addEventListener("scroll", onScroll, { passive: true });
1866
2348
  document.addEventListener("visibilitychange", onVisibilityChange);
1867
2349
  window.addEventListener("pagehide", onPageHide);
2350
+ gate.rebaseline();
1868
2351
  return () => {
1869
2352
  if (rafId !== null) cancelAnimationFrame(rafId);
1870
- history.pushState = originalPushState;
1871
- history.replaceState = originalReplaceState;
1872
- window.removeEventListener("popstate", onNavigate);
2353
+ gate.cancel();
2354
+ unsubscribeNav();
1873
2355
  window.removeEventListener("scroll", onScroll);
1874
2356
  document.removeEventListener("visibilitychange", onVisibilityChange);
1875
2357
  window.removeEventListener("pagehide", onPageHide);
@@ -2084,6 +2566,14 @@ function fireRecordedConversions(firing, input, recorded, sale, currency) {
2084
2566
  const txnBase = input.external_id ?? sale.id;
2085
2567
  for (const item of recorded) {
2086
2568
  if (!item.service) continue;
2569
+ if ("fireConversion" in firing) {
2570
+ firing.fireConversion(item.service, {
2571
+ value: item.amount_cents != null ? fromMinor(item.amount_cents, currency) : void 0,
2572
+ currency,
2573
+ transactionId: `${txnBase}:${item.service}`
2574
+ });
2575
+ continue;
2576
+ }
2087
2577
  const config = firing.getFiring(item.service);
2088
2578
  if (!config) continue;
2089
2579
  const cents = item.amount_cents ?? config.value_cents ?? null;
@@ -2126,6 +2616,14 @@ function createSalesClient(config) {
2126
2616
  // recordSale is the intent-revealing alias — same behavior, clearer call site.
2127
2617
  recordSale: record,
2128
2618
  trackConversion(key, options) {
2619
+ if (config.firing && "fireConversion" in config.firing) {
2620
+ config.firing.fireConversion(key, {
2621
+ value: options?.value ?? void 0,
2622
+ currency: options?.currency ?? config.defaultCurrency ?? void 0,
2623
+ transactionId: options?.transactionId ?? null
2624
+ });
2625
+ return;
2626
+ }
2129
2627
  const firing = config.firing?.getFiring(key);
2130
2628
  if (!firing) return;
2131
2629
  const currency = firing.currency ?? options?.currency ?? config.defaultCurrency ?? null;
@@ -2631,14 +3129,24 @@ var ANIMATION_KEYFRAMES = `
2631
3129
 
2632
3130
  // src/AdPlatformTracking.tsx
2633
3131
  var import_script = __toESM(require("next/script"));
3132
+ var import_react3 = require("react");
2634
3133
  var import_jsx_runtime2 = require("react/jsx-runtime");
2635
3134
  function AdPlatformTracking({
2636
3135
  gtagId,
2637
3136
  gtagIds,
3137
+ trackingConfig,
3138
+ standalonePageView = false,
2638
3139
  metaPixelId,
2639
3140
  metaPixelIds
2640
3141
  }) {
2641
- const gtagAll = gtagIds ? Object.values(gtagIds).filter((id) => id != null) : gtagId ? [gtagId] : [];
3142
+ const trackingConfigKey = trackingConfig ? `${trackingConfig.cdnUrl}:${trackingConfig.businessId}:${trackingConfig.environment}` : "";
3143
+ (0, import_react3.useEffect)(() => {
3144
+ if (!trackingConfig) return;
3145
+ const runtime = getTrackingConfigRuntime(trackingConfig);
3146
+ if (standalonePageView) runtime.queuePageView();
3147
+ else void runtime.revalidate();
3148
+ }, [trackingConfigKey, standalonePageView]);
3149
+ const gtagAll = trackingConfig ? [] : gtagIds ? Object.values(gtagIds).filter((id) => id != null) : gtagId ? [gtagId] : [];
2642
3150
  const metaAll = metaPixelIds ? Object.values(metaPixelIds).filter((id) => id != null) : metaPixelId ? [metaPixelId] : [];
2643
3151
  const gtagLoaderId = gtagAll[0];
2644
3152
  const gtagInitScript = gtagAll.length === 1 ? createGtagInitScript(gtagAll[0]) : createGtagInitScriptMulti(gtagAll);
@@ -2685,20 +3193,20 @@ function GoogleAdsTracking(props) {
2685
3193
  }
2686
3194
 
2687
3195
  // src/factory.tsx
2688
- var import_navigation = require("next/navigation");
3196
+ var import_navigation3 = require("next/navigation");
2689
3197
 
2690
3198
  // package.json
2691
- var version = "0.17.2";
3199
+ var version = "0.18.0";
2692
3200
 
2693
3201
  // src/factory.tsx
2694
- var import_react4 = require("react");
3202
+ var import_react5 = require("react");
2695
3203
 
2696
3204
  // ../tracking-core/src/phone-react.tsx
2697
- var import_react3 = require("react");
3205
+ var import_react4 = require("react");
2698
3206
  var import_jsx_runtime4 = require("react/jsx-runtime");
2699
3207
  var _phoneConfigContext;
2700
3208
  function phoneConfigContext() {
2701
- return _phoneConfigContext ?? (_phoneConfigContext = (0, import_react3.createContext)(null));
3209
+ return _phoneConfigContext ?? (_phoneConfigContext = (0, import_react4.createContext)(null));
2702
3210
  }
2703
3211
  function PhoneConfigProvider({
2704
3212
  value,
@@ -2708,7 +3216,7 @@ function PhoneConfigProvider({
2708
3216
  return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Ctx.Provider, { value, children });
2709
3217
  }
2710
3218
  function usePhoneConfig() {
2711
- const ctx = (0, import_react3.useContext)(phoneConfigContext());
3219
+ const ctx = (0, import_react4.useContext)(phoneConfigContext());
2712
3220
  return {
2713
3221
  defaultCountry: ctx?.defaultCountry ?? DEFAULT_PHONE_COUNTRY,
2714
3222
  display: ctx?.display ?? "national"
@@ -2719,10 +3227,10 @@ function usePhoneField(opts = {}) {
2719
3227
  const country = opts.country ?? cfg.defaultCountry;
2720
3228
  const display = opts.display ?? cfg.display;
2721
3229
  const { onValueChange } = opts;
2722
- const [value, setValue] = (0, import_react3.useState)(() => formatPhoneAsTyped(opts.defaultValue ?? "", country));
2723
- const [touched, setTouched] = (0, import_react3.useState)(false);
2724
- const parsed = (0, import_react3.useMemo)(() => parsePhone(value, country), [value, country]);
2725
- const onChange = (0, import_react3.useCallback)(
3230
+ const [value, setValue] = (0, import_react4.useState)(() => formatPhoneAsTyped(opts.defaultValue ?? "", country));
3231
+ const [touched, setTouched] = (0, import_react4.useState)(false);
3232
+ const parsed = (0, import_react4.useMemo)(() => parsePhone(value, country), [value, country]);
3233
+ const onChange = (0, import_react4.useCallback)(
2726
3234
  (event) => {
2727
3235
  const next = formatPhoneAsTyped(event.target.value, country);
2728
3236
  setValue(next);
@@ -2730,7 +3238,7 @@ function usePhoneField(opts = {}) {
2730
3238
  },
2731
3239
  [country, onValueChange]
2732
3240
  );
2733
- const onBlur = (0, import_react3.useCallback)(
3241
+ const onBlur = (0, import_react4.useCallback)(
2734
3242
  (_event) => {
2735
3243
  setTouched(true);
2736
3244
  setValue((current) => {
@@ -2750,11 +3258,11 @@ function usePhoneField(opts = {}) {
2750
3258
  inputProps: { value, onChange, onBlur, type: "tel", inputMode: "tel", autoComplete: "tel" }
2751
3259
  };
2752
3260
  }
2753
- var PhoneField = (0, import_react3.forwardRef)(function PhoneField2({ country, value, defaultValue, onChange, onE164Change, ...rest }, ref) {
3261
+ var PhoneField = (0, import_react4.forwardRef)(function PhoneField2({ country, value, defaultValue, onChange, onE164Change, ...rest }, ref) {
2754
3262
  const cfg = usePhoneConfig();
2755
3263
  const resolvedCountry = country ?? cfg.defaultCountry;
2756
3264
  const isControlled = value !== void 0;
2757
- const [internal, setInternal] = (0, import_react3.useState)(
3265
+ const [internal, setInternal] = (0, import_react4.useState)(
2758
3266
  () => formatPhoneAsTyped(defaultValue ?? "", resolvedCountry)
2759
3267
  );
2760
3268
  const handleChange = (event) => {
@@ -2782,15 +3290,25 @@ var PhoneField = (0, import_react3.forwardRef)(function PhoneField2({ country, v
2782
3290
  // src/factory.tsx
2783
3291
  var import_jsx_runtime5 = require("react/jsx-runtime");
2784
3292
  function PageViewTracker({ fire }) {
2785
- const pathname = (0, import_navigation.usePathname)();
2786
- const searchParams = (0, import_navigation.useSearchParams)();
2787
- (0, import_react4.useEffect)(() => {
3293
+ const pathname = (0, import_navigation3.usePathname)();
3294
+ const searchParams = (0, import_navigation3.useSearchParams)();
3295
+ (0, import_react5.useEffect)(() => {
2788
3296
  fire();
2789
3297
  }, [pathname, searchParams, fire]);
2790
3298
  return null;
2791
3299
  }
2792
3300
  function createTracking(options) {
2793
- const { apiKey, endpoint, triggers, environment, gtagIds, debug, phone, conversionConfig } = options;
3301
+ const {
3302
+ apiKey,
3303
+ endpoint,
3304
+ triggers,
3305
+ environment,
3306
+ gtagIds,
3307
+ debug,
3308
+ phone,
3309
+ conversionConfig,
3310
+ trackingConfig
3311
+ } = options;
2794
3312
  if (!apiKey || !endpoint) {
2795
3313
  if (apiKey || endpoint) {
2796
3314
  console.warn(
@@ -2810,12 +3328,12 @@ function createTracking(options) {
2810
3328
  useTracking: () => noopClient
2811
3329
  };
2812
3330
  }
2813
- const TrackingContext = (0, import_react4.createContext)(null);
3331
+ const TrackingContext = (0, import_react5.createContext)(null);
2814
3332
  const specificPageVisitConfig = triggers.automatic.specific_page_visit ?? null;
2815
3333
  const timeOnSiteConfig = triggers.automatic.time_on_site ?? null;
2816
3334
  function TrackingProvider({ children }) {
2817
- const pathname = (0, import_navigation.usePathname)();
2818
- const client = (0, import_react4.useMemo)(
3335
+ const pathname = (0, import_navigation3.usePathname)();
3336
+ const client = (0, import_react5.useMemo)(
2819
3337
  () => createTypedClient(
2820
3338
  getOrCreateTrackingClient({
2821
3339
  apiKey,
@@ -2833,18 +3351,18 @@ function createTracking(options) {
2833
3351
  ),
2834
3352
  []
2835
3353
  );
2836
- const conversionStore = (0, import_react4.useMemo)(
2837
- () => conversionConfig ? resolveConversionConfig({
3354
+ const conversionStore = (0, import_react5.useMemo)(
3355
+ () => trackingConfig ? getTrackingConfigRuntime(trackingConfig) : conversionConfig ? resolveConversionConfig({
2838
3356
  cdnUrl: conversionConfig.cdnUrl,
2839
3357
  baked: conversionConfig.baked
2840
3358
  }) : null,
2841
3359
  []
2842
3360
  );
2843
- const wrapClient = (0, import_react4.useMemo)(() => {
3361
+ const wrapClient = (0, import_react5.useMemo)(() => {
2844
3362
  const autoFire = conversionStore ? createConversionAutoFire(conversionStore) : null;
2845
3363
  return (c) => autoFire ? withConversionAutoFire(c, autoFire) : c;
2846
3364
  }, [conversionStore]);
2847
- const firePageView = (0, import_react4.useCallback)(() => {
3365
+ const firePageView = (0, import_react5.useCallback)(() => {
2848
3366
  const rawClient = getOrCreateTrackingClient({
2849
3367
  apiKey,
2850
3368
  endpoint,
@@ -2856,9 +3374,10 @@ function createTracking(options) {
2856
3374
  debug
2857
3375
  });
2858
3376
  fireManualPageView(wrapClient(rawClient));
3377
+ if (trackingConfig) getTrackingConfigRuntime(trackingConfig).queuePageView();
2859
3378
  }, [wrapClient]);
2860
- const firedPagesRef = (0, import_react4.useRef)(/* @__PURE__ */ new Set());
2861
- (0, import_react4.useEffect)(() => {
3379
+ const firedPagesRef = (0, import_react5.useRef)(/* @__PURE__ */ new Set());
3380
+ (0, import_react5.useEffect)(() => {
2862
3381
  if (!specificPageVisitConfig || pathname === null) return;
2863
3382
  const rawClient = getOrCreateTrackingClient({
2864
3383
  apiKey,
@@ -2888,8 +3407,8 @@ function createTracking(options) {
2888
3407
  }
2889
3408
  }, [pathname]);
2890
3409
  const multiPageSessionConfig = triggers.automatic.multi_page_session ?? null;
2891
- const mpsLastPathRef = (0, import_react4.useRef)(null);
2892
- (0, import_react4.useEffect)(() => {
3410
+ const mpsLastPathRef = (0, import_react5.useRef)(null);
3411
+ (0, import_react5.useEffect)(() => {
2893
3412
  if (!multiPageSessionConfig || pathname === null) return;
2894
3413
  if (pathname === mpsLastPathRef.current) return;
2895
3414
  mpsLastPathRef.current = pathname;
@@ -2936,7 +3455,7 @@ function createTracking(options) {
2936
3455
  } catch {
2937
3456
  }
2938
3457
  }, [pathname]);
2939
- (0, import_react4.useEffect)(() => {
3458
+ (0, import_react5.useEffect)(() => {
2940
3459
  const rawClient = getOrCreateTrackingClient({
2941
3460
  apiKey,
2942
3461
  endpoint,
@@ -2979,12 +3498,12 @@ function createTracking(options) {
2979
3498
  });
2980
3499
  }, []);
2981
3500
  return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(TrackingContext.Provider, { value: client, children: [
2982
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_react4.Suspense, { fallback: null, children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(PageViewTracker, { fire: firePageView }) }),
3501
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_react5.Suspense, { fallback: null, children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(PageViewTracker, { fire: firePageView }) }),
2983
3502
  /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(PhoneConfigProvider, { value: phone ?? null, children })
2984
3503
  ] });
2985
3504
  }
2986
3505
  function useTracking() {
2987
- const client = (0, import_react4.useContext)(TrackingContext);
3506
+ const client = (0, import_react5.useContext)(TrackingContext);
2988
3507
  if (client === null) {
2989
3508
  throw new Error(
2990
3509
  "useTracking must be called inside a <TrackingProvider> returned by createTracking()"
@@ -3001,6 +3520,7 @@ function createTracking(options) {
3001
3520
  ConsentBanner,
3002
3521
  DEFAULT_DECLINE_TTL_DAYS,
3003
3522
  DEFAULT_PHONE_COUNTRY,
3523
+ EVENT_SEMANTICS,
3004
3524
  GoogleAdsTracking,
3005
3525
  NAMED_RANGES,
3006
3526
  PhoneField,
@@ -3021,6 +3541,8 @@ function createTracking(options) {
3021
3541
  fromMinor,
3022
3542
  getConsentChoice,
3023
3543
  getConsentState,
3544
+ getEventSemantics,
3545
+ getTrackingConfigRuntime,
3024
3546
  onConsentChange,
3025
3547
  optIn,
3026
3548
  optOut,