@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.mjs
CHANGED
|
@@ -1,6 +1,13 @@
|
|
|
1
1
|
// ../tracking-core/src/consent.ts
|
|
2
2
|
var CONSENT_STATE_KEY = "consent_state";
|
|
3
3
|
var CONSENT_TIMESTAMP_KEY = "consent_timestamp";
|
|
4
|
+
var grantedListeners = /* @__PURE__ */ new Set();
|
|
5
|
+
function onConsentGranted(listener) {
|
|
6
|
+
grantedListeners.add(listener);
|
|
7
|
+
return () => {
|
|
8
|
+
grantedListeners.delete(listener);
|
|
9
|
+
};
|
|
10
|
+
}
|
|
4
11
|
function buildConsentPayload(state) {
|
|
5
12
|
return {
|
|
6
13
|
ad_storage: state,
|
|
@@ -29,6 +36,14 @@ function setConsentState(state) {
|
|
|
29
36
|
window.gtag("consent", "update", buildConsentPayload(state));
|
|
30
37
|
if (typeof window.fbq === "function")
|
|
31
38
|
window.fbq("consent", state === "granted" ? "grant" : "revoke");
|
|
39
|
+
if (state === "granted") {
|
|
40
|
+
for (const listener of grantedListeners) {
|
|
41
|
+
try {
|
|
42
|
+
listener();
|
|
43
|
+
} catch {
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
32
47
|
}
|
|
33
48
|
function resetConsent() {
|
|
34
49
|
if (typeof window === "undefined") return;
|
|
@@ -185,6 +200,24 @@ try {
|
|
|
185
200
|
} catch (e) {}
|
|
186
201
|
`.trim();
|
|
187
202
|
}
|
|
203
|
+
var SEND_TO_RE = /^AW-[A-Za-z0-9]+\/[A-Za-z0-9_-]+$/;
|
|
204
|
+
function isValidSendTo(sendTo) {
|
|
205
|
+
return SEND_TO_RE.test(sendTo);
|
|
206
|
+
}
|
|
207
|
+
function fireGtagConversion(input) {
|
|
208
|
+
if (typeof window === "undefined" || typeof window.gtag !== "function") return false;
|
|
209
|
+
if (!isValidSendTo(input.sendTo)) return false;
|
|
210
|
+
const params = { send_to: input.sendTo };
|
|
211
|
+
if (input.value != null) params.value = input.value;
|
|
212
|
+
if (input.currency) params.currency = input.currency;
|
|
213
|
+
if (input.transactionId) params.transaction_id = input.transactionId;
|
|
214
|
+
try {
|
|
215
|
+
window.gtag("event", "conversion", params);
|
|
216
|
+
return true;
|
|
217
|
+
} catch {
|
|
218
|
+
return false;
|
|
219
|
+
}
|
|
220
|
+
}
|
|
188
221
|
function createGtagInitScriptMulti(gtagIds) {
|
|
189
222
|
const safeIds = gtagIds.filter(isValidGtagId);
|
|
190
223
|
const configs = safeIds.map((id) => `gtag('config', '${id}');`).join("\n");
|
|
@@ -313,6 +346,329 @@ function createTrackingEventCreatePayload(trackingParams, input, context) {
|
|
|
313
346
|
};
|
|
314
347
|
}
|
|
315
348
|
|
|
349
|
+
// ../tracking-core/src/resources/conversion-firing.ts
|
|
350
|
+
var DEDUP_PREFIX = "_aranova_conv_";
|
|
351
|
+
var MAX_PENDING = 100;
|
|
352
|
+
var pendingQueue = [];
|
|
353
|
+
function dedupKey(input) {
|
|
354
|
+
return `${DEDUP_PREFIX}${input.transactionId ?? ""}:${input.sendTo}`;
|
|
355
|
+
}
|
|
356
|
+
function alreadyFired(input) {
|
|
357
|
+
if (!input.transactionId || typeof window === "undefined") return false;
|
|
358
|
+
try {
|
|
359
|
+
return window.sessionStorage.getItem(dedupKey(input)) !== null;
|
|
360
|
+
} catch {
|
|
361
|
+
return false;
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
function markFired(input) {
|
|
365
|
+
if (!input.transactionId || typeof window === "undefined") return;
|
|
366
|
+
try {
|
|
367
|
+
window.sessionStorage.setItem(dedupKey(input), "1");
|
|
368
|
+
} catch {
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
function fireOnce(input) {
|
|
372
|
+
if (alreadyFired(input)) return;
|
|
373
|
+
if (fireGtagConversion(input)) markFired(input);
|
|
374
|
+
}
|
|
375
|
+
function fireConversionWithConsent(input) {
|
|
376
|
+
const state = getConsentState();
|
|
377
|
+
if (state === "denied") return;
|
|
378
|
+
if (state === "pending") {
|
|
379
|
+
if (pendingQueue.length >= MAX_PENDING) pendingQueue.shift();
|
|
380
|
+
pendingQueue.push(input);
|
|
381
|
+
return;
|
|
382
|
+
}
|
|
383
|
+
fireOnce(input);
|
|
384
|
+
}
|
|
385
|
+
function flushPendingConversions() {
|
|
386
|
+
if (getConsentState() !== "granted") return;
|
|
387
|
+
while (pendingQueue.length > 0) {
|
|
388
|
+
const input = pendingQueue.shift();
|
|
389
|
+
if (input) fireOnce(input);
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
if (typeof window !== "undefined") onConsentGranted(flushPendingConversions);
|
|
393
|
+
|
|
394
|
+
// ../tracking-core/src/resources/conversion-config.ts
|
|
395
|
+
function isStringMap(value) {
|
|
396
|
+
return typeof value === "object" && value !== null && Object.values(value).every((v) => typeof v === "string");
|
|
397
|
+
}
|
|
398
|
+
function parseFiring(value) {
|
|
399
|
+
if (!value || typeof value !== "object") return null;
|
|
400
|
+
const f = value;
|
|
401
|
+
if (typeof f.send_to !== "string") return null;
|
|
402
|
+
return {
|
|
403
|
+
send_to: f.send_to,
|
|
404
|
+
value_cents: typeof f.value_cents === "number" ? f.value_cents : null,
|
|
405
|
+
currency: typeof f.currency === "string" ? f.currency : null
|
|
406
|
+
};
|
|
407
|
+
}
|
|
408
|
+
function parseTrigger(value) {
|
|
409
|
+
if (!value || typeof value !== "object") return null;
|
|
410
|
+
const t = value;
|
|
411
|
+
if (typeof t.event_type !== "string") return null;
|
|
412
|
+
const spec = { event_type: t.event_type };
|
|
413
|
+
if (typeof t.threshold_percent === "number") spec.threshold_percent = t.threshold_percent;
|
|
414
|
+
if (typeof t.threshold_seconds === "number") spec.threshold_seconds = t.threshold_seconds;
|
|
415
|
+
if (typeof t.page_threshold === "number") spec.page_threshold = t.page_threshold;
|
|
416
|
+
if (typeof t.page_name === "string") spec.page_name = t.page_name;
|
|
417
|
+
return spec;
|
|
418
|
+
}
|
|
419
|
+
function parseConversionConfig(raw) {
|
|
420
|
+
if (!raw || typeof raw !== "object") return null;
|
|
421
|
+
const obj = raw;
|
|
422
|
+
const servicesRaw = Array.isArray(obj.services) ? obj.services : [];
|
|
423
|
+
const services = servicesRaw.flatMap((entry) => {
|
|
424
|
+
if (!entry || typeof entry !== "object") return [];
|
|
425
|
+
const s = entry;
|
|
426
|
+
if (typeof s.key !== "string") return [];
|
|
427
|
+
return [
|
|
428
|
+
{
|
|
429
|
+
key: s.key,
|
|
430
|
+
label: typeof s.label === "string" ? s.label : void 0,
|
|
431
|
+
firing: parseFiring(s.firing)
|
|
432
|
+
}
|
|
433
|
+
];
|
|
434
|
+
});
|
|
435
|
+
const goalsRaw = Array.isArray(obj.goals) ? obj.goals : null;
|
|
436
|
+
const goals = goalsRaw ? goalsRaw.flatMap((entry) => {
|
|
437
|
+
if (!entry || typeof entry !== "object") return [];
|
|
438
|
+
const g = entry;
|
|
439
|
+
if (typeof g.key !== "string") return [];
|
|
440
|
+
return [
|
|
441
|
+
{
|
|
442
|
+
key: g.key,
|
|
443
|
+
label: typeof g.label === "string" ? g.label : void 0,
|
|
444
|
+
kind: g.kind === "event" ? "event" : "sale",
|
|
445
|
+
trigger: parseTrigger(g.trigger),
|
|
446
|
+
firing: parseFiring(g.firing)
|
|
447
|
+
}
|
|
448
|
+
];
|
|
449
|
+
}) : services.map((s) => ({
|
|
450
|
+
key: s.key,
|
|
451
|
+
label: s.label,
|
|
452
|
+
kind: "sale",
|
|
453
|
+
trigger: null,
|
|
454
|
+
firing: s.firing
|
|
455
|
+
}));
|
|
456
|
+
return {
|
|
457
|
+
schema_version: typeof obj.schema_version === "number" ? obj.schema_version : 1,
|
|
458
|
+
config_version: typeof obj.config_version === "number" ? obj.config_version : 0,
|
|
459
|
+
business_id: typeof obj.business_id === "string" ? obj.business_id : void 0,
|
|
460
|
+
customer_id: typeof obj.customer_id === "string" ? obj.customer_id : null,
|
|
461
|
+
environment: typeof obj.environment === "string" ? obj.environment : void 0,
|
|
462
|
+
gtag_ids: isStringMap(obj.gtag_ids) ? obj.gtag_ids : {},
|
|
463
|
+
meta_pixel_ids: isStringMap(obj.meta_pixel_ids) ? obj.meta_pixel_ids : {},
|
|
464
|
+
services,
|
|
465
|
+
goals
|
|
466
|
+
};
|
|
467
|
+
}
|
|
468
|
+
var CACHE_PREFIX = "_aranova_cfg_";
|
|
469
|
+
function cacheKey(url) {
|
|
470
|
+
return `${CACHE_PREFIX}${url}`;
|
|
471
|
+
}
|
|
472
|
+
function readCache(url) {
|
|
473
|
+
if (typeof window === "undefined") return null;
|
|
474
|
+
try {
|
|
475
|
+
const raw = window.sessionStorage.getItem(cacheKey(url));
|
|
476
|
+
if (!raw) return null;
|
|
477
|
+
const parsed = JSON.parse(raw);
|
|
478
|
+
const config = parseConversionConfig(parsed.config);
|
|
479
|
+
if (!config) return null;
|
|
480
|
+
return {
|
|
481
|
+
etag: typeof parsed.etag === "string" ? parsed.etag : null,
|
|
482
|
+
config
|
|
483
|
+
};
|
|
484
|
+
} catch {
|
|
485
|
+
return null;
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
function writeCache(url, entry) {
|
|
489
|
+
if (typeof window === "undefined") return;
|
|
490
|
+
try {
|
|
491
|
+
window.sessionStorage.setItem(cacheKey(url), JSON.stringify(entry));
|
|
492
|
+
} catch {
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
function resolveConversionConfig(options) {
|
|
496
|
+
const cached = readCache(options.cdnUrl);
|
|
497
|
+
let current = cached?.config ?? options.baked ?? null;
|
|
498
|
+
let etag = cached?.etag ?? null;
|
|
499
|
+
const goalsByKey = /* @__PURE__ */ new Map();
|
|
500
|
+
const resolveListeners = /* @__PURE__ */ new Set();
|
|
501
|
+
function rebuildIndex() {
|
|
502
|
+
goalsByKey.clear();
|
|
503
|
+
for (const goal of current?.goals ?? []) {
|
|
504
|
+
goalsByKey.set(goal.key, goal);
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
function notifyResolved() {
|
|
508
|
+
const listeners = [...resolveListeners];
|
|
509
|
+
resolveListeners.clear();
|
|
510
|
+
for (const listener of listeners) {
|
|
511
|
+
try {
|
|
512
|
+
listener();
|
|
513
|
+
} catch {
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
function adopt(next, nextEtag) {
|
|
518
|
+
if (!next) return;
|
|
519
|
+
if (current && next.config_version <= current.config_version) return;
|
|
520
|
+
const wasEmpty = current === null;
|
|
521
|
+
current = next;
|
|
522
|
+
etag = nextEtag;
|
|
523
|
+
rebuildIndex();
|
|
524
|
+
writeCache(options.cdnUrl, { etag, config: next });
|
|
525
|
+
if (wasEmpty) notifyResolved();
|
|
526
|
+
}
|
|
527
|
+
async function revalidate() {
|
|
528
|
+
if (typeof window === "undefined") return;
|
|
529
|
+
try {
|
|
530
|
+
const doFetch = options.fetchImpl ?? globalThis.fetch;
|
|
531
|
+
if (!doFetch) return;
|
|
532
|
+
const headers = {};
|
|
533
|
+
if (etag) headers["If-None-Match"] = etag;
|
|
534
|
+
const response = await doFetch(options.cdnUrl, {
|
|
535
|
+
method: "GET",
|
|
536
|
+
headers
|
|
537
|
+
});
|
|
538
|
+
if (response.status === 304 || !response.ok) return;
|
|
539
|
+
adopt(parseConversionConfig(await response.json()), response.headers.get("ETag"));
|
|
540
|
+
} catch {
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
rebuildIndex();
|
|
544
|
+
void revalidate();
|
|
545
|
+
return {
|
|
546
|
+
getFiring: (key) => goalsByKey.get(key)?.firing ?? null,
|
|
547
|
+
getGoal: (key) => goalsByKey.get(key) ?? null,
|
|
548
|
+
listGoals: () => [...goalsByKey.values()],
|
|
549
|
+
current: () => current,
|
|
550
|
+
isReady: () => current !== null,
|
|
551
|
+
onResolve: (listener) => {
|
|
552
|
+
if (current !== null) {
|
|
553
|
+
listener();
|
|
554
|
+
return () => {
|
|
555
|
+
};
|
|
556
|
+
}
|
|
557
|
+
resolveListeners.add(listener);
|
|
558
|
+
return () => resolveListeners.delete(listener);
|
|
559
|
+
},
|
|
560
|
+
revalidate
|
|
561
|
+
};
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
// ../tracking-core/src/resources/sales/money.ts
|
|
565
|
+
var MINOR_UNIT_EXPONENT = {
|
|
566
|
+
USD: 2,
|
|
567
|
+
CAD: 2
|
|
568
|
+
};
|
|
569
|
+
function exponentFor(currency) {
|
|
570
|
+
return MINOR_UNIT_EXPONENT[currency] ?? 2;
|
|
571
|
+
}
|
|
572
|
+
function toMinor(amount, currency) {
|
|
573
|
+
return Math.round(amount * 10 ** exponentFor(currency));
|
|
574
|
+
}
|
|
575
|
+
function fromMinor(cents, currency) {
|
|
576
|
+
return cents / 10 ** exponentFor(currency);
|
|
577
|
+
}
|
|
578
|
+
function formatMoney(cents, currency, locale) {
|
|
579
|
+
return new Intl.NumberFormat(locale, { style: "currency", currency }).format(
|
|
580
|
+
fromMinor(cents, currency)
|
|
581
|
+
);
|
|
582
|
+
}
|
|
583
|
+
function formatDateInTz(iso, timeZone, opts, locale) {
|
|
584
|
+
const date = new Date(iso);
|
|
585
|
+
if (Number.isNaN(date.getTime())) return iso;
|
|
586
|
+
return new Intl.DateTimeFormat(locale, {
|
|
587
|
+
year: "numeric",
|
|
588
|
+
month: "short",
|
|
589
|
+
day: "2-digit",
|
|
590
|
+
hour: "2-digit",
|
|
591
|
+
minute: "2-digit",
|
|
592
|
+
...opts,
|
|
593
|
+
timeZone
|
|
594
|
+
}).format(date);
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
// ../tracking-core/src/resources/conversion-autofire.ts
|
|
598
|
+
function thresholdMet(goal, eventType, metadata) {
|
|
599
|
+
const t = goal.trigger;
|
|
600
|
+
if (!t || t.event_type !== eventType) return false;
|
|
601
|
+
switch (eventType) {
|
|
602
|
+
case "scroll_depth":
|
|
603
|
+
return typeof metadata.depth_percent === "number" && t.threshold_percent != null && metadata.depth_percent >= t.threshold_percent;
|
|
604
|
+
case "time_on_site":
|
|
605
|
+
return typeof metadata.duration_ms === "number" && t.threshold_seconds != null && metadata.duration_ms >= t.threshold_seconds * 1e3;
|
|
606
|
+
case "multi_page_session":
|
|
607
|
+
return typeof metadata.page_count === "number" && t.page_threshold != null && metadata.page_count >= t.page_threshold;
|
|
608
|
+
case "specific_page_visit":
|
|
609
|
+
return typeof metadata.page_name === "string" && metadata.page_name === t.page_name;
|
|
610
|
+
case "page_view":
|
|
611
|
+
case "form_start":
|
|
612
|
+
return true;
|
|
613
|
+
// no threshold — fire whenever the detector emits
|
|
614
|
+
default:
|
|
615
|
+
return false;
|
|
616
|
+
}
|
|
617
|
+
}
|
|
618
|
+
function currentPath() {
|
|
619
|
+
return typeof window === "undefined" ? "" : window.location.pathname;
|
|
620
|
+
}
|
|
621
|
+
var MAX_BUFFERED_EVENTS = 50;
|
|
622
|
+
function createConversionAutoFire(store) {
|
|
623
|
+
const pending = [];
|
|
624
|
+
let subscribed = false;
|
|
625
|
+
function fireMatching(eventType, metadata) {
|
|
626
|
+
for (const goal of store.listGoals()) {
|
|
627
|
+
if (goal.kind !== "event" || !goal.firing) continue;
|
|
628
|
+
if (!thresholdMet(goal, eventType, metadata)) continue;
|
|
629
|
+
const firing = goal.firing;
|
|
630
|
+
const cents = firing.value_cents ?? null;
|
|
631
|
+
const currency = firing.currency ?? null;
|
|
632
|
+
fireConversionWithConsent({
|
|
633
|
+
sendTo: firing.send_to,
|
|
634
|
+
value: cents != null && currency ? fromMinor(cents, currency) : null,
|
|
635
|
+
currency,
|
|
636
|
+
// Page-scoped txn id → fire once per (goal, path) per session; engagement conversions
|
|
637
|
+
// shouldn't re-fire as the visitor scrolls back and forth or re-enters a page.
|
|
638
|
+
transactionId: `auto:${goal.key}:${currentPath()}`
|
|
639
|
+
});
|
|
640
|
+
}
|
|
641
|
+
}
|
|
642
|
+
return {
|
|
643
|
+
onAutomaticEvent(eventType, metadata) {
|
|
644
|
+
if (store.isReady()) {
|
|
645
|
+
fireMatching(eventType, metadata);
|
|
646
|
+
return;
|
|
647
|
+
}
|
|
648
|
+
if (pending.length < MAX_BUFFERED_EVENTS) pending.push({ eventType, metadata });
|
|
649
|
+
if (!subscribed) {
|
|
650
|
+
subscribed = true;
|
|
651
|
+
store.onResolve(() => {
|
|
652
|
+
const buffered = pending.splice(0);
|
|
653
|
+
for (const event of buffered) fireMatching(event.eventType, event.metadata);
|
|
654
|
+
});
|
|
655
|
+
}
|
|
656
|
+
}
|
|
657
|
+
};
|
|
658
|
+
}
|
|
659
|
+
function withConversionAutoFire(client, autoFire) {
|
|
660
|
+
return {
|
|
661
|
+
...client,
|
|
662
|
+
trackEvent: (input) => {
|
|
663
|
+
client.trackEvent(input);
|
|
664
|
+
try {
|
|
665
|
+
autoFire.onAutomaticEvent(input.eventType, input.metadata ?? {});
|
|
666
|
+
} catch {
|
|
667
|
+
}
|
|
668
|
+
}
|
|
669
|
+
};
|
|
670
|
+
}
|
|
671
|
+
|
|
316
672
|
// ../tracking-core/src/session.ts
|
|
317
673
|
var VISITOR_STORAGE_KEY = "aranova_tracking_visitor";
|
|
318
674
|
var SESSION_STORAGE_KEY = "aranova_tracking_session";
|
|
@@ -1049,7 +1405,7 @@ function attachScrollDepth(client, config) {
|
|
|
1049
1405
|
}
|
|
1050
1406
|
const thresholds = new Set(config.thresholds);
|
|
1051
1407
|
let firedForPath = /* @__PURE__ */ new Set();
|
|
1052
|
-
let
|
|
1408
|
+
let currentPath2 = window.location.pathname;
|
|
1053
1409
|
let rafId = null;
|
|
1054
1410
|
function getScrollPercent() {
|
|
1055
1411
|
const doc = document.documentElement;
|
|
@@ -1068,7 +1424,7 @@ function attachScrollDepth(client, config) {
|
|
|
1068
1424
|
eventType: "scroll_depth",
|
|
1069
1425
|
metadata: {
|
|
1070
1426
|
depth_percent: threshold,
|
|
1071
|
-
page: { path:
|
|
1427
|
+
page: { path: currentPath2 }
|
|
1072
1428
|
},
|
|
1073
1429
|
pageUrl: window.location.href,
|
|
1074
1430
|
occurredAt: null
|
|
@@ -1085,8 +1441,8 @@ function attachScrollDepth(client, config) {
|
|
|
1085
1441
|
}
|
|
1086
1442
|
function resetIfPathChanged() {
|
|
1087
1443
|
const newPath = window.location.pathname;
|
|
1088
|
-
if (newPath ===
|
|
1089
|
-
|
|
1444
|
+
if (newPath === currentPath2) return;
|
|
1445
|
+
currentPath2 = newPath;
|
|
1090
1446
|
firedForPath = /* @__PURE__ */ new Set();
|
|
1091
1447
|
setTimeout(checkThresholds, 0);
|
|
1092
1448
|
}
|
|
@@ -1122,7 +1478,7 @@ function attachFormStart(client, config) {
|
|
|
1122
1478
|
}
|
|
1123
1479
|
const selector = config.selector ?? "form";
|
|
1124
1480
|
let firedForms = /* @__PURE__ */ new Set();
|
|
1125
|
-
let
|
|
1481
|
+
let currentPath2 = window.location.pathname;
|
|
1126
1482
|
function getFormKey(form) {
|
|
1127
1483
|
if (form.id) return `id:${form.id}`;
|
|
1128
1484
|
const explicitAction = form.getAttribute("action");
|
|
@@ -1153,8 +1509,8 @@ function attachFormStart(client, config) {
|
|
|
1153
1509
|
}
|
|
1154
1510
|
function resetIfPathChanged() {
|
|
1155
1511
|
const newPath = window.location.pathname;
|
|
1156
|
-
if (newPath ===
|
|
1157
|
-
|
|
1512
|
+
if (newPath === currentPath2) return;
|
|
1513
|
+
currentPath2 = newPath;
|
|
1158
1514
|
firedForms = /* @__PURE__ */ new Set();
|
|
1159
1515
|
}
|
|
1160
1516
|
const originalPushState = history.pushState.bind(history);
|
|
@@ -1233,21 +1589,64 @@ async function salesRequest(config, method, path, body) {
|
|
|
1233
1589
|
}
|
|
1234
1590
|
|
|
1235
1591
|
// ../tracking-core/src/resources/sales/client.ts
|
|
1592
|
+
function fireRecordedConversions(firing, input, recorded, sale, currency) {
|
|
1593
|
+
if (!firing) return;
|
|
1594
|
+
const txnBase = input.external_id ?? sale.id;
|
|
1595
|
+
for (const item of recorded) {
|
|
1596
|
+
if (!item.service) continue;
|
|
1597
|
+
const config = firing.getFiring(item.service);
|
|
1598
|
+
if (!config) continue;
|
|
1599
|
+
const cents = item.amount_cents ?? config.value_cents ?? null;
|
|
1600
|
+
fireConversionWithConsent({
|
|
1601
|
+
sendTo: config.send_to,
|
|
1602
|
+
value: cents != null ? fromMinor(cents, currency) : null,
|
|
1603
|
+
currency: config.currency ?? currency,
|
|
1604
|
+
transactionId: `${txnBase}:${item.service}`
|
|
1605
|
+
});
|
|
1606
|
+
}
|
|
1607
|
+
}
|
|
1236
1608
|
function createSalesClient(config) {
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1609
|
+
async function record(input) {
|
|
1610
|
+
const currency = input.currency ?? config.defaultCurrency;
|
|
1611
|
+
if (!currency) {
|
|
1612
|
+
throw new Error(
|
|
1613
|
+
"record: `currency` is required (pass it on the sale or set config.defaultCurrency)"
|
|
1614
|
+
);
|
|
1615
|
+
}
|
|
1616
|
+
const body = {
|
|
1617
|
+
...input,
|
|
1618
|
+
currency,
|
|
1619
|
+
occurred_at: input.occurred_at ?? (/* @__PURE__ */ new Date()).toISOString()
|
|
1620
|
+
};
|
|
1621
|
+
const sale = await salesRequest(config, "POST", "/sales", body);
|
|
1622
|
+
const recorded = input.services?.length ? input.services.map((s) => ({
|
|
1623
|
+
service: s.service,
|
|
1624
|
+
amount_cents: s.amount_cents
|
|
1625
|
+
})) : [
|
|
1626
|
+
{
|
|
1627
|
+
service: input.service,
|
|
1628
|
+
amount_cents: input.amount_total_cents ?? null
|
|
1244
1629
|
}
|
|
1245
|
-
|
|
1246
|
-
|
|
1630
|
+
];
|
|
1631
|
+
fireRecordedConversions(config.firing, input, recorded, sale, currency);
|
|
1632
|
+
return sale;
|
|
1633
|
+
}
|
|
1634
|
+
return {
|
|
1635
|
+
record,
|
|
1636
|
+
// recordSale is the intent-revealing alias — same behavior, clearer call site.
|
|
1637
|
+
recordSale: record,
|
|
1638
|
+
trackConversion(key, options) {
|
|
1639
|
+
const firing = config.firing?.getFiring(key);
|
|
1640
|
+
if (!firing) return;
|
|
1641
|
+
const currency = firing.currency ?? options?.currency ?? config.defaultCurrency ?? null;
|
|
1642
|
+
const cents = firing.value_cents ?? null;
|
|
1643
|
+
const value = options?.value ?? (cents != null && currency ? fromMinor(cents, currency) : null);
|
|
1644
|
+
fireConversionWithConsent({
|
|
1645
|
+
sendTo: firing.send_to,
|
|
1646
|
+
value,
|
|
1247
1647
|
currency,
|
|
1248
|
-
|
|
1249
|
-
};
|
|
1250
|
-
return salesRequest(config, "POST", "/sales", body);
|
|
1648
|
+
transactionId: options?.transactionId ?? null
|
|
1649
|
+
});
|
|
1251
1650
|
},
|
|
1252
1651
|
async list(query) {
|
|
1253
1652
|
const { cursor, limit, sort, order, want_total, ...filters } = query ?? {};
|
|
@@ -1341,39 +1740,6 @@ function createSalesClient(config) {
|
|
|
1341
1740
|
};
|
|
1342
1741
|
}
|
|
1343
1742
|
|
|
1344
|
-
// ../tracking-core/src/resources/sales/money.ts
|
|
1345
|
-
var MINOR_UNIT_EXPONENT = {
|
|
1346
|
-
USD: 2,
|
|
1347
|
-
CAD: 2
|
|
1348
|
-
};
|
|
1349
|
-
function exponentFor(currency) {
|
|
1350
|
-
return MINOR_UNIT_EXPONENT[currency] ?? 2;
|
|
1351
|
-
}
|
|
1352
|
-
function toMinor(amount, currency) {
|
|
1353
|
-
return Math.round(amount * 10 ** exponentFor(currency));
|
|
1354
|
-
}
|
|
1355
|
-
function fromMinor(cents, currency) {
|
|
1356
|
-
return cents / 10 ** exponentFor(currency);
|
|
1357
|
-
}
|
|
1358
|
-
function formatMoney(cents, currency, locale) {
|
|
1359
|
-
return new Intl.NumberFormat(locale, { style: "currency", currency }).format(
|
|
1360
|
-
fromMinor(cents, currency)
|
|
1361
|
-
);
|
|
1362
|
-
}
|
|
1363
|
-
function formatDateInTz(iso, timeZone, opts, locale) {
|
|
1364
|
-
const date = new Date(iso);
|
|
1365
|
-
if (Number.isNaN(date.getTime())) return iso;
|
|
1366
|
-
return new Intl.DateTimeFormat(locale, {
|
|
1367
|
-
year: "numeric",
|
|
1368
|
-
month: "short",
|
|
1369
|
-
day: "2-digit",
|
|
1370
|
-
hour: "2-digit",
|
|
1371
|
-
minute: "2-digit",
|
|
1372
|
-
...opts,
|
|
1373
|
-
timeZone
|
|
1374
|
-
}).format(date);
|
|
1375
|
-
}
|
|
1376
|
-
|
|
1377
1743
|
// ../tracking-core/src/resources/sales/schema.ts
|
|
1378
1744
|
import { z as z11 } from "zod";
|
|
1379
1745
|
var SUPPORTED_CURRENCIES = ["USD", "CAD"];
|
|
@@ -1838,7 +2204,7 @@ function GoogleAdsTracking(props) {
|
|
|
1838
2204
|
import { usePathname, useSearchParams } from "next/navigation";
|
|
1839
2205
|
|
|
1840
2206
|
// package.json
|
|
1841
|
-
var version = "0.
|
|
2207
|
+
var version = "0.14.1";
|
|
1842
2208
|
|
|
1843
2209
|
// src/factory.tsx
|
|
1844
2210
|
import { createContext as createContext2, useContext as useContext2, useEffect as useEffect3, useMemo as useMemo2, useRef } from "react";
|
|
@@ -1939,7 +2305,7 @@ var PhoneField = forwardRef(function PhoneField2({ country, value, defaultValue,
|
|
|
1939
2305
|
// src/factory.tsx
|
|
1940
2306
|
import { jsx as jsx5 } from "react/jsx-runtime";
|
|
1941
2307
|
function createTracking(options) {
|
|
1942
|
-
const { apiKey, endpoint, triggers, environment, gtagIds, debug, phone } = options;
|
|
2308
|
+
const { apiKey, endpoint, triggers, environment, gtagIds, debug, phone, conversionConfig } = options;
|
|
1943
2309
|
if (!apiKey || !endpoint) {
|
|
1944
2310
|
if (apiKey || endpoint) {
|
|
1945
2311
|
console.warn(
|
|
@@ -1983,6 +2349,17 @@ function createTracking(options) {
|
|
|
1983
2349
|
),
|
|
1984
2350
|
[]
|
|
1985
2351
|
);
|
|
2352
|
+
const conversionStore = useMemo2(
|
|
2353
|
+
() => conversionConfig ? resolveConversionConfig({
|
|
2354
|
+
cdnUrl: conversionConfig.cdnUrl,
|
|
2355
|
+
baked: conversionConfig.baked
|
|
2356
|
+
}) : null,
|
|
2357
|
+
[]
|
|
2358
|
+
);
|
|
2359
|
+
const wrapClient = useMemo2(() => {
|
|
2360
|
+
const autoFire = conversionStore ? createConversionAutoFire(conversionStore) : null;
|
|
2361
|
+
return (c) => autoFire ? withConversionAutoFire(c, autoFire) : c;
|
|
2362
|
+
}, [conversionStore]);
|
|
1986
2363
|
useEffect3(() => {
|
|
1987
2364
|
const rawClient = getOrCreateTrackingClient({
|
|
1988
2365
|
apiKey,
|
|
@@ -1994,7 +2371,7 @@ function createTracking(options) {
|
|
|
1994
2371
|
activeGtagIds: gtagIds,
|
|
1995
2372
|
debug
|
|
1996
2373
|
});
|
|
1997
|
-
fireManualPageView(rawClient);
|
|
2374
|
+
fireManualPageView(wrapClient(rawClient));
|
|
1998
2375
|
}, [pathname, searchParams]);
|
|
1999
2376
|
const firedPagesRef = useRef(/* @__PURE__ */ new Set());
|
|
2000
2377
|
useEffect3(() => {
|
|
@@ -2009,14 +2386,18 @@ function createTracking(options) {
|
|
|
2009
2386
|
activeGtagIds: gtagIds,
|
|
2010
2387
|
debug
|
|
2011
2388
|
});
|
|
2389
|
+
const detectorClient = wrapClient(rawClient);
|
|
2012
2390
|
for (const { name, pathPattern } of specificPageVisitConfig.pages) {
|
|
2013
2391
|
if (!pathPattern.test(pathname)) continue;
|
|
2014
2392
|
const key = `${name}:${pathname}`;
|
|
2015
2393
|
if (firedPagesRef.current.has(key)) continue;
|
|
2016
2394
|
firedPagesRef.current.add(key);
|
|
2017
|
-
|
|
2395
|
+
detectorClient.trackEvent({
|
|
2018
2396
|
eventType: "specific_page_visit",
|
|
2019
|
-
metadata: {
|
|
2397
|
+
metadata: {
|
|
2398
|
+
page_name: name,
|
|
2399
|
+
page: { path: pathname }
|
|
2400
|
+
},
|
|
2020
2401
|
pageUrl: null,
|
|
2021
2402
|
occurredAt: null
|
|
2022
2403
|
});
|
|
@@ -2033,7 +2414,7 @@ function createTracking(options) {
|
|
|
2033
2414
|
activeGtagIds: gtagIds,
|
|
2034
2415
|
debug
|
|
2035
2416
|
});
|
|
2036
|
-
return attachBfcacheRestore(rawClient);
|
|
2417
|
+
return attachBfcacheRestore(wrapClient(rawClient));
|
|
2037
2418
|
}, []);
|
|
2038
2419
|
useEffect3(() => {
|
|
2039
2420
|
if (!timeOnSiteConfig) return;
|
|
@@ -2047,7 +2428,7 @@ function createTracking(options) {
|
|
|
2047
2428
|
activeGtagIds: gtagIds,
|
|
2048
2429
|
debug
|
|
2049
2430
|
});
|
|
2050
|
-
return attachTimeOnSite(rawClient, timeOnSiteConfig);
|
|
2431
|
+
return attachTimeOnSite(wrapClient(rawClient), timeOnSiteConfig);
|
|
2051
2432
|
}, []);
|
|
2052
2433
|
const multiPageSessionConfig = triggers.automatic.multi_page_session ?? null;
|
|
2053
2434
|
const mpsLastPathRef = useRef(null);
|
|
@@ -2085,7 +2466,7 @@ function createTracking(options) {
|
|
|
2085
2466
|
activeGtagIds: gtagIds,
|
|
2086
2467
|
debug
|
|
2087
2468
|
});
|
|
2088
|
-
rawClient.trackEvent({
|
|
2469
|
+
wrapClient(rawClient).trackEvent({
|
|
2089
2470
|
eventType: "multi_page_session",
|
|
2090
2471
|
metadata: {
|
|
2091
2472
|
page_count: paths.size,
|
|
@@ -2110,13 +2491,14 @@ function createTracking(options) {
|
|
|
2110
2491
|
activeGtagIds: gtagIds,
|
|
2111
2492
|
debug
|
|
2112
2493
|
});
|
|
2494
|
+
const detectorClient = wrapClient(rawClient);
|
|
2113
2495
|
const scrollDepth = triggers.automatic.scroll_depth;
|
|
2114
2496
|
if (scrollDepth) {
|
|
2115
|
-
detachers.push(attachScrollDepth(
|
|
2497
|
+
detachers.push(attachScrollDepth(detectorClient, scrollDepth));
|
|
2116
2498
|
}
|
|
2117
2499
|
const formStart = triggers.automatic.form_start;
|
|
2118
2500
|
if (formStart) {
|
|
2119
|
-
detachers.push(attachFormStart(
|
|
2501
|
+
detachers.push(attachFormStart(detectorClient, formStart));
|
|
2120
2502
|
}
|
|
2121
2503
|
return () => {
|
|
2122
2504
|
for (let i = detachers.length - 1; i >= 0; i--) {
|
|
@@ -2164,6 +2546,7 @@ export {
|
|
|
2164
2546
|
parsePhone,
|
|
2165
2547
|
phoneField,
|
|
2166
2548
|
resetConsent,
|
|
2549
|
+
resolveConversionConfig,
|
|
2167
2550
|
saleCreateSchema,
|
|
2168
2551
|
saleItemSchema,
|
|
2169
2552
|
saleServiceSchema,
|