@aranova/tracking-react 0.13.0 → 0.14.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/README.md +38 -0
- package/dist/index.d.mts +26 -14
- package/dist/index.d.ts +26 -14
- package/dist/index.js +398 -65
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +397 -65
- package/dist/index.mjs.map +1 -1
- package/dist/phone.js +73 -0
- package/dist/phone.js.map +1 -1
- package/dist/phone.mjs +73 -0
- package/dist/phone.mjs.map +1 -1
- package/dist/sales-BsJodmh0.d.mts +887 -0
- package/dist/sales-BsJodmh0.d.ts +887 -0
- package/dist/sales.d.mts +2 -784
- package/dist/sales.d.ts +2 -784
- package/dist/sales.js +172 -45
- package/dist/sales.js.map +1 -1
- package/dist/sales.mjs +172 -45
- package/dist/sales.mjs.map +1 -1
- package/package.json +1 -1
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;
|
|
@@ -235,6 +251,24 @@ function ensureGtagFunction() {
|
|
|
235
251
|
};
|
|
236
252
|
return window.gtag;
|
|
237
253
|
}
|
|
254
|
+
var SEND_TO_RE = /^AW-[A-Za-z0-9]+\/[A-Za-z0-9_-]+$/;
|
|
255
|
+
function isValidSendTo(sendTo) {
|
|
256
|
+
return SEND_TO_RE.test(sendTo);
|
|
257
|
+
}
|
|
258
|
+
function fireGtagConversion(input) {
|
|
259
|
+
if (typeof window === "undefined" || typeof window.gtag !== "function") return false;
|
|
260
|
+
if (!isValidSendTo(input.sendTo)) return false;
|
|
261
|
+
const params = { send_to: input.sendTo };
|
|
262
|
+
if (input.value != null) params.value = input.value;
|
|
263
|
+
if (input.currency) params.currency = input.currency;
|
|
264
|
+
if (input.transactionId) params.transaction_id = input.transactionId;
|
|
265
|
+
try {
|
|
266
|
+
window.gtag("event", "conversion", params);
|
|
267
|
+
return true;
|
|
268
|
+
} catch {
|
|
269
|
+
return false;
|
|
270
|
+
}
|
|
271
|
+
}
|
|
238
272
|
function applyDefaultConsentState() {
|
|
239
273
|
const gtag = ensureGtagFunction();
|
|
240
274
|
gtag("consent", "default", {
|
|
@@ -441,6 +475,289 @@ function createTrackingEventCreatePayload(trackingParams, input, context) {
|
|
|
441
475
|
};
|
|
442
476
|
}
|
|
443
477
|
|
|
478
|
+
// ../tracking-core/src/resources/conversion-firing.ts
|
|
479
|
+
var DEDUP_PREFIX = "_aranova_conv_";
|
|
480
|
+
var MAX_PENDING = 100;
|
|
481
|
+
var pendingQueue = [];
|
|
482
|
+
function dedupKey(input) {
|
|
483
|
+
return `${DEDUP_PREFIX}${input.transactionId ?? ""}:${input.sendTo}`;
|
|
484
|
+
}
|
|
485
|
+
function alreadyFired(input) {
|
|
486
|
+
if (!input.transactionId || typeof window === "undefined") return false;
|
|
487
|
+
try {
|
|
488
|
+
return window.sessionStorage.getItem(dedupKey(input)) !== null;
|
|
489
|
+
} catch {
|
|
490
|
+
return false;
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
function markFired(input) {
|
|
494
|
+
if (!input.transactionId || typeof window === "undefined") return;
|
|
495
|
+
try {
|
|
496
|
+
window.sessionStorage.setItem(dedupKey(input), "1");
|
|
497
|
+
} catch {
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
function fireOnce(input) {
|
|
501
|
+
if (alreadyFired(input)) return;
|
|
502
|
+
if (fireGtagConversion(input)) markFired(input);
|
|
503
|
+
}
|
|
504
|
+
function fireConversionWithConsent(input) {
|
|
505
|
+
const state = getConsentState();
|
|
506
|
+
if (state === "denied") return;
|
|
507
|
+
if (state === "pending") {
|
|
508
|
+
if (pendingQueue.length >= MAX_PENDING) pendingQueue.shift();
|
|
509
|
+
pendingQueue.push(input);
|
|
510
|
+
return;
|
|
511
|
+
}
|
|
512
|
+
fireOnce(input);
|
|
513
|
+
}
|
|
514
|
+
function flushPendingConversions() {
|
|
515
|
+
if (getConsentState() !== "granted") return;
|
|
516
|
+
while (pendingQueue.length > 0) {
|
|
517
|
+
const input = pendingQueue.shift();
|
|
518
|
+
if (input) fireOnce(input);
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
if (typeof window !== "undefined") onConsentGranted(flushPendingConversions);
|
|
522
|
+
|
|
523
|
+
// ../tracking-core/src/resources/conversion-config.ts
|
|
524
|
+
function isStringMap(value) {
|
|
525
|
+
return typeof value === "object" && value !== null && Object.values(value).every((v) => typeof v === "string");
|
|
526
|
+
}
|
|
527
|
+
function parseFiring(value) {
|
|
528
|
+
if (!value || typeof value !== "object") return null;
|
|
529
|
+
const f = value;
|
|
530
|
+
if (typeof f.send_to !== "string") return null;
|
|
531
|
+
return {
|
|
532
|
+
send_to: f.send_to,
|
|
533
|
+
value_cents: typeof f.value_cents === "number" ? f.value_cents : null,
|
|
534
|
+
currency: typeof f.currency === "string" ? f.currency : null
|
|
535
|
+
};
|
|
536
|
+
}
|
|
537
|
+
function parseTrigger(value) {
|
|
538
|
+
if (!value || typeof value !== "object") return null;
|
|
539
|
+
const t = value;
|
|
540
|
+
if (typeof t.event_type !== "string") return null;
|
|
541
|
+
const spec = { event_type: t.event_type };
|
|
542
|
+
if (typeof t.threshold_percent === "number") spec.threshold_percent = t.threshold_percent;
|
|
543
|
+
if (typeof t.threshold_seconds === "number") spec.threshold_seconds = t.threshold_seconds;
|
|
544
|
+
if (typeof t.page_threshold === "number") spec.page_threshold = t.page_threshold;
|
|
545
|
+
if (typeof t.page_name === "string") spec.page_name = t.page_name;
|
|
546
|
+
return spec;
|
|
547
|
+
}
|
|
548
|
+
function parseConversionConfig(raw) {
|
|
549
|
+
if (!raw || typeof raw !== "object") return null;
|
|
550
|
+
const obj = raw;
|
|
551
|
+
const servicesRaw = Array.isArray(obj.services) ? obj.services : [];
|
|
552
|
+
const services = servicesRaw.flatMap((entry) => {
|
|
553
|
+
if (!entry || typeof entry !== "object") return [];
|
|
554
|
+
const s = entry;
|
|
555
|
+
if (typeof s.key !== "string") return [];
|
|
556
|
+
return [
|
|
557
|
+
{
|
|
558
|
+
key: s.key,
|
|
559
|
+
label: typeof s.label === "string" ? s.label : void 0,
|
|
560
|
+
firing: parseFiring(s.firing)
|
|
561
|
+
}
|
|
562
|
+
];
|
|
563
|
+
});
|
|
564
|
+
const goalsRaw = Array.isArray(obj.goals) ? obj.goals : null;
|
|
565
|
+
const goals = goalsRaw ? goalsRaw.flatMap((entry) => {
|
|
566
|
+
if (!entry || typeof entry !== "object") return [];
|
|
567
|
+
const g = entry;
|
|
568
|
+
if (typeof g.key !== "string") return [];
|
|
569
|
+
return [
|
|
570
|
+
{
|
|
571
|
+
key: g.key,
|
|
572
|
+
label: typeof g.label === "string" ? g.label : void 0,
|
|
573
|
+
kind: g.kind === "event" ? "event" : "sale",
|
|
574
|
+
trigger: parseTrigger(g.trigger),
|
|
575
|
+
firing: parseFiring(g.firing)
|
|
576
|
+
}
|
|
577
|
+
];
|
|
578
|
+
}) : services.map((s) => ({
|
|
579
|
+
key: s.key,
|
|
580
|
+
label: s.label,
|
|
581
|
+
kind: "sale",
|
|
582
|
+
trigger: null,
|
|
583
|
+
firing: s.firing
|
|
584
|
+
}));
|
|
585
|
+
return {
|
|
586
|
+
schema_version: typeof obj.schema_version === "number" ? obj.schema_version : 1,
|
|
587
|
+
config_version: typeof obj.config_version === "number" ? obj.config_version : 0,
|
|
588
|
+
business_id: typeof obj.business_id === "string" ? obj.business_id : void 0,
|
|
589
|
+
customer_id: typeof obj.customer_id === "string" ? obj.customer_id : null,
|
|
590
|
+
environment: typeof obj.environment === "string" ? obj.environment : void 0,
|
|
591
|
+
gtag_ids: isStringMap(obj.gtag_ids) ? obj.gtag_ids : {},
|
|
592
|
+
meta_pixel_ids: isStringMap(obj.meta_pixel_ids) ? obj.meta_pixel_ids : {},
|
|
593
|
+
services,
|
|
594
|
+
goals
|
|
595
|
+
};
|
|
596
|
+
}
|
|
597
|
+
var CACHE_PREFIX = "_aranova_cfg_";
|
|
598
|
+
function cacheKey(url) {
|
|
599
|
+
return `${CACHE_PREFIX}${url}`;
|
|
600
|
+
}
|
|
601
|
+
function readCache(url) {
|
|
602
|
+
if (typeof window === "undefined") return null;
|
|
603
|
+
try {
|
|
604
|
+
const raw = window.sessionStorage.getItem(cacheKey(url));
|
|
605
|
+
if (!raw) return null;
|
|
606
|
+
const parsed = JSON.parse(raw);
|
|
607
|
+
const config = parseConversionConfig(parsed.config);
|
|
608
|
+
if (!config) return null;
|
|
609
|
+
return {
|
|
610
|
+
etag: typeof parsed.etag === "string" ? parsed.etag : null,
|
|
611
|
+
config
|
|
612
|
+
};
|
|
613
|
+
} catch {
|
|
614
|
+
return null;
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
function writeCache(url, entry) {
|
|
618
|
+
if (typeof window === "undefined") return;
|
|
619
|
+
try {
|
|
620
|
+
window.sessionStorage.setItem(cacheKey(url), JSON.stringify(entry));
|
|
621
|
+
} catch {
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
function resolveConversionConfig(options) {
|
|
625
|
+
const cached = readCache(options.cdnUrl);
|
|
626
|
+
let current = cached?.config ?? options.baked ?? null;
|
|
627
|
+
let etag = cached?.etag ?? null;
|
|
628
|
+
const goalsByKey = /* @__PURE__ */ new Map();
|
|
629
|
+
function rebuildIndex() {
|
|
630
|
+
goalsByKey.clear();
|
|
631
|
+
for (const goal of current?.goals ?? []) {
|
|
632
|
+
goalsByKey.set(goal.key, goal);
|
|
633
|
+
}
|
|
634
|
+
}
|
|
635
|
+
function adopt(next, nextEtag) {
|
|
636
|
+
if (!next) return;
|
|
637
|
+
if (current && next.config_version <= current.config_version) return;
|
|
638
|
+
current = next;
|
|
639
|
+
etag = nextEtag;
|
|
640
|
+
rebuildIndex();
|
|
641
|
+
writeCache(options.cdnUrl, { etag, config: next });
|
|
642
|
+
}
|
|
643
|
+
async function revalidate() {
|
|
644
|
+
if (typeof window === "undefined") return;
|
|
645
|
+
try {
|
|
646
|
+
const doFetch = options.fetchImpl ?? globalThis.fetch;
|
|
647
|
+
if (!doFetch) return;
|
|
648
|
+
const headers = {};
|
|
649
|
+
if (etag) headers["If-None-Match"] = etag;
|
|
650
|
+
const response = await doFetch(options.cdnUrl, {
|
|
651
|
+
method: "GET",
|
|
652
|
+
headers
|
|
653
|
+
});
|
|
654
|
+
if (response.status === 304 || !response.ok) return;
|
|
655
|
+
adopt(parseConversionConfig(await response.json()), response.headers.get("ETag"));
|
|
656
|
+
} catch {
|
|
657
|
+
}
|
|
658
|
+
}
|
|
659
|
+
rebuildIndex();
|
|
660
|
+
void revalidate();
|
|
661
|
+
return {
|
|
662
|
+
getFiring: (key) => goalsByKey.get(key)?.firing ?? null,
|
|
663
|
+
getGoal: (key) => goalsByKey.get(key) ?? null,
|
|
664
|
+
listGoals: () => [...goalsByKey.values()],
|
|
665
|
+
current: () => current,
|
|
666
|
+
revalidate
|
|
667
|
+
};
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
// ../tracking-core/src/resources/sales/money.ts
|
|
671
|
+
var MINOR_UNIT_EXPONENT = {
|
|
672
|
+
USD: 2,
|
|
673
|
+
CAD: 2
|
|
674
|
+
};
|
|
675
|
+
function exponentFor(currency) {
|
|
676
|
+
return MINOR_UNIT_EXPONENT[currency] ?? 2;
|
|
677
|
+
}
|
|
678
|
+
function toMinor(amount, currency) {
|
|
679
|
+
return Math.round(amount * 10 ** exponentFor(currency));
|
|
680
|
+
}
|
|
681
|
+
function fromMinor(cents, currency) {
|
|
682
|
+
return cents / 10 ** exponentFor(currency);
|
|
683
|
+
}
|
|
684
|
+
function formatMoney(cents, currency, locale) {
|
|
685
|
+
return new Intl.NumberFormat(locale, { style: "currency", currency }).format(
|
|
686
|
+
fromMinor(cents, currency)
|
|
687
|
+
);
|
|
688
|
+
}
|
|
689
|
+
function formatDateInTz(iso, timeZone, opts, locale) {
|
|
690
|
+
const date = new Date(iso);
|
|
691
|
+
if (Number.isNaN(date.getTime())) return iso;
|
|
692
|
+
return new Intl.DateTimeFormat(locale, {
|
|
693
|
+
year: "numeric",
|
|
694
|
+
month: "short",
|
|
695
|
+
day: "2-digit",
|
|
696
|
+
hour: "2-digit",
|
|
697
|
+
minute: "2-digit",
|
|
698
|
+
...opts,
|
|
699
|
+
timeZone
|
|
700
|
+
}).format(date);
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
// ../tracking-core/src/resources/conversion-autofire.ts
|
|
704
|
+
function thresholdMet(goal, eventType, metadata) {
|
|
705
|
+
const t = goal.trigger;
|
|
706
|
+
if (!t || t.event_type !== eventType) return false;
|
|
707
|
+
switch (eventType) {
|
|
708
|
+
case "scroll_depth":
|
|
709
|
+
return typeof metadata.depth_percent === "number" && t.threshold_percent != null && metadata.depth_percent >= t.threshold_percent;
|
|
710
|
+
case "time_on_site":
|
|
711
|
+
return typeof metadata.duration_ms === "number" && t.threshold_seconds != null && metadata.duration_ms >= t.threshold_seconds * 1e3;
|
|
712
|
+
case "multi_page_session":
|
|
713
|
+
return typeof metadata.page_count === "number" && t.page_threshold != null && metadata.page_count >= t.page_threshold;
|
|
714
|
+
case "specific_page_visit":
|
|
715
|
+
return typeof metadata.page_name === "string" && metadata.page_name === t.page_name;
|
|
716
|
+
case "page_view":
|
|
717
|
+
case "form_start":
|
|
718
|
+
return true;
|
|
719
|
+
// no threshold — fire whenever the detector emits
|
|
720
|
+
default:
|
|
721
|
+
return false;
|
|
722
|
+
}
|
|
723
|
+
}
|
|
724
|
+
function currentPath() {
|
|
725
|
+
return typeof window === "undefined" ? "" : window.location.pathname;
|
|
726
|
+
}
|
|
727
|
+
function createConversionAutoFire(store) {
|
|
728
|
+
return {
|
|
729
|
+
onAutomaticEvent(eventType, metadata) {
|
|
730
|
+
for (const goal of store.listGoals()) {
|
|
731
|
+
if (goal.kind !== "event" || !goal.firing) continue;
|
|
732
|
+
if (!thresholdMet(goal, eventType, metadata)) continue;
|
|
733
|
+
const firing = goal.firing;
|
|
734
|
+
const cents = firing.value_cents ?? null;
|
|
735
|
+
const currency = firing.currency ?? null;
|
|
736
|
+
fireConversionWithConsent({
|
|
737
|
+
sendTo: firing.send_to,
|
|
738
|
+
value: cents != null && currency ? fromMinor(cents, currency) : null,
|
|
739
|
+
currency,
|
|
740
|
+
// Page-scoped txn id → fire once per (goal, path) per session; engagement conversions
|
|
741
|
+
// shouldn't re-fire as the visitor scrolls back and forth or re-enters a page.
|
|
742
|
+
transactionId: `auto:${goal.key}:${currentPath()}`
|
|
743
|
+
});
|
|
744
|
+
}
|
|
745
|
+
}
|
|
746
|
+
};
|
|
747
|
+
}
|
|
748
|
+
function withConversionAutoFire(client, autoFire) {
|
|
749
|
+
return {
|
|
750
|
+
...client,
|
|
751
|
+
trackEvent: (input) => {
|
|
752
|
+
client.trackEvent(input);
|
|
753
|
+
try {
|
|
754
|
+
autoFire.onAutomaticEvent(input.eventType, input.metadata ?? {});
|
|
755
|
+
} catch {
|
|
756
|
+
}
|
|
757
|
+
}
|
|
758
|
+
};
|
|
759
|
+
}
|
|
760
|
+
|
|
444
761
|
// ../tracking-core/src/session.ts
|
|
445
762
|
var VISITOR_STORAGE_KEY = "aranova_tracking_visitor";
|
|
446
763
|
var SESSION_STORAGE_KEY = "aranova_tracking_session";
|
|
@@ -1260,7 +1577,7 @@ function attachScrollDepth(client, config) {
|
|
|
1260
1577
|
}
|
|
1261
1578
|
const thresholds = new Set(config.thresholds);
|
|
1262
1579
|
let firedForPath = /* @__PURE__ */ new Set();
|
|
1263
|
-
let
|
|
1580
|
+
let currentPath2 = window.location.pathname;
|
|
1264
1581
|
let rafId = null;
|
|
1265
1582
|
function getScrollPercent() {
|
|
1266
1583
|
const doc = document.documentElement;
|
|
@@ -1279,7 +1596,7 @@ function attachScrollDepth(client, config) {
|
|
|
1279
1596
|
eventType: "scroll_depth",
|
|
1280
1597
|
metadata: {
|
|
1281
1598
|
depth_percent: threshold,
|
|
1282
|
-
page: { path:
|
|
1599
|
+
page: { path: currentPath2 }
|
|
1283
1600
|
},
|
|
1284
1601
|
pageUrl: window.location.href,
|
|
1285
1602
|
occurredAt: null
|
|
@@ -1296,8 +1613,8 @@ function attachScrollDepth(client, config) {
|
|
|
1296
1613
|
}
|
|
1297
1614
|
function resetIfPathChanged() {
|
|
1298
1615
|
const newPath = window.location.pathname;
|
|
1299
|
-
if (newPath ===
|
|
1300
|
-
|
|
1616
|
+
if (newPath === currentPath2) return;
|
|
1617
|
+
currentPath2 = newPath;
|
|
1301
1618
|
firedForPath = /* @__PURE__ */ new Set();
|
|
1302
1619
|
setTimeout(checkThresholds, 0);
|
|
1303
1620
|
}
|
|
@@ -1373,13 +1690,13 @@ function attachMultiPageSession(client, config) {
|
|
|
1373
1690
|
return storage.getItem(FIRED_KEY) === "1";
|
|
1374
1691
|
}
|
|
1375
1692
|
function check() {
|
|
1376
|
-
const
|
|
1377
|
-
if (
|
|
1378
|
-
lastCheckedPath =
|
|
1693
|
+
const currentPath2 = window.location.pathname;
|
|
1694
|
+
if (currentPath2 === lastCheckedPath) return;
|
|
1695
|
+
lastCheckedPath = currentPath2;
|
|
1379
1696
|
resetIfSessionChanged();
|
|
1380
1697
|
if (hasFired()) return;
|
|
1381
1698
|
const paths = getDistinctPaths();
|
|
1382
|
-
paths.add(
|
|
1699
|
+
paths.add(currentPath2);
|
|
1383
1700
|
saveDistinctPaths(paths);
|
|
1384
1701
|
if (paths.size >= pageThreshold) {
|
|
1385
1702
|
storage.setItem(FIRED_KEY, "1");
|
|
@@ -1423,7 +1740,7 @@ function attachFormStart(client, config) {
|
|
|
1423
1740
|
}
|
|
1424
1741
|
const selector = config.selector ?? "form";
|
|
1425
1742
|
let firedForms = /* @__PURE__ */ new Set();
|
|
1426
|
-
let
|
|
1743
|
+
let currentPath2 = window.location.pathname;
|
|
1427
1744
|
function getFormKey(form) {
|
|
1428
1745
|
if (form.id) return `id:${form.id}`;
|
|
1429
1746
|
const explicitAction = form.getAttribute("action");
|
|
@@ -1454,8 +1771,8 @@ function attachFormStart(client, config) {
|
|
|
1454
1771
|
}
|
|
1455
1772
|
function resetIfPathChanged() {
|
|
1456
1773
|
const newPath = window.location.pathname;
|
|
1457
|
-
if (newPath ===
|
|
1458
|
-
|
|
1774
|
+
if (newPath === currentPath2) return;
|
|
1775
|
+
currentPath2 = newPath;
|
|
1459
1776
|
firedForms = /* @__PURE__ */ new Set();
|
|
1460
1777
|
}
|
|
1461
1778
|
const originalPushState = history.pushState.bind(history);
|
|
@@ -1534,21 +1851,64 @@ async function salesRequest(config, method, path, body) {
|
|
|
1534
1851
|
}
|
|
1535
1852
|
|
|
1536
1853
|
// ../tracking-core/src/resources/sales/client.ts
|
|
1854
|
+
function fireRecordedConversions(firing, input, recorded, sale, currency) {
|
|
1855
|
+
if (!firing) return;
|
|
1856
|
+
const txnBase = input.external_id ?? sale.id;
|
|
1857
|
+
for (const item of recorded) {
|
|
1858
|
+
if (!item.service) continue;
|
|
1859
|
+
const config = firing.getFiring(item.service);
|
|
1860
|
+
if (!config) continue;
|
|
1861
|
+
const cents = item.amount_cents ?? config.value_cents ?? null;
|
|
1862
|
+
fireConversionWithConsent({
|
|
1863
|
+
sendTo: config.send_to,
|
|
1864
|
+
value: cents != null ? fromMinor(cents, currency) : null,
|
|
1865
|
+
currency: config.currency ?? currency,
|
|
1866
|
+
transactionId: `${txnBase}:${item.service}`
|
|
1867
|
+
});
|
|
1868
|
+
}
|
|
1869
|
+
}
|
|
1537
1870
|
function createSalesClient(config) {
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1871
|
+
async function record(input) {
|
|
1872
|
+
const currency = input.currency ?? config.defaultCurrency;
|
|
1873
|
+
if (!currency) {
|
|
1874
|
+
throw new Error(
|
|
1875
|
+
"record: `currency` is required (pass it on the sale or set config.defaultCurrency)"
|
|
1876
|
+
);
|
|
1877
|
+
}
|
|
1878
|
+
const body = {
|
|
1879
|
+
...input,
|
|
1880
|
+
currency,
|
|
1881
|
+
occurred_at: input.occurred_at ?? (/* @__PURE__ */ new Date()).toISOString()
|
|
1882
|
+
};
|
|
1883
|
+
const sale = await salesRequest(config, "POST", "/sales", body);
|
|
1884
|
+
const recorded = input.services?.length ? input.services.map((s) => ({
|
|
1885
|
+
service: s.service,
|
|
1886
|
+
amount_cents: s.amount_cents
|
|
1887
|
+
})) : [
|
|
1888
|
+
{
|
|
1889
|
+
service: input.service,
|
|
1890
|
+
amount_cents: input.amount_total_cents ?? null
|
|
1545
1891
|
}
|
|
1546
|
-
|
|
1547
|
-
|
|
1892
|
+
];
|
|
1893
|
+
fireRecordedConversions(config.firing, input, recorded, sale, currency);
|
|
1894
|
+
return sale;
|
|
1895
|
+
}
|
|
1896
|
+
return {
|
|
1897
|
+
record,
|
|
1898
|
+
// recordSale is the intent-revealing alias — same behavior, clearer call site.
|
|
1899
|
+
recordSale: record,
|
|
1900
|
+
trackConversion(key, options) {
|
|
1901
|
+
const firing = config.firing?.getFiring(key);
|
|
1902
|
+
if (!firing) return;
|
|
1903
|
+
const currency = firing.currency ?? options?.currency ?? config.defaultCurrency ?? null;
|
|
1904
|
+
const cents = firing.value_cents ?? null;
|
|
1905
|
+
const value = options?.value ?? (cents != null && currency ? fromMinor(cents, currency) : null);
|
|
1906
|
+
fireConversionWithConsent({
|
|
1907
|
+
sendTo: firing.send_to,
|
|
1908
|
+
value,
|
|
1548
1909
|
currency,
|
|
1549
|
-
|
|
1550
|
-
};
|
|
1551
|
-
return salesRequest(config, "POST", "/sales", body);
|
|
1910
|
+
transactionId: options?.transactionId ?? null
|
|
1911
|
+
});
|
|
1552
1912
|
},
|
|
1553
1913
|
async list(query) {
|
|
1554
1914
|
const { cursor, limit, sort, order, want_total, ...filters } = query ?? {};
|
|
@@ -1642,39 +2002,6 @@ function createSalesClient(config) {
|
|
|
1642
2002
|
};
|
|
1643
2003
|
}
|
|
1644
2004
|
|
|
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
2005
|
// ../tracking-core/src/resources/sales/schema.ts
|
|
1679
2006
|
var import_zod11 = require("zod");
|
|
1680
2007
|
var SUPPORTED_CURRENCIES = ["USD", "CAD"];
|
|
@@ -2122,7 +2449,7 @@ function GoogleAdsTracking(props) {
|
|
|
2122
2449
|
var import_react6 = require("react");
|
|
2123
2450
|
|
|
2124
2451
|
// package.json
|
|
2125
|
-
var version = "0.
|
|
2452
|
+
var version = "0.14.0";
|
|
2126
2453
|
|
|
2127
2454
|
// ../tracking-core/src/phone-react.tsx
|
|
2128
2455
|
var import_react5 = require("react");
|
|
@@ -2221,7 +2548,7 @@ var NOOP_CLIENT = {
|
|
|
2221
2548
|
getVisitorId: () => ""
|
|
2222
2549
|
};
|
|
2223
2550
|
function createTracking(options) {
|
|
2224
|
-
const { apiKey, endpoint, triggers, environment, debug, phone } = options;
|
|
2551
|
+
const { apiKey, endpoint, triggers, environment, debug, phone, conversionConfig } = options;
|
|
2225
2552
|
if (!apiKey || !endpoint) {
|
|
2226
2553
|
if (apiKey || endpoint) {
|
|
2227
2554
|
console.warn(
|
|
@@ -2296,27 +2623,32 @@ function createTracking(options) {
|
|
|
2296
2623
|
activeGtagIds: resolvedGtagIds,
|
|
2297
2624
|
debug
|
|
2298
2625
|
});
|
|
2299
|
-
|
|
2300
|
-
|
|
2626
|
+
const conversionStore = conversionConfig ? resolveConversionConfig({
|
|
2627
|
+
cdnUrl: conversionConfig.cdnUrl,
|
|
2628
|
+
baked: conversionConfig.baked
|
|
2629
|
+
}) : null;
|
|
2630
|
+
const detectorClient = conversionStore ? withConversionAutoFire(rawClient, createConversionAutoFire(conversionStore)) : rawClient;
|
|
2631
|
+
detachers.push(attachAutoPageView(detectorClient));
|
|
2632
|
+
detachers.push(attachBfcacheRestore(detectorClient));
|
|
2301
2633
|
const timeOnSite = triggers.automatic.time_on_site;
|
|
2302
2634
|
if (timeOnSite) {
|
|
2303
|
-
detachers.push(attachTimeOnSite(
|
|
2635
|
+
detachers.push(attachTimeOnSite(detectorClient, timeOnSite));
|
|
2304
2636
|
}
|
|
2305
2637
|
const specificPageVisit = triggers.automatic.specific_page_visit;
|
|
2306
2638
|
if (specificPageVisit) {
|
|
2307
|
-
detachers.push(attachSpecificPageVisit(
|
|
2639
|
+
detachers.push(attachSpecificPageVisit(detectorClient, specificPageVisit));
|
|
2308
2640
|
}
|
|
2309
2641
|
const scrollDepth = triggers.automatic.scroll_depth;
|
|
2310
2642
|
if (scrollDepth) {
|
|
2311
|
-
detachers.push(attachScrollDepth(
|
|
2643
|
+
detachers.push(attachScrollDepth(detectorClient, scrollDepth));
|
|
2312
2644
|
}
|
|
2313
2645
|
const multiPageSession = triggers.automatic.multi_page_session;
|
|
2314
2646
|
if (multiPageSession) {
|
|
2315
|
-
detachers.push(attachMultiPageSession(
|
|
2647
|
+
detachers.push(attachMultiPageSession(detectorClient, multiPageSession));
|
|
2316
2648
|
}
|
|
2317
2649
|
const formStart = triggers.automatic.form_start;
|
|
2318
2650
|
if (formStart) {
|
|
2319
|
-
detachers.push(attachFormStart(
|
|
2651
|
+
detachers.push(attachFormStart(detectorClient, formStart));
|
|
2320
2652
|
}
|
|
2321
2653
|
return () => {
|
|
2322
2654
|
for (let i = detachers.length - 1; i >= 0; i--) {
|
|
@@ -2365,6 +2697,7 @@ function createTracking(options) {
|
|
|
2365
2697
|
parsePhone,
|
|
2366
2698
|
phoneField,
|
|
2367
2699
|
resetConsent,
|
|
2700
|
+
resolveConversionConfig,
|
|
2368
2701
|
saleCreateSchema,
|
|
2369
2702
|
saleItemSchema,
|
|
2370
2703
|
saleServiceSchema,
|