@aranova/tracking-next 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 +25 -14
- package/dist/index.d.ts +25 -14
- package/dist/index.js +406 -62
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +405 -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-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/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,289 @@ 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
|
+
function rebuildIndex() {
|
|
577
|
+
goalsByKey.clear();
|
|
578
|
+
for (const goal of current?.goals ?? []) {
|
|
579
|
+
goalsByKey.set(goal.key, goal);
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
function adopt(next, nextEtag) {
|
|
583
|
+
if (!next) return;
|
|
584
|
+
if (current && next.config_version <= current.config_version) return;
|
|
585
|
+
current = next;
|
|
586
|
+
etag = nextEtag;
|
|
587
|
+
rebuildIndex();
|
|
588
|
+
writeCache(options.cdnUrl, { etag, config: next });
|
|
589
|
+
}
|
|
590
|
+
async function revalidate() {
|
|
591
|
+
if (typeof window === "undefined") return;
|
|
592
|
+
try {
|
|
593
|
+
const doFetch = options.fetchImpl ?? globalThis.fetch;
|
|
594
|
+
if (!doFetch) return;
|
|
595
|
+
const headers = {};
|
|
596
|
+
if (etag) headers["If-None-Match"] = etag;
|
|
597
|
+
const response = await doFetch(options.cdnUrl, {
|
|
598
|
+
method: "GET",
|
|
599
|
+
headers
|
|
600
|
+
});
|
|
601
|
+
if (response.status === 304 || !response.ok) return;
|
|
602
|
+
adopt(parseConversionConfig(await response.json()), response.headers.get("ETag"));
|
|
603
|
+
} catch {
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
rebuildIndex();
|
|
607
|
+
void revalidate();
|
|
608
|
+
return {
|
|
609
|
+
getFiring: (key) => goalsByKey.get(key)?.firing ?? null,
|
|
610
|
+
getGoal: (key) => goalsByKey.get(key) ?? null,
|
|
611
|
+
listGoals: () => [...goalsByKey.values()],
|
|
612
|
+
current: () => current,
|
|
613
|
+
revalidate
|
|
614
|
+
};
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
// ../tracking-core/src/resources/sales/money.ts
|
|
618
|
+
var MINOR_UNIT_EXPONENT = {
|
|
619
|
+
USD: 2,
|
|
620
|
+
CAD: 2
|
|
621
|
+
};
|
|
622
|
+
function exponentFor(currency) {
|
|
623
|
+
return MINOR_UNIT_EXPONENT[currency] ?? 2;
|
|
624
|
+
}
|
|
625
|
+
function toMinor(amount, currency) {
|
|
626
|
+
return Math.round(amount * 10 ** exponentFor(currency));
|
|
627
|
+
}
|
|
628
|
+
function fromMinor(cents, currency) {
|
|
629
|
+
return cents / 10 ** exponentFor(currency);
|
|
630
|
+
}
|
|
631
|
+
function formatMoney(cents, currency, locale) {
|
|
632
|
+
return new Intl.NumberFormat(locale, { style: "currency", currency }).format(
|
|
633
|
+
fromMinor(cents, currency)
|
|
634
|
+
);
|
|
635
|
+
}
|
|
636
|
+
function formatDateInTz(iso, timeZone, opts, locale) {
|
|
637
|
+
const date = new Date(iso);
|
|
638
|
+
if (Number.isNaN(date.getTime())) return iso;
|
|
639
|
+
return new Intl.DateTimeFormat(locale, {
|
|
640
|
+
year: "numeric",
|
|
641
|
+
month: "short",
|
|
642
|
+
day: "2-digit",
|
|
643
|
+
hour: "2-digit",
|
|
644
|
+
minute: "2-digit",
|
|
645
|
+
...opts,
|
|
646
|
+
timeZone
|
|
647
|
+
}).format(date);
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
// ../tracking-core/src/resources/conversion-autofire.ts
|
|
651
|
+
function thresholdMet(goal, eventType, metadata) {
|
|
652
|
+
const t = goal.trigger;
|
|
653
|
+
if (!t || t.event_type !== eventType) return false;
|
|
654
|
+
switch (eventType) {
|
|
655
|
+
case "scroll_depth":
|
|
656
|
+
return typeof metadata.depth_percent === "number" && t.threshold_percent != null && metadata.depth_percent >= t.threshold_percent;
|
|
657
|
+
case "time_on_site":
|
|
658
|
+
return typeof metadata.duration_ms === "number" && t.threshold_seconds != null && metadata.duration_ms >= t.threshold_seconds * 1e3;
|
|
659
|
+
case "multi_page_session":
|
|
660
|
+
return typeof metadata.page_count === "number" && t.page_threshold != null && metadata.page_count >= t.page_threshold;
|
|
661
|
+
case "specific_page_visit":
|
|
662
|
+
return typeof metadata.page_name === "string" && metadata.page_name === t.page_name;
|
|
663
|
+
case "page_view":
|
|
664
|
+
case "form_start":
|
|
665
|
+
return true;
|
|
666
|
+
// no threshold — fire whenever the detector emits
|
|
667
|
+
default:
|
|
668
|
+
return false;
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
function currentPath() {
|
|
672
|
+
return typeof window === "undefined" ? "" : window.location.pathname;
|
|
673
|
+
}
|
|
674
|
+
function createConversionAutoFire(store) {
|
|
675
|
+
return {
|
|
676
|
+
onAutomaticEvent(eventType, metadata) {
|
|
677
|
+
for (const goal of store.listGoals()) {
|
|
678
|
+
if (goal.kind !== "event" || !goal.firing) continue;
|
|
679
|
+
if (!thresholdMet(goal, eventType, metadata)) continue;
|
|
680
|
+
const firing = goal.firing;
|
|
681
|
+
const cents = firing.value_cents ?? null;
|
|
682
|
+
const currency = firing.currency ?? null;
|
|
683
|
+
fireConversionWithConsent({
|
|
684
|
+
sendTo: firing.send_to,
|
|
685
|
+
value: cents != null && currency ? fromMinor(cents, currency) : null,
|
|
686
|
+
currency,
|
|
687
|
+
// Page-scoped txn id → fire once per (goal, path) per session; engagement conversions
|
|
688
|
+
// shouldn't re-fire as the visitor scrolls back and forth or re-enters a page.
|
|
689
|
+
transactionId: `auto:${goal.key}:${currentPath()}`
|
|
690
|
+
});
|
|
691
|
+
}
|
|
692
|
+
}
|
|
693
|
+
};
|
|
694
|
+
}
|
|
695
|
+
function withConversionAutoFire(client, autoFire) {
|
|
696
|
+
return {
|
|
697
|
+
...client,
|
|
698
|
+
trackEvent: (input) => {
|
|
699
|
+
client.trackEvent(input);
|
|
700
|
+
try {
|
|
701
|
+
autoFire.onAutomaticEvent(input.eventType, input.metadata ?? {});
|
|
702
|
+
} catch {
|
|
703
|
+
}
|
|
704
|
+
}
|
|
705
|
+
};
|
|
706
|
+
}
|
|
707
|
+
|
|
391
708
|
// ../tracking-core/src/session.ts
|
|
392
709
|
var VISITOR_STORAGE_KEY = "aranova_tracking_visitor";
|
|
393
710
|
var SESSION_STORAGE_KEY = "aranova_tracking_session";
|
|
@@ -1124,7 +1441,7 @@ function attachScrollDepth(client, config) {
|
|
|
1124
1441
|
}
|
|
1125
1442
|
const thresholds = new Set(config.thresholds);
|
|
1126
1443
|
let firedForPath = /* @__PURE__ */ new Set();
|
|
1127
|
-
let
|
|
1444
|
+
let currentPath2 = window.location.pathname;
|
|
1128
1445
|
let rafId = null;
|
|
1129
1446
|
function getScrollPercent() {
|
|
1130
1447
|
const doc = document.documentElement;
|
|
@@ -1143,7 +1460,7 @@ function attachScrollDepth(client, config) {
|
|
|
1143
1460
|
eventType: "scroll_depth",
|
|
1144
1461
|
metadata: {
|
|
1145
1462
|
depth_percent: threshold,
|
|
1146
|
-
page: { path:
|
|
1463
|
+
page: { path: currentPath2 }
|
|
1147
1464
|
},
|
|
1148
1465
|
pageUrl: window.location.href,
|
|
1149
1466
|
occurredAt: null
|
|
@@ -1160,8 +1477,8 @@ function attachScrollDepth(client, config) {
|
|
|
1160
1477
|
}
|
|
1161
1478
|
function resetIfPathChanged() {
|
|
1162
1479
|
const newPath = window.location.pathname;
|
|
1163
|
-
if (newPath ===
|
|
1164
|
-
|
|
1480
|
+
if (newPath === currentPath2) return;
|
|
1481
|
+
currentPath2 = newPath;
|
|
1165
1482
|
firedForPath = /* @__PURE__ */ new Set();
|
|
1166
1483
|
setTimeout(checkThresholds, 0);
|
|
1167
1484
|
}
|
|
@@ -1197,7 +1514,7 @@ function attachFormStart(client, config) {
|
|
|
1197
1514
|
}
|
|
1198
1515
|
const selector = config.selector ?? "form";
|
|
1199
1516
|
let firedForms = /* @__PURE__ */ new Set();
|
|
1200
|
-
let
|
|
1517
|
+
let currentPath2 = window.location.pathname;
|
|
1201
1518
|
function getFormKey(form) {
|
|
1202
1519
|
if (form.id) return `id:${form.id}`;
|
|
1203
1520
|
const explicitAction = form.getAttribute("action");
|
|
@@ -1228,8 +1545,8 @@ function attachFormStart(client, config) {
|
|
|
1228
1545
|
}
|
|
1229
1546
|
function resetIfPathChanged() {
|
|
1230
1547
|
const newPath = window.location.pathname;
|
|
1231
|
-
if (newPath ===
|
|
1232
|
-
|
|
1548
|
+
if (newPath === currentPath2) return;
|
|
1549
|
+
currentPath2 = newPath;
|
|
1233
1550
|
firedForms = /* @__PURE__ */ new Set();
|
|
1234
1551
|
}
|
|
1235
1552
|
const originalPushState = history.pushState.bind(history);
|
|
@@ -1308,21 +1625,64 @@ async function salesRequest(config, method, path, body) {
|
|
|
1308
1625
|
}
|
|
1309
1626
|
|
|
1310
1627
|
// ../tracking-core/src/resources/sales/client.ts
|
|
1628
|
+
function fireRecordedConversions(firing, input, recorded, sale, currency) {
|
|
1629
|
+
if (!firing) return;
|
|
1630
|
+
const txnBase = input.external_id ?? sale.id;
|
|
1631
|
+
for (const item of recorded) {
|
|
1632
|
+
if (!item.service) continue;
|
|
1633
|
+
const config = firing.getFiring(item.service);
|
|
1634
|
+
if (!config) continue;
|
|
1635
|
+
const cents = item.amount_cents ?? config.value_cents ?? null;
|
|
1636
|
+
fireConversionWithConsent({
|
|
1637
|
+
sendTo: config.send_to,
|
|
1638
|
+
value: cents != null ? fromMinor(cents, currency) : null,
|
|
1639
|
+
currency: config.currency ?? currency,
|
|
1640
|
+
transactionId: `${txnBase}:${item.service}`
|
|
1641
|
+
});
|
|
1642
|
+
}
|
|
1643
|
+
}
|
|
1311
1644
|
function createSalesClient(config) {
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1645
|
+
async function record(input) {
|
|
1646
|
+
const currency = input.currency ?? config.defaultCurrency;
|
|
1647
|
+
if (!currency) {
|
|
1648
|
+
throw new Error(
|
|
1649
|
+
"record: `currency` is required (pass it on the sale or set config.defaultCurrency)"
|
|
1650
|
+
);
|
|
1651
|
+
}
|
|
1652
|
+
const body = {
|
|
1653
|
+
...input,
|
|
1654
|
+
currency,
|
|
1655
|
+
occurred_at: input.occurred_at ?? (/* @__PURE__ */ new Date()).toISOString()
|
|
1656
|
+
};
|
|
1657
|
+
const sale = await salesRequest(config, "POST", "/sales", body);
|
|
1658
|
+
const recorded = input.services?.length ? input.services.map((s) => ({
|
|
1659
|
+
service: s.service,
|
|
1660
|
+
amount_cents: s.amount_cents
|
|
1661
|
+
})) : [
|
|
1662
|
+
{
|
|
1663
|
+
service: input.service,
|
|
1664
|
+
amount_cents: input.amount_total_cents ?? null
|
|
1319
1665
|
}
|
|
1320
|
-
|
|
1321
|
-
|
|
1666
|
+
];
|
|
1667
|
+
fireRecordedConversions(config.firing, input, recorded, sale, currency);
|
|
1668
|
+
return sale;
|
|
1669
|
+
}
|
|
1670
|
+
return {
|
|
1671
|
+
record,
|
|
1672
|
+
// recordSale is the intent-revealing alias — same behavior, clearer call site.
|
|
1673
|
+
recordSale: record,
|
|
1674
|
+
trackConversion(key, options) {
|
|
1675
|
+
const firing = config.firing?.getFiring(key);
|
|
1676
|
+
if (!firing) return;
|
|
1677
|
+
const currency = firing.currency ?? options?.currency ?? config.defaultCurrency ?? null;
|
|
1678
|
+
const cents = firing.value_cents ?? null;
|
|
1679
|
+
const value = options?.value ?? (cents != null && currency ? fromMinor(cents, currency) : null);
|
|
1680
|
+
fireConversionWithConsent({
|
|
1681
|
+
sendTo: firing.send_to,
|
|
1682
|
+
value,
|
|
1322
1683
|
currency,
|
|
1323
|
-
|
|
1324
|
-
};
|
|
1325
|
-
return salesRequest(config, "POST", "/sales", body);
|
|
1684
|
+
transactionId: options?.transactionId ?? null
|
|
1685
|
+
});
|
|
1326
1686
|
},
|
|
1327
1687
|
async list(query) {
|
|
1328
1688
|
const { cursor, limit, sort, order, want_total, ...filters } = query ?? {};
|
|
@@ -1416,39 +1776,6 @@ function createSalesClient(config) {
|
|
|
1416
1776
|
};
|
|
1417
1777
|
}
|
|
1418
1778
|
|
|
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
1779
|
// ../tracking-core/src/resources/sales/schema.ts
|
|
1453
1780
|
var import_zod11 = require("zod");
|
|
1454
1781
|
var SUPPORTED_CURRENCIES = ["USD", "CAD"];
|
|
@@ -1913,7 +2240,7 @@ function GoogleAdsTracking(props) {
|
|
|
1913
2240
|
var import_navigation = require("next/navigation");
|
|
1914
2241
|
|
|
1915
2242
|
// package.json
|
|
1916
|
-
var version = "0.
|
|
2243
|
+
var version = "0.14.0";
|
|
1917
2244
|
|
|
1918
2245
|
// src/factory.tsx
|
|
1919
2246
|
var import_react4 = require("react");
|
|
@@ -2007,7 +2334,7 @@ var PhoneField = (0, import_react3.forwardRef)(function PhoneField2({ country, v
|
|
|
2007
2334
|
// src/factory.tsx
|
|
2008
2335
|
var import_jsx_runtime5 = require("react/jsx-runtime");
|
|
2009
2336
|
function createTracking(options) {
|
|
2010
|
-
const { apiKey, endpoint, triggers, environment, gtagIds, debug, phone } = options;
|
|
2337
|
+
const { apiKey, endpoint, triggers, environment, gtagIds, debug, phone, conversionConfig } = options;
|
|
2011
2338
|
if (!apiKey || !endpoint) {
|
|
2012
2339
|
if (apiKey || endpoint) {
|
|
2013
2340
|
console.warn(
|
|
@@ -2051,6 +2378,17 @@ function createTracking(options) {
|
|
|
2051
2378
|
),
|
|
2052
2379
|
[]
|
|
2053
2380
|
);
|
|
2381
|
+
const conversionStore = (0, import_react4.useMemo)(
|
|
2382
|
+
() => conversionConfig ? resolveConversionConfig({
|
|
2383
|
+
cdnUrl: conversionConfig.cdnUrl,
|
|
2384
|
+
baked: conversionConfig.baked
|
|
2385
|
+
}) : null,
|
|
2386
|
+
[]
|
|
2387
|
+
);
|
|
2388
|
+
const wrapClient = (0, import_react4.useMemo)(() => {
|
|
2389
|
+
const autoFire = conversionStore ? createConversionAutoFire(conversionStore) : null;
|
|
2390
|
+
return (c) => autoFire ? withConversionAutoFire(c, autoFire) : c;
|
|
2391
|
+
}, [conversionStore]);
|
|
2054
2392
|
(0, import_react4.useEffect)(() => {
|
|
2055
2393
|
const rawClient = getOrCreateTrackingClient({
|
|
2056
2394
|
apiKey,
|
|
@@ -2062,7 +2400,7 @@ function createTracking(options) {
|
|
|
2062
2400
|
activeGtagIds: gtagIds,
|
|
2063
2401
|
debug
|
|
2064
2402
|
});
|
|
2065
|
-
fireManualPageView(rawClient);
|
|
2403
|
+
fireManualPageView(wrapClient(rawClient));
|
|
2066
2404
|
}, [pathname, searchParams]);
|
|
2067
2405
|
const firedPagesRef = (0, import_react4.useRef)(/* @__PURE__ */ new Set());
|
|
2068
2406
|
(0, import_react4.useEffect)(() => {
|
|
@@ -2077,14 +2415,18 @@ function createTracking(options) {
|
|
|
2077
2415
|
activeGtagIds: gtagIds,
|
|
2078
2416
|
debug
|
|
2079
2417
|
});
|
|
2418
|
+
const detectorClient = wrapClient(rawClient);
|
|
2080
2419
|
for (const { name, pathPattern } of specificPageVisitConfig.pages) {
|
|
2081
2420
|
if (!pathPattern.test(pathname)) continue;
|
|
2082
2421
|
const key = `${name}:${pathname}`;
|
|
2083
2422
|
if (firedPagesRef.current.has(key)) continue;
|
|
2084
2423
|
firedPagesRef.current.add(key);
|
|
2085
|
-
|
|
2424
|
+
detectorClient.trackEvent({
|
|
2086
2425
|
eventType: "specific_page_visit",
|
|
2087
|
-
metadata: {
|
|
2426
|
+
metadata: {
|
|
2427
|
+
page_name: name,
|
|
2428
|
+
page: { path: pathname }
|
|
2429
|
+
},
|
|
2088
2430
|
pageUrl: null,
|
|
2089
2431
|
occurredAt: null
|
|
2090
2432
|
});
|
|
@@ -2101,7 +2443,7 @@ function createTracking(options) {
|
|
|
2101
2443
|
activeGtagIds: gtagIds,
|
|
2102
2444
|
debug
|
|
2103
2445
|
});
|
|
2104
|
-
return attachBfcacheRestore(rawClient);
|
|
2446
|
+
return attachBfcacheRestore(wrapClient(rawClient));
|
|
2105
2447
|
}, []);
|
|
2106
2448
|
(0, import_react4.useEffect)(() => {
|
|
2107
2449
|
if (!timeOnSiteConfig) return;
|
|
@@ -2115,7 +2457,7 @@ function createTracking(options) {
|
|
|
2115
2457
|
activeGtagIds: gtagIds,
|
|
2116
2458
|
debug
|
|
2117
2459
|
});
|
|
2118
|
-
return attachTimeOnSite(rawClient, timeOnSiteConfig);
|
|
2460
|
+
return attachTimeOnSite(wrapClient(rawClient), timeOnSiteConfig);
|
|
2119
2461
|
}, []);
|
|
2120
2462
|
const multiPageSessionConfig = triggers.automatic.multi_page_session ?? null;
|
|
2121
2463
|
const mpsLastPathRef = (0, import_react4.useRef)(null);
|
|
@@ -2153,7 +2495,7 @@ function createTracking(options) {
|
|
|
2153
2495
|
activeGtagIds: gtagIds,
|
|
2154
2496
|
debug
|
|
2155
2497
|
});
|
|
2156
|
-
rawClient.trackEvent({
|
|
2498
|
+
wrapClient(rawClient).trackEvent({
|
|
2157
2499
|
eventType: "multi_page_session",
|
|
2158
2500
|
metadata: {
|
|
2159
2501
|
page_count: paths.size,
|
|
@@ -2178,13 +2520,14 @@ function createTracking(options) {
|
|
|
2178
2520
|
activeGtagIds: gtagIds,
|
|
2179
2521
|
debug
|
|
2180
2522
|
});
|
|
2523
|
+
const detectorClient = wrapClient(rawClient);
|
|
2181
2524
|
const scrollDepth = triggers.automatic.scroll_depth;
|
|
2182
2525
|
if (scrollDepth) {
|
|
2183
|
-
detachers.push(attachScrollDepth(
|
|
2526
|
+
detachers.push(attachScrollDepth(detectorClient, scrollDepth));
|
|
2184
2527
|
}
|
|
2185
2528
|
const formStart = triggers.automatic.form_start;
|
|
2186
2529
|
if (formStart) {
|
|
2187
|
-
detachers.push(attachFormStart(
|
|
2530
|
+
detachers.push(attachFormStart(detectorClient, formStart));
|
|
2188
2531
|
}
|
|
2189
2532
|
return () => {
|
|
2190
2533
|
for (let i = detachers.length - 1; i >= 0; i--) {
|
|
@@ -2233,6 +2576,7 @@ function createTracking(options) {
|
|
|
2233
2576
|
parsePhone,
|
|
2234
2577
|
phoneField,
|
|
2235
2578
|
resetConsent,
|
|
2579
|
+
resolveConversionConfig,
|
|
2236
2580
|
saleCreateSchema,
|
|
2237
2581
|
saleItemSchema,
|
|
2238
2582
|
saleServiceSchema,
|