@aranova/tracking-next 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
@@ -56,6 +56,7 @@ __export(src_exports, {
56
56
  parsePhone: () => parsePhone,
57
57
  phoneField: () => phoneField,
58
58
  resetConsent: () => resetConsent,
59
+ resolveConversionConfig: () => resolveConversionConfig,
59
60
  saleCreateSchema: () => saleCreateSchema,
60
61
  saleItemSchema: () => saleItemSchema,
61
62
  saleServiceSchema: () => saleServiceSchema,
@@ -76,6 +77,13 @@ module.exports = __toCommonJS(src_exports);
76
77
  // ../tracking-core/src/consent.ts
77
78
  var CONSENT_STATE_KEY = "consent_state";
78
79
  var CONSENT_TIMESTAMP_KEY = "consent_timestamp";
80
+ var grantedListeners = /* @__PURE__ */ new Set();
81
+ function onConsentGranted(listener) {
82
+ grantedListeners.add(listener);
83
+ return () => {
84
+ grantedListeners.delete(listener);
85
+ };
86
+ }
79
87
  function buildConsentPayload(state) {
80
88
  return {
81
89
  ad_storage: state,
@@ -104,6 +112,14 @@ function setConsentState(state) {
104
112
  window.gtag("consent", "update", buildConsentPayload(state));
105
113
  if (typeof window.fbq === "function")
106
114
  window.fbq("consent", state === "granted" ? "grant" : "revoke");
115
+ if (state === "granted") {
116
+ for (const listener of grantedListeners) {
117
+ try {
118
+ listener();
119
+ } catch {
120
+ }
121
+ }
122
+ }
107
123
  }
108
124
  function resetConsent() {
109
125
  if (typeof window === "undefined") return;
@@ -260,6 +276,24 @@ try {
260
276
  } catch (e) {}
261
277
  `.trim();
262
278
  }
279
+ var SEND_TO_RE = /^AW-[A-Za-z0-9]+\/[A-Za-z0-9_-]+$/;
280
+ function isValidSendTo(sendTo) {
281
+ return SEND_TO_RE.test(sendTo);
282
+ }
283
+ function fireGtagConversion(input) {
284
+ if (typeof window === "undefined" || typeof window.gtag !== "function") return false;
285
+ if (!isValidSendTo(input.sendTo)) return false;
286
+ const params = { send_to: input.sendTo };
287
+ if (input.value != null) params.value = input.value;
288
+ if (input.currency) params.currency = input.currency;
289
+ if (input.transactionId) params.transaction_id = input.transactionId;
290
+ try {
291
+ window.gtag("event", "conversion", params);
292
+ return true;
293
+ } catch {
294
+ return false;
295
+ }
296
+ }
263
297
  function createGtagInitScriptMulti(gtagIds) {
264
298
  const safeIds = gtagIds.filter(isValidGtagId);
265
299
  const configs = safeIds.map((id) => `gtag('config', '${id}');`).join("\n");
@@ -388,6 +422,329 @@ function createTrackingEventCreatePayload(trackingParams, input, context) {
388
422
  };
389
423
  }
390
424
 
425
+ // ../tracking-core/src/resources/conversion-firing.ts
426
+ var DEDUP_PREFIX = "_aranova_conv_";
427
+ var MAX_PENDING = 100;
428
+ var pendingQueue = [];
429
+ function dedupKey(input) {
430
+ return `${DEDUP_PREFIX}${input.transactionId ?? ""}:${input.sendTo}`;
431
+ }
432
+ function alreadyFired(input) {
433
+ if (!input.transactionId || typeof window === "undefined") return false;
434
+ try {
435
+ return window.sessionStorage.getItem(dedupKey(input)) !== null;
436
+ } catch {
437
+ return false;
438
+ }
439
+ }
440
+ function markFired(input) {
441
+ if (!input.transactionId || typeof window === "undefined") return;
442
+ try {
443
+ window.sessionStorage.setItem(dedupKey(input), "1");
444
+ } catch {
445
+ }
446
+ }
447
+ function fireOnce(input) {
448
+ if (alreadyFired(input)) return;
449
+ if (fireGtagConversion(input)) markFired(input);
450
+ }
451
+ function fireConversionWithConsent(input) {
452
+ const state = getConsentState();
453
+ if (state === "denied") return;
454
+ if (state === "pending") {
455
+ if (pendingQueue.length >= MAX_PENDING) pendingQueue.shift();
456
+ pendingQueue.push(input);
457
+ return;
458
+ }
459
+ fireOnce(input);
460
+ }
461
+ function flushPendingConversions() {
462
+ if (getConsentState() !== "granted") return;
463
+ while (pendingQueue.length > 0) {
464
+ const input = pendingQueue.shift();
465
+ if (input) fireOnce(input);
466
+ }
467
+ }
468
+ if (typeof window !== "undefined") onConsentGranted(flushPendingConversions);
469
+
470
+ // ../tracking-core/src/resources/conversion-config.ts
471
+ function isStringMap(value) {
472
+ return typeof value === "object" && value !== null && Object.values(value).every((v) => typeof v === "string");
473
+ }
474
+ function parseFiring(value) {
475
+ if (!value || typeof value !== "object") return null;
476
+ const f = value;
477
+ if (typeof f.send_to !== "string") return null;
478
+ return {
479
+ send_to: f.send_to,
480
+ value_cents: typeof f.value_cents === "number" ? f.value_cents : null,
481
+ currency: typeof f.currency === "string" ? f.currency : null
482
+ };
483
+ }
484
+ function parseTrigger(value) {
485
+ if (!value || typeof value !== "object") return null;
486
+ const t = value;
487
+ if (typeof t.event_type !== "string") return null;
488
+ const spec = { event_type: t.event_type };
489
+ if (typeof t.threshold_percent === "number") spec.threshold_percent = t.threshold_percent;
490
+ if (typeof t.threshold_seconds === "number") spec.threshold_seconds = t.threshold_seconds;
491
+ if (typeof t.page_threshold === "number") spec.page_threshold = t.page_threshold;
492
+ if (typeof t.page_name === "string") spec.page_name = t.page_name;
493
+ return spec;
494
+ }
495
+ function parseConversionConfig(raw) {
496
+ if (!raw || typeof raw !== "object") return null;
497
+ const obj = raw;
498
+ const servicesRaw = Array.isArray(obj.services) ? obj.services : [];
499
+ const services = servicesRaw.flatMap((entry) => {
500
+ if (!entry || typeof entry !== "object") return [];
501
+ const s = entry;
502
+ if (typeof s.key !== "string") return [];
503
+ return [
504
+ {
505
+ key: s.key,
506
+ label: typeof s.label === "string" ? s.label : void 0,
507
+ firing: parseFiring(s.firing)
508
+ }
509
+ ];
510
+ });
511
+ const goalsRaw = Array.isArray(obj.goals) ? obj.goals : null;
512
+ const goals = goalsRaw ? goalsRaw.flatMap((entry) => {
513
+ if (!entry || typeof entry !== "object") return [];
514
+ const g = entry;
515
+ if (typeof g.key !== "string") return [];
516
+ return [
517
+ {
518
+ key: g.key,
519
+ label: typeof g.label === "string" ? g.label : void 0,
520
+ kind: g.kind === "event" ? "event" : "sale",
521
+ trigger: parseTrigger(g.trigger),
522
+ firing: parseFiring(g.firing)
523
+ }
524
+ ];
525
+ }) : services.map((s) => ({
526
+ key: s.key,
527
+ label: s.label,
528
+ kind: "sale",
529
+ trigger: null,
530
+ firing: s.firing
531
+ }));
532
+ return {
533
+ schema_version: typeof obj.schema_version === "number" ? obj.schema_version : 1,
534
+ config_version: typeof obj.config_version === "number" ? obj.config_version : 0,
535
+ business_id: typeof obj.business_id === "string" ? obj.business_id : void 0,
536
+ customer_id: typeof obj.customer_id === "string" ? obj.customer_id : null,
537
+ environment: typeof obj.environment === "string" ? obj.environment : void 0,
538
+ gtag_ids: isStringMap(obj.gtag_ids) ? obj.gtag_ids : {},
539
+ meta_pixel_ids: isStringMap(obj.meta_pixel_ids) ? obj.meta_pixel_ids : {},
540
+ services,
541
+ goals
542
+ };
543
+ }
544
+ var CACHE_PREFIX = "_aranova_cfg_";
545
+ function cacheKey(url) {
546
+ return `${CACHE_PREFIX}${url}`;
547
+ }
548
+ function readCache(url) {
549
+ if (typeof window === "undefined") return null;
550
+ try {
551
+ const raw = window.sessionStorage.getItem(cacheKey(url));
552
+ if (!raw) return null;
553
+ const parsed = JSON.parse(raw);
554
+ const config = parseConversionConfig(parsed.config);
555
+ if (!config) return null;
556
+ return {
557
+ etag: typeof parsed.etag === "string" ? parsed.etag : null,
558
+ config
559
+ };
560
+ } catch {
561
+ return null;
562
+ }
563
+ }
564
+ function writeCache(url, entry) {
565
+ if (typeof window === "undefined") return;
566
+ try {
567
+ window.sessionStorage.setItem(cacheKey(url), JSON.stringify(entry));
568
+ } catch {
569
+ }
570
+ }
571
+ function resolveConversionConfig(options) {
572
+ const cached = readCache(options.cdnUrl);
573
+ let current = cached?.config ?? options.baked ?? null;
574
+ let etag = cached?.etag ?? null;
575
+ const goalsByKey = /* @__PURE__ */ new Map();
576
+ const resolveListeners = /* @__PURE__ */ new Set();
577
+ function rebuildIndex() {
578
+ goalsByKey.clear();
579
+ for (const goal of current?.goals ?? []) {
580
+ goalsByKey.set(goal.key, goal);
581
+ }
582
+ }
583
+ function notifyResolved() {
584
+ const listeners = [...resolveListeners];
585
+ resolveListeners.clear();
586
+ for (const listener of listeners) {
587
+ try {
588
+ listener();
589
+ } catch {
590
+ }
591
+ }
592
+ }
593
+ function adopt(next, nextEtag) {
594
+ if (!next) return;
595
+ if (current && next.config_version <= current.config_version) return;
596
+ const wasEmpty = current === null;
597
+ current = next;
598
+ etag = nextEtag;
599
+ rebuildIndex();
600
+ writeCache(options.cdnUrl, { etag, config: next });
601
+ if (wasEmpty) notifyResolved();
602
+ }
603
+ async function revalidate() {
604
+ if (typeof window === "undefined") return;
605
+ try {
606
+ const doFetch = options.fetchImpl ?? globalThis.fetch;
607
+ if (!doFetch) return;
608
+ const headers = {};
609
+ if (etag) headers["If-None-Match"] = etag;
610
+ const response = await doFetch(options.cdnUrl, {
611
+ method: "GET",
612
+ headers
613
+ });
614
+ if (response.status === 304 || !response.ok) return;
615
+ adopt(parseConversionConfig(await response.json()), response.headers.get("ETag"));
616
+ } catch {
617
+ }
618
+ }
619
+ rebuildIndex();
620
+ void revalidate();
621
+ return {
622
+ getFiring: (key) => goalsByKey.get(key)?.firing ?? null,
623
+ getGoal: (key) => goalsByKey.get(key) ?? null,
624
+ listGoals: () => [...goalsByKey.values()],
625
+ current: () => current,
626
+ isReady: () => current !== null,
627
+ onResolve: (listener) => {
628
+ if (current !== null) {
629
+ listener();
630
+ return () => {
631
+ };
632
+ }
633
+ resolveListeners.add(listener);
634
+ return () => resolveListeners.delete(listener);
635
+ },
636
+ revalidate
637
+ };
638
+ }
639
+
640
+ // ../tracking-core/src/resources/sales/money.ts
641
+ var MINOR_UNIT_EXPONENT = {
642
+ USD: 2,
643
+ CAD: 2
644
+ };
645
+ function exponentFor(currency) {
646
+ return MINOR_UNIT_EXPONENT[currency] ?? 2;
647
+ }
648
+ function toMinor(amount, currency) {
649
+ return Math.round(amount * 10 ** exponentFor(currency));
650
+ }
651
+ function fromMinor(cents, currency) {
652
+ return cents / 10 ** exponentFor(currency);
653
+ }
654
+ function formatMoney(cents, currency, locale) {
655
+ return new Intl.NumberFormat(locale, { style: "currency", currency }).format(
656
+ fromMinor(cents, currency)
657
+ );
658
+ }
659
+ function formatDateInTz(iso, timeZone, opts, locale) {
660
+ const date = new Date(iso);
661
+ if (Number.isNaN(date.getTime())) return iso;
662
+ return new Intl.DateTimeFormat(locale, {
663
+ year: "numeric",
664
+ month: "short",
665
+ day: "2-digit",
666
+ hour: "2-digit",
667
+ minute: "2-digit",
668
+ ...opts,
669
+ timeZone
670
+ }).format(date);
671
+ }
672
+
673
+ // ../tracking-core/src/resources/conversion-autofire.ts
674
+ function thresholdMet(goal, eventType, metadata) {
675
+ const t = goal.trigger;
676
+ if (!t || t.event_type !== eventType) return false;
677
+ switch (eventType) {
678
+ case "scroll_depth":
679
+ return typeof metadata.depth_percent === "number" && t.threshold_percent != null && metadata.depth_percent >= t.threshold_percent;
680
+ case "time_on_site":
681
+ return typeof metadata.duration_ms === "number" && t.threshold_seconds != null && metadata.duration_ms >= t.threshold_seconds * 1e3;
682
+ case "multi_page_session":
683
+ return typeof metadata.page_count === "number" && t.page_threshold != null && metadata.page_count >= t.page_threshold;
684
+ case "specific_page_visit":
685
+ return typeof metadata.page_name === "string" && metadata.page_name === t.page_name;
686
+ case "page_view":
687
+ case "form_start":
688
+ return true;
689
+ // no threshold — fire whenever the detector emits
690
+ default:
691
+ return false;
692
+ }
693
+ }
694
+ function currentPath() {
695
+ return typeof window === "undefined" ? "" : window.location.pathname;
696
+ }
697
+ var MAX_BUFFERED_EVENTS = 50;
698
+ function createConversionAutoFire(store) {
699
+ const pending = [];
700
+ let subscribed = false;
701
+ function fireMatching(eventType, metadata) {
702
+ for (const goal of store.listGoals()) {
703
+ if (goal.kind !== "event" || !goal.firing) continue;
704
+ if (!thresholdMet(goal, eventType, metadata)) continue;
705
+ const firing = goal.firing;
706
+ const cents = firing.value_cents ?? null;
707
+ const currency = firing.currency ?? null;
708
+ fireConversionWithConsent({
709
+ sendTo: firing.send_to,
710
+ value: cents != null && currency ? fromMinor(cents, currency) : null,
711
+ currency,
712
+ // Page-scoped txn id → fire once per (goal, path) per session; engagement conversions
713
+ // shouldn't re-fire as the visitor scrolls back and forth or re-enters a page.
714
+ transactionId: `auto:${goal.key}:${currentPath()}`
715
+ });
716
+ }
717
+ }
718
+ return {
719
+ onAutomaticEvent(eventType, metadata) {
720
+ if (store.isReady()) {
721
+ fireMatching(eventType, metadata);
722
+ return;
723
+ }
724
+ if (pending.length < MAX_BUFFERED_EVENTS) pending.push({ eventType, metadata });
725
+ if (!subscribed) {
726
+ subscribed = true;
727
+ store.onResolve(() => {
728
+ const buffered = pending.splice(0);
729
+ for (const event of buffered) fireMatching(event.eventType, event.metadata);
730
+ });
731
+ }
732
+ }
733
+ };
734
+ }
735
+ function withConversionAutoFire(client, autoFire) {
736
+ return {
737
+ ...client,
738
+ trackEvent: (input) => {
739
+ client.trackEvent(input);
740
+ try {
741
+ autoFire.onAutomaticEvent(input.eventType, input.metadata ?? {});
742
+ } catch {
743
+ }
744
+ }
745
+ };
746
+ }
747
+
391
748
  // ../tracking-core/src/session.ts
392
749
  var VISITOR_STORAGE_KEY = "aranova_tracking_visitor";
393
750
  var SESSION_STORAGE_KEY = "aranova_tracking_session";
@@ -1124,7 +1481,7 @@ function attachScrollDepth(client, config) {
1124
1481
  }
1125
1482
  const thresholds = new Set(config.thresholds);
1126
1483
  let firedForPath = /* @__PURE__ */ new Set();
1127
- let currentPath = window.location.pathname;
1484
+ let currentPath2 = window.location.pathname;
1128
1485
  let rafId = null;
1129
1486
  function getScrollPercent() {
1130
1487
  const doc = document.documentElement;
@@ -1143,7 +1500,7 @@ function attachScrollDepth(client, config) {
1143
1500
  eventType: "scroll_depth",
1144
1501
  metadata: {
1145
1502
  depth_percent: threshold,
1146
- page: { path: currentPath }
1503
+ page: { path: currentPath2 }
1147
1504
  },
1148
1505
  pageUrl: window.location.href,
1149
1506
  occurredAt: null
@@ -1160,8 +1517,8 @@ function attachScrollDepth(client, config) {
1160
1517
  }
1161
1518
  function resetIfPathChanged() {
1162
1519
  const newPath = window.location.pathname;
1163
- if (newPath === currentPath) return;
1164
- currentPath = newPath;
1520
+ if (newPath === currentPath2) return;
1521
+ currentPath2 = newPath;
1165
1522
  firedForPath = /* @__PURE__ */ new Set();
1166
1523
  setTimeout(checkThresholds, 0);
1167
1524
  }
@@ -1197,7 +1554,7 @@ function attachFormStart(client, config) {
1197
1554
  }
1198
1555
  const selector = config.selector ?? "form";
1199
1556
  let firedForms = /* @__PURE__ */ new Set();
1200
- let currentPath = window.location.pathname;
1557
+ let currentPath2 = window.location.pathname;
1201
1558
  function getFormKey(form) {
1202
1559
  if (form.id) return `id:${form.id}`;
1203
1560
  const explicitAction = form.getAttribute("action");
@@ -1228,8 +1585,8 @@ function attachFormStart(client, config) {
1228
1585
  }
1229
1586
  function resetIfPathChanged() {
1230
1587
  const newPath = window.location.pathname;
1231
- if (newPath === currentPath) return;
1232
- currentPath = newPath;
1588
+ if (newPath === currentPath2) return;
1589
+ currentPath2 = newPath;
1233
1590
  firedForms = /* @__PURE__ */ new Set();
1234
1591
  }
1235
1592
  const originalPushState = history.pushState.bind(history);
@@ -1308,21 +1665,64 @@ async function salesRequest(config, method, path, body) {
1308
1665
  }
1309
1666
 
1310
1667
  // ../tracking-core/src/resources/sales/client.ts
1668
+ function fireRecordedConversions(firing, input, recorded, sale, currency) {
1669
+ if (!firing) return;
1670
+ const txnBase = input.external_id ?? sale.id;
1671
+ for (const item of recorded) {
1672
+ if (!item.service) continue;
1673
+ const config = firing.getFiring(item.service);
1674
+ if (!config) continue;
1675
+ const cents = item.amount_cents ?? config.value_cents ?? null;
1676
+ fireConversionWithConsent({
1677
+ sendTo: config.send_to,
1678
+ value: cents != null ? fromMinor(cents, currency) : null,
1679
+ currency: config.currency ?? currency,
1680
+ transactionId: `${txnBase}:${item.service}`
1681
+ });
1682
+ }
1683
+ }
1311
1684
  function createSalesClient(config) {
1312
- return {
1313
- async record(input) {
1314
- const currency = input.currency ?? config.defaultCurrency;
1315
- if (!currency) {
1316
- throw new Error(
1317
- "record: `currency` is required (pass it on the sale or set config.defaultCurrency)"
1318
- );
1685
+ async function record(input) {
1686
+ const currency = input.currency ?? config.defaultCurrency;
1687
+ if (!currency) {
1688
+ throw new Error(
1689
+ "record: `currency` is required (pass it on the sale or set config.defaultCurrency)"
1690
+ );
1691
+ }
1692
+ const body = {
1693
+ ...input,
1694
+ currency,
1695
+ occurred_at: input.occurred_at ?? (/* @__PURE__ */ new Date()).toISOString()
1696
+ };
1697
+ const sale = await salesRequest(config, "POST", "/sales", body);
1698
+ const recorded = input.services?.length ? input.services.map((s) => ({
1699
+ service: s.service,
1700
+ amount_cents: s.amount_cents
1701
+ })) : [
1702
+ {
1703
+ service: input.service,
1704
+ amount_cents: input.amount_total_cents ?? null
1319
1705
  }
1320
- const body = {
1321
- ...input,
1706
+ ];
1707
+ fireRecordedConversions(config.firing, input, recorded, sale, currency);
1708
+ return sale;
1709
+ }
1710
+ return {
1711
+ record,
1712
+ // recordSale is the intent-revealing alias — same behavior, clearer call site.
1713
+ recordSale: record,
1714
+ trackConversion(key, options) {
1715
+ const firing = config.firing?.getFiring(key);
1716
+ if (!firing) return;
1717
+ const currency = firing.currency ?? options?.currency ?? config.defaultCurrency ?? null;
1718
+ const cents = firing.value_cents ?? null;
1719
+ const value = options?.value ?? (cents != null && currency ? fromMinor(cents, currency) : null);
1720
+ fireConversionWithConsent({
1721
+ sendTo: firing.send_to,
1722
+ value,
1322
1723
  currency,
1323
- occurred_at: input.occurred_at ?? (/* @__PURE__ */ new Date()).toISOString()
1324
- };
1325
- return salesRequest(config, "POST", "/sales", body);
1724
+ transactionId: options?.transactionId ?? null
1725
+ });
1326
1726
  },
1327
1727
  async list(query) {
1328
1728
  const { cursor, limit, sort, order, want_total, ...filters } = query ?? {};
@@ -1416,39 +1816,6 @@ function createSalesClient(config) {
1416
1816
  };
1417
1817
  }
1418
1818
 
1419
- // ../tracking-core/src/resources/sales/money.ts
1420
- var MINOR_UNIT_EXPONENT = {
1421
- USD: 2,
1422
- CAD: 2
1423
- };
1424
- function exponentFor(currency) {
1425
- return MINOR_UNIT_EXPONENT[currency] ?? 2;
1426
- }
1427
- function toMinor(amount, currency) {
1428
- return Math.round(amount * 10 ** exponentFor(currency));
1429
- }
1430
- function fromMinor(cents, currency) {
1431
- return cents / 10 ** exponentFor(currency);
1432
- }
1433
- function formatMoney(cents, currency, locale) {
1434
- return new Intl.NumberFormat(locale, { style: "currency", currency }).format(
1435
- fromMinor(cents, currency)
1436
- );
1437
- }
1438
- function formatDateInTz(iso, timeZone, opts, locale) {
1439
- const date = new Date(iso);
1440
- if (Number.isNaN(date.getTime())) return iso;
1441
- return new Intl.DateTimeFormat(locale, {
1442
- year: "numeric",
1443
- month: "short",
1444
- day: "2-digit",
1445
- hour: "2-digit",
1446
- minute: "2-digit",
1447
- ...opts,
1448
- timeZone
1449
- }).format(date);
1450
- }
1451
-
1452
1819
  // ../tracking-core/src/resources/sales/schema.ts
1453
1820
  var import_zod11 = require("zod");
1454
1821
  var SUPPORTED_CURRENCIES = ["USD", "CAD"];
@@ -1913,7 +2280,7 @@ function GoogleAdsTracking(props) {
1913
2280
  var import_navigation = require("next/navigation");
1914
2281
 
1915
2282
  // package.json
1916
- var version = "0.13.0";
2283
+ var version = "0.14.1";
1917
2284
 
1918
2285
  // src/factory.tsx
1919
2286
  var import_react4 = require("react");
@@ -2007,7 +2374,7 @@ var PhoneField = (0, import_react3.forwardRef)(function PhoneField2({ country, v
2007
2374
  // src/factory.tsx
2008
2375
  var import_jsx_runtime5 = require("react/jsx-runtime");
2009
2376
  function createTracking(options) {
2010
- const { apiKey, endpoint, triggers, environment, gtagIds, debug, phone } = options;
2377
+ const { apiKey, endpoint, triggers, environment, gtagIds, debug, phone, conversionConfig } = options;
2011
2378
  if (!apiKey || !endpoint) {
2012
2379
  if (apiKey || endpoint) {
2013
2380
  console.warn(
@@ -2051,6 +2418,17 @@ function createTracking(options) {
2051
2418
  ),
2052
2419
  []
2053
2420
  );
2421
+ const conversionStore = (0, import_react4.useMemo)(
2422
+ () => conversionConfig ? resolveConversionConfig({
2423
+ cdnUrl: conversionConfig.cdnUrl,
2424
+ baked: conversionConfig.baked
2425
+ }) : null,
2426
+ []
2427
+ );
2428
+ const wrapClient = (0, import_react4.useMemo)(() => {
2429
+ const autoFire = conversionStore ? createConversionAutoFire(conversionStore) : null;
2430
+ return (c) => autoFire ? withConversionAutoFire(c, autoFire) : c;
2431
+ }, [conversionStore]);
2054
2432
  (0, import_react4.useEffect)(() => {
2055
2433
  const rawClient = getOrCreateTrackingClient({
2056
2434
  apiKey,
@@ -2062,7 +2440,7 @@ function createTracking(options) {
2062
2440
  activeGtagIds: gtagIds,
2063
2441
  debug
2064
2442
  });
2065
- fireManualPageView(rawClient);
2443
+ fireManualPageView(wrapClient(rawClient));
2066
2444
  }, [pathname, searchParams]);
2067
2445
  const firedPagesRef = (0, import_react4.useRef)(/* @__PURE__ */ new Set());
2068
2446
  (0, import_react4.useEffect)(() => {
@@ -2077,14 +2455,18 @@ function createTracking(options) {
2077
2455
  activeGtagIds: gtagIds,
2078
2456
  debug
2079
2457
  });
2458
+ const detectorClient = wrapClient(rawClient);
2080
2459
  for (const { name, pathPattern } of specificPageVisitConfig.pages) {
2081
2460
  if (!pathPattern.test(pathname)) continue;
2082
2461
  const key = `${name}:${pathname}`;
2083
2462
  if (firedPagesRef.current.has(key)) continue;
2084
2463
  firedPagesRef.current.add(key);
2085
- rawClient.trackEvent({
2464
+ detectorClient.trackEvent({
2086
2465
  eventType: "specific_page_visit",
2087
- metadata: { page_name: name, page: { path: pathname } },
2466
+ metadata: {
2467
+ page_name: name,
2468
+ page: { path: pathname }
2469
+ },
2088
2470
  pageUrl: null,
2089
2471
  occurredAt: null
2090
2472
  });
@@ -2101,7 +2483,7 @@ function createTracking(options) {
2101
2483
  activeGtagIds: gtagIds,
2102
2484
  debug
2103
2485
  });
2104
- return attachBfcacheRestore(rawClient);
2486
+ return attachBfcacheRestore(wrapClient(rawClient));
2105
2487
  }, []);
2106
2488
  (0, import_react4.useEffect)(() => {
2107
2489
  if (!timeOnSiteConfig) return;
@@ -2115,7 +2497,7 @@ function createTracking(options) {
2115
2497
  activeGtagIds: gtagIds,
2116
2498
  debug
2117
2499
  });
2118
- return attachTimeOnSite(rawClient, timeOnSiteConfig);
2500
+ return attachTimeOnSite(wrapClient(rawClient), timeOnSiteConfig);
2119
2501
  }, []);
2120
2502
  const multiPageSessionConfig = triggers.automatic.multi_page_session ?? null;
2121
2503
  const mpsLastPathRef = (0, import_react4.useRef)(null);
@@ -2153,7 +2535,7 @@ function createTracking(options) {
2153
2535
  activeGtagIds: gtagIds,
2154
2536
  debug
2155
2537
  });
2156
- rawClient.trackEvent({
2538
+ wrapClient(rawClient).trackEvent({
2157
2539
  eventType: "multi_page_session",
2158
2540
  metadata: {
2159
2541
  page_count: paths.size,
@@ -2178,13 +2560,14 @@ function createTracking(options) {
2178
2560
  activeGtagIds: gtagIds,
2179
2561
  debug
2180
2562
  });
2563
+ const detectorClient = wrapClient(rawClient);
2181
2564
  const scrollDepth = triggers.automatic.scroll_depth;
2182
2565
  if (scrollDepth) {
2183
- detachers.push(attachScrollDepth(rawClient, scrollDepth));
2566
+ detachers.push(attachScrollDepth(detectorClient, scrollDepth));
2184
2567
  }
2185
2568
  const formStart = triggers.automatic.form_start;
2186
2569
  if (formStart) {
2187
- detachers.push(attachFormStart(rawClient, formStart));
2570
+ detachers.push(attachFormStart(detectorClient, formStart));
2188
2571
  }
2189
2572
  return () => {
2190
2573
  for (let i = detachers.length - 1; i >= 0; i--) {
@@ -2233,6 +2616,7 @@ function createTracking(options) {
2233
2616
  parsePhone,
2234
2617
  phoneField,
2235
2618
  resetConsent,
2619
+ resolveConversionConfig,
2236
2620
  saleCreateSchema,
2237
2621
  saleItemSchema,
2238
2622
  saleServiceSchema,