@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/README.md +38 -0
- package/dist/index.d.mts +25 -14
- package/dist/index.d.ts +25 -14
- package/dist/index.js +446 -62
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +445 -62
- package/dist/index.mjs.map +1 -1
- package/dist/middleware.js +73 -0
- package/dist/middleware.js.map +1 -1
- package/dist/middleware.mjs +73 -0
- package/dist/middleware.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/dist/server.js +73 -0
- package/dist/server.js.map +1 -1
- package/dist/server.mjs +73 -0
- package/dist/server.mjs.map +1 -1
- package/package.json +1 -1
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
|
|
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:
|
|
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 ===
|
|
1164
|
-
|
|
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
|
|
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 ===
|
|
1232
|
-
|
|
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
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
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
|
-
|
|
1321
|
-
|
|
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
|
-
|
|
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.
|
|
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
|
-
|
|
2464
|
+
detectorClient.trackEvent({
|
|
2086
2465
|
eventType: "specific_page_visit",
|
|
2087
|
-
metadata: {
|
|
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(
|
|
2566
|
+
detachers.push(attachScrollDepth(detectorClient, scrollDepth));
|
|
2184
2567
|
}
|
|
2185
2568
|
const formStart = triggers.automatic.form_start;
|
|
2186
2569
|
if (formStart) {
|
|
2187
|
-
detachers.push(attachFormStart(
|
|
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,
|