@aranova/tracking-react 0.13.0 → 0.14.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
@@ -46,6 +46,7 @@ __export(src_exports, {
46
46
  parsePhone: () => parsePhone,
47
47
  phoneField: () => phoneField,
48
48
  resetConsent: () => resetConsent,
49
+ resolveConversionConfig: () => resolveConversionConfig,
49
50
  saleCreateSchema: () => saleCreateSchema,
50
51
  saleItemSchema: () => saleItemSchema,
51
52
  saleServiceSchema: () => saleServiceSchema,
@@ -72,6 +73,13 @@ var import_react = require("react");
72
73
  // ../tracking-core/src/consent.ts
73
74
  var CONSENT_STATE_KEY = "consent_state";
74
75
  var CONSENT_TIMESTAMP_KEY = "consent_timestamp";
76
+ var grantedListeners = /* @__PURE__ */ new Set();
77
+ function onConsentGranted(listener) {
78
+ grantedListeners.add(listener);
79
+ return () => {
80
+ grantedListeners.delete(listener);
81
+ };
82
+ }
75
83
  function buildConsentPayload(state) {
76
84
  return {
77
85
  ad_storage: state,
@@ -100,6 +108,14 @@ function setConsentState(state) {
100
108
  window.gtag("consent", "update", buildConsentPayload(state));
101
109
  if (typeof window.fbq === "function")
102
110
  window.fbq("consent", state === "granted" ? "grant" : "revoke");
111
+ if (state === "granted") {
112
+ for (const listener of grantedListeners) {
113
+ try {
114
+ listener();
115
+ } catch {
116
+ }
117
+ }
118
+ }
103
119
  }
104
120
  function resetConsent() {
105
121
  if (typeof window === "undefined") return;
@@ -230,11 +246,30 @@ function isValidGtagId(id) {
230
246
  function ensureGtagFunction() {
231
247
  window.dataLayer = window.dataLayer || [];
232
248
  if (typeof window.gtag === "function") return window.gtag;
233
- window.gtag = (...args) => {
234
- window.dataLayer?.push(args);
235
- };
249
+ function gtag() {
250
+ window.dataLayer?.push(arguments);
251
+ }
252
+ window.gtag = gtag;
236
253
  return window.gtag;
237
254
  }
255
+ var SEND_TO_RE = /^AW-[A-Za-z0-9]+\/[A-Za-z0-9_-]+$/;
256
+ function isValidSendTo(sendTo) {
257
+ return SEND_TO_RE.test(sendTo);
258
+ }
259
+ function fireGtagConversion(input) {
260
+ if (typeof window === "undefined" || typeof window.gtag !== "function") return false;
261
+ if (!isValidSendTo(input.sendTo)) return false;
262
+ const params = { send_to: input.sendTo };
263
+ if (input.value != null) params.value = input.value;
264
+ if (input.currency) params.currency = input.currency;
265
+ if (input.transactionId) params.transaction_id = input.transactionId;
266
+ try {
267
+ window.gtag("event", "conversion", params);
268
+ return true;
269
+ } catch {
270
+ return false;
271
+ }
272
+ }
238
273
  function applyDefaultConsentState() {
239
274
  const gtag = ensureGtagFunction();
240
275
  gtag("consent", "default", {
@@ -441,6 +476,329 @@ function createTrackingEventCreatePayload(trackingParams, input, context) {
441
476
  };
442
477
  }
443
478
 
479
+ // ../tracking-core/src/resources/conversion-firing.ts
480
+ var DEDUP_PREFIX = "_aranova_conv_";
481
+ var MAX_PENDING = 100;
482
+ var pendingQueue = [];
483
+ function dedupKey(input) {
484
+ return `${DEDUP_PREFIX}${input.transactionId ?? ""}:${input.sendTo}`;
485
+ }
486
+ function alreadyFired(input) {
487
+ if (!input.transactionId || typeof window === "undefined") return false;
488
+ try {
489
+ return window.sessionStorage.getItem(dedupKey(input)) !== null;
490
+ } catch {
491
+ return false;
492
+ }
493
+ }
494
+ function markFired(input) {
495
+ if (!input.transactionId || typeof window === "undefined") return;
496
+ try {
497
+ window.sessionStorage.setItem(dedupKey(input), "1");
498
+ } catch {
499
+ }
500
+ }
501
+ function fireOnce(input) {
502
+ if (alreadyFired(input)) return;
503
+ if (fireGtagConversion(input)) markFired(input);
504
+ }
505
+ function fireConversionWithConsent(input) {
506
+ const state = getConsentState();
507
+ if (state === "denied") return;
508
+ if (state === "pending") {
509
+ if (pendingQueue.length >= MAX_PENDING) pendingQueue.shift();
510
+ pendingQueue.push(input);
511
+ return;
512
+ }
513
+ fireOnce(input);
514
+ }
515
+ function flushPendingConversions() {
516
+ if (getConsentState() !== "granted") return;
517
+ while (pendingQueue.length > 0) {
518
+ const input = pendingQueue.shift();
519
+ if (input) fireOnce(input);
520
+ }
521
+ }
522
+ if (typeof window !== "undefined") onConsentGranted(flushPendingConversions);
523
+
524
+ // ../tracking-core/src/resources/conversion-config.ts
525
+ function isStringMap(value) {
526
+ return typeof value === "object" && value !== null && Object.values(value).every((v) => typeof v === "string");
527
+ }
528
+ function parseFiring(value) {
529
+ if (!value || typeof value !== "object") return null;
530
+ const f = value;
531
+ if (typeof f.send_to !== "string") return null;
532
+ return {
533
+ send_to: f.send_to,
534
+ value_cents: typeof f.value_cents === "number" ? f.value_cents : null,
535
+ currency: typeof f.currency === "string" ? f.currency : null
536
+ };
537
+ }
538
+ function parseTrigger(value) {
539
+ if (!value || typeof value !== "object") return null;
540
+ const t = value;
541
+ if (typeof t.event_type !== "string") return null;
542
+ const spec = { event_type: t.event_type };
543
+ if (typeof t.threshold_percent === "number") spec.threshold_percent = t.threshold_percent;
544
+ if (typeof t.threshold_seconds === "number") spec.threshold_seconds = t.threshold_seconds;
545
+ if (typeof t.page_threshold === "number") spec.page_threshold = t.page_threshold;
546
+ if (typeof t.page_name === "string") spec.page_name = t.page_name;
547
+ return spec;
548
+ }
549
+ function parseConversionConfig(raw) {
550
+ if (!raw || typeof raw !== "object") return null;
551
+ const obj = raw;
552
+ const servicesRaw = Array.isArray(obj.services) ? obj.services : [];
553
+ const services = servicesRaw.flatMap((entry) => {
554
+ if (!entry || typeof entry !== "object") return [];
555
+ const s = entry;
556
+ if (typeof s.key !== "string") return [];
557
+ return [
558
+ {
559
+ key: s.key,
560
+ label: typeof s.label === "string" ? s.label : void 0,
561
+ firing: parseFiring(s.firing)
562
+ }
563
+ ];
564
+ });
565
+ const goalsRaw = Array.isArray(obj.goals) ? obj.goals : null;
566
+ const goals = goalsRaw ? goalsRaw.flatMap((entry) => {
567
+ if (!entry || typeof entry !== "object") return [];
568
+ const g = entry;
569
+ if (typeof g.key !== "string") return [];
570
+ return [
571
+ {
572
+ key: g.key,
573
+ label: typeof g.label === "string" ? g.label : void 0,
574
+ kind: g.kind === "event" ? "event" : "sale",
575
+ trigger: parseTrigger(g.trigger),
576
+ firing: parseFiring(g.firing)
577
+ }
578
+ ];
579
+ }) : services.map((s) => ({
580
+ key: s.key,
581
+ label: s.label,
582
+ kind: "sale",
583
+ trigger: null,
584
+ firing: s.firing
585
+ }));
586
+ return {
587
+ schema_version: typeof obj.schema_version === "number" ? obj.schema_version : 1,
588
+ config_version: typeof obj.config_version === "number" ? obj.config_version : 0,
589
+ business_id: typeof obj.business_id === "string" ? obj.business_id : void 0,
590
+ customer_id: typeof obj.customer_id === "string" ? obj.customer_id : null,
591
+ environment: typeof obj.environment === "string" ? obj.environment : void 0,
592
+ gtag_ids: isStringMap(obj.gtag_ids) ? obj.gtag_ids : {},
593
+ meta_pixel_ids: isStringMap(obj.meta_pixel_ids) ? obj.meta_pixel_ids : {},
594
+ services,
595
+ goals
596
+ };
597
+ }
598
+ var CACHE_PREFIX = "_aranova_cfg_";
599
+ function cacheKey(url) {
600
+ return `${CACHE_PREFIX}${url}`;
601
+ }
602
+ function readCache(url) {
603
+ if (typeof window === "undefined") return null;
604
+ try {
605
+ const raw = window.sessionStorage.getItem(cacheKey(url));
606
+ if (!raw) return null;
607
+ const parsed = JSON.parse(raw);
608
+ const config = parseConversionConfig(parsed.config);
609
+ if (!config) return null;
610
+ return {
611
+ etag: typeof parsed.etag === "string" ? parsed.etag : null,
612
+ config
613
+ };
614
+ } catch {
615
+ return null;
616
+ }
617
+ }
618
+ function writeCache(url, entry) {
619
+ if (typeof window === "undefined") return;
620
+ try {
621
+ window.sessionStorage.setItem(cacheKey(url), JSON.stringify(entry));
622
+ } catch {
623
+ }
624
+ }
625
+ function resolveConversionConfig(options) {
626
+ const cached = readCache(options.cdnUrl);
627
+ let current = cached?.config ?? options.baked ?? null;
628
+ let etag = cached?.etag ?? null;
629
+ const goalsByKey = /* @__PURE__ */ new Map();
630
+ const resolveListeners = /* @__PURE__ */ new Set();
631
+ function rebuildIndex() {
632
+ goalsByKey.clear();
633
+ for (const goal of current?.goals ?? []) {
634
+ goalsByKey.set(goal.key, goal);
635
+ }
636
+ }
637
+ function notifyResolved() {
638
+ const listeners = [...resolveListeners];
639
+ resolveListeners.clear();
640
+ for (const listener of listeners) {
641
+ try {
642
+ listener();
643
+ } catch {
644
+ }
645
+ }
646
+ }
647
+ function adopt(next, nextEtag) {
648
+ if (!next) return;
649
+ if (current && next.config_version <= current.config_version) return;
650
+ const wasEmpty = current === null;
651
+ current = next;
652
+ etag = nextEtag;
653
+ rebuildIndex();
654
+ writeCache(options.cdnUrl, { etag, config: next });
655
+ if (wasEmpty) notifyResolved();
656
+ }
657
+ async function revalidate() {
658
+ if (typeof window === "undefined") return;
659
+ try {
660
+ const doFetch = options.fetchImpl ?? globalThis.fetch;
661
+ if (!doFetch) return;
662
+ const headers = {};
663
+ if (etag) headers["If-None-Match"] = etag;
664
+ const response = await doFetch(options.cdnUrl, {
665
+ method: "GET",
666
+ headers
667
+ });
668
+ if (response.status === 304 || !response.ok) return;
669
+ adopt(parseConversionConfig(await response.json()), response.headers.get("ETag"));
670
+ } catch {
671
+ }
672
+ }
673
+ rebuildIndex();
674
+ void revalidate();
675
+ return {
676
+ getFiring: (key) => goalsByKey.get(key)?.firing ?? null,
677
+ getGoal: (key) => goalsByKey.get(key) ?? null,
678
+ listGoals: () => [...goalsByKey.values()],
679
+ current: () => current,
680
+ isReady: () => current !== null,
681
+ onResolve: (listener) => {
682
+ if (current !== null) {
683
+ listener();
684
+ return () => {
685
+ };
686
+ }
687
+ resolveListeners.add(listener);
688
+ return () => resolveListeners.delete(listener);
689
+ },
690
+ revalidate
691
+ };
692
+ }
693
+
694
+ // ../tracking-core/src/resources/sales/money.ts
695
+ var MINOR_UNIT_EXPONENT = {
696
+ USD: 2,
697
+ CAD: 2
698
+ };
699
+ function exponentFor(currency) {
700
+ return MINOR_UNIT_EXPONENT[currency] ?? 2;
701
+ }
702
+ function toMinor(amount, currency) {
703
+ return Math.round(amount * 10 ** exponentFor(currency));
704
+ }
705
+ function fromMinor(cents, currency) {
706
+ return cents / 10 ** exponentFor(currency);
707
+ }
708
+ function formatMoney(cents, currency, locale) {
709
+ return new Intl.NumberFormat(locale, { style: "currency", currency }).format(
710
+ fromMinor(cents, currency)
711
+ );
712
+ }
713
+ function formatDateInTz(iso, timeZone, opts, locale) {
714
+ const date = new Date(iso);
715
+ if (Number.isNaN(date.getTime())) return iso;
716
+ return new Intl.DateTimeFormat(locale, {
717
+ year: "numeric",
718
+ month: "short",
719
+ day: "2-digit",
720
+ hour: "2-digit",
721
+ minute: "2-digit",
722
+ ...opts,
723
+ timeZone
724
+ }).format(date);
725
+ }
726
+
727
+ // ../tracking-core/src/resources/conversion-autofire.ts
728
+ function thresholdMet(goal, eventType, metadata) {
729
+ const t = goal.trigger;
730
+ if (!t || t.event_type !== eventType) return false;
731
+ switch (eventType) {
732
+ case "scroll_depth":
733
+ return typeof metadata.depth_percent === "number" && t.threshold_percent != null && metadata.depth_percent >= t.threshold_percent;
734
+ case "time_on_site":
735
+ return typeof metadata.duration_ms === "number" && t.threshold_seconds != null && metadata.duration_ms >= t.threshold_seconds * 1e3;
736
+ case "multi_page_session":
737
+ return typeof metadata.page_count === "number" && t.page_threshold != null && metadata.page_count >= t.page_threshold;
738
+ case "specific_page_visit":
739
+ return typeof metadata.page_name === "string" && metadata.page_name === t.page_name;
740
+ case "page_view":
741
+ case "form_start":
742
+ return true;
743
+ // no threshold — fire whenever the detector emits
744
+ default:
745
+ return false;
746
+ }
747
+ }
748
+ function currentPath() {
749
+ return typeof window === "undefined" ? "" : window.location.pathname;
750
+ }
751
+ var MAX_BUFFERED_EVENTS = 50;
752
+ function createConversionAutoFire(store) {
753
+ const pending = [];
754
+ let subscribed = false;
755
+ function fireMatching(eventType, metadata) {
756
+ for (const goal of store.listGoals()) {
757
+ if (goal.kind !== "event" || !goal.firing) continue;
758
+ if (!thresholdMet(goal, eventType, metadata)) continue;
759
+ const firing = goal.firing;
760
+ const cents = firing.value_cents ?? null;
761
+ const currency = firing.currency ?? null;
762
+ fireConversionWithConsent({
763
+ sendTo: firing.send_to,
764
+ value: cents != null && currency ? fromMinor(cents, currency) : null,
765
+ currency,
766
+ // Page-scoped txn id → fire once per (goal, path) per session; engagement conversions
767
+ // shouldn't re-fire as the visitor scrolls back and forth or re-enters a page.
768
+ transactionId: `auto:${goal.key}:${currentPath()}`
769
+ });
770
+ }
771
+ }
772
+ return {
773
+ onAutomaticEvent(eventType, metadata) {
774
+ if (store.isReady()) {
775
+ fireMatching(eventType, metadata);
776
+ return;
777
+ }
778
+ if (pending.length < MAX_BUFFERED_EVENTS) pending.push({ eventType, metadata });
779
+ if (!subscribed) {
780
+ subscribed = true;
781
+ store.onResolve(() => {
782
+ const buffered = pending.splice(0);
783
+ for (const event of buffered) fireMatching(event.eventType, event.metadata);
784
+ });
785
+ }
786
+ }
787
+ };
788
+ }
789
+ function withConversionAutoFire(client, autoFire) {
790
+ return {
791
+ ...client,
792
+ trackEvent: (input) => {
793
+ client.trackEvent(input);
794
+ try {
795
+ autoFire.onAutomaticEvent(input.eventType, input.metadata ?? {});
796
+ } catch {
797
+ }
798
+ }
799
+ };
800
+ }
801
+
444
802
  // ../tracking-core/src/session.ts
445
803
  var VISITOR_STORAGE_KEY = "aranova_tracking_visitor";
446
804
  var SESSION_STORAGE_KEY = "aranova_tracking_session";
@@ -1260,7 +1618,7 @@ function attachScrollDepth(client, config) {
1260
1618
  }
1261
1619
  const thresholds = new Set(config.thresholds);
1262
1620
  let firedForPath = /* @__PURE__ */ new Set();
1263
- let currentPath = window.location.pathname;
1621
+ let currentPath2 = window.location.pathname;
1264
1622
  let rafId = null;
1265
1623
  function getScrollPercent() {
1266
1624
  const doc = document.documentElement;
@@ -1279,7 +1637,7 @@ function attachScrollDepth(client, config) {
1279
1637
  eventType: "scroll_depth",
1280
1638
  metadata: {
1281
1639
  depth_percent: threshold,
1282
- page: { path: currentPath }
1640
+ page: { path: currentPath2 }
1283
1641
  },
1284
1642
  pageUrl: window.location.href,
1285
1643
  occurredAt: null
@@ -1296,8 +1654,8 @@ function attachScrollDepth(client, config) {
1296
1654
  }
1297
1655
  function resetIfPathChanged() {
1298
1656
  const newPath = window.location.pathname;
1299
- if (newPath === currentPath) return;
1300
- currentPath = newPath;
1657
+ if (newPath === currentPath2) return;
1658
+ currentPath2 = newPath;
1301
1659
  firedForPath = /* @__PURE__ */ new Set();
1302
1660
  setTimeout(checkThresholds, 0);
1303
1661
  }
@@ -1373,13 +1731,13 @@ function attachMultiPageSession(client, config) {
1373
1731
  return storage.getItem(FIRED_KEY) === "1";
1374
1732
  }
1375
1733
  function check() {
1376
- const currentPath = window.location.pathname;
1377
- if (currentPath === lastCheckedPath) return;
1378
- lastCheckedPath = currentPath;
1734
+ const currentPath2 = window.location.pathname;
1735
+ if (currentPath2 === lastCheckedPath) return;
1736
+ lastCheckedPath = currentPath2;
1379
1737
  resetIfSessionChanged();
1380
1738
  if (hasFired()) return;
1381
1739
  const paths = getDistinctPaths();
1382
- paths.add(currentPath);
1740
+ paths.add(currentPath2);
1383
1741
  saveDistinctPaths(paths);
1384
1742
  if (paths.size >= pageThreshold) {
1385
1743
  storage.setItem(FIRED_KEY, "1");
@@ -1423,7 +1781,7 @@ function attachFormStart(client, config) {
1423
1781
  }
1424
1782
  const selector = config.selector ?? "form";
1425
1783
  let firedForms = /* @__PURE__ */ new Set();
1426
- let currentPath = window.location.pathname;
1784
+ let currentPath2 = window.location.pathname;
1427
1785
  function getFormKey(form) {
1428
1786
  if (form.id) return `id:${form.id}`;
1429
1787
  const explicitAction = form.getAttribute("action");
@@ -1454,8 +1812,8 @@ function attachFormStart(client, config) {
1454
1812
  }
1455
1813
  function resetIfPathChanged() {
1456
1814
  const newPath = window.location.pathname;
1457
- if (newPath === currentPath) return;
1458
- currentPath = newPath;
1815
+ if (newPath === currentPath2) return;
1816
+ currentPath2 = newPath;
1459
1817
  firedForms = /* @__PURE__ */ new Set();
1460
1818
  }
1461
1819
  const originalPushState = history.pushState.bind(history);
@@ -1534,21 +1892,64 @@ async function salesRequest(config, method, path, body) {
1534
1892
  }
1535
1893
 
1536
1894
  // ../tracking-core/src/resources/sales/client.ts
1895
+ function fireRecordedConversions(firing, input, recorded, sale, currency) {
1896
+ if (!firing) return;
1897
+ const txnBase = input.external_id ?? sale.id;
1898
+ for (const item of recorded) {
1899
+ if (!item.service) continue;
1900
+ const config = firing.getFiring(item.service);
1901
+ if (!config) continue;
1902
+ const cents = item.amount_cents ?? config.value_cents ?? null;
1903
+ fireConversionWithConsent({
1904
+ sendTo: config.send_to,
1905
+ value: cents != null ? fromMinor(cents, currency) : null,
1906
+ currency: config.currency ?? currency,
1907
+ transactionId: `${txnBase}:${item.service}`
1908
+ });
1909
+ }
1910
+ }
1537
1911
  function createSalesClient(config) {
1538
- return {
1539
- async record(input) {
1540
- const currency = input.currency ?? config.defaultCurrency;
1541
- if (!currency) {
1542
- throw new Error(
1543
- "record: `currency` is required (pass it on the sale or set config.defaultCurrency)"
1544
- );
1912
+ async function record(input) {
1913
+ const currency = input.currency ?? config.defaultCurrency;
1914
+ if (!currency) {
1915
+ throw new Error(
1916
+ "record: `currency` is required (pass it on the sale or set config.defaultCurrency)"
1917
+ );
1918
+ }
1919
+ const body = {
1920
+ ...input,
1921
+ currency,
1922
+ occurred_at: input.occurred_at ?? (/* @__PURE__ */ new Date()).toISOString()
1923
+ };
1924
+ const sale = await salesRequest(config, "POST", "/sales", body);
1925
+ const recorded = input.services?.length ? input.services.map((s) => ({
1926
+ service: s.service,
1927
+ amount_cents: s.amount_cents
1928
+ })) : [
1929
+ {
1930
+ service: input.service,
1931
+ amount_cents: input.amount_total_cents ?? null
1545
1932
  }
1546
- const body = {
1547
- ...input,
1933
+ ];
1934
+ fireRecordedConversions(config.firing, input, recorded, sale, currency);
1935
+ return sale;
1936
+ }
1937
+ return {
1938
+ record,
1939
+ // recordSale is the intent-revealing alias — same behavior, clearer call site.
1940
+ recordSale: record,
1941
+ trackConversion(key, options) {
1942
+ const firing = config.firing?.getFiring(key);
1943
+ if (!firing) return;
1944
+ const currency = firing.currency ?? options?.currency ?? config.defaultCurrency ?? null;
1945
+ const cents = firing.value_cents ?? null;
1946
+ const value = options?.value ?? (cents != null && currency ? fromMinor(cents, currency) : null);
1947
+ fireConversionWithConsent({
1948
+ sendTo: firing.send_to,
1949
+ value,
1548
1950
  currency,
1549
- occurred_at: input.occurred_at ?? (/* @__PURE__ */ new Date()).toISOString()
1550
- };
1551
- return salesRequest(config, "POST", "/sales", body);
1951
+ transactionId: options?.transactionId ?? null
1952
+ });
1552
1953
  },
1553
1954
  async list(query) {
1554
1955
  const { cursor, limit, sort, order, want_total, ...filters } = query ?? {};
@@ -1642,39 +2043,6 @@ function createSalesClient(config) {
1642
2043
  };
1643
2044
  }
1644
2045
 
1645
- // ../tracking-core/src/resources/sales/money.ts
1646
- var MINOR_UNIT_EXPONENT = {
1647
- USD: 2,
1648
- CAD: 2
1649
- };
1650
- function exponentFor(currency) {
1651
- return MINOR_UNIT_EXPONENT[currency] ?? 2;
1652
- }
1653
- function toMinor(amount, currency) {
1654
- return Math.round(amount * 10 ** exponentFor(currency));
1655
- }
1656
- function fromMinor(cents, currency) {
1657
- return cents / 10 ** exponentFor(currency);
1658
- }
1659
- function formatMoney(cents, currency, locale) {
1660
- return new Intl.NumberFormat(locale, { style: "currency", currency }).format(
1661
- fromMinor(cents, currency)
1662
- );
1663
- }
1664
- function formatDateInTz(iso, timeZone, opts, locale) {
1665
- const date = new Date(iso);
1666
- if (Number.isNaN(date.getTime())) return iso;
1667
- return new Intl.DateTimeFormat(locale, {
1668
- year: "numeric",
1669
- month: "short",
1670
- day: "2-digit",
1671
- hour: "2-digit",
1672
- minute: "2-digit",
1673
- ...opts,
1674
- timeZone
1675
- }).format(date);
1676
- }
1677
-
1678
2046
  // ../tracking-core/src/resources/sales/schema.ts
1679
2047
  var import_zod11 = require("zod");
1680
2048
  var SUPPORTED_CURRENCIES = ["USD", "CAD"];
@@ -2122,7 +2490,7 @@ function GoogleAdsTracking(props) {
2122
2490
  var import_react6 = require("react");
2123
2491
 
2124
2492
  // package.json
2125
- var version = "0.13.0";
2493
+ var version = "0.14.1";
2126
2494
 
2127
2495
  // ../tracking-core/src/phone-react.tsx
2128
2496
  var import_react5 = require("react");
@@ -2221,7 +2589,7 @@ var NOOP_CLIENT = {
2221
2589
  getVisitorId: () => ""
2222
2590
  };
2223
2591
  function createTracking(options) {
2224
- const { apiKey, endpoint, triggers, environment, debug, phone } = options;
2592
+ const { apiKey, endpoint, triggers, environment, debug, phone, conversionConfig } = options;
2225
2593
  if (!apiKey || !endpoint) {
2226
2594
  if (apiKey || endpoint) {
2227
2595
  console.warn(
@@ -2296,27 +2664,32 @@ function createTracking(options) {
2296
2664
  activeGtagIds: resolvedGtagIds,
2297
2665
  debug
2298
2666
  });
2299
- detachers.push(attachAutoPageView(rawClient));
2300
- detachers.push(attachBfcacheRestore(rawClient));
2667
+ const conversionStore = conversionConfig ? resolveConversionConfig({
2668
+ cdnUrl: conversionConfig.cdnUrl,
2669
+ baked: conversionConfig.baked
2670
+ }) : null;
2671
+ const detectorClient = conversionStore ? withConversionAutoFire(rawClient, createConversionAutoFire(conversionStore)) : rawClient;
2672
+ detachers.push(attachAutoPageView(detectorClient));
2673
+ detachers.push(attachBfcacheRestore(detectorClient));
2301
2674
  const timeOnSite = triggers.automatic.time_on_site;
2302
2675
  if (timeOnSite) {
2303
- detachers.push(attachTimeOnSite(rawClient, timeOnSite));
2676
+ detachers.push(attachTimeOnSite(detectorClient, timeOnSite));
2304
2677
  }
2305
2678
  const specificPageVisit = triggers.automatic.specific_page_visit;
2306
2679
  if (specificPageVisit) {
2307
- detachers.push(attachSpecificPageVisit(rawClient, specificPageVisit));
2680
+ detachers.push(attachSpecificPageVisit(detectorClient, specificPageVisit));
2308
2681
  }
2309
2682
  const scrollDepth = triggers.automatic.scroll_depth;
2310
2683
  if (scrollDepth) {
2311
- detachers.push(attachScrollDepth(rawClient, scrollDepth));
2684
+ detachers.push(attachScrollDepth(detectorClient, scrollDepth));
2312
2685
  }
2313
2686
  const multiPageSession = triggers.automatic.multi_page_session;
2314
2687
  if (multiPageSession) {
2315
- detachers.push(attachMultiPageSession(rawClient, multiPageSession));
2688
+ detachers.push(attachMultiPageSession(detectorClient, multiPageSession));
2316
2689
  }
2317
2690
  const formStart = triggers.automatic.form_start;
2318
2691
  if (formStart) {
2319
- detachers.push(attachFormStart(rawClient, formStart));
2692
+ detachers.push(attachFormStart(detectorClient, formStart));
2320
2693
  }
2321
2694
  return () => {
2322
2695
  for (let i = detachers.length - 1; i >= 0; i--) {
@@ -2365,6 +2738,7 @@ function createTracking(options) {
2365
2738
  parsePhone,
2366
2739
  phoneField,
2367
2740
  resetConsent,
2741
+ resolveConversionConfig,
2368
2742
  saleCreateSchema,
2369
2743
  saleItemSchema,
2370
2744
  saleServiceSchema,