@aranova/tracking-react 0.27.0 → 1.1.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/dist/client.d.mts +11 -4
- package/dist/client.d.ts +11 -4
- package/dist/client.js +620 -350
- package/dist/client.js.map +1 -1
- package/dist/client.mjs +610 -350
- package/dist/client.mjs.map +1 -1
- package/dist/index.d.mts +7 -6
- package/dist/index.d.ts +7 -6
- package/dist/index.js +339 -393
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +328 -393
- package/dist/index.mjs.map +1 -1
- package/dist/ingest-typed-MBuMe9Pn.d.ts +883 -0
- package/dist/ingest-typed-UA13xTfI.d.mts +883 -0
- package/dist/{phone-utils-BVzSNBf1.d.mts → phone-utils-C0ds6cqZ.d.mts} +24 -78
- package/dist/{phone-utils-BVzSNBf1.d.ts → phone-utils-C0ds6cqZ.d.ts} +24 -78
- package/dist/phone.d.mts +2 -2
- package/dist/phone.d.ts +2 -2
- package/dist/phone.js +0 -464
- package/dist/phone.js.map +1 -1
- package/dist/phone.mjs +0 -464
- package/dist/phone.mjs.map +1 -1
- package/dist/{sales-D4TB9gRS.d.ts → sales-BuKD_bdo.d.mts} +90 -329
- package/dist/{sales-cYjB07y8.d.mts → sales-C3O_xxZ_.d.ts} +90 -329
- package/dist/sales.d.mts +3 -3
- package/dist/sales.d.ts +3 -3
- package/dist/sales.js +270 -127
- package/dist/sales.js.map +1 -1
- package/dist/sales.mjs +259 -127
- package/dist/sales.mjs.map +1 -1
- package/dist/{tracking-config-runtime-D7prdTE1.d.mts → tracking-config-runtime-C6H7ofmo.d.mts} +6 -0
- package/dist/{tracking-config-runtime-D7prdTE1.d.ts → tracking-config-runtime-C6H7ofmo.d.ts} +6 -0
- package/package.json +2 -2
- package/dist/ingest-typed-BxLFlZ7N.d.mts +0 -1371
- package/dist/ingest-typed-ByQVRMvP.d.ts +0 -1371
package/dist/client.mjs
CHANGED
|
@@ -248,6 +248,7 @@ var TRACKING_CAPABILITIES = [
|
|
|
248
248
|
"consent_controls",
|
|
249
249
|
"conversion_goals_auto",
|
|
250
250
|
"conversion_goals_manual",
|
|
251
|
+
"cross_site_linker",
|
|
251
252
|
"cta_click_capture",
|
|
252
253
|
"form_capture",
|
|
253
254
|
"phone_click_capture",
|
|
@@ -274,6 +275,9 @@ function readDomMarkers() {
|
|
|
274
275
|
return found;
|
|
275
276
|
}
|
|
276
277
|
|
|
278
|
+
// ../tracking-core/src/cross-site-link.ts
|
|
279
|
+
import * as z from "zod/mini";
|
|
280
|
+
|
|
277
281
|
// ../tracking-core/src/tracking.ts
|
|
278
282
|
var TRACKING_COOKIE_MAX_AGE_SECONDS = 7776e3;
|
|
279
283
|
var TRACKING_PARAM_KEYS = [
|
|
@@ -595,10 +599,10 @@ function readStoredRecord() {
|
|
|
595
599
|
return null;
|
|
596
600
|
}
|
|
597
601
|
}
|
|
598
|
-
function writeRecord(
|
|
599
|
-
memoryRecord =
|
|
602
|
+
function writeRecord(record3) {
|
|
603
|
+
memoryRecord = record3;
|
|
600
604
|
try {
|
|
601
|
-
window.localStorage.setItem(LANDING_STORAGE_KEY, JSON.stringify(
|
|
605
|
+
window.localStorage.setItem(LANDING_STORAGE_KEY, JSON.stringify(record3));
|
|
602
606
|
} catch {
|
|
603
607
|
}
|
|
604
608
|
}
|
|
@@ -620,9 +624,9 @@ function getOrCaptureLandingParams(sessionId, url) {
|
|
|
620
624
|
if (memoryRecord && memoryRecord.session_id === sessionId) {
|
|
621
625
|
return memoryRecord.params;
|
|
622
626
|
}
|
|
623
|
-
const
|
|
624
|
-
writeRecord(
|
|
625
|
-
return
|
|
627
|
+
const record3 = { session_id: sessionId, params: captureFromUrl(url) };
|
|
628
|
+
writeRecord(record3);
|
|
629
|
+
return record3.params;
|
|
626
630
|
}
|
|
627
631
|
function buildLandingPayloadFields(sessionId, override) {
|
|
628
632
|
const params = override ?? (typeof window === "undefined" ? null : getOrCaptureLandingParams(sessionId));
|
|
@@ -641,6 +645,244 @@ function buildLandingPayloadFields(sessionId, override) {
|
|
|
641
645
|
};
|
|
642
646
|
}
|
|
643
647
|
|
|
648
|
+
// ../tracking-core/src/session.ts
|
|
649
|
+
var VISITOR_STORAGE_KEY = "aranova_tracking_visitor";
|
|
650
|
+
var SESSION_STORAGE_KEY = "aranova_tracking_session";
|
|
651
|
+
var SESSION_IDLE_MS = 30 * 60 * 1e3;
|
|
652
|
+
function safeUuid() {
|
|
653
|
+
if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function")
|
|
654
|
+
return crypto.randomUUID();
|
|
655
|
+
return `${Date.now().toString(16)}-${Math.random().toString(16).slice(2)}-${Math.random().toString(16).slice(2)}`;
|
|
656
|
+
}
|
|
657
|
+
function readLocalStorage(key) {
|
|
658
|
+
try {
|
|
659
|
+
return window.localStorage.getItem(key);
|
|
660
|
+
} catch {
|
|
661
|
+
return null;
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
function writeLocalStorage(key, value) {
|
|
665
|
+
try {
|
|
666
|
+
window.localStorage.setItem(key, value);
|
|
667
|
+
} catch {
|
|
668
|
+
}
|
|
669
|
+
}
|
|
670
|
+
function getVisitorId() {
|
|
671
|
+
if (typeof window === "undefined") return safeUuid();
|
|
672
|
+
const existing = readLocalStorage(VISITOR_STORAGE_KEY);
|
|
673
|
+
if (existing && existing.length > 0) return existing;
|
|
674
|
+
const fresh = safeUuid();
|
|
675
|
+
writeLocalStorage(VISITOR_STORAGE_KEY, fresh);
|
|
676
|
+
return fresh;
|
|
677
|
+
}
|
|
678
|
+
function adoptVisitorId(visitorId) {
|
|
679
|
+
if (typeof window === "undefined" || visitorId.length === 0) return false;
|
|
680
|
+
const existing = readLocalStorage(VISITOR_STORAGE_KEY);
|
|
681
|
+
if (existing && existing.length > 0) return false;
|
|
682
|
+
writeLocalStorage(VISITOR_STORAGE_KEY, visitorId);
|
|
683
|
+
return readLocalStorage(VISITOR_STORAGE_KEY) === visitorId;
|
|
684
|
+
}
|
|
685
|
+
function getOrRotateSessionId(now = Date.now()) {
|
|
686
|
+
if (typeof window === "undefined") return { id: safeUuid(), isNew: true };
|
|
687
|
+
const raw = readLocalStorage(SESSION_STORAGE_KEY);
|
|
688
|
+
if (raw) {
|
|
689
|
+
try {
|
|
690
|
+
const parsed = JSON.parse(raw);
|
|
691
|
+
if (typeof parsed.id === "string" && typeof parsed.last_event_at === "number") {
|
|
692
|
+
if (now - parsed.last_event_at <= SESSION_IDLE_MS) {
|
|
693
|
+
const refreshed = { id: parsed.id, last_event_at: now };
|
|
694
|
+
writeLocalStorage(SESSION_STORAGE_KEY, JSON.stringify(refreshed));
|
|
695
|
+
return { id: parsed.id, isNew: false };
|
|
696
|
+
}
|
|
697
|
+
}
|
|
698
|
+
} catch {
|
|
699
|
+
}
|
|
700
|
+
}
|
|
701
|
+
const fresh = { id: safeUuid(), last_event_at: now };
|
|
702
|
+
writeLocalStorage(SESSION_STORAGE_KEY, JSON.stringify(fresh));
|
|
703
|
+
return { id: fresh.id, isNew: true };
|
|
704
|
+
}
|
|
705
|
+
|
|
706
|
+
// ../tracking-core/src/cross-site-link.ts
|
|
707
|
+
var LINK_PARAM = "_arv";
|
|
708
|
+
var LINK_TOKEN_VERSION = "1";
|
|
709
|
+
var LINK_TOKEN_TTL_MS = 5 * 60 * 1e3;
|
|
710
|
+
var FUTURE_SKEW_MS = 60 * 1e3;
|
|
711
|
+
var CARRIED_KEYS = {
|
|
712
|
+
g: "gclid",
|
|
713
|
+
w: "wbraid",
|
|
714
|
+
gb: "gbraid",
|
|
715
|
+
f: "fbclid",
|
|
716
|
+
y: "ylpcid"
|
|
717
|
+
};
|
|
718
|
+
var CARRIED_ENTRIES = Object.entries(CARRIED_KEYS);
|
|
719
|
+
var identifier = z.string().check(z.minLength(1), z.maxLength(128));
|
|
720
|
+
var optionalIdentifier = z.optional(identifier);
|
|
721
|
+
var linkTokenSchema = z.object({
|
|
722
|
+
v: identifier,
|
|
723
|
+
s: identifier,
|
|
724
|
+
// Epoch ms in base36.
|
|
725
|
+
t: z.string().check(z.regex(/^[0-9a-z]{1,12}$/)),
|
|
726
|
+
g: optionalIdentifier,
|
|
727
|
+
w: optionalIdentifier,
|
|
728
|
+
gb: optionalIdentifier,
|
|
729
|
+
f: optionalIdentifier,
|
|
730
|
+
fc: optionalIdentifier,
|
|
731
|
+
y: optionalIdentifier
|
|
732
|
+
});
|
|
733
|
+
function toBase64Url(input) {
|
|
734
|
+
const bytes = new TextEncoder().encode(input);
|
|
735
|
+
let binary = "";
|
|
736
|
+
for (const byte of bytes) binary += String.fromCharCode(byte);
|
|
737
|
+
return btoa(binary).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
|
|
738
|
+
}
|
|
739
|
+
function fromBase64Url(input) {
|
|
740
|
+
try {
|
|
741
|
+
const padded = input.replace(/-/g, "+").replace(/_/g, "/");
|
|
742
|
+
const binary = atob(padded + "=".repeat((4 - padded.length % 4) % 4));
|
|
743
|
+
const bytes = Uint8Array.from(binary, (char) => char.charCodeAt(0));
|
|
744
|
+
return new TextDecoder().decode(bytes);
|
|
745
|
+
} catch {
|
|
746
|
+
return null;
|
|
747
|
+
}
|
|
748
|
+
}
|
|
749
|
+
function encodeLinkToken(payload) {
|
|
750
|
+
const params = new URLSearchParams();
|
|
751
|
+
for (const [key, value] of Object.entries(payload)) {
|
|
752
|
+
if (typeof value === "string" && value.length > 0) params.set(key, value);
|
|
753
|
+
}
|
|
754
|
+
return `${LINK_TOKEN_VERSION}.${toBase64Url(params.toString())}`;
|
|
755
|
+
}
|
|
756
|
+
function decodeLinkToken(raw) {
|
|
757
|
+
const dot = raw.indexOf(".");
|
|
758
|
+
if (dot <= 0 || raw.slice(0, dot) !== LINK_TOKEN_VERSION) return null;
|
|
759
|
+
const decoded = fromBase64Url(raw.slice(dot + 1));
|
|
760
|
+
if (decoded === null) return null;
|
|
761
|
+
const params = new URLSearchParams(decoded);
|
|
762
|
+
const candidate = {};
|
|
763
|
+
params.forEach((value, key) => {
|
|
764
|
+
candidate[key] = value;
|
|
765
|
+
});
|
|
766
|
+
const result = linkTokenSchema.safeParse(candidate);
|
|
767
|
+
return result.success ? result.data : null;
|
|
768
|
+
}
|
|
769
|
+
function payloadToLink(payload) {
|
|
770
|
+
const carried = {};
|
|
771
|
+
for (const [wire, param] of CARRIED_ENTRIES) {
|
|
772
|
+
const value = payload[wire];
|
|
773
|
+
if (value) carried[param] = value;
|
|
774
|
+
}
|
|
775
|
+
return {
|
|
776
|
+
visitorId: payload.v,
|
|
777
|
+
sessionId: payload.s,
|
|
778
|
+
linkedAt: Number.parseInt(payload.t, 36),
|
|
779
|
+
carried,
|
|
780
|
+
fbc: payload.fc ?? null
|
|
781
|
+
};
|
|
782
|
+
}
|
|
783
|
+
function stripWww(hostname) {
|
|
784
|
+
return hostname.toLowerCase().replace(/^www\./, "");
|
|
785
|
+
}
|
|
786
|
+
function isSiblingHost(hostname, sibling) {
|
|
787
|
+
const host = stripWww(hostname);
|
|
788
|
+
const target = stripWww(sibling);
|
|
789
|
+
return host === target || host.endsWith(`.${target}`);
|
|
790
|
+
}
|
|
791
|
+
function resolveCrossSiteLink(url, linkedHostnames, now = Date.now()) {
|
|
792
|
+
if (typeof window === "undefined" || linkedHostnames.length === 0) return null;
|
|
793
|
+
let target;
|
|
794
|
+
try {
|
|
795
|
+
target = new URL(url, window.location.href);
|
|
796
|
+
} catch {
|
|
797
|
+
return null;
|
|
798
|
+
}
|
|
799
|
+
if (target.protocol !== "http:" && target.protocol !== "https:") return null;
|
|
800
|
+
const here = window.location.hostname;
|
|
801
|
+
if (here && isSiblingHost(target.hostname, here)) return null;
|
|
802
|
+
const hostname = linkedHostnames.find((sibling) => isSiblingHost(target.hostname, sibling));
|
|
803
|
+
if (hostname === void 0) return null;
|
|
804
|
+
const consent = getConsentChoice();
|
|
805
|
+
if (consent.state === "denied" && consent.source === "explicit") {
|
|
806
|
+
return { hostname, decorated: url };
|
|
807
|
+
}
|
|
808
|
+
target.searchParams.set(LINK_PARAM, buildOutboundLinkToken(now));
|
|
809
|
+
return { hostname, decorated: target.href };
|
|
810
|
+
}
|
|
811
|
+
function stripLinkParam(url) {
|
|
812
|
+
try {
|
|
813
|
+
const parsed = new URL(url, typeof window === "undefined" ? void 0 : window.location.href);
|
|
814
|
+
if (!parsed.searchParams.has(LINK_PARAM)) return url;
|
|
815
|
+
parsed.searchParams.delete(LINK_PARAM);
|
|
816
|
+
return parsed.href;
|
|
817
|
+
} catch {
|
|
818
|
+
return url;
|
|
819
|
+
}
|
|
820
|
+
}
|
|
821
|
+
function stripLinkParamFromSearch(search) {
|
|
822
|
+
if (!search.includes(LINK_PARAM)) return search;
|
|
823
|
+
const params = new URLSearchParams(search);
|
|
824
|
+
if (!params.has(LINK_PARAM)) return search;
|
|
825
|
+
params.delete(LINK_PARAM);
|
|
826
|
+
const rest = params.toString();
|
|
827
|
+
return rest.length > 0 ? `?${rest}` : "";
|
|
828
|
+
}
|
|
829
|
+
function readCarried() {
|
|
830
|
+
const out = {};
|
|
831
|
+
for (const [wire, param] of CARRIED_ENTRIES) {
|
|
832
|
+
const value = readCookieValue(param);
|
|
833
|
+
if (value) out[wire] = value;
|
|
834
|
+
}
|
|
835
|
+
return out;
|
|
836
|
+
}
|
|
837
|
+
function buildOutboundLinkToken(now = Date.now()) {
|
|
838
|
+
const fbc = getFbcCookie();
|
|
839
|
+
return encodeLinkToken({
|
|
840
|
+
v: getVisitorId(),
|
|
841
|
+
s: getOrRotateSessionId(now).id,
|
|
842
|
+
t: now.toString(36),
|
|
843
|
+
...readCarried(),
|
|
844
|
+
...fbc ? { fc: fbc } : {}
|
|
845
|
+
});
|
|
846
|
+
}
|
|
847
|
+
function decorateCrossSiteUrl(url, linkedHostnames, now = Date.now()) {
|
|
848
|
+
return resolveCrossSiteLink(url, linkedHostnames, now)?.decorated ?? url;
|
|
849
|
+
}
|
|
850
|
+
function consumeInboundLink(href = typeof window === "undefined" ? "" : window.location.href, now = Date.now()) {
|
|
851
|
+
if (typeof window === "undefined" || !href) return null;
|
|
852
|
+
let raw;
|
|
853
|
+
try {
|
|
854
|
+
raw = new URL(href, window.location.href).searchParams.get(LINK_PARAM);
|
|
855
|
+
} catch {
|
|
856
|
+
return null;
|
|
857
|
+
}
|
|
858
|
+
if (!raw) return null;
|
|
859
|
+
const payload = decodeLinkToken(raw);
|
|
860
|
+
if (payload === null) return { outcome: "invalid", link: null, adoptedVisitor: false };
|
|
861
|
+
const link = payloadToLink(payload);
|
|
862
|
+
const age = now - link.linkedAt;
|
|
863
|
+
if (!Number.isFinite(age) || age < -FUTURE_SKEW_MS) {
|
|
864
|
+
return { outcome: "invalid", link: null, adoptedVisitor: false };
|
|
865
|
+
}
|
|
866
|
+
if (age > LINK_TOKEN_TTL_MS) {
|
|
867
|
+
return { outcome: "stale", link: null, adoptedVisitor: false };
|
|
868
|
+
}
|
|
869
|
+
const adoptedVisitor = adoptVisitorId(link.visitorId);
|
|
870
|
+
for (const [param, value] of Object.entries(link.carried)) {
|
|
871
|
+
if (!readCookieValue(param)) setTrackingCookie(param, value);
|
|
872
|
+
}
|
|
873
|
+
if (link.fbc && !getFbcCookie()) persistCookieValue(FBC_COOKIE, link.fbc);
|
|
874
|
+
return { outcome: "linked", link, adoptedVisitor };
|
|
875
|
+
}
|
|
876
|
+
function stripLinkParamFromLocation() {
|
|
877
|
+
if (typeof window === "undefined" || typeof history === "undefined") return;
|
|
878
|
+
const clean = stripLinkParam(window.location.href);
|
|
879
|
+
if (clean === window.location.href) return;
|
|
880
|
+
try {
|
|
881
|
+
history.replaceState(null, "", clean);
|
|
882
|
+
} catch {
|
|
883
|
+
}
|
|
884
|
+
}
|
|
885
|
+
|
|
644
886
|
// ../tracking-core/src/resources/conversion-firing.ts
|
|
645
887
|
var DEDUP_PREFIX = "_aranova_conv_";
|
|
646
888
|
function dedupKey(input) {
|
|
@@ -747,7 +989,8 @@ function parseConversionConfig(raw) {
|
|
|
747
989
|
gtag_ids: isStringMap(obj.gtag_ids) ? obj.gtag_ids : {},
|
|
748
990
|
meta_pixel_ids: isStringMap(obj.meta_pixel_ids) ? obj.meta_pixel_ids : {},
|
|
749
991
|
services,
|
|
750
|
-
goals
|
|
992
|
+
goals,
|
|
993
|
+
linked_hostnames: Array.isArray(obj.linked_hostnames) ? obj.linked_hostnames.filter((h) => typeof h === "string" && h.length > 0) : []
|
|
751
994
|
};
|
|
752
995
|
}
|
|
753
996
|
var CACHE_PREFIX = "_aranova_cfg_";
|
|
@@ -831,6 +1074,7 @@ function resolveConversionConfig(options) {
|
|
|
831
1074
|
getFiring: (key) => goalsByKey.get(key)?.firing ?? null,
|
|
832
1075
|
getGoal: (key) => goalsByKey.get(key) ?? null,
|
|
833
1076
|
listGoals: () => [...goalsByKey.values()],
|
|
1077
|
+
linkedHostnames: () => current?.linked_hostnames ?? [],
|
|
834
1078
|
current: () => current,
|
|
835
1079
|
isReady: () => current !== null,
|
|
836
1080
|
onResolve: (listener) => {
|
|
@@ -1015,7 +1259,7 @@ function validateConfig(config, ref) {
|
|
|
1015
1259
|
function pageViewSnapshot() {
|
|
1016
1260
|
if (typeof window === "undefined" || typeof document === "undefined") return null;
|
|
1017
1261
|
return {
|
|
1018
|
-
href: window.location.href,
|
|
1262
|
+
href: stripLinkParam(window.location.href),
|
|
1019
1263
|
title: document.title || null,
|
|
1020
1264
|
referrer: document.referrer || null
|
|
1021
1265
|
};
|
|
@@ -1166,6 +1410,10 @@ var TrackingConfigRuntime = class {
|
|
|
1166
1410
|
listGoals() {
|
|
1167
1411
|
return this.current?.goals ?? [];
|
|
1168
1412
|
}
|
|
1413
|
+
/** Sibling hostnames for the linker — read from the cached object even while unconfirmed. */
|
|
1414
|
+
linkedHostnames() {
|
|
1415
|
+
return this.current?.linked_hostnames ?? [];
|
|
1416
|
+
}
|
|
1169
1417
|
async revalidateNow() {
|
|
1170
1418
|
try {
|
|
1171
1419
|
const headers = {};
|
|
@@ -1403,76 +1651,32 @@ function withConversionAutoFire(client, autoFire) {
|
|
|
1403
1651
|
};
|
|
1404
1652
|
}
|
|
1405
1653
|
|
|
1406
|
-
// ../tracking-core/src/
|
|
1407
|
-
|
|
1408
|
-
var SESSION_STORAGE_KEY = "aranova_tracking_session";
|
|
1409
|
-
var SESSION_IDLE_MS = 30 * 60 * 1e3;
|
|
1410
|
-
function safeUuid() {
|
|
1411
|
-
if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function")
|
|
1412
|
-
return crypto.randomUUID();
|
|
1413
|
-
return `${Date.now().toString(16)}-${Math.random().toString(16).slice(2)}-${Math.random().toString(16).slice(2)}`;
|
|
1414
|
-
}
|
|
1415
|
-
function readLocalStorage(key) {
|
|
1416
|
-
try {
|
|
1417
|
-
return window.localStorage.getItem(key);
|
|
1418
|
-
} catch {
|
|
1419
|
-
return null;
|
|
1420
|
-
}
|
|
1421
|
-
}
|
|
1422
|
-
function writeLocalStorage(key, value) {
|
|
1423
|
-
try {
|
|
1424
|
-
window.localStorage.setItem(key, value);
|
|
1425
|
-
} catch {
|
|
1426
|
-
}
|
|
1427
|
-
}
|
|
1428
|
-
function getVisitorId() {
|
|
1429
|
-
if (typeof window === "undefined") return safeUuid();
|
|
1430
|
-
const existing = readLocalStorage(VISITOR_STORAGE_KEY);
|
|
1431
|
-
if (existing && existing.length > 0) return existing;
|
|
1432
|
-
const fresh = safeUuid();
|
|
1433
|
-
writeLocalStorage(VISITOR_STORAGE_KEY, fresh);
|
|
1434
|
-
return fresh;
|
|
1435
|
-
}
|
|
1436
|
-
function getOrRotateSessionId(now = Date.now()) {
|
|
1437
|
-
if (typeof window === "undefined") return { id: safeUuid(), isNew: true };
|
|
1438
|
-
const raw = readLocalStorage(SESSION_STORAGE_KEY);
|
|
1439
|
-
if (raw) {
|
|
1440
|
-
try {
|
|
1441
|
-
const parsed = JSON.parse(raw);
|
|
1442
|
-
if (typeof parsed.id === "string" && typeof parsed.last_event_at === "number") {
|
|
1443
|
-
if (now - parsed.last_event_at <= SESSION_IDLE_MS) {
|
|
1444
|
-
const refreshed = { id: parsed.id, last_event_at: now };
|
|
1445
|
-
writeLocalStorage(SESSION_STORAGE_KEY, JSON.stringify(refreshed));
|
|
1446
|
-
return { id: parsed.id, isNew: false };
|
|
1447
|
-
}
|
|
1448
|
-
}
|
|
1449
|
-
} catch {
|
|
1450
|
-
}
|
|
1451
|
-
}
|
|
1452
|
-
const fresh = { id: safeUuid(), last_event_at: now };
|
|
1453
|
-
writeLocalStorage(SESSION_STORAGE_KEY, JSON.stringify(fresh));
|
|
1454
|
-
return { id: fresh.id, isNew: true };
|
|
1455
|
-
}
|
|
1654
|
+
// ../tracking-core/src/page-view.ts
|
|
1655
|
+
import { parse } from "zod/mini";
|
|
1456
1656
|
|
|
1457
1657
|
// ../tracking-core/src/events/page-view.ts
|
|
1458
|
-
import
|
|
1459
|
-
var pageViewMetadataSchema =
|
|
1460
|
-
page:
|
|
1461
|
-
title:
|
|
1462
|
-
path:
|
|
1463
|
-
search:
|
|
1464
|
-
hash:
|
|
1465
|
-
})
|
|
1466
|
-
referrer:
|
|
1467
|
-
//
|
|
1658
|
+
import * as z2 from "zod/mini";
|
|
1659
|
+
var pageViewMetadataSchema = z2.strictObject({
|
|
1660
|
+
page: z2.strictObject({
|
|
1661
|
+
title: z2.nullable(z2.string()),
|
|
1662
|
+
path: z2.string(),
|
|
1663
|
+
search: z2.string(),
|
|
1664
|
+
hash: z2.string()
|
|
1665
|
+
}),
|
|
1666
|
+
referrer: z2.nullable(z2.string()),
|
|
1667
|
+
// Optional + nullable — absent (undefined) OR explicit null OR a
|
|
1468
1668
|
// real viewport object. Mirrors Pydantic's `_Viewport | None = None`
|
|
1469
1669
|
// on the backend side so the drift test stays clean.
|
|
1470
|
-
viewport:
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1670
|
+
viewport: z2.optional(
|
|
1671
|
+
z2.nullable(
|
|
1672
|
+
z2.strictObject({
|
|
1673
|
+
w: z2.number(),
|
|
1674
|
+
h: z2.number()
|
|
1675
|
+
})
|
|
1676
|
+
)
|
|
1677
|
+
)
|
|
1678
|
+
});
|
|
1679
|
+
var pageViewConfigSchema = z2.strictObject({});
|
|
1476
1680
|
|
|
1477
1681
|
// ../tracking-core/src/page-view.ts
|
|
1478
1682
|
var LAST_FIRED_URL_STORAGE_KEY = "aranova_tracking_last_fired_url";
|
|
@@ -1508,11 +1712,11 @@ function setLastFiredUrl(url) {
|
|
|
1508
1712
|
}
|
|
1509
1713
|
function buildPageViewMetadata(referrerOverride) {
|
|
1510
1714
|
if (typeof window === "undefined" || typeof document === "undefined") return null;
|
|
1511
|
-
return
|
|
1715
|
+
return parse(pageViewMetadataSchema, {
|
|
1512
1716
|
page: {
|
|
1513
1717
|
title: document.title || null,
|
|
1514
1718
|
path: window.location.pathname,
|
|
1515
|
-
search: window.location.search,
|
|
1719
|
+
search: stripLinkParamFromSearch(window.location.search),
|
|
1516
1720
|
hash: window.location.hash
|
|
1517
1721
|
},
|
|
1518
1722
|
referrer: referrerOverride !== void 0 ? referrerOverride : document.referrer || null,
|
|
@@ -1811,11 +2015,19 @@ function createTrackingClient(config) {
|
|
|
1811
2015
|
let firstPage = null;
|
|
1812
2016
|
let initialParams = createEmptyTrackingParams();
|
|
1813
2017
|
let destroyed = false;
|
|
2018
|
+
let inboundLink = null;
|
|
2019
|
+
if (typeof window !== "undefined") {
|
|
2020
|
+
try {
|
|
2021
|
+
inboundLink = consumeInboundLink();
|
|
2022
|
+
} catch {
|
|
2023
|
+
}
|
|
2024
|
+
}
|
|
2025
|
+
let originLink = inboundLink?.link ?? null;
|
|
1814
2026
|
const visitorId = getVisitorId();
|
|
1815
2027
|
const initialSession = getOrRotateSessionId();
|
|
1816
2028
|
let sessionId = initialSession.id;
|
|
1817
2029
|
if (typeof window !== "undefined") {
|
|
1818
|
-
firstPage = window.location.href;
|
|
2030
|
+
firstPage = stripLinkParam(window.location.href);
|
|
1819
2031
|
try {
|
|
1820
2032
|
initialParams = captureTrackingParamsFromLocation();
|
|
1821
2033
|
} catch {
|
|
@@ -1826,25 +2038,50 @@ function createTrackingClient(config) {
|
|
|
1826
2038
|
} catch {
|
|
1827
2039
|
}
|
|
1828
2040
|
}
|
|
1829
|
-
function
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
packageName,
|
|
1834
|
-
config.triggers ?? null,
|
|
1835
|
-
activeGtagIds,
|
|
1836
|
-
config.configSiteId ?? null
|
|
1837
|
-
);
|
|
2041
|
+
function currentPageUrl() {
|
|
2042
|
+
return typeof window === "undefined" ? null : stripLinkParam(window.location.href);
|
|
2043
|
+
}
|
|
2044
|
+
function enqueueInternal(eventType, metadata, pageUrl) {
|
|
1838
2045
|
queue.push({
|
|
1839
|
-
event_type:
|
|
1840
|
-
page_url:
|
|
2046
|
+
event_type: eventType,
|
|
2047
|
+
page_url: pageUrl,
|
|
1841
2048
|
metadata,
|
|
1842
2049
|
occurred_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
1843
2050
|
});
|
|
1844
2051
|
}
|
|
2052
|
+
function enqueueHeartbeat() {
|
|
2053
|
+
enqueueInternal(
|
|
2054
|
+
"sdk_heartbeat",
|
|
2055
|
+
buildHeartbeatMetadata(
|
|
2056
|
+
config.surface,
|
|
2057
|
+
sdkVersion,
|
|
2058
|
+
packageName,
|
|
2059
|
+
config.triggers ?? null,
|
|
2060
|
+
activeGtagIds,
|
|
2061
|
+
config.configSiteId ?? null
|
|
2062
|
+
),
|
|
2063
|
+
currentPageUrl()
|
|
2064
|
+
);
|
|
2065
|
+
}
|
|
1845
2066
|
if (initialSession.isNew) {
|
|
1846
2067
|
enqueueHeartbeat();
|
|
1847
2068
|
}
|
|
2069
|
+
if (inboundLink !== null && typeof window !== "undefined") {
|
|
2070
|
+
const link = inboundLink.link;
|
|
2071
|
+
enqueueInternal(
|
|
2072
|
+
"site_link_arrival",
|
|
2073
|
+
{
|
|
2074
|
+
outcome: inboundLink.outcome,
|
|
2075
|
+
origin_session_id: link?.sessionId ?? null,
|
|
2076
|
+
origin_visitor_id: link?.visitorId ?? null,
|
|
2077
|
+
transit_ms: link ? Math.max(0, Date.now() - link.linkedAt) : null,
|
|
2078
|
+
adopted_visitor: inboundLink.adoptedVisitor,
|
|
2079
|
+
page: { path: window.location.pathname }
|
|
2080
|
+
},
|
|
2081
|
+
firstPage
|
|
2082
|
+
);
|
|
2083
|
+
scheduleFlush();
|
|
2084
|
+
}
|
|
1848
2085
|
if (pending.length > 0) {
|
|
1849
2086
|
scheduleFlush();
|
|
1850
2087
|
}
|
|
@@ -1852,6 +2089,7 @@ function createTrackingClient(config) {
|
|
|
1852
2089
|
const rotated = getOrRotateSessionId();
|
|
1853
2090
|
if (rotated.isNew && rotated.id !== sessionId) {
|
|
1854
2091
|
enqueueHeartbeat();
|
|
2092
|
+
originLink = null;
|
|
1855
2093
|
}
|
|
1856
2094
|
sessionId = rotated.id;
|
|
1857
2095
|
const params = mergeTrackingParams(readTrackingParams(), initialParams);
|
|
@@ -1882,6 +2120,11 @@ function createTrackingClient(config) {
|
|
|
1882
2120
|
// landing), reused from the stored record otherwise. Keys are omitted
|
|
1883
2121
|
// entirely when the landing isn't observable (SSR).
|
|
1884
2122
|
...buildLandingPayloadFields(sessionId),
|
|
2123
|
+
...originLink ? {
|
|
2124
|
+
origin_session_id: originLink.sessionId,
|
|
2125
|
+
origin_visitor_id: originLink.visitorId,
|
|
2126
|
+
origin_linked_at: new Date(originLink.linkedAt).toISOString()
|
|
2127
|
+
} : {},
|
|
1885
2128
|
first_page: firstPage,
|
|
1886
2129
|
consent_state: consentSnapshot(),
|
|
1887
2130
|
context
|
|
@@ -1958,7 +2201,8 @@ function createTrackingClient(config) {
|
|
|
1958
2201
|
const occurredAt = input.occurredAt instanceof Date ? input.occurredAt.toISOString() : typeof input.occurredAt === "string" ? input.occurredAt : (/* @__PURE__ */ new Date()).toISOString();
|
|
1959
2202
|
queue.push({
|
|
1960
2203
|
event_type: input.eventType,
|
|
1961
|
-
|
|
2204
|
+
// A caller's href still carries `_arv` until the surface strips it after hydration.
|
|
2205
|
+
page_url: input.pageUrl != null ? stripLinkParam(input.pageUrl) : currentPageUrl(),
|
|
1962
2206
|
metadata: input.metadata ?? null,
|
|
1963
2207
|
occurred_at: occurredAt
|
|
1964
2208
|
});
|
|
@@ -2004,186 +2248,222 @@ function createTrackingClient(config) {
|
|
|
2004
2248
|
};
|
|
2005
2249
|
}
|
|
2006
2250
|
|
|
2251
|
+
// ../tracking-core/src/ingest-typed.ts
|
|
2252
|
+
import { parse as parse2 } from "zod/mini";
|
|
2253
|
+
|
|
2007
2254
|
// ../tracking-core/src/events/cta-click.ts
|
|
2008
|
-
import
|
|
2009
|
-
var ctaClickMetadataSchema =
|
|
2010
|
-
cta_name:
|
|
2011
|
-
page:
|
|
2012
|
-
path:
|
|
2013
|
-
})
|
|
2014
|
-
section:
|
|
2015
|
-
destination_url:
|
|
2255
|
+
import * as z3 from "zod/mini";
|
|
2256
|
+
var ctaClickMetadataSchema = z3.strictObject({
|
|
2257
|
+
cta_name: z3.string(),
|
|
2258
|
+
page: z3.strictObject({
|
|
2259
|
+
path: z3.string()
|
|
2260
|
+
}),
|
|
2261
|
+
section: z3.optional(z3.nullable(z3.string())),
|
|
2262
|
+
destination_url: z3.optional(z3.nullable(z3.string())),
|
|
2016
2263
|
// Set by auto-capture (and available to manual callers): the link target
|
|
2017
2264
|
// and a short element descriptor (tag#id) for tying clicks to specific UI.
|
|
2018
|
-
href:
|
|
2019
|
-
element:
|
|
2020
|
-
})
|
|
2021
|
-
var ctaClickConfigSchema =
|
|
2022
|
-
autoCapture:
|
|
2023
|
-
|
|
2024
|
-
|
|
2025
|
-
})
|
|
2265
|
+
href: z3.optional(z3.nullable(z3.string())),
|
|
2266
|
+
element: z3.optional(z3.nullable(z3.string()))
|
|
2267
|
+
});
|
|
2268
|
+
var ctaClickConfigSchema = z3.strictObject({
|
|
2269
|
+
autoCapture: z3.optional(
|
|
2270
|
+
z3.strictObject({
|
|
2271
|
+
selector: z3.optional(z3.string())
|
|
2272
|
+
})
|
|
2273
|
+
)
|
|
2274
|
+
});
|
|
2026
2275
|
|
|
2027
2276
|
// ../tracking-core/src/events/sdk-heartbeat.ts
|
|
2028
|
-
import
|
|
2029
|
-
var sdkHeartbeatTriggersSchema =
|
|
2030
|
-
automatic:
|
|
2031
|
-
manual:
|
|
2032
|
-
})
|
|
2033
|
-
var sdkHeartbeatMetadataSchema =
|
|
2034
|
-
sdk_version:
|
|
2035
|
-
package_name:
|
|
2036
|
-
surface:
|
|
2277
|
+
import * as z4 from "zod/mini";
|
|
2278
|
+
var sdkHeartbeatTriggersSchema = z4.strictObject({
|
|
2279
|
+
automatic: z4.array(z4.string()),
|
|
2280
|
+
manual: z4.array(z4.string())
|
|
2281
|
+
});
|
|
2282
|
+
var sdkHeartbeatMetadataSchema = z4.strictObject({
|
|
2283
|
+
sdk_version: z4.string(),
|
|
2284
|
+
package_name: z4.nullable(z4.string()),
|
|
2285
|
+
surface: z4.enum(["next", "react", "script"]),
|
|
2037
2286
|
triggers: sdkHeartbeatTriggersSchema,
|
|
2038
|
-
trigger_config:
|
|
2039
|
-
configured_gtag_ids:
|
|
2287
|
+
trigger_config: z4.optional(z4.nullable(z4.record(z4.string(), z4.record(z4.string(), z4.unknown())))),
|
|
2288
|
+
configured_gtag_ids: z4.optional(z4.nullable(z4.record(z4.string(), z4.string()))),
|
|
2040
2289
|
// Site whose config object the SDK resolves; null on a legacy business ref.
|
|
2041
|
-
config_site_id:
|
|
2042
|
-
})
|
|
2043
|
-
var sdkHeartbeatConfigSchema =
|
|
2290
|
+
config_site_id: z4.optional(z4.nullable(z4.uuid()))
|
|
2291
|
+
});
|
|
2292
|
+
var sdkHeartbeatConfigSchema = z4.strictObject({});
|
|
2044
2293
|
|
|
2045
2294
|
// ../tracking-core/src/events/form-start.ts
|
|
2046
|
-
import
|
|
2047
|
-
var formStartMetadataSchema =
|
|
2048
|
-
form:
|
|
2049
|
-
id:
|
|
2050
|
-
action:
|
|
2051
|
-
})
|
|
2052
|
-
page:
|
|
2053
|
-
path:
|
|
2054
|
-
})
|
|
2055
|
-
})
|
|
2056
|
-
var formStartConfigSchema =
|
|
2057
|
-
selector:
|
|
2058
|
-
})
|
|
2295
|
+
import * as z5 from "zod/mini";
|
|
2296
|
+
var formStartMetadataSchema = z5.strictObject({
|
|
2297
|
+
form: z5.strictObject({
|
|
2298
|
+
id: z5.string(),
|
|
2299
|
+
action: z5.nullable(z5.string())
|
|
2300
|
+
}),
|
|
2301
|
+
page: z5.strictObject({
|
|
2302
|
+
path: z5.string()
|
|
2303
|
+
})
|
|
2304
|
+
});
|
|
2305
|
+
var formStartConfigSchema = z5.strictObject({
|
|
2306
|
+
selector: z5.optional(z5.string())
|
|
2307
|
+
});
|
|
2059
2308
|
|
|
2060
2309
|
// ../tracking-core/src/events/form-submit.ts
|
|
2061
|
-
import
|
|
2062
|
-
var jsonValueSchema =
|
|
2063
|
-
() =>
|
|
2064
|
-
|
|
2065
|
-
|
|
2066
|
-
|
|
2067
|
-
|
|
2068
|
-
|
|
2069
|
-
|
|
2310
|
+
import * as z6 from "zod/mini";
|
|
2311
|
+
var jsonValueSchema = z6.lazy(
|
|
2312
|
+
() => z6.union([
|
|
2313
|
+
z6.string(),
|
|
2314
|
+
z6.number(),
|
|
2315
|
+
z6.boolean(),
|
|
2316
|
+
z6.null(),
|
|
2317
|
+
z6.array(jsonValueSchema),
|
|
2318
|
+
z6.record(z6.string(), jsonValueSchema)
|
|
2070
2319
|
])
|
|
2071
|
-
);
|
|
2072
|
-
var formSubmitMetadataSchema =
|
|
2073
|
-
form:
|
|
2074
|
-
id:
|
|
2075
|
-
action:
|
|
2076
|
-
fields:
|
|
2077
|
-
|
|
2078
|
-
|
|
2079
|
-
|
|
2080
|
-
|
|
2081
|
-
|
|
2082
|
-
|
|
2083
|
-
|
|
2084
|
-
|
|
2085
|
-
|
|
2086
|
-
|
|
2087
|
-
|
|
2088
|
-
|
|
2089
|
-
|
|
2320
|
+
).register(z6.globalRegistry, { id: "JsonSerializableValue" });
|
|
2321
|
+
var formSubmitMetadataSchema = z6.strictObject({
|
|
2322
|
+
form: z6.strictObject({
|
|
2323
|
+
id: z6.string(),
|
|
2324
|
+
action: z6.nullable(z6.string()),
|
|
2325
|
+
fields: z6.optional(
|
|
2326
|
+
z6.array(
|
|
2327
|
+
z6.strictObject({
|
|
2328
|
+
name: z6.string(),
|
|
2329
|
+
type: z6.string(),
|
|
2330
|
+
label: z6.nullable(z6.string()),
|
|
2331
|
+
value: jsonValueSchema
|
|
2332
|
+
})
|
|
2333
|
+
)
|
|
2334
|
+
)
|
|
2335
|
+
}),
|
|
2336
|
+
page: z6.strictObject({
|
|
2337
|
+
path: z6.string()
|
|
2338
|
+
})
|
|
2339
|
+
});
|
|
2340
|
+
var formSubmitConfigSchema = z6.strictObject({});
|
|
2090
2341
|
|
|
2091
2342
|
// ../tracking-core/src/events/multi-page-session.ts
|
|
2092
|
-
import
|
|
2093
|
-
var multiPageSessionMetadataSchema =
|
|
2094
|
-
page_count:
|
|
2095
|
-
page:
|
|
2096
|
-
path:
|
|
2097
|
-
})
|
|
2098
|
-
})
|
|
2099
|
-
var multiPageSessionConfigSchema =
|
|
2100
|
-
pageThreshold:
|
|
2101
|
-
})
|
|
2343
|
+
import * as z7 from "zod/mini";
|
|
2344
|
+
var multiPageSessionMetadataSchema = z7.strictObject({
|
|
2345
|
+
page_count: z7.int().check(z7.gte(2)),
|
|
2346
|
+
page: z7.strictObject({
|
|
2347
|
+
path: z7.string()
|
|
2348
|
+
})
|
|
2349
|
+
});
|
|
2350
|
+
var multiPageSessionConfigSchema = z7.strictObject({
|
|
2351
|
+
pageThreshold: z7.int().check(z7.gte(2))
|
|
2352
|
+
});
|
|
2102
2353
|
|
|
2103
2354
|
// ../tracking-core/src/events/page-exit.ts
|
|
2104
|
-
import
|
|
2105
|
-
var pageExitMetadataSchema =
|
|
2106
|
-
dwell_ms:
|
|
2355
|
+
import * as z8 from "zod/mini";
|
|
2356
|
+
var pageExitMetadataSchema = z8.strictObject({
|
|
2357
|
+
dwell_ms: z8.int().check(z8.gte(0)),
|
|
2107
2358
|
// null = left without any scroll signal; floor is 0 so a valid 0% is never
|
|
2108
2359
|
// rejected (a single bad field 422s the whole keepalive beacon batch).
|
|
2109
|
-
max_scroll_percent:
|
|
2360
|
+
max_scroll_percent: z8.nullable(z8.int().check(z8.gte(0), z8.lte(100))),
|
|
2110
2361
|
// The gating baseline: the fraction of the page visible at load with
|
|
2111
2362
|
// zero scrolling — or, for pages that grew after the post-paint snapshot
|
|
2112
2363
|
// (skeleton/streaming renders), the first-scroll position that
|
|
2113
2364
|
// established it. null = page had no scrollable range, or the segment
|
|
2114
|
-
// ended before the snapshot.
|
|
2365
|
+
// ended before the snapshot. Optionality is load-bearing: SDK builds
|
|
2115
2366
|
// predating this field keep POSTing page_exit without the key —
|
|
2116
2367
|
// requiring it would 422 whole keepalive beacon batches.
|
|
2117
|
-
scroll_baseline_percent:
|
|
2118
|
-
page:
|
|
2119
|
-
path:
|
|
2120
|
-
})
|
|
2121
|
-
})
|
|
2122
|
-
var pageExitConfigSchema =
|
|
2368
|
+
scroll_baseline_percent: z8.optional(z8.nullable(z8.int().check(z8.gte(0), z8.lte(100)))),
|
|
2369
|
+
page: z8.strictObject({
|
|
2370
|
+
path: z8.string()
|
|
2371
|
+
})
|
|
2372
|
+
});
|
|
2373
|
+
var pageExitConfigSchema = z8.strictObject({});
|
|
2123
2374
|
|
|
2124
2375
|
// ../tracking-core/src/events/phone-click.ts
|
|
2125
|
-
import
|
|
2126
|
-
var phoneClickMetadataSchema =
|
|
2127
|
-
phone_number:
|
|
2128
|
-
page:
|
|
2129
|
-
path:
|
|
2130
|
-
})
|
|
2131
|
-
section:
|
|
2132
|
-
})
|
|
2133
|
-
var phoneClickConfigSchema =
|
|
2134
|
-
autoCapture:
|
|
2135
|
-
|
|
2136
|
-
|
|
2137
|
-
})
|
|
2376
|
+
import * as z9 from "zod/mini";
|
|
2377
|
+
var phoneClickMetadataSchema = z9.strictObject({
|
|
2378
|
+
phone_number: z9.string(),
|
|
2379
|
+
page: z9.strictObject({
|
|
2380
|
+
path: z9.string()
|
|
2381
|
+
}),
|
|
2382
|
+
section: z9.optional(z9.nullable(z9.string()))
|
|
2383
|
+
});
|
|
2384
|
+
var phoneClickConfigSchema = z9.strictObject({
|
|
2385
|
+
autoCapture: z9.optional(
|
|
2386
|
+
z9.strictObject({
|
|
2387
|
+
selector: z9.optional(z9.string())
|
|
2388
|
+
})
|
|
2389
|
+
)
|
|
2390
|
+
});
|
|
2138
2391
|
|
|
2139
2392
|
// ../tracking-core/src/events/scroll-depth.ts
|
|
2140
|
-
import
|
|
2141
|
-
var scrollDepthMetadataSchema =
|
|
2142
|
-
depth_percent:
|
|
2143
|
-
page:
|
|
2144
|
-
path:
|
|
2145
|
-
})
|
|
2146
|
-
})
|
|
2147
|
-
var scrollDepthConfigSchema =
|
|
2148
|
-
thresholds:
|
|
2149
|
-
})
|
|
2393
|
+
import * as z10 from "zod/mini";
|
|
2394
|
+
var scrollDepthMetadataSchema = z10.strictObject({
|
|
2395
|
+
depth_percent: z10.int().check(z10.gte(1), z10.lte(100)),
|
|
2396
|
+
page: z10.strictObject({
|
|
2397
|
+
path: z10.string()
|
|
2398
|
+
})
|
|
2399
|
+
});
|
|
2400
|
+
var scrollDepthConfigSchema = z10.strictObject({
|
|
2401
|
+
thresholds: z10.array(z10.int().check(z10.gte(1), z10.lte(100))).check(z10.minLength(1))
|
|
2402
|
+
});
|
|
2150
2403
|
|
|
2151
2404
|
// ../tracking-core/src/events/specific-page-visit.ts
|
|
2152
|
-
import
|
|
2405
|
+
import * as z11 from "zod/mini";
|
|
2153
2406
|
var PAGE_IDENTITY_MAX_LENGTH = 64;
|
|
2154
2407
|
var PAGE_IDENTITY_PATTERN = /^[a-z][a-z0-9]*(?:_[a-z0-9]+)*$/;
|
|
2155
|
-
var pageIdentitySchema =
|
|
2156
|
-
|
|
2157
|
-
|
|
2408
|
+
var pageIdentitySchema = z11.string().check(
|
|
2409
|
+
z11.maxLength(PAGE_IDENTITY_MAX_LENGTH),
|
|
2410
|
+
z11.regex(PAGE_IDENTITY_PATTERN, {
|
|
2411
|
+
message: "page identity must be lowercase snake_case, start with a letter, and contain at most 64 characters"
|
|
2412
|
+
})
|
|
2413
|
+
);
|
|
2158
2414
|
var specificPageNameSchema = pageIdentitySchema;
|
|
2159
|
-
var specificPageVisitMetadataSchema =
|
|
2415
|
+
var specificPageVisitMetadataSchema = z11.strictObject({
|
|
2160
2416
|
page_name: specificPageNameSchema,
|
|
2161
|
-
page:
|
|
2162
|
-
path:
|
|
2163
|
-
})
|
|
2164
|
-
})
|
|
2165
|
-
var specificPageVisitConfigSchema =
|
|
2166
|
-
pages:
|
|
2167
|
-
|
|
2417
|
+
page: z11.strictObject({
|
|
2418
|
+
path: z11.string()
|
|
2419
|
+
})
|
|
2420
|
+
});
|
|
2421
|
+
var specificPageVisitConfigSchema = z11.strictObject({
|
|
2422
|
+
pages: z11.array(
|
|
2423
|
+
z11.strictObject({
|
|
2168
2424
|
name: specificPageNameSchema,
|
|
2169
|
-
pathPattern:
|
|
2425
|
+
pathPattern: z11.custom((value) => value instanceof RegExp, {
|
|
2170
2426
|
message: "pathPattern must be a RegExp"
|
|
2171
2427
|
})
|
|
2172
|
-
})
|
|
2173
|
-
).
|
|
2174
|
-
})
|
|
2428
|
+
})
|
|
2429
|
+
).check(z11.minLength(1))
|
|
2430
|
+
});
|
|
2431
|
+
|
|
2432
|
+
// ../tracking-core/src/events/site-link.ts
|
|
2433
|
+
import * as z12 from "zod/mini";
|
|
2434
|
+
var siteLinkClickMetadataSchema = z12.strictObject({
|
|
2435
|
+
destination_host: z12.string(),
|
|
2436
|
+
destination_url: z12.string(),
|
|
2437
|
+
page: z12.strictObject({
|
|
2438
|
+
path: z12.string()
|
|
2439
|
+
})
|
|
2440
|
+
});
|
|
2441
|
+
var siteLinkClickConfigSchema = z12.strictObject({});
|
|
2442
|
+
var siteLinkArrivalMetadataSchema = z12.strictObject({
|
|
2443
|
+
outcome: z12.enum(["linked", "stale", "invalid"]),
|
|
2444
|
+
origin_session_id: z12.optional(z12.nullable(z12.string())),
|
|
2445
|
+
origin_visitor_id: z12.optional(z12.nullable(z12.string())),
|
|
2446
|
+
// Milliseconds between the origin click and this page load.
|
|
2447
|
+
transit_ms: z12.optional(z12.nullable(z12.number())),
|
|
2448
|
+
// True when this browser had no visitor id and took the origin's.
|
|
2449
|
+
adopted_visitor: z12.boolean(),
|
|
2450
|
+
page: z12.strictObject({
|
|
2451
|
+
path: z12.string()
|
|
2452
|
+
})
|
|
2453
|
+
});
|
|
2454
|
+
var siteLinkArrivalConfigSchema = z12.strictObject({});
|
|
2175
2455
|
|
|
2176
2456
|
// ../tracking-core/src/events/time-on-site.ts
|
|
2177
|
-
import
|
|
2178
|
-
var timeOnSiteMetadataSchema =
|
|
2179
|
-
duration_ms:
|
|
2180
|
-
page:
|
|
2181
|
-
path:
|
|
2182
|
-
})
|
|
2183
|
-
})
|
|
2184
|
-
var timeOnSiteConfigSchema =
|
|
2185
|
-
thresholdSeconds:
|
|
2186
|
-
})
|
|
2457
|
+
import * as z13 from "zod/mini";
|
|
2458
|
+
var timeOnSiteMetadataSchema = z13.strictObject({
|
|
2459
|
+
duration_ms: z13.int().check(z13.nonnegative()),
|
|
2460
|
+
page: z13.strictObject({
|
|
2461
|
+
path: z13.string()
|
|
2462
|
+
})
|
|
2463
|
+
});
|
|
2464
|
+
var timeOnSiteConfigSchema = z13.strictObject({
|
|
2465
|
+
thresholdSeconds: z13.int().check(z13.positive())
|
|
2466
|
+
});
|
|
2187
2467
|
|
|
2188
2468
|
// ../tracking-core/src/events/semantics.ts
|
|
2189
2469
|
var EVENT_SEMANTICS = {
|
|
@@ -2252,6 +2532,18 @@ var EVENT_SEMANTICS = {
|
|
|
2252
2532
|
category: "engagement",
|
|
2253
2533
|
outcomeRole: "engagement",
|
|
2254
2534
|
clientVisibility: "simple"
|
|
2535
|
+
},
|
|
2536
|
+
site_link_click: {
|
|
2537
|
+
label: "Left for sibling site",
|
|
2538
|
+
category: "page",
|
|
2539
|
+
outcomeRole: "navigation",
|
|
2540
|
+
clientVisibility: "detailed"
|
|
2541
|
+
},
|
|
2542
|
+
site_link_arrival: {
|
|
2543
|
+
label: "Arrived from sibling site",
|
|
2544
|
+
category: "page",
|
|
2545
|
+
outcomeRole: "navigation",
|
|
2546
|
+
clientVisibility: "simple"
|
|
2255
2547
|
}
|
|
2256
2548
|
};
|
|
2257
2549
|
|
|
@@ -2307,6 +2599,20 @@ var EVENT_REGISTRY = {
|
|
|
2307
2599
|
metadataSchema: pageExitMetadataSchema,
|
|
2308
2600
|
configSchema: pageExitConfigSchema
|
|
2309
2601
|
},
|
|
2602
|
+
// Cross-site linkage: emitted by the linker (roster comes from the site
|
|
2603
|
+
// config, not the trigger registry), so neither is consumer-configurable.
|
|
2604
|
+
site_link_click: {
|
|
2605
|
+
kind: "automatic",
|
|
2606
|
+
semantics: EVENT_SEMANTICS.site_link_click,
|
|
2607
|
+
metadataSchema: siteLinkClickMetadataSchema,
|
|
2608
|
+
configSchema: siteLinkClickConfigSchema
|
|
2609
|
+
},
|
|
2610
|
+
site_link_arrival: {
|
|
2611
|
+
kind: "automatic",
|
|
2612
|
+
semantics: EVENT_SEMANTICS.site_link_arrival,
|
|
2613
|
+
metadataSchema: siteLinkArrivalMetadataSchema,
|
|
2614
|
+
configSchema: siteLinkArrivalConfigSchema
|
|
2615
|
+
},
|
|
2310
2616
|
// --- manual triggers ---
|
|
2311
2617
|
form_submit: {
|
|
2312
2618
|
kind: "manual",
|
|
@@ -2342,7 +2648,7 @@ function createTypedClient(raw, registry, options = {}) {
|
|
|
2342
2648
|
trackEvent(eventType, metadata, opts) {
|
|
2343
2649
|
if (debug) {
|
|
2344
2650
|
const def = getEventDefinition(eventType);
|
|
2345
|
-
def.metadataSchema
|
|
2651
|
+
parse2(def.metadataSchema, metadata);
|
|
2346
2652
|
}
|
|
2347
2653
|
raw.trackEvent({
|
|
2348
2654
|
eventType,
|
|
@@ -2912,6 +3218,57 @@ function attachCtaClickCapture(client, config) {
|
|
|
2912
3218
|
};
|
|
2913
3219
|
}
|
|
2914
3220
|
|
|
3221
|
+
// ../tracking-core/src/triggers/site-link-capture.ts
|
|
3222
|
+
function anchorFrom(target) {
|
|
3223
|
+
if (!(target instanceof Element)) return null;
|
|
3224
|
+
const anchor = target.closest("a[href]");
|
|
3225
|
+
return anchor instanceof HTMLAnchorElement ? anchor : null;
|
|
3226
|
+
}
|
|
3227
|
+
function opensTheLink(event) {
|
|
3228
|
+
return event.button === 0 || event.button === 1;
|
|
3229
|
+
}
|
|
3230
|
+
function attachSiteLinkCapture(client, options) {
|
|
3231
|
+
if (typeof window === "undefined" || typeof document === "undefined") {
|
|
3232
|
+
return () => {
|
|
3233
|
+
};
|
|
3234
|
+
}
|
|
3235
|
+
registerCapability("cross_site_linker");
|
|
3236
|
+
function hopFrom(event) {
|
|
3237
|
+
const anchor = anchorFrom(event.target);
|
|
3238
|
+
if (anchor === null) return null;
|
|
3239
|
+
const link = resolveCrossSiteLink(anchor.href, options.linkedHostnames());
|
|
3240
|
+
return link === null ? null : { anchor, link };
|
|
3241
|
+
}
|
|
3242
|
+
function onPointerDown(event) {
|
|
3243
|
+
if (!opensTheLink(event)) return;
|
|
3244
|
+
const hop = hopFrom(event);
|
|
3245
|
+
if (hop !== null) hop.anchor.href = hop.link.decorated;
|
|
3246
|
+
}
|
|
3247
|
+
function onClick(event) {
|
|
3248
|
+
if (!opensTheLink(event)) return;
|
|
3249
|
+
const hop = hopFrom(event);
|
|
3250
|
+
if (hop === null) return;
|
|
3251
|
+
hop.anchor.href = hop.link.decorated;
|
|
3252
|
+
client.trackEvent({
|
|
3253
|
+
eventType: "site_link_click",
|
|
3254
|
+
metadata: {
|
|
3255
|
+
destination_host: hop.anchor.hostname,
|
|
3256
|
+
destination_url: stripLinkParam(hop.anchor.href),
|
|
3257
|
+
page: { path: window.location.pathname }
|
|
3258
|
+
},
|
|
3259
|
+
pageUrl: window.location.href,
|
|
3260
|
+
occurredAt: null
|
|
3261
|
+
});
|
|
3262
|
+
client.flushBeacon();
|
|
3263
|
+
}
|
|
3264
|
+
document.addEventListener("pointerdown", onPointerDown, true);
|
|
3265
|
+
document.addEventListener("click", onClick, true);
|
|
3266
|
+
return () => {
|
|
3267
|
+
document.removeEventListener("pointerdown", onPointerDown, true);
|
|
3268
|
+
document.removeEventListener("click", onClick, true);
|
|
3269
|
+
};
|
|
3270
|
+
}
|
|
3271
|
+
|
|
2915
3272
|
// ../tracking-core/src/triggers/phone-click-capture.ts
|
|
2916
3273
|
var DEFAULT_TEL_SELECTOR = 'a[href^="tel:"]';
|
|
2917
3274
|
function safeDecodeURIComponent(value) {
|
|
@@ -2966,117 +3323,6 @@ function attachPhoneClickCapture(client, config) {
|
|
|
2966
3323
|
};
|
|
2967
3324
|
}
|
|
2968
3325
|
|
|
2969
|
-
// ../tracking-core/src/resources/sales/schema.ts
|
|
2970
|
-
import { z as z12 } from "zod";
|
|
2971
|
-
var SUPPORTED_CURRENCIES = ["USD", "CAD"];
|
|
2972
|
-
var TRACKING_ENVIRONMENTS = ["production", "development"];
|
|
2973
|
-
var currencySchema = z12.enum(SUPPORTED_CURRENCIES);
|
|
2974
|
-
var centsSchema = z12.number().int().nonnegative();
|
|
2975
|
-
var quantitySchema = z12.string().regex(/^\d+(\.\d{1,3})?$/);
|
|
2976
|
-
var metadataSchema = z12.record(z12.unknown());
|
|
2977
|
-
var saleItemSchema = z12.object({
|
|
2978
|
-
external_item_id: z12.string().nullable().optional(),
|
|
2979
|
-
name: z12.string().nullable().optional(),
|
|
2980
|
-
category: z12.string().nullable().optional(),
|
|
2981
|
-
quantity: quantitySchema,
|
|
2982
|
-
unit_price_cents: centsSchema,
|
|
2983
|
-
// Non-negativity validated on the wire — same contract as the other cents
|
|
2984
|
-
// fields — and backstopped by the DB CHECK.
|
|
2985
|
-
unit_cost_cents: centsSchema.nullable().optional()
|
|
2986
|
-
}).strict();
|
|
2987
|
-
var saleServiceSchema = z12.object({
|
|
2988
|
-
service: z12.string(),
|
|
2989
|
-
amount_cents: centsSchema
|
|
2990
|
-
}).strict();
|
|
2991
|
-
var customerNameSchema = z12.string().max(200);
|
|
2992
|
-
var customerPhoneSchema = z12.string().max(64);
|
|
2993
|
-
var customerEmailSchema = z12.string().max(320).email();
|
|
2994
|
-
function refineServiceXor(val, ctx, { requireAmount }) {
|
|
2995
|
-
if (val.services != null) {
|
|
2996
|
-
if (val.service != null) {
|
|
2997
|
-
ctx.addIssue({
|
|
2998
|
-
code: z12.ZodIssueCode.custom,
|
|
2999
|
-
message: "pass either `service` or `services`, not both",
|
|
3000
|
-
path: ["services"]
|
|
3001
|
-
});
|
|
3002
|
-
}
|
|
3003
|
-
if (val.services.length === 0) {
|
|
3004
|
-
ctx.addIssue({
|
|
3005
|
-
code: z12.ZodIssueCode.custom,
|
|
3006
|
-
message: "`services` must not be empty",
|
|
3007
|
-
path: ["services"]
|
|
3008
|
-
});
|
|
3009
|
-
}
|
|
3010
|
-
const keys = val.services.map((s) => s.service);
|
|
3011
|
-
if (new Set(keys).size !== keys.length) {
|
|
3012
|
-
ctx.addIssue({
|
|
3013
|
-
code: z12.ZodIssueCode.custom,
|
|
3014
|
-
message: "`services` must not list the same service more than once",
|
|
3015
|
-
path: ["services"]
|
|
3016
|
-
});
|
|
3017
|
-
}
|
|
3018
|
-
if (val.amount_total_cents != null) {
|
|
3019
|
-
const sum = val.services.reduce((acc, s) => acc + s.amount_cents, 0);
|
|
3020
|
-
if (val.amount_total_cents !== sum) {
|
|
3021
|
-
ctx.addIssue({
|
|
3022
|
-
code: z12.ZodIssueCode.custom,
|
|
3023
|
-
message: "amount_total_cents must equal the sum of the services amounts (omit it to derive it automatically)",
|
|
3024
|
-
path: ["amount_total_cents"]
|
|
3025
|
-
});
|
|
3026
|
-
}
|
|
3027
|
-
}
|
|
3028
|
-
} else if (requireAmount && val.amount_total_cents == null) {
|
|
3029
|
-
ctx.addIssue({
|
|
3030
|
-
code: z12.ZodIssueCode.custom,
|
|
3031
|
-
message: "amount_total_cents is required unless `services` is provided",
|
|
3032
|
-
path: ["amount_total_cents"]
|
|
3033
|
-
});
|
|
3034
|
-
}
|
|
3035
|
-
}
|
|
3036
|
-
var saleCreateSchema = z12.object({
|
|
3037
|
-
external_id: z12.string().nullable().optional(),
|
|
3038
|
-
description: z12.string().nullable().optional(),
|
|
3039
|
-
service: z12.string().nullable().optional(),
|
|
3040
|
-
services: z12.array(saleServiceSchema).nullable().optional(),
|
|
3041
|
-
currency: currencySchema,
|
|
3042
|
-
// Optional only because the plural `services` form derives it from the sum
|
|
3043
|
-
// (see refineServiceXor); the singular/serviceless path still requires it.
|
|
3044
|
-
amount_total_cents: centsSchema.nullable().optional(),
|
|
3045
|
-
occurred_at: z12.string().datetime(),
|
|
3046
|
-
environment: z12.enum(TRACKING_ENVIRONMENTS).default("production"),
|
|
3047
|
-
items: z12.array(saleItemSchema).default([]),
|
|
3048
|
-
metadata: metadataSchema.nullable().optional(),
|
|
3049
|
-
customer_name: customerNameSchema.nullable().optional(),
|
|
3050
|
-
customer_phone: customerPhoneSchema.nullable().optional(),
|
|
3051
|
-
customer_email: customerEmailSchema.nullable().optional(),
|
|
3052
|
-
// CASL consent attestation: the customer agreed to receive SMS. Recorded
|
|
3053
|
-
// with a timestamp server-side; every SMS send path gates on it.
|
|
3054
|
-
//
|
|
3055
|
-
// Tri-state: omit it (or send null) to let the server apply the business's
|
|
3056
|
-
// configured default-opt-in policy, honouring a returning customer's
|
|
3057
|
-
// remembered preference. Send an explicit boolean to assert consent state
|
|
3058
|
-
// yourself — `false` records a deliberate opt-out.
|
|
3059
|
-
sms_consent: z12.boolean().nullable().optional()
|
|
3060
|
-
}).strict().superRefine((val, ctx) => refineServiceXor(val, ctx, { requireAmount: true }));
|
|
3061
|
-
var saleUpdateSchema = z12.object({
|
|
3062
|
-
description: z12.string().nullable().optional(),
|
|
3063
|
-
service: z12.string().nullable().optional(),
|
|
3064
|
-
services: z12.array(saleServiceSchema).nullable().optional(),
|
|
3065
|
-
currency: currencySchema.optional(),
|
|
3066
|
-
amount_total_cents: centsSchema.optional(),
|
|
3067
|
-
occurred_at: z12.string().datetime().optional(),
|
|
3068
|
-
items: z12.array(saleItemSchema).optional(),
|
|
3069
|
-
metadata: metadataSchema.nullable().optional(),
|
|
3070
|
-
customer_name: customerNameSchema.nullable().optional(),
|
|
3071
|
-
// Re-attest when changing a filler-looking name; the server clears the
|
|
3072
|
-
// prior attestation whenever `customer_name` changes.
|
|
3073
|
-
customer_name_placeholder_confirmed: z12.boolean().optional(),
|
|
3074
|
-
customer_phone: customerPhoneSchema.nullable().optional(),
|
|
3075
|
-
customer_email: customerEmailSchema.nullable().optional(),
|
|
3076
|
-
// NOT NULL server-side: omit to leave unchanged (explicit null is rejected).
|
|
3077
|
-
sms_consent: z12.boolean().optional()
|
|
3078
|
-
}).strict().superRefine((val, ctx) => refineServiceXor(val, ctx, { requireAmount: false }));
|
|
3079
|
-
|
|
3080
3326
|
// src/hooks.ts
|
|
3081
3327
|
function useGclid() {
|
|
3082
3328
|
const [gclid, setGclid] = useState(null);
|
|
@@ -3408,7 +3654,7 @@ function GoogleAdsTracking(props) {
|
|
|
3408
3654
|
import { createContext as createContext2, useContext as useContext2, useEffect as useEffect5, useMemo as useMemo4 } from "react";
|
|
3409
3655
|
|
|
3410
3656
|
// package.json
|
|
3411
|
-
var version = "
|
|
3657
|
+
var version = "1.1.0";
|
|
3412
3658
|
|
|
3413
3659
|
// ../tracking-core/src/phone-react.tsx
|
|
3414
3660
|
import {
|
|
@@ -3538,7 +3784,8 @@ function createTracking(options) {
|
|
|
3538
3784
|
// Still publish phone config so usePhoneField/<PhoneField> work even when
|
|
3539
3785
|
// tracking is disabled (missing apiKey/endpoint).
|
|
3540
3786
|
TrackingProvider: ({ children }) => /* @__PURE__ */ jsx3(PhoneConfigProvider, { value: phone ?? null, children }),
|
|
3541
|
-
useTracking: () => noopTyped
|
|
3787
|
+
useTracking: () => noopTyped,
|
|
3788
|
+
decorateUrl: (url) => url
|
|
3542
3789
|
};
|
|
3543
3790
|
}
|
|
3544
3791
|
const TrackingContext = createContext2(null);
|
|
@@ -3557,8 +3804,8 @@ function createTracking(options) {
|
|
|
3557
3804
|
// eslint-disable-next-line react-hooks/exhaustive-deps -- gtagIdsKey is stable proxy
|
|
3558
3805
|
[gtagId, gtagIdsKey]
|
|
3559
3806
|
);
|
|
3560
|
-
const rawClient = useMemo4(
|
|
3561
|
-
|
|
3807
|
+
const rawClient = useMemo4(() => {
|
|
3808
|
+
const created = getOrCreateTrackingClient({
|
|
3562
3809
|
apiKey,
|
|
3563
3810
|
endpoint,
|
|
3564
3811
|
surface: "react",
|
|
@@ -3569,9 +3816,10 @@ function createTracking(options) {
|
|
|
3569
3816
|
activeGtagIds: resolvedGtagIds,
|
|
3570
3817
|
configSiteId: trackingConfig?.siteId ?? null,
|
|
3571
3818
|
debug
|
|
3572
|
-
})
|
|
3573
|
-
|
|
3574
|
-
|
|
3819
|
+
});
|
|
3820
|
+
stripLinkParamFromLocation();
|
|
3821
|
+
return created;
|
|
3822
|
+
}, [resolvedGtagIds]);
|
|
3575
3823
|
const conversionStore = useMemo4(
|
|
3576
3824
|
() => trackingConfig ? getTrackingConfigRuntime(trackingConfig) : conversionConfig ? resolveConversionConfig({
|
|
3577
3825
|
cdnUrl: conversionConfig.cdnUrl,
|
|
@@ -3625,6 +3873,11 @@ function createTracking(options) {
|
|
|
3625
3873
|
if (timeOnSite) {
|
|
3626
3874
|
detachers.push(attachTimeOnSite(detectorClient, timeOnSite));
|
|
3627
3875
|
}
|
|
3876
|
+
detachers.push(
|
|
3877
|
+
attachSiteLinkCapture(detectorClient, {
|
|
3878
|
+
linkedHostnames: () => conversionStore?.linkedHostnames() ?? []
|
|
3879
|
+
})
|
|
3880
|
+
);
|
|
3628
3881
|
const specificPageVisit = triggers.automatic.specific_page_visit;
|
|
3629
3882
|
if (specificPageVisit) {
|
|
3630
3883
|
detachers.push(attachSpecificPageVisit(detectorClient, specificPageVisit));
|
|
@@ -3667,7 +3920,14 @@ function createTracking(options) {
|
|
|
3667
3920
|
}
|
|
3668
3921
|
return client;
|
|
3669
3922
|
}
|
|
3670
|
-
return {
|
|
3923
|
+
return {
|
|
3924
|
+
TrackingProvider,
|
|
3925
|
+
useTracking,
|
|
3926
|
+
decorateUrl: (url) => decorateCrossSiteUrl(
|
|
3927
|
+
url,
|
|
3928
|
+
trackingConfig ? getTrackingConfigRuntime(trackingConfig).linkedHostnames() : []
|
|
3929
|
+
)
|
|
3930
|
+
};
|
|
3671
3931
|
}
|
|
3672
3932
|
export {
|
|
3673
3933
|
AdPlatformTracking,
|