@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/README.md +38 -0
- package/dist/index.d.mts +26 -14
- package/dist/index.d.ts +26 -14
- package/dist/index.js +442 -68
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +441 -68
- 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-7w7-Ud60.d.mts +895 -0
- package/dist/sales-7w7-Ud60.d.ts +895 -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.mjs
CHANGED
|
@@ -7,6 +7,13 @@ import { useCallback, useEffect, useState } from "react";
|
|
|
7
7
|
// ../tracking-core/src/consent.ts
|
|
8
8
|
var CONSENT_STATE_KEY = "consent_state";
|
|
9
9
|
var CONSENT_TIMESTAMP_KEY = "consent_timestamp";
|
|
10
|
+
var grantedListeners = /* @__PURE__ */ new Set();
|
|
11
|
+
function onConsentGranted(listener) {
|
|
12
|
+
grantedListeners.add(listener);
|
|
13
|
+
return () => {
|
|
14
|
+
grantedListeners.delete(listener);
|
|
15
|
+
};
|
|
16
|
+
}
|
|
10
17
|
function buildConsentPayload(state) {
|
|
11
18
|
return {
|
|
12
19
|
ad_storage: state,
|
|
@@ -35,6 +42,14 @@ function setConsentState(state) {
|
|
|
35
42
|
window.gtag("consent", "update", buildConsentPayload(state));
|
|
36
43
|
if (typeof window.fbq === "function")
|
|
37
44
|
window.fbq("consent", state === "granted" ? "grant" : "revoke");
|
|
45
|
+
if (state === "granted") {
|
|
46
|
+
for (const listener of grantedListeners) {
|
|
47
|
+
try {
|
|
48
|
+
listener();
|
|
49
|
+
} catch {
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
38
53
|
}
|
|
39
54
|
function resetConsent() {
|
|
40
55
|
if (typeof window === "undefined") return;
|
|
@@ -165,11 +180,30 @@ function isValidGtagId(id) {
|
|
|
165
180
|
function ensureGtagFunction() {
|
|
166
181
|
window.dataLayer = window.dataLayer || [];
|
|
167
182
|
if (typeof window.gtag === "function") return window.gtag;
|
|
168
|
-
|
|
169
|
-
window.dataLayer?.push(
|
|
170
|
-
}
|
|
183
|
+
function gtag() {
|
|
184
|
+
window.dataLayer?.push(arguments);
|
|
185
|
+
}
|
|
186
|
+
window.gtag = gtag;
|
|
171
187
|
return window.gtag;
|
|
172
188
|
}
|
|
189
|
+
var SEND_TO_RE = /^AW-[A-Za-z0-9]+\/[A-Za-z0-9_-]+$/;
|
|
190
|
+
function isValidSendTo(sendTo) {
|
|
191
|
+
return SEND_TO_RE.test(sendTo);
|
|
192
|
+
}
|
|
193
|
+
function fireGtagConversion(input) {
|
|
194
|
+
if (typeof window === "undefined" || typeof window.gtag !== "function") return false;
|
|
195
|
+
if (!isValidSendTo(input.sendTo)) return false;
|
|
196
|
+
const params = { send_to: input.sendTo };
|
|
197
|
+
if (input.value != null) params.value = input.value;
|
|
198
|
+
if (input.currency) params.currency = input.currency;
|
|
199
|
+
if (input.transactionId) params.transaction_id = input.transactionId;
|
|
200
|
+
try {
|
|
201
|
+
window.gtag("event", "conversion", params);
|
|
202
|
+
return true;
|
|
203
|
+
} catch {
|
|
204
|
+
return false;
|
|
205
|
+
}
|
|
206
|
+
}
|
|
173
207
|
function applyDefaultConsentState() {
|
|
174
208
|
const gtag = ensureGtagFunction();
|
|
175
209
|
gtag("consent", "default", {
|
|
@@ -376,6 +410,329 @@ function createTrackingEventCreatePayload(trackingParams, input, context) {
|
|
|
376
410
|
};
|
|
377
411
|
}
|
|
378
412
|
|
|
413
|
+
// ../tracking-core/src/resources/conversion-firing.ts
|
|
414
|
+
var DEDUP_PREFIX = "_aranova_conv_";
|
|
415
|
+
var MAX_PENDING = 100;
|
|
416
|
+
var pendingQueue = [];
|
|
417
|
+
function dedupKey(input) {
|
|
418
|
+
return `${DEDUP_PREFIX}${input.transactionId ?? ""}:${input.sendTo}`;
|
|
419
|
+
}
|
|
420
|
+
function alreadyFired(input) {
|
|
421
|
+
if (!input.transactionId || typeof window === "undefined") return false;
|
|
422
|
+
try {
|
|
423
|
+
return window.sessionStorage.getItem(dedupKey(input)) !== null;
|
|
424
|
+
} catch {
|
|
425
|
+
return false;
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
function markFired(input) {
|
|
429
|
+
if (!input.transactionId || typeof window === "undefined") return;
|
|
430
|
+
try {
|
|
431
|
+
window.sessionStorage.setItem(dedupKey(input), "1");
|
|
432
|
+
} catch {
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
function fireOnce(input) {
|
|
436
|
+
if (alreadyFired(input)) return;
|
|
437
|
+
if (fireGtagConversion(input)) markFired(input);
|
|
438
|
+
}
|
|
439
|
+
function fireConversionWithConsent(input) {
|
|
440
|
+
const state = getConsentState();
|
|
441
|
+
if (state === "denied") return;
|
|
442
|
+
if (state === "pending") {
|
|
443
|
+
if (pendingQueue.length >= MAX_PENDING) pendingQueue.shift();
|
|
444
|
+
pendingQueue.push(input);
|
|
445
|
+
return;
|
|
446
|
+
}
|
|
447
|
+
fireOnce(input);
|
|
448
|
+
}
|
|
449
|
+
function flushPendingConversions() {
|
|
450
|
+
if (getConsentState() !== "granted") return;
|
|
451
|
+
while (pendingQueue.length > 0) {
|
|
452
|
+
const input = pendingQueue.shift();
|
|
453
|
+
if (input) fireOnce(input);
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
if (typeof window !== "undefined") onConsentGranted(flushPendingConversions);
|
|
457
|
+
|
|
458
|
+
// ../tracking-core/src/resources/conversion-config.ts
|
|
459
|
+
function isStringMap(value) {
|
|
460
|
+
return typeof value === "object" && value !== null && Object.values(value).every((v) => typeof v === "string");
|
|
461
|
+
}
|
|
462
|
+
function parseFiring(value) {
|
|
463
|
+
if (!value || typeof value !== "object") return null;
|
|
464
|
+
const f = value;
|
|
465
|
+
if (typeof f.send_to !== "string") return null;
|
|
466
|
+
return {
|
|
467
|
+
send_to: f.send_to,
|
|
468
|
+
value_cents: typeof f.value_cents === "number" ? f.value_cents : null,
|
|
469
|
+
currency: typeof f.currency === "string" ? f.currency : null
|
|
470
|
+
};
|
|
471
|
+
}
|
|
472
|
+
function parseTrigger(value) {
|
|
473
|
+
if (!value || typeof value !== "object") return null;
|
|
474
|
+
const t = value;
|
|
475
|
+
if (typeof t.event_type !== "string") return null;
|
|
476
|
+
const spec = { event_type: t.event_type };
|
|
477
|
+
if (typeof t.threshold_percent === "number") spec.threshold_percent = t.threshold_percent;
|
|
478
|
+
if (typeof t.threshold_seconds === "number") spec.threshold_seconds = t.threshold_seconds;
|
|
479
|
+
if (typeof t.page_threshold === "number") spec.page_threshold = t.page_threshold;
|
|
480
|
+
if (typeof t.page_name === "string") spec.page_name = t.page_name;
|
|
481
|
+
return spec;
|
|
482
|
+
}
|
|
483
|
+
function parseConversionConfig(raw) {
|
|
484
|
+
if (!raw || typeof raw !== "object") return null;
|
|
485
|
+
const obj = raw;
|
|
486
|
+
const servicesRaw = Array.isArray(obj.services) ? obj.services : [];
|
|
487
|
+
const services = servicesRaw.flatMap((entry) => {
|
|
488
|
+
if (!entry || typeof entry !== "object") return [];
|
|
489
|
+
const s = entry;
|
|
490
|
+
if (typeof s.key !== "string") return [];
|
|
491
|
+
return [
|
|
492
|
+
{
|
|
493
|
+
key: s.key,
|
|
494
|
+
label: typeof s.label === "string" ? s.label : void 0,
|
|
495
|
+
firing: parseFiring(s.firing)
|
|
496
|
+
}
|
|
497
|
+
];
|
|
498
|
+
});
|
|
499
|
+
const goalsRaw = Array.isArray(obj.goals) ? obj.goals : null;
|
|
500
|
+
const goals = goalsRaw ? goalsRaw.flatMap((entry) => {
|
|
501
|
+
if (!entry || typeof entry !== "object") return [];
|
|
502
|
+
const g = entry;
|
|
503
|
+
if (typeof g.key !== "string") return [];
|
|
504
|
+
return [
|
|
505
|
+
{
|
|
506
|
+
key: g.key,
|
|
507
|
+
label: typeof g.label === "string" ? g.label : void 0,
|
|
508
|
+
kind: g.kind === "event" ? "event" : "sale",
|
|
509
|
+
trigger: parseTrigger(g.trigger),
|
|
510
|
+
firing: parseFiring(g.firing)
|
|
511
|
+
}
|
|
512
|
+
];
|
|
513
|
+
}) : services.map((s) => ({
|
|
514
|
+
key: s.key,
|
|
515
|
+
label: s.label,
|
|
516
|
+
kind: "sale",
|
|
517
|
+
trigger: null,
|
|
518
|
+
firing: s.firing
|
|
519
|
+
}));
|
|
520
|
+
return {
|
|
521
|
+
schema_version: typeof obj.schema_version === "number" ? obj.schema_version : 1,
|
|
522
|
+
config_version: typeof obj.config_version === "number" ? obj.config_version : 0,
|
|
523
|
+
business_id: typeof obj.business_id === "string" ? obj.business_id : void 0,
|
|
524
|
+
customer_id: typeof obj.customer_id === "string" ? obj.customer_id : null,
|
|
525
|
+
environment: typeof obj.environment === "string" ? obj.environment : void 0,
|
|
526
|
+
gtag_ids: isStringMap(obj.gtag_ids) ? obj.gtag_ids : {},
|
|
527
|
+
meta_pixel_ids: isStringMap(obj.meta_pixel_ids) ? obj.meta_pixel_ids : {},
|
|
528
|
+
services,
|
|
529
|
+
goals
|
|
530
|
+
};
|
|
531
|
+
}
|
|
532
|
+
var CACHE_PREFIX = "_aranova_cfg_";
|
|
533
|
+
function cacheKey(url) {
|
|
534
|
+
return `${CACHE_PREFIX}${url}`;
|
|
535
|
+
}
|
|
536
|
+
function readCache(url) {
|
|
537
|
+
if (typeof window === "undefined") return null;
|
|
538
|
+
try {
|
|
539
|
+
const raw = window.sessionStorage.getItem(cacheKey(url));
|
|
540
|
+
if (!raw) return null;
|
|
541
|
+
const parsed = JSON.parse(raw);
|
|
542
|
+
const config = parseConversionConfig(parsed.config);
|
|
543
|
+
if (!config) return null;
|
|
544
|
+
return {
|
|
545
|
+
etag: typeof parsed.etag === "string" ? parsed.etag : null,
|
|
546
|
+
config
|
|
547
|
+
};
|
|
548
|
+
} catch {
|
|
549
|
+
return null;
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
function writeCache(url, entry) {
|
|
553
|
+
if (typeof window === "undefined") return;
|
|
554
|
+
try {
|
|
555
|
+
window.sessionStorage.setItem(cacheKey(url), JSON.stringify(entry));
|
|
556
|
+
} catch {
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
function resolveConversionConfig(options) {
|
|
560
|
+
const cached = readCache(options.cdnUrl);
|
|
561
|
+
let current = cached?.config ?? options.baked ?? null;
|
|
562
|
+
let etag = cached?.etag ?? null;
|
|
563
|
+
const goalsByKey = /* @__PURE__ */ new Map();
|
|
564
|
+
const resolveListeners = /* @__PURE__ */ new Set();
|
|
565
|
+
function rebuildIndex() {
|
|
566
|
+
goalsByKey.clear();
|
|
567
|
+
for (const goal of current?.goals ?? []) {
|
|
568
|
+
goalsByKey.set(goal.key, goal);
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
function notifyResolved() {
|
|
572
|
+
const listeners = [...resolveListeners];
|
|
573
|
+
resolveListeners.clear();
|
|
574
|
+
for (const listener of listeners) {
|
|
575
|
+
try {
|
|
576
|
+
listener();
|
|
577
|
+
} catch {
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
function adopt(next, nextEtag) {
|
|
582
|
+
if (!next) return;
|
|
583
|
+
if (current && next.config_version <= current.config_version) return;
|
|
584
|
+
const wasEmpty = current === null;
|
|
585
|
+
current = next;
|
|
586
|
+
etag = nextEtag;
|
|
587
|
+
rebuildIndex();
|
|
588
|
+
writeCache(options.cdnUrl, { etag, config: next });
|
|
589
|
+
if (wasEmpty) notifyResolved();
|
|
590
|
+
}
|
|
591
|
+
async function revalidate() {
|
|
592
|
+
if (typeof window === "undefined") return;
|
|
593
|
+
try {
|
|
594
|
+
const doFetch = options.fetchImpl ?? globalThis.fetch;
|
|
595
|
+
if (!doFetch) return;
|
|
596
|
+
const headers = {};
|
|
597
|
+
if (etag) headers["If-None-Match"] = etag;
|
|
598
|
+
const response = await doFetch(options.cdnUrl, {
|
|
599
|
+
method: "GET",
|
|
600
|
+
headers
|
|
601
|
+
});
|
|
602
|
+
if (response.status === 304 || !response.ok) return;
|
|
603
|
+
adopt(parseConversionConfig(await response.json()), response.headers.get("ETag"));
|
|
604
|
+
} catch {
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
rebuildIndex();
|
|
608
|
+
void revalidate();
|
|
609
|
+
return {
|
|
610
|
+
getFiring: (key) => goalsByKey.get(key)?.firing ?? null,
|
|
611
|
+
getGoal: (key) => goalsByKey.get(key) ?? null,
|
|
612
|
+
listGoals: () => [...goalsByKey.values()],
|
|
613
|
+
current: () => current,
|
|
614
|
+
isReady: () => current !== null,
|
|
615
|
+
onResolve: (listener) => {
|
|
616
|
+
if (current !== null) {
|
|
617
|
+
listener();
|
|
618
|
+
return () => {
|
|
619
|
+
};
|
|
620
|
+
}
|
|
621
|
+
resolveListeners.add(listener);
|
|
622
|
+
return () => resolveListeners.delete(listener);
|
|
623
|
+
},
|
|
624
|
+
revalidate
|
|
625
|
+
};
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
// ../tracking-core/src/resources/sales/money.ts
|
|
629
|
+
var MINOR_UNIT_EXPONENT = {
|
|
630
|
+
USD: 2,
|
|
631
|
+
CAD: 2
|
|
632
|
+
};
|
|
633
|
+
function exponentFor(currency) {
|
|
634
|
+
return MINOR_UNIT_EXPONENT[currency] ?? 2;
|
|
635
|
+
}
|
|
636
|
+
function toMinor(amount, currency) {
|
|
637
|
+
return Math.round(amount * 10 ** exponentFor(currency));
|
|
638
|
+
}
|
|
639
|
+
function fromMinor(cents, currency) {
|
|
640
|
+
return cents / 10 ** exponentFor(currency);
|
|
641
|
+
}
|
|
642
|
+
function formatMoney(cents, currency, locale) {
|
|
643
|
+
return new Intl.NumberFormat(locale, { style: "currency", currency }).format(
|
|
644
|
+
fromMinor(cents, currency)
|
|
645
|
+
);
|
|
646
|
+
}
|
|
647
|
+
function formatDateInTz(iso, timeZone, opts, locale) {
|
|
648
|
+
const date = new Date(iso);
|
|
649
|
+
if (Number.isNaN(date.getTime())) return iso;
|
|
650
|
+
return new Intl.DateTimeFormat(locale, {
|
|
651
|
+
year: "numeric",
|
|
652
|
+
month: "short",
|
|
653
|
+
day: "2-digit",
|
|
654
|
+
hour: "2-digit",
|
|
655
|
+
minute: "2-digit",
|
|
656
|
+
...opts,
|
|
657
|
+
timeZone
|
|
658
|
+
}).format(date);
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
// ../tracking-core/src/resources/conversion-autofire.ts
|
|
662
|
+
function thresholdMet(goal, eventType, metadata) {
|
|
663
|
+
const t = goal.trigger;
|
|
664
|
+
if (!t || t.event_type !== eventType) return false;
|
|
665
|
+
switch (eventType) {
|
|
666
|
+
case "scroll_depth":
|
|
667
|
+
return typeof metadata.depth_percent === "number" && t.threshold_percent != null && metadata.depth_percent >= t.threshold_percent;
|
|
668
|
+
case "time_on_site":
|
|
669
|
+
return typeof metadata.duration_ms === "number" && t.threshold_seconds != null && metadata.duration_ms >= t.threshold_seconds * 1e3;
|
|
670
|
+
case "multi_page_session":
|
|
671
|
+
return typeof metadata.page_count === "number" && t.page_threshold != null && metadata.page_count >= t.page_threshold;
|
|
672
|
+
case "specific_page_visit":
|
|
673
|
+
return typeof metadata.page_name === "string" && metadata.page_name === t.page_name;
|
|
674
|
+
case "page_view":
|
|
675
|
+
case "form_start":
|
|
676
|
+
return true;
|
|
677
|
+
// no threshold — fire whenever the detector emits
|
|
678
|
+
default:
|
|
679
|
+
return false;
|
|
680
|
+
}
|
|
681
|
+
}
|
|
682
|
+
function currentPath() {
|
|
683
|
+
return typeof window === "undefined" ? "" : window.location.pathname;
|
|
684
|
+
}
|
|
685
|
+
var MAX_BUFFERED_EVENTS = 50;
|
|
686
|
+
function createConversionAutoFire(store) {
|
|
687
|
+
const pending = [];
|
|
688
|
+
let subscribed = false;
|
|
689
|
+
function fireMatching(eventType, metadata) {
|
|
690
|
+
for (const goal of store.listGoals()) {
|
|
691
|
+
if (goal.kind !== "event" || !goal.firing) continue;
|
|
692
|
+
if (!thresholdMet(goal, eventType, metadata)) continue;
|
|
693
|
+
const firing = goal.firing;
|
|
694
|
+
const cents = firing.value_cents ?? null;
|
|
695
|
+
const currency = firing.currency ?? null;
|
|
696
|
+
fireConversionWithConsent({
|
|
697
|
+
sendTo: firing.send_to,
|
|
698
|
+
value: cents != null && currency ? fromMinor(cents, currency) : null,
|
|
699
|
+
currency,
|
|
700
|
+
// Page-scoped txn id → fire once per (goal, path) per session; engagement conversions
|
|
701
|
+
// shouldn't re-fire as the visitor scrolls back and forth or re-enters a page.
|
|
702
|
+
transactionId: `auto:${goal.key}:${currentPath()}`
|
|
703
|
+
});
|
|
704
|
+
}
|
|
705
|
+
}
|
|
706
|
+
return {
|
|
707
|
+
onAutomaticEvent(eventType, metadata) {
|
|
708
|
+
if (store.isReady()) {
|
|
709
|
+
fireMatching(eventType, metadata);
|
|
710
|
+
return;
|
|
711
|
+
}
|
|
712
|
+
if (pending.length < MAX_BUFFERED_EVENTS) pending.push({ eventType, metadata });
|
|
713
|
+
if (!subscribed) {
|
|
714
|
+
subscribed = true;
|
|
715
|
+
store.onResolve(() => {
|
|
716
|
+
const buffered = pending.splice(0);
|
|
717
|
+
for (const event of buffered) fireMatching(event.eventType, event.metadata);
|
|
718
|
+
});
|
|
719
|
+
}
|
|
720
|
+
}
|
|
721
|
+
};
|
|
722
|
+
}
|
|
723
|
+
function withConversionAutoFire(client, autoFire) {
|
|
724
|
+
return {
|
|
725
|
+
...client,
|
|
726
|
+
trackEvent: (input) => {
|
|
727
|
+
client.trackEvent(input);
|
|
728
|
+
try {
|
|
729
|
+
autoFire.onAutomaticEvent(input.eventType, input.metadata ?? {});
|
|
730
|
+
} catch {
|
|
731
|
+
}
|
|
732
|
+
}
|
|
733
|
+
};
|
|
734
|
+
}
|
|
735
|
+
|
|
379
736
|
// ../tracking-core/src/session.ts
|
|
380
737
|
var VISITOR_STORAGE_KEY = "aranova_tracking_visitor";
|
|
381
738
|
var SESSION_STORAGE_KEY = "aranova_tracking_session";
|
|
@@ -1195,7 +1552,7 @@ function attachScrollDepth(client, config) {
|
|
|
1195
1552
|
}
|
|
1196
1553
|
const thresholds = new Set(config.thresholds);
|
|
1197
1554
|
let firedForPath = /* @__PURE__ */ new Set();
|
|
1198
|
-
let
|
|
1555
|
+
let currentPath2 = window.location.pathname;
|
|
1199
1556
|
let rafId = null;
|
|
1200
1557
|
function getScrollPercent() {
|
|
1201
1558
|
const doc = document.documentElement;
|
|
@@ -1214,7 +1571,7 @@ function attachScrollDepth(client, config) {
|
|
|
1214
1571
|
eventType: "scroll_depth",
|
|
1215
1572
|
metadata: {
|
|
1216
1573
|
depth_percent: threshold,
|
|
1217
|
-
page: { path:
|
|
1574
|
+
page: { path: currentPath2 }
|
|
1218
1575
|
},
|
|
1219
1576
|
pageUrl: window.location.href,
|
|
1220
1577
|
occurredAt: null
|
|
@@ -1231,8 +1588,8 @@ function attachScrollDepth(client, config) {
|
|
|
1231
1588
|
}
|
|
1232
1589
|
function resetIfPathChanged() {
|
|
1233
1590
|
const newPath = window.location.pathname;
|
|
1234
|
-
if (newPath ===
|
|
1235
|
-
|
|
1591
|
+
if (newPath === currentPath2) return;
|
|
1592
|
+
currentPath2 = newPath;
|
|
1236
1593
|
firedForPath = /* @__PURE__ */ new Set();
|
|
1237
1594
|
setTimeout(checkThresholds, 0);
|
|
1238
1595
|
}
|
|
@@ -1308,13 +1665,13 @@ function attachMultiPageSession(client, config) {
|
|
|
1308
1665
|
return storage.getItem(FIRED_KEY) === "1";
|
|
1309
1666
|
}
|
|
1310
1667
|
function check() {
|
|
1311
|
-
const
|
|
1312
|
-
if (
|
|
1313
|
-
lastCheckedPath =
|
|
1668
|
+
const currentPath2 = window.location.pathname;
|
|
1669
|
+
if (currentPath2 === lastCheckedPath) return;
|
|
1670
|
+
lastCheckedPath = currentPath2;
|
|
1314
1671
|
resetIfSessionChanged();
|
|
1315
1672
|
if (hasFired()) return;
|
|
1316
1673
|
const paths = getDistinctPaths();
|
|
1317
|
-
paths.add(
|
|
1674
|
+
paths.add(currentPath2);
|
|
1318
1675
|
saveDistinctPaths(paths);
|
|
1319
1676
|
if (paths.size >= pageThreshold) {
|
|
1320
1677
|
storage.setItem(FIRED_KEY, "1");
|
|
@@ -1358,7 +1715,7 @@ function attachFormStart(client, config) {
|
|
|
1358
1715
|
}
|
|
1359
1716
|
const selector = config.selector ?? "form";
|
|
1360
1717
|
let firedForms = /* @__PURE__ */ new Set();
|
|
1361
|
-
let
|
|
1718
|
+
let currentPath2 = window.location.pathname;
|
|
1362
1719
|
function getFormKey(form) {
|
|
1363
1720
|
if (form.id) return `id:${form.id}`;
|
|
1364
1721
|
const explicitAction = form.getAttribute("action");
|
|
@@ -1389,8 +1746,8 @@ function attachFormStart(client, config) {
|
|
|
1389
1746
|
}
|
|
1390
1747
|
function resetIfPathChanged() {
|
|
1391
1748
|
const newPath = window.location.pathname;
|
|
1392
|
-
if (newPath ===
|
|
1393
|
-
|
|
1749
|
+
if (newPath === currentPath2) return;
|
|
1750
|
+
currentPath2 = newPath;
|
|
1394
1751
|
firedForms = /* @__PURE__ */ new Set();
|
|
1395
1752
|
}
|
|
1396
1753
|
const originalPushState = history.pushState.bind(history);
|
|
@@ -1469,21 +1826,64 @@ async function salesRequest(config, method, path, body) {
|
|
|
1469
1826
|
}
|
|
1470
1827
|
|
|
1471
1828
|
// ../tracking-core/src/resources/sales/client.ts
|
|
1829
|
+
function fireRecordedConversions(firing, input, recorded, sale, currency) {
|
|
1830
|
+
if (!firing) return;
|
|
1831
|
+
const txnBase = input.external_id ?? sale.id;
|
|
1832
|
+
for (const item of recorded) {
|
|
1833
|
+
if (!item.service) continue;
|
|
1834
|
+
const config = firing.getFiring(item.service);
|
|
1835
|
+
if (!config) continue;
|
|
1836
|
+
const cents = item.amount_cents ?? config.value_cents ?? null;
|
|
1837
|
+
fireConversionWithConsent({
|
|
1838
|
+
sendTo: config.send_to,
|
|
1839
|
+
value: cents != null ? fromMinor(cents, currency) : null,
|
|
1840
|
+
currency: config.currency ?? currency,
|
|
1841
|
+
transactionId: `${txnBase}:${item.service}`
|
|
1842
|
+
});
|
|
1843
|
+
}
|
|
1844
|
+
}
|
|
1472
1845
|
function createSalesClient(config) {
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1846
|
+
async function record(input) {
|
|
1847
|
+
const currency = input.currency ?? config.defaultCurrency;
|
|
1848
|
+
if (!currency) {
|
|
1849
|
+
throw new Error(
|
|
1850
|
+
"record: `currency` is required (pass it on the sale or set config.defaultCurrency)"
|
|
1851
|
+
);
|
|
1852
|
+
}
|
|
1853
|
+
const body = {
|
|
1854
|
+
...input,
|
|
1855
|
+
currency,
|
|
1856
|
+
occurred_at: input.occurred_at ?? (/* @__PURE__ */ new Date()).toISOString()
|
|
1857
|
+
};
|
|
1858
|
+
const sale = await salesRequest(config, "POST", "/sales", body);
|
|
1859
|
+
const recorded = input.services?.length ? input.services.map((s) => ({
|
|
1860
|
+
service: s.service,
|
|
1861
|
+
amount_cents: s.amount_cents
|
|
1862
|
+
})) : [
|
|
1863
|
+
{
|
|
1864
|
+
service: input.service,
|
|
1865
|
+
amount_cents: input.amount_total_cents ?? null
|
|
1480
1866
|
}
|
|
1481
|
-
|
|
1482
|
-
|
|
1867
|
+
];
|
|
1868
|
+
fireRecordedConversions(config.firing, input, recorded, sale, currency);
|
|
1869
|
+
return sale;
|
|
1870
|
+
}
|
|
1871
|
+
return {
|
|
1872
|
+
record,
|
|
1873
|
+
// recordSale is the intent-revealing alias — same behavior, clearer call site.
|
|
1874
|
+
recordSale: record,
|
|
1875
|
+
trackConversion(key, options) {
|
|
1876
|
+
const firing = config.firing?.getFiring(key);
|
|
1877
|
+
if (!firing) return;
|
|
1878
|
+
const currency = firing.currency ?? options?.currency ?? config.defaultCurrency ?? null;
|
|
1879
|
+
const cents = firing.value_cents ?? null;
|
|
1880
|
+
const value = options?.value ?? (cents != null && currency ? fromMinor(cents, currency) : null);
|
|
1881
|
+
fireConversionWithConsent({
|
|
1882
|
+
sendTo: firing.send_to,
|
|
1883
|
+
value,
|
|
1483
1884
|
currency,
|
|
1484
|
-
|
|
1485
|
-
};
|
|
1486
|
-
return salesRequest(config, "POST", "/sales", body);
|
|
1885
|
+
transactionId: options?.transactionId ?? null
|
|
1886
|
+
});
|
|
1487
1887
|
},
|
|
1488
1888
|
async list(query) {
|
|
1489
1889
|
const { cursor, limit, sort, order, want_total, ...filters } = query ?? {};
|
|
@@ -1577,39 +1977,6 @@ function createSalesClient(config) {
|
|
|
1577
1977
|
};
|
|
1578
1978
|
}
|
|
1579
1979
|
|
|
1580
|
-
// ../tracking-core/src/resources/sales/money.ts
|
|
1581
|
-
var MINOR_UNIT_EXPONENT = {
|
|
1582
|
-
USD: 2,
|
|
1583
|
-
CAD: 2
|
|
1584
|
-
};
|
|
1585
|
-
function exponentFor(currency) {
|
|
1586
|
-
return MINOR_UNIT_EXPONENT[currency] ?? 2;
|
|
1587
|
-
}
|
|
1588
|
-
function toMinor(amount, currency) {
|
|
1589
|
-
return Math.round(amount * 10 ** exponentFor(currency));
|
|
1590
|
-
}
|
|
1591
|
-
function fromMinor(cents, currency) {
|
|
1592
|
-
return cents / 10 ** exponentFor(currency);
|
|
1593
|
-
}
|
|
1594
|
-
function formatMoney(cents, currency, locale) {
|
|
1595
|
-
return new Intl.NumberFormat(locale, { style: "currency", currency }).format(
|
|
1596
|
-
fromMinor(cents, currency)
|
|
1597
|
-
);
|
|
1598
|
-
}
|
|
1599
|
-
function formatDateInTz(iso, timeZone, opts, locale) {
|
|
1600
|
-
const date = new Date(iso);
|
|
1601
|
-
if (Number.isNaN(date.getTime())) return iso;
|
|
1602
|
-
return new Intl.DateTimeFormat(locale, {
|
|
1603
|
-
year: "numeric",
|
|
1604
|
-
month: "short",
|
|
1605
|
-
day: "2-digit",
|
|
1606
|
-
hour: "2-digit",
|
|
1607
|
-
minute: "2-digit",
|
|
1608
|
-
...opts,
|
|
1609
|
-
timeZone
|
|
1610
|
-
}).format(date);
|
|
1611
|
-
}
|
|
1612
|
-
|
|
1613
1980
|
// ../tracking-core/src/resources/sales/schema.ts
|
|
1614
1981
|
import { z as z11 } from "zod";
|
|
1615
1982
|
var SUPPORTED_CURRENCIES = ["USD", "CAD"];
|
|
@@ -2057,7 +2424,7 @@ function GoogleAdsTracking(props) {
|
|
|
2057
2424
|
import { createContext as createContext2, useContext as useContext2, useEffect as useEffect5, useMemo as useMemo4 } from "react";
|
|
2058
2425
|
|
|
2059
2426
|
// package.json
|
|
2060
|
-
var version = "0.
|
|
2427
|
+
var version = "0.14.1";
|
|
2061
2428
|
|
|
2062
2429
|
// ../tracking-core/src/phone-react.tsx
|
|
2063
2430
|
import {
|
|
@@ -2163,7 +2530,7 @@ var NOOP_CLIENT = {
|
|
|
2163
2530
|
getVisitorId: () => ""
|
|
2164
2531
|
};
|
|
2165
2532
|
function createTracking(options) {
|
|
2166
|
-
const { apiKey, endpoint, triggers, environment, debug, phone } = options;
|
|
2533
|
+
const { apiKey, endpoint, triggers, environment, debug, phone, conversionConfig } = options;
|
|
2167
2534
|
if (!apiKey || !endpoint) {
|
|
2168
2535
|
if (apiKey || endpoint) {
|
|
2169
2536
|
console.warn(
|
|
@@ -2238,27 +2605,32 @@ function createTracking(options) {
|
|
|
2238
2605
|
activeGtagIds: resolvedGtagIds,
|
|
2239
2606
|
debug
|
|
2240
2607
|
});
|
|
2241
|
-
|
|
2242
|
-
|
|
2608
|
+
const conversionStore = conversionConfig ? resolveConversionConfig({
|
|
2609
|
+
cdnUrl: conversionConfig.cdnUrl,
|
|
2610
|
+
baked: conversionConfig.baked
|
|
2611
|
+
}) : null;
|
|
2612
|
+
const detectorClient = conversionStore ? withConversionAutoFire(rawClient, createConversionAutoFire(conversionStore)) : rawClient;
|
|
2613
|
+
detachers.push(attachAutoPageView(detectorClient));
|
|
2614
|
+
detachers.push(attachBfcacheRestore(detectorClient));
|
|
2243
2615
|
const timeOnSite = triggers.automatic.time_on_site;
|
|
2244
2616
|
if (timeOnSite) {
|
|
2245
|
-
detachers.push(attachTimeOnSite(
|
|
2617
|
+
detachers.push(attachTimeOnSite(detectorClient, timeOnSite));
|
|
2246
2618
|
}
|
|
2247
2619
|
const specificPageVisit = triggers.automatic.specific_page_visit;
|
|
2248
2620
|
if (specificPageVisit) {
|
|
2249
|
-
detachers.push(attachSpecificPageVisit(
|
|
2621
|
+
detachers.push(attachSpecificPageVisit(detectorClient, specificPageVisit));
|
|
2250
2622
|
}
|
|
2251
2623
|
const scrollDepth = triggers.automatic.scroll_depth;
|
|
2252
2624
|
if (scrollDepth) {
|
|
2253
|
-
detachers.push(attachScrollDepth(
|
|
2625
|
+
detachers.push(attachScrollDepth(detectorClient, scrollDepth));
|
|
2254
2626
|
}
|
|
2255
2627
|
const multiPageSession = triggers.automatic.multi_page_session;
|
|
2256
2628
|
if (multiPageSession) {
|
|
2257
|
-
detachers.push(attachMultiPageSession(
|
|
2629
|
+
detachers.push(attachMultiPageSession(detectorClient, multiPageSession));
|
|
2258
2630
|
}
|
|
2259
2631
|
const formStart = triggers.automatic.form_start;
|
|
2260
2632
|
if (formStart) {
|
|
2261
|
-
detachers.push(attachFormStart(
|
|
2633
|
+
detachers.push(attachFormStart(detectorClient, formStart));
|
|
2262
2634
|
}
|
|
2263
2635
|
return () => {
|
|
2264
2636
|
for (let i = detachers.length - 1; i >= 0; i--) {
|
|
@@ -2306,6 +2678,7 @@ export {
|
|
|
2306
2678
|
parsePhone,
|
|
2307
2679
|
phoneField,
|
|
2308
2680
|
resetConsent,
|
|
2681
|
+
resolveConversionConfig,
|
|
2309
2682
|
saleCreateSchema,
|
|
2310
2683
|
saleItemSchema,
|
|
2311
2684
|
saleServiceSchema,
|