@aranova/tracking-next 0.17.2 → 0.18.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 +49 -23
- package/dist/index.d.mts +175 -12
- package/dist/index.d.ts +175 -12
- package/dist/index.js +610 -88
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +584 -65
- package/dist/index.mjs.map +1 -1
- package/dist/middleware.js +90 -0
- package/dist/middleware.js.map +1 -1
- package/dist/middleware.mjs +90 -0
- package/dist/middleware.mjs.map +1 -1
- package/dist/phone.js +90 -0
- package/dist/phone.js.map +1 -1
- package/dist/phone.mjs +90 -0
- package/dist/phone.mjs.map +1 -1
- package/dist/{sales-C3jFBx08.d.mts → sales-QqKh6_iy.d.mts} +55 -2
- package/dist/{sales-C3jFBx08.d.ts → sales-QqKh6_iy.d.ts} +55 -2
- package/dist/sales.d.mts +1 -1
- package/dist/sales.d.ts +1 -1
- package/dist/sales.js +16 -0
- package/dist/sales.js.map +1 -1
- package/dist/sales.mjs +16 -0
- package/dist/sales.mjs.map +1 -1
- package/dist/server.js +90 -0
- package/dist/server.js.map +1 -1
- package/dist/server.mjs +90 -0
- package/dist/server.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -35,6 +35,7 @@ __export(src_exports, {
|
|
|
35
35
|
ConsentBanner: () => ConsentBanner,
|
|
36
36
|
DEFAULT_DECLINE_TTL_DAYS: () => DEFAULT_DECLINE_TTL_DAYS,
|
|
37
37
|
DEFAULT_PHONE_COUNTRY: () => DEFAULT_PHONE_COUNTRY,
|
|
38
|
+
EVENT_SEMANTICS: () => EVENT_SEMANTICS,
|
|
38
39
|
GoogleAdsTracking: () => GoogleAdsTracking,
|
|
39
40
|
NAMED_RANGES: () => NAMED_RANGES,
|
|
40
41
|
PhoneField: () => PhoneField,
|
|
@@ -55,6 +56,8 @@ __export(src_exports, {
|
|
|
55
56
|
fromMinor: () => fromMinor,
|
|
56
57
|
getConsentChoice: () => getConsentChoice,
|
|
57
58
|
getConsentState: () => getConsentState,
|
|
59
|
+
getEventSemantics: () => getEventSemantics,
|
|
60
|
+
getTrackingConfigRuntime: () => getTrackingConfigRuntime,
|
|
58
61
|
onConsentChange: () => onConsentChange,
|
|
59
62
|
optIn: () => optIn,
|
|
60
63
|
optOut: () => optOut,
|
|
@@ -302,6 +305,11 @@ function captureTrackingParamsFromLocation(url = typeof window === "undefined" ?
|
|
|
302
305
|
}
|
|
303
306
|
|
|
304
307
|
// ../tracking-core/src/gtag.ts
|
|
308
|
+
var GTAG_SCRIPT_HOST = "https://www.googletagmanager.com/gtag/js";
|
|
309
|
+
var TRACKING_SCRIPT_ATTRIBUTE = "data-aranova-tracking";
|
|
310
|
+
function getScriptMarker(id) {
|
|
311
|
+
return `aranova-${id}`;
|
|
312
|
+
}
|
|
305
313
|
var GTAG_ID_PATTERN = /^[A-Z]{1,3}-[A-Za-z0-9_-]+$/;
|
|
306
314
|
function isValidGtagId(id) {
|
|
307
315
|
return GTAG_ID_PATTERN.test(id);
|
|
@@ -330,6 +338,15 @@ gtag('js', new Date());
|
|
|
330
338
|
gtag('config', '${gtagId}');
|
|
331
339
|
`.trim();
|
|
332
340
|
}
|
|
341
|
+
function ensureGtagFunction() {
|
|
342
|
+
window.dataLayer = window.dataLayer || [];
|
|
343
|
+
if (typeof window.gtag === "function") return window.gtag;
|
|
344
|
+
function gtag() {
|
|
345
|
+
window.dataLayer?.push(arguments);
|
|
346
|
+
}
|
|
347
|
+
window.gtag = gtag;
|
|
348
|
+
return window.gtag;
|
|
349
|
+
}
|
|
333
350
|
var SEND_TO_RE = /^AW-[A-Za-z0-9]+\/[A-Za-z0-9_-]+$/;
|
|
334
351
|
function isValidSendTo(sendTo) {
|
|
335
352
|
return SEND_TO_RE.test(sendTo);
|
|
@@ -348,6 +365,27 @@ function fireGtagConversion(input) {
|
|
|
348
365
|
return false;
|
|
349
366
|
}
|
|
350
367
|
}
|
|
368
|
+
function applyDefaultConsentState() {
|
|
369
|
+
const gtag = ensureGtagFunction();
|
|
370
|
+
gtag(
|
|
371
|
+
"consent",
|
|
372
|
+
"default",
|
|
373
|
+
buildConsentPayload(getConsentState() === "denied" ? "denied" : "granted")
|
|
374
|
+
);
|
|
375
|
+
}
|
|
376
|
+
function loadGtagScript(gtagId) {
|
|
377
|
+
if (typeof document === "undefined") return;
|
|
378
|
+
const marker = getScriptMarker("gtag-loader");
|
|
379
|
+
const existingScript = document.querySelector(
|
|
380
|
+
`script[${TRACKING_SCRIPT_ATTRIBUTE}="${marker}"]`
|
|
381
|
+
);
|
|
382
|
+
if (existingScript) return;
|
|
383
|
+
const script = document.createElement("script");
|
|
384
|
+
script.async = true;
|
|
385
|
+
script.src = `${GTAG_SCRIPT_HOST}?id=${encodeURIComponent(gtagId)}`;
|
|
386
|
+
script.setAttribute(TRACKING_SCRIPT_ATTRIBUTE, marker);
|
|
387
|
+
document.head.append(script);
|
|
388
|
+
}
|
|
351
389
|
function createGtagInitScriptMulti(gtagIds) {
|
|
352
390
|
const safeIds = gtagIds.filter(isValidGtagId);
|
|
353
391
|
const configs = safeIds.map((id) => `gtag('config', '${id}');`).join("\n");
|
|
@@ -634,6 +672,7 @@ function parseConversionConfig(raw) {
|
|
|
634
672
|
business_id: typeof obj.business_id === "string" ? obj.business_id : void 0,
|
|
635
673
|
customer_id: typeof obj.customer_id === "string" ? obj.customer_id : null,
|
|
636
674
|
environment: typeof obj.environment === "string" ? obj.environment : void 0,
|
|
675
|
+
google_tracking_state: obj.google_tracking_state === "active" ? "active" : "disabled",
|
|
637
676
|
gtag_ids: isStringMap(obj.gtag_ids) ? obj.gtag_ids : {},
|
|
638
677
|
meta_pixel_ids: isStringMap(obj.meta_pixel_ids) ? obj.meta_pixel_ids : {},
|
|
639
678
|
services,
|
|
@@ -680,9 +719,9 @@ function resolveConversionConfig(options) {
|
|
|
680
719
|
}
|
|
681
720
|
}
|
|
682
721
|
function notifyResolved() {
|
|
683
|
-
const
|
|
722
|
+
const listeners2 = [...resolveListeners];
|
|
684
723
|
resolveListeners.clear();
|
|
685
|
-
for (const listener of
|
|
724
|
+
for (const listener of listeners2) {
|
|
686
725
|
try {
|
|
687
726
|
listener();
|
|
688
727
|
} catch {
|
|
@@ -769,6 +808,283 @@ function formatDateInTz(iso, timeZone, opts, locale) {
|
|
|
769
808
|
}).format(date);
|
|
770
809
|
}
|
|
771
810
|
|
|
811
|
+
// ../tracking-core/src/resources/tracking-config-runtime.ts
|
|
812
|
+
var CACHE_PREFIX2 = "_aranova_cfg_runtime_";
|
|
813
|
+
var AUTHORITY_TTL_MS = 6e4;
|
|
814
|
+
var runtimes = /* @__PURE__ */ new Map();
|
|
815
|
+
var configuredIds = /* @__PURE__ */ new Set();
|
|
816
|
+
var scriptLoad = null;
|
|
817
|
+
var jsInitialized = false;
|
|
818
|
+
function cacheKey2(url) {
|
|
819
|
+
return `${CACHE_PREFIX2}${url}`;
|
|
820
|
+
}
|
|
821
|
+
function readCache2(url) {
|
|
822
|
+
if (typeof window === "undefined") return null;
|
|
823
|
+
try {
|
|
824
|
+
const raw = window.sessionStorage.getItem(cacheKey2(url));
|
|
825
|
+
if (!raw) return null;
|
|
826
|
+
const parsed = JSON.parse(raw);
|
|
827
|
+
const config = parseConversionConfig(parsed.config);
|
|
828
|
+
if (!config) return null;
|
|
829
|
+
return {
|
|
830
|
+
etag: typeof parsed.etag === "string" ? parsed.etag : null,
|
|
831
|
+
config
|
|
832
|
+
};
|
|
833
|
+
} catch {
|
|
834
|
+
return null;
|
|
835
|
+
}
|
|
836
|
+
}
|
|
837
|
+
function writeCache2(url, entry) {
|
|
838
|
+
if (typeof window === "undefined") return;
|
|
839
|
+
try {
|
|
840
|
+
window.sessionStorage.setItem(cacheKey2(url), JSON.stringify(entry));
|
|
841
|
+
} catch {
|
|
842
|
+
}
|
|
843
|
+
}
|
|
844
|
+
function isTombstone(config) {
|
|
845
|
+
return config.google_tracking_state === "disabled" || Object.keys(config.gtag_ids).length === 0;
|
|
846
|
+
}
|
|
847
|
+
function validateConfig(config, ref) {
|
|
848
|
+
return config.business_id === ref.businessId && config.environment === ref.environment && (config.google_tracking_state === "active" || config.google_tracking_state === "disabled");
|
|
849
|
+
}
|
|
850
|
+
function pageViewSnapshot() {
|
|
851
|
+
if (typeof window === "undefined" || typeof document === "undefined") return null;
|
|
852
|
+
return {
|
|
853
|
+
href: window.location.href,
|
|
854
|
+
title: document.title || null,
|
|
855
|
+
referrer: document.referrer || null
|
|
856
|
+
};
|
|
857
|
+
}
|
|
858
|
+
function ensureScript(gtagId) {
|
|
859
|
+
if (typeof window === "undefined" || typeof document === "undefined") return Promise.resolve();
|
|
860
|
+
applyDefaultConsentState();
|
|
861
|
+
loadGtagScript(gtagId);
|
|
862
|
+
if (scriptLoad) return scriptLoad;
|
|
863
|
+
scriptLoad = new Promise((resolve) => {
|
|
864
|
+
const script = document.querySelector(
|
|
865
|
+
'script[data-aranova-tracking="aranova-gtag-loader"]'
|
|
866
|
+
);
|
|
867
|
+
if (!script) {
|
|
868
|
+
resolve();
|
|
869
|
+
return;
|
|
870
|
+
}
|
|
871
|
+
if (script.dataset.loaded === "true") {
|
|
872
|
+
resolve();
|
|
873
|
+
return;
|
|
874
|
+
}
|
|
875
|
+
window.setTimeout(resolve, 0);
|
|
876
|
+
script.addEventListener(
|
|
877
|
+
"load",
|
|
878
|
+
() => {
|
|
879
|
+
script.dataset.loaded = "true";
|
|
880
|
+
resolve();
|
|
881
|
+
},
|
|
882
|
+
{ once: true }
|
|
883
|
+
);
|
|
884
|
+
script.addEventListener("error", () => resolve(), { once: true });
|
|
885
|
+
});
|
|
886
|
+
return scriptLoad;
|
|
887
|
+
}
|
|
888
|
+
var TrackingConfigRuntime = class {
|
|
889
|
+
constructor(ref, fetchImpl = globalThis.fetch) {
|
|
890
|
+
this.ref = ref;
|
|
891
|
+
this.fetchImpl = fetchImpl;
|
|
892
|
+
this.current = null;
|
|
893
|
+
this.etag = null;
|
|
894
|
+
this.stateValue = "unconfirmed";
|
|
895
|
+
this.confirmedAt = 0;
|
|
896
|
+
this.inFlight = null;
|
|
897
|
+
this.conversionQueue = [];
|
|
898
|
+
this.automaticQueue = [];
|
|
899
|
+
this.pageQueue = [];
|
|
900
|
+
this.listeners = /* @__PURE__ */ new Set();
|
|
901
|
+
const cached = readCache2(ref.cdnUrl);
|
|
902
|
+
this.current = cached?.config ?? null;
|
|
903
|
+
this.etag = cached?.etag ?? null;
|
|
904
|
+
void this.revalidate();
|
|
905
|
+
if (typeof window !== "undefined") {
|
|
906
|
+
window.addEventListener("visibilitychange", () => {
|
|
907
|
+
if (document.visibilityState === "visible") void this.revalidate();
|
|
908
|
+
});
|
|
909
|
+
window.addEventListener("focus", () => void this.revalidate());
|
|
910
|
+
}
|
|
911
|
+
}
|
|
912
|
+
state() {
|
|
913
|
+
return this.stateValue;
|
|
914
|
+
}
|
|
915
|
+
config() {
|
|
916
|
+
return this.stateValue === "active" || this.stateValue === "tombstone" ? this.current : null;
|
|
917
|
+
}
|
|
918
|
+
__unsafeExpireAuthorityForTests() {
|
|
919
|
+
this.confirmedAt = 0;
|
|
920
|
+
}
|
|
921
|
+
subscribe(listener) {
|
|
922
|
+
this.listeners.add(listener);
|
|
923
|
+
return () => this.listeners.delete(listener);
|
|
924
|
+
}
|
|
925
|
+
async ensureAuthority() {
|
|
926
|
+
if (this.stateValue !== "unconfirmed" && Date.now() - this.confirmedAt < AUTHORITY_TTL_MS) {
|
|
927
|
+
return true;
|
|
928
|
+
}
|
|
929
|
+
await this.revalidate();
|
|
930
|
+
return this.stateValue !== "unconfirmed" && Date.now() - this.confirmedAt < AUTHORITY_TTL_MS;
|
|
931
|
+
}
|
|
932
|
+
async revalidate() {
|
|
933
|
+
if (typeof window === "undefined" || !this.fetchImpl) return;
|
|
934
|
+
if (this.inFlight) return this.inFlight;
|
|
935
|
+
this.inFlight = this.revalidateNow().finally(() => {
|
|
936
|
+
this.inFlight = null;
|
|
937
|
+
});
|
|
938
|
+
return this.inFlight;
|
|
939
|
+
}
|
|
940
|
+
queuePageView(snapshot = pageViewSnapshot()) {
|
|
941
|
+
if (!snapshot) return;
|
|
942
|
+
this.pageQueue.push(snapshot);
|
|
943
|
+
void this.flush();
|
|
944
|
+
}
|
|
945
|
+
fireConversion(key, options) {
|
|
946
|
+
this.conversionQueue.push({ key, ...options });
|
|
947
|
+
void this.flush();
|
|
948
|
+
}
|
|
949
|
+
queueAutomaticEvent(eventType, metadata, transactionPath) {
|
|
950
|
+
this.automaticQueue.push({ eventType, metadata, transactionPath });
|
|
951
|
+
void this.flush();
|
|
952
|
+
}
|
|
953
|
+
listGoals() {
|
|
954
|
+
return this.current?.goals ?? [];
|
|
955
|
+
}
|
|
956
|
+
async revalidateNow() {
|
|
957
|
+
try {
|
|
958
|
+
const headers = {};
|
|
959
|
+
if (this.etag) headers["If-None-Match"] = this.etag;
|
|
960
|
+
const response = await this.fetchImpl(this.ref.cdnUrl, {
|
|
961
|
+
method: "GET",
|
|
962
|
+
headers,
|
|
963
|
+
cache: "no-cache"
|
|
964
|
+
});
|
|
965
|
+
if (response.status === 304 && this.current && validateConfig(this.current, this.ref)) {
|
|
966
|
+
this.confirm(this.current, this.etag);
|
|
967
|
+
await this.flush();
|
|
968
|
+
return;
|
|
969
|
+
}
|
|
970
|
+
if (!response.ok) {
|
|
971
|
+
this.expireAuthority();
|
|
972
|
+
return;
|
|
973
|
+
}
|
|
974
|
+
const next = parseConversionConfig(await response.json());
|
|
975
|
+
if (!next || !validateConfig(next, this.ref)) {
|
|
976
|
+
this.expireAuthority();
|
|
977
|
+
return;
|
|
978
|
+
}
|
|
979
|
+
if (this.current && next.config_version < this.current.config_version) {
|
|
980
|
+
this.expireAuthority();
|
|
981
|
+
return;
|
|
982
|
+
}
|
|
983
|
+
this.confirm(next, response.headers.get("ETag"));
|
|
984
|
+
await this.flush();
|
|
985
|
+
} catch {
|
|
986
|
+
this.expireAuthority();
|
|
987
|
+
}
|
|
988
|
+
}
|
|
989
|
+
expireAuthority() {
|
|
990
|
+
if (this.stateValue !== "unconfirmed") this.stateValue = "unconfirmed";
|
|
991
|
+
}
|
|
992
|
+
confirm(config, etag) {
|
|
993
|
+
this.current = config;
|
|
994
|
+
this.etag = etag;
|
|
995
|
+
this.confirmedAt = Date.now();
|
|
996
|
+
this.stateValue = isTombstone(config) ? "tombstone" : "active";
|
|
997
|
+
writeCache2(this.ref.cdnUrl, { etag, config });
|
|
998
|
+
if (this.stateValue === "tombstone") {
|
|
999
|
+
this.conversionQueue.length = 0;
|
|
1000
|
+
this.automaticQueue.length = 0;
|
|
1001
|
+
this.pageQueue.length = 0;
|
|
1002
|
+
}
|
|
1003
|
+
for (const listener of this.listeners) listener();
|
|
1004
|
+
}
|
|
1005
|
+
async flush() {
|
|
1006
|
+
if (!await this.ensureAuthority()) return;
|
|
1007
|
+
if (this.stateValue !== "active" || !this.current) return;
|
|
1008
|
+
const ids = Object.values(this.current.gtag_ids).filter(
|
|
1009
|
+
(id) => typeof id === "string" && isValidGtagId(id)
|
|
1010
|
+
);
|
|
1011
|
+
if (ids.length === 0) return;
|
|
1012
|
+
await ensureScript(ids[0]);
|
|
1013
|
+
const gtag = window.gtag;
|
|
1014
|
+
if (typeof gtag !== "function") return;
|
|
1015
|
+
if (!jsInitialized) {
|
|
1016
|
+
gtag("js", /* @__PURE__ */ new Date());
|
|
1017
|
+
jsInitialized = true;
|
|
1018
|
+
}
|
|
1019
|
+
for (const id of ids) {
|
|
1020
|
+
if (configuredIds.has(id)) continue;
|
|
1021
|
+
gtag("config", id, { send_page_view: false });
|
|
1022
|
+
configuredIds.add(id);
|
|
1023
|
+
}
|
|
1024
|
+
while (this.pageQueue.length) {
|
|
1025
|
+
const page = this.pageQueue.shift();
|
|
1026
|
+
gtag("event", "page_view", {
|
|
1027
|
+
page_location: page.href,
|
|
1028
|
+
page_title: page.title ?? void 0,
|
|
1029
|
+
page_referrer: page.referrer ?? void 0
|
|
1030
|
+
});
|
|
1031
|
+
}
|
|
1032
|
+
while (this.automaticQueue.length) {
|
|
1033
|
+
const event = this.automaticQueue.shift();
|
|
1034
|
+
for (const goal of this.current.goals) {
|
|
1035
|
+
if (goal.kind !== "event" || !goal.firing) continue;
|
|
1036
|
+
if (!automaticThresholdMet(goal, event.eventType, event.metadata)) continue;
|
|
1037
|
+
this.conversionQueue.push({
|
|
1038
|
+
key: goal.key,
|
|
1039
|
+
transactionId: `auto:${goal.key}:${event.transactionPath}`
|
|
1040
|
+
});
|
|
1041
|
+
}
|
|
1042
|
+
}
|
|
1043
|
+
while (this.conversionQueue.length) {
|
|
1044
|
+
const item = this.conversionQueue.shift();
|
|
1045
|
+
const goal = this.current.goals.find((g) => g.key === item.key);
|
|
1046
|
+
const firing = goal?.firing;
|
|
1047
|
+
if (!firing || getConsentState() === "denied") continue;
|
|
1048
|
+
const currency = item.currency ?? firing.currency ?? null;
|
|
1049
|
+
const value = item.value ?? (firing.value_cents != null && currency ? fromMinor(firing.value_cents, currency) : null);
|
|
1050
|
+
fireGtagConversion({
|
|
1051
|
+
sendTo: firing.send_to,
|
|
1052
|
+
value,
|
|
1053
|
+
currency,
|
|
1054
|
+
transactionId: item.transactionId ?? null
|
|
1055
|
+
});
|
|
1056
|
+
}
|
|
1057
|
+
}
|
|
1058
|
+
};
|
|
1059
|
+
function automaticThresholdMet(goal, eventType, metadata) {
|
|
1060
|
+
const t = goal.trigger;
|
|
1061
|
+
if (!t || t.event_type !== eventType) return false;
|
|
1062
|
+
switch (eventType) {
|
|
1063
|
+
case "scroll_depth":
|
|
1064
|
+
return typeof metadata.depth_percent === "number" && t.threshold_percent != null && metadata.depth_percent >= t.threshold_percent;
|
|
1065
|
+
case "time_on_site":
|
|
1066
|
+
return typeof metadata.duration_ms === "number" && t.threshold_seconds != null && metadata.duration_ms >= t.threshold_seconds * 1e3;
|
|
1067
|
+
case "multi_page_session":
|
|
1068
|
+
return typeof metadata.page_count === "number" && t.page_threshold != null && metadata.page_count >= t.page_threshold;
|
|
1069
|
+
case "specific_page_visit":
|
|
1070
|
+
return typeof metadata.page_name === "string" && metadata.page_name === t.page_name;
|
|
1071
|
+
case "page_view":
|
|
1072
|
+
case "form_start":
|
|
1073
|
+
case "phone_click":
|
|
1074
|
+
return true;
|
|
1075
|
+
default:
|
|
1076
|
+
return false;
|
|
1077
|
+
}
|
|
1078
|
+
}
|
|
1079
|
+
function getTrackingConfigRuntime(ref, fetchImpl) {
|
|
1080
|
+
const key = `${ref.cdnUrl}|${ref.businessId}|${ref.environment}`;
|
|
1081
|
+
const existing = runtimes.get(key);
|
|
1082
|
+
if (existing) return existing;
|
|
1083
|
+
const runtime = new TrackingConfigRuntime(ref, fetchImpl ?? globalThis.fetch);
|
|
1084
|
+
runtimes.set(key, runtime);
|
|
1085
|
+
return runtime;
|
|
1086
|
+
}
|
|
1087
|
+
|
|
772
1088
|
// ../tracking-core/src/resources/conversion-autofire.ts
|
|
773
1089
|
function thresholdMet(goal, eventType, metadata) {
|
|
774
1090
|
const t = goal.trigger;
|
|
@@ -801,6 +1117,12 @@ function createConversionAutoFire(store) {
|
|
|
801
1117
|
for (const goal of store.listGoals()) {
|
|
802
1118
|
if (goal.kind !== "event" || !goal.firing) continue;
|
|
803
1119
|
if (!thresholdMet(goal, eventType, metadata)) continue;
|
|
1120
|
+
if ("queueAutomaticEvent" in store) {
|
|
1121
|
+
store.fireConversion(goal.key, {
|
|
1122
|
+
transactionId: `auto:${goal.key}:${currentPath()}`
|
|
1123
|
+
});
|
|
1124
|
+
continue;
|
|
1125
|
+
}
|
|
804
1126
|
const firing = goal.firing;
|
|
805
1127
|
const cents = firing.value_cents ?? null;
|
|
806
1128
|
const currency = firing.currency ?? null;
|
|
@@ -816,6 +1138,10 @@ function createConversionAutoFire(store) {
|
|
|
816
1138
|
}
|
|
817
1139
|
return {
|
|
818
1140
|
onAutomaticEvent(eventType, metadata) {
|
|
1141
|
+
if ("queueAutomaticEvent" in store) {
|
|
1142
|
+
store.queueAutomaticEvent(eventType, metadata, currentPath());
|
|
1143
|
+
return;
|
|
1144
|
+
}
|
|
819
1145
|
if (store.isReady()) {
|
|
820
1146
|
fireMatching(eventType, metadata);
|
|
821
1147
|
return;
|
|
@@ -1412,6 +1738,14 @@ var pageExitMetadataSchema = import_zod7.z.object({
|
|
|
1412
1738
|
// null = left without any scroll signal; floor is 0 so a valid 0% is never
|
|
1413
1739
|
// rejected (a single bad field 422s the whole keepalive beacon batch).
|
|
1414
1740
|
max_scroll_percent: import_zod7.z.number().int().min(0).max(100).nullable(),
|
|
1741
|
+
// The gating baseline: the fraction of the page visible at load with
|
|
1742
|
+
// zero scrolling — or, for pages that grew after the post-paint snapshot
|
|
1743
|
+
// (skeleton/streaming renders), the first-scroll position that
|
|
1744
|
+
// established it. null = page had no scrollable range, or the segment
|
|
1745
|
+
// ended before the snapshot. `.optional()` is load-bearing: SDK builds
|
|
1746
|
+
// predating this field keep POSTing page_exit without the key —
|
|
1747
|
+
// requiring it would 422 whole keepalive beacon batches.
|
|
1748
|
+
scroll_baseline_percent: import_zod7.z.number().int().min(0).max(100).nullable().optional(),
|
|
1415
1749
|
page: import_zod7.z.object({
|
|
1416
1750
|
path: import_zod7.z.string()
|
|
1417
1751
|
}).strict()
|
|
@@ -1487,69 +1821,154 @@ var timeOnSiteConfigSchema = import_zod11.z.object({
|
|
|
1487
1821
|
thresholdSeconds: import_zod11.z.number().int().positive()
|
|
1488
1822
|
}).strict();
|
|
1489
1823
|
|
|
1824
|
+
// ../tracking-core/src/events/semantics.ts
|
|
1825
|
+
var EVENT_SEMANTICS = {
|
|
1826
|
+
page_view: {
|
|
1827
|
+
label: "Page view",
|
|
1828
|
+
category: "page",
|
|
1829
|
+
outcomeRole: "navigation",
|
|
1830
|
+
clientVisibility: "simple"
|
|
1831
|
+
},
|
|
1832
|
+
time_on_site: {
|
|
1833
|
+
label: "Time on site",
|
|
1834
|
+
category: "engagement",
|
|
1835
|
+
outcomeRole: "engagement",
|
|
1836
|
+
clientVisibility: "detailed"
|
|
1837
|
+
},
|
|
1838
|
+
specific_page_visit: {
|
|
1839
|
+
label: "Key page visit",
|
|
1840
|
+
category: "engagement",
|
|
1841
|
+
outcomeRole: "engagement",
|
|
1842
|
+
clientVisibility: "detailed"
|
|
1843
|
+
},
|
|
1844
|
+
scroll_depth: {
|
|
1845
|
+
label: "Scroll depth",
|
|
1846
|
+
category: "engagement",
|
|
1847
|
+
outcomeRole: "engagement",
|
|
1848
|
+
clientVisibility: "detailed"
|
|
1849
|
+
},
|
|
1850
|
+
multi_page_session: {
|
|
1851
|
+
label: "Multi-page session",
|
|
1852
|
+
category: "engagement",
|
|
1853
|
+
outcomeRole: "engagement",
|
|
1854
|
+
clientVisibility: "detailed"
|
|
1855
|
+
},
|
|
1856
|
+
form_start: {
|
|
1857
|
+
label: "Form started",
|
|
1858
|
+
category: "engagement",
|
|
1859
|
+
outcomeRole: "engagement",
|
|
1860
|
+
clientVisibility: "simple"
|
|
1861
|
+
},
|
|
1862
|
+
sdk_heartbeat: {
|
|
1863
|
+
label: "SDK heartbeat",
|
|
1864
|
+
category: "system",
|
|
1865
|
+
outcomeRole: "diagnostic",
|
|
1866
|
+
clientVisibility: "hidden"
|
|
1867
|
+
},
|
|
1868
|
+
page_exit: {
|
|
1869
|
+
label: "Page exit",
|
|
1870
|
+
category: "engagement",
|
|
1871
|
+
outcomeRole: "diagnostic",
|
|
1872
|
+
clientVisibility: "detailed"
|
|
1873
|
+
},
|
|
1874
|
+
form_submit: {
|
|
1875
|
+
label: "Form submitted",
|
|
1876
|
+
category: "lead",
|
|
1877
|
+
outcomeRole: "lead",
|
|
1878
|
+
clientVisibility: "simple"
|
|
1879
|
+
},
|
|
1880
|
+
phone_click: {
|
|
1881
|
+
label: "Phone click",
|
|
1882
|
+
category: "lead",
|
|
1883
|
+
outcomeRole: "lead",
|
|
1884
|
+
clientVisibility: "simple"
|
|
1885
|
+
},
|
|
1886
|
+
cta_click: {
|
|
1887
|
+
label: "CTA click",
|
|
1888
|
+
category: "engagement",
|
|
1889
|
+
outcomeRole: "engagement",
|
|
1890
|
+
clientVisibility: "simple"
|
|
1891
|
+
}
|
|
1892
|
+
};
|
|
1893
|
+
function getEventSemantics(eventName) {
|
|
1894
|
+
return eventName in EVENT_SEMANTICS ? EVENT_SEMANTICS[eventName] : null;
|
|
1895
|
+
}
|
|
1896
|
+
|
|
1490
1897
|
// ../tracking-core/src/events/registry.ts
|
|
1491
1898
|
var EVENT_REGISTRY = {
|
|
1492
1899
|
// --- automatic triggers ---
|
|
1493
1900
|
page_view: {
|
|
1494
1901
|
kind: "automatic",
|
|
1902
|
+
semantics: EVENT_SEMANTICS.page_view,
|
|
1495
1903
|
metadataSchema: pageViewMetadataSchema,
|
|
1496
1904
|
configSchema: pageViewConfigSchema
|
|
1497
1905
|
},
|
|
1498
1906
|
time_on_site: {
|
|
1499
1907
|
kind: "automatic",
|
|
1908
|
+
semantics: EVENT_SEMANTICS.time_on_site,
|
|
1500
1909
|
metadataSchema: timeOnSiteMetadataSchema,
|
|
1501
1910
|
configSchema: timeOnSiteConfigSchema
|
|
1502
1911
|
},
|
|
1503
1912
|
specific_page_visit: {
|
|
1504
1913
|
kind: "automatic",
|
|
1914
|
+
semantics: EVENT_SEMANTICS.specific_page_visit,
|
|
1505
1915
|
metadataSchema: specificPageVisitMetadataSchema,
|
|
1506
1916
|
configSchema: specificPageVisitConfigSchema
|
|
1507
1917
|
},
|
|
1508
1918
|
scroll_depth: {
|
|
1509
1919
|
kind: "automatic",
|
|
1920
|
+
semantics: EVENT_SEMANTICS.scroll_depth,
|
|
1510
1921
|
metadataSchema: scrollDepthMetadataSchema,
|
|
1511
1922
|
configSchema: scrollDepthConfigSchema
|
|
1512
1923
|
},
|
|
1513
1924
|
multi_page_session: {
|
|
1514
1925
|
kind: "automatic",
|
|
1926
|
+
semantics: EVENT_SEMANTICS.multi_page_session,
|
|
1515
1927
|
metadataSchema: multiPageSessionMetadataSchema,
|
|
1516
1928
|
configSchema: multiPageSessionConfigSchema
|
|
1517
1929
|
},
|
|
1518
1930
|
form_start: {
|
|
1519
1931
|
kind: "automatic",
|
|
1932
|
+
semantics: EVENT_SEMANTICS.form_start,
|
|
1520
1933
|
metadataSchema: formStartMetadataSchema,
|
|
1521
1934
|
configSchema: formStartConfigSchema
|
|
1522
1935
|
},
|
|
1523
1936
|
// --- SDK-internal automatic (not consumer-configurable) ---
|
|
1524
1937
|
sdk_heartbeat: {
|
|
1525
1938
|
kind: "automatic",
|
|
1939
|
+
semantics: EVENT_SEMANTICS.sdk_heartbeat,
|
|
1526
1940
|
metadataSchema: sdkHeartbeatMetadataSchema,
|
|
1527
1941
|
configSchema: sdkHeartbeatConfigSchema
|
|
1528
1942
|
},
|
|
1529
1943
|
page_exit: {
|
|
1530
1944
|
kind: "automatic",
|
|
1945
|
+
semantics: EVENT_SEMANTICS.page_exit,
|
|
1531
1946
|
metadataSchema: pageExitMetadataSchema,
|
|
1532
1947
|
configSchema: pageExitConfigSchema
|
|
1533
1948
|
},
|
|
1534
1949
|
// --- manual triggers ---
|
|
1535
1950
|
form_submit: {
|
|
1536
1951
|
kind: "manual",
|
|
1952
|
+
semantics: EVENT_SEMANTICS.form_submit,
|
|
1537
1953
|
metadataSchema: formSubmitMetadataSchema,
|
|
1538
1954
|
configSchema: formSubmitConfigSchema
|
|
1539
1955
|
},
|
|
1540
1956
|
phone_click: {
|
|
1541
1957
|
kind: "manual",
|
|
1958
|
+
semantics: EVENT_SEMANTICS.phone_click,
|
|
1542
1959
|
metadataSchema: phoneClickMetadataSchema,
|
|
1543
1960
|
configSchema: phoneClickConfigSchema
|
|
1544
1961
|
},
|
|
1545
1962
|
cta_click: {
|
|
1546
1963
|
kind: "manual",
|
|
1964
|
+
semantics: EVENT_SEMANTICS.cta_click,
|
|
1547
1965
|
metadataSchema: ctaClickMetadataSchema,
|
|
1548
1966
|
configSchema: ctaClickConfigSchema
|
|
1549
1967
|
}
|
|
1550
1968
|
};
|
|
1551
1969
|
var ALL_AUTOMATIC_EVENT_NAMES = Object.entries(EVENT_REGISTRY).filter(([, def]) => def.kind === "automatic").map(([name]) => name);
|
|
1552
1970
|
var ALL_MANUAL_EVENT_NAMES = Object.entries(EVENT_REGISTRY).filter(([, def]) => def.kind === "manual").map(([name]) => name);
|
|
1971
|
+
var ALL_LEAD_EVENT_NAMES = Object.entries(EVENT_REGISTRY).filter(([, def]) => def.semantics.outcomeRole === "lead").map(([name]) => name);
|
|
1553
1972
|
function getEventDefinition(name) {
|
|
1554
1973
|
return EVENT_REGISTRY[name];
|
|
1555
1974
|
}
|
|
@@ -1636,6 +2055,97 @@ function attachTimeOnSite(client, config) {
|
|
|
1636
2055
|
};
|
|
1637
2056
|
}
|
|
1638
2057
|
|
|
2058
|
+
// ../tracking-core/src/triggers/navigation.ts
|
|
2059
|
+
var listeners = /* @__PURE__ */ new Set();
|
|
2060
|
+
var restorePatch = null;
|
|
2061
|
+
function notify() {
|
|
2062
|
+
for (const listener of listeners) listener();
|
|
2063
|
+
}
|
|
2064
|
+
function notifyDeferred() {
|
|
2065
|
+
setTimeout(notify, 0);
|
|
2066
|
+
}
|
|
2067
|
+
function installPatch() {
|
|
2068
|
+
const originalPushState = history.pushState;
|
|
2069
|
+
const originalReplaceState = history.replaceState;
|
|
2070
|
+
function patchedPushState(...args) {
|
|
2071
|
+
originalPushState.apply(this, args);
|
|
2072
|
+
notifyDeferred();
|
|
2073
|
+
}
|
|
2074
|
+
function patchedReplaceState(...args) {
|
|
2075
|
+
originalReplaceState.apply(this, args);
|
|
2076
|
+
notifyDeferred();
|
|
2077
|
+
}
|
|
2078
|
+
history.pushState = patchedPushState;
|
|
2079
|
+
history.replaceState = patchedReplaceState;
|
|
2080
|
+
window.addEventListener("popstate", notify);
|
|
2081
|
+
restorePatch = () => {
|
|
2082
|
+
history.pushState = originalPushState;
|
|
2083
|
+
history.replaceState = originalReplaceState;
|
|
2084
|
+
window.removeEventListener("popstate", notify);
|
|
2085
|
+
restorePatch = null;
|
|
2086
|
+
};
|
|
2087
|
+
}
|
|
2088
|
+
function onHistoryChange(listener) {
|
|
2089
|
+
if (listeners.size === 0) installPatch();
|
|
2090
|
+
listeners.add(listener);
|
|
2091
|
+
return () => {
|
|
2092
|
+
if (!listeners.delete(listener)) return;
|
|
2093
|
+
if (listeners.size === 0) restorePatch?.();
|
|
2094
|
+
};
|
|
2095
|
+
}
|
|
2096
|
+
|
|
2097
|
+
// ../tracking-core/src/triggers/scroll-measurement.ts
|
|
2098
|
+
var BOTTOM_EPSILON_PX = 2;
|
|
2099
|
+
function measureScrollPercent() {
|
|
2100
|
+
const root = document.scrollingElement ?? document.documentElement;
|
|
2101
|
+
const scrollHeight = root.scrollHeight;
|
|
2102
|
+
const clientHeight = root.clientHeight;
|
|
2103
|
+
if (scrollHeight <= 0 || clientHeight <= 0) return null;
|
|
2104
|
+
if (scrollHeight <= clientHeight + BOTTOM_EPSILON_PX) return null;
|
|
2105
|
+
const maxTop = scrollHeight - clientHeight;
|
|
2106
|
+
const scrollTop = Math.min(Math.max(root.scrollTop, 0), maxTop);
|
|
2107
|
+
if (scrollTop + clientHeight >= scrollHeight - BOTTOM_EPSILON_PX) return 100;
|
|
2108
|
+
return Math.max(1, Math.min(100, Math.round((scrollTop + clientHeight) / scrollHeight * 100)));
|
|
2109
|
+
}
|
|
2110
|
+
function scheduleBaselineSnapshot(onSnapshot) {
|
|
2111
|
+
let rafId = requestAnimationFrame(() => {
|
|
2112
|
+
rafId = requestAnimationFrame(() => {
|
|
2113
|
+
onSnapshot(measureScrollPercent());
|
|
2114
|
+
});
|
|
2115
|
+
});
|
|
2116
|
+
return () => cancelAnimationFrame(rafId);
|
|
2117
|
+
}
|
|
2118
|
+
function createBaselineGate() {
|
|
2119
|
+
let baselinePercent = null;
|
|
2120
|
+
let ready = false;
|
|
2121
|
+
let cancelSnapshot = null;
|
|
2122
|
+
return {
|
|
2123
|
+
rebaseline() {
|
|
2124
|
+
cancelSnapshot?.();
|
|
2125
|
+
ready = false;
|
|
2126
|
+
baselinePercent = null;
|
|
2127
|
+
cancelSnapshot = scheduleBaselineSnapshot((b) => {
|
|
2128
|
+
baselinePercent = b;
|
|
2129
|
+
ready = true;
|
|
2130
|
+
});
|
|
2131
|
+
},
|
|
2132
|
+
cancel() {
|
|
2133
|
+
cancelSnapshot?.();
|
|
2134
|
+
},
|
|
2135
|
+
baseline() {
|
|
2136
|
+
return ready ? baselinePercent : null;
|
|
2137
|
+
},
|
|
2138
|
+
sample() {
|
|
2139
|
+
if (!ready) return null;
|
|
2140
|
+
if (baselinePercent === null) {
|
|
2141
|
+
baselinePercent = measureScrollPercent();
|
|
2142
|
+
return null;
|
|
2143
|
+
}
|
|
2144
|
+
return measureScrollPercent();
|
|
2145
|
+
}
|
|
2146
|
+
};
|
|
2147
|
+
}
|
|
2148
|
+
|
|
1639
2149
|
// ../tracking-core/src/triggers/scroll-depth.ts
|
|
1640
2150
|
function attachScrollDepth(client, config) {
|
|
1641
2151
|
if (typeof window === "undefined" || typeof document === "undefined") {
|
|
@@ -1646,17 +2156,14 @@ function attachScrollDepth(client, config) {
|
|
|
1646
2156
|
let firedForPath = /* @__PURE__ */ new Set();
|
|
1647
2157
|
let currentPath2 = window.location.pathname;
|
|
1648
2158
|
let rafId = null;
|
|
1649
|
-
|
|
1650
|
-
const doc = document.documentElement;
|
|
1651
|
-
const scrollTop = window.scrollY || doc.scrollTop;
|
|
1652
|
-
const scrollHeight = doc.scrollHeight;
|
|
1653
|
-
const clientHeight = doc.clientHeight;
|
|
1654
|
-
if (scrollHeight <= clientHeight) return 100;
|
|
1655
|
-
return Math.round((scrollTop + clientHeight) / scrollHeight * 100);
|
|
1656
|
-
}
|
|
2159
|
+
const gate = createBaselineGate();
|
|
1657
2160
|
function checkThresholds() {
|
|
1658
|
-
const percent =
|
|
2161
|
+
const percent = gate.sample();
|
|
2162
|
+
if (percent === null) return;
|
|
2163
|
+
const baseline = gate.baseline();
|
|
2164
|
+
if (baseline === null) return;
|
|
1659
2165
|
for (const threshold of thresholds) {
|
|
2166
|
+
if (threshold <= baseline) continue;
|
|
1660
2167
|
if (percent >= threshold && !firedForPath.has(threshold)) {
|
|
1661
2168
|
firedForPath.add(threshold);
|
|
1662
2169
|
client.trackEvent({
|
|
@@ -1683,28 +2190,15 @@ function attachScrollDepth(client, config) {
|
|
|
1683
2190
|
if (newPath === currentPath2) return;
|
|
1684
2191
|
currentPath2 = newPath;
|
|
1685
2192
|
firedForPath = /* @__PURE__ */ new Set();
|
|
1686
|
-
|
|
1687
|
-
}
|
|
1688
|
-
const originalPushState = history.pushState.bind(history);
|
|
1689
|
-
const originalReplaceState = history.replaceState.bind(history);
|
|
1690
|
-
function patchedPushState(...args) {
|
|
1691
|
-
originalPushState(...args);
|
|
1692
|
-
setTimeout(resetIfPathChanged, 0);
|
|
1693
|
-
}
|
|
1694
|
-
function patchedReplaceState(...args) {
|
|
1695
|
-
originalReplaceState(...args);
|
|
1696
|
-
setTimeout(resetIfPathChanged, 0);
|
|
2193
|
+
gate.rebaseline();
|
|
1697
2194
|
}
|
|
1698
|
-
|
|
1699
|
-
history.replaceState = patchedReplaceState;
|
|
1700
|
-
window.addEventListener("popstate", resetIfPathChanged);
|
|
2195
|
+
const unsubscribeNav = onHistoryChange(resetIfPathChanged);
|
|
1701
2196
|
window.addEventListener("scroll", onScroll, { passive: true });
|
|
1702
|
-
|
|
2197
|
+
gate.rebaseline();
|
|
1703
2198
|
return () => {
|
|
1704
2199
|
if (rafId !== null) cancelAnimationFrame(rafId);
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
window.removeEventListener("popstate", resetIfPathChanged);
|
|
2200
|
+
gate.cancel();
|
|
2201
|
+
unsubscribeNav();
|
|
1708
2202
|
window.removeEventListener("scroll", onScroll);
|
|
1709
2203
|
};
|
|
1710
2204
|
}
|
|
@@ -1786,21 +2280,14 @@ function attachPageExit(client) {
|
|
|
1786
2280
|
let accumulatedMs = 0;
|
|
1787
2281
|
let maxScrollPercent = null;
|
|
1788
2282
|
let rafId = null;
|
|
1789
|
-
|
|
1790
|
-
const doc = document.documentElement;
|
|
1791
|
-
const scrollTop = window.scrollY || doc.scrollTop;
|
|
1792
|
-
const scrollHeight = doc.scrollHeight;
|
|
1793
|
-
const clientHeight = doc.clientHeight;
|
|
1794
|
-
if (scrollHeight <= clientHeight) return 100;
|
|
1795
|
-
return Math.round((scrollTop + clientHeight) / scrollHeight * 100);
|
|
1796
|
-
}
|
|
2283
|
+
const gate = createBaselineGate();
|
|
1797
2284
|
function onScroll() {
|
|
1798
2285
|
if (rafId !== null) return;
|
|
1799
2286
|
rafId = requestAnimationFrame(() => {
|
|
1800
2287
|
rafId = null;
|
|
1801
|
-
const percent =
|
|
1802
|
-
if (percent
|
|
1803
|
-
maxScrollPercent =
|
|
2288
|
+
const percent = gate.sample();
|
|
2289
|
+
if (percent !== null && (maxScrollPercent === null || percent > maxScrollPercent)) {
|
|
2290
|
+
maxScrollPercent = percent;
|
|
1804
2291
|
}
|
|
1805
2292
|
});
|
|
1806
2293
|
}
|
|
@@ -1819,6 +2306,12 @@ function attachPageExit(client) {
|
|
|
1819
2306
|
metadata: {
|
|
1820
2307
|
dwell_ms: dwell,
|
|
1821
2308
|
max_scroll_percent: maxScrollPercent,
|
|
2309
|
+
// Lets the backend tell "scrolled to the bottom" apart from "the page
|
|
2310
|
+
// was barely scrollable". null = unscrollable page or the segment
|
|
2311
|
+
// ended before the post-paint snapshot landed. For pages that grew
|
|
2312
|
+
// after the snapshot this is the first-scroll position, not the
|
|
2313
|
+
// at-load fraction (see BaselineGate.baseline).
|
|
2314
|
+
scroll_baseline_percent: gate.baseline(),
|
|
1822
2315
|
page: { path }
|
|
1823
2316
|
},
|
|
1824
2317
|
pageUrl: window.location.href,
|
|
@@ -1836,6 +2329,7 @@ function attachPageExit(client) {
|
|
|
1836
2329
|
emitSegment(currentPath2, false);
|
|
1837
2330
|
currentPath2 = newPath;
|
|
1838
2331
|
maxScrollPercent = null;
|
|
2332
|
+
gate.rebaseline();
|
|
1839
2333
|
accumulatedMs = 0;
|
|
1840
2334
|
activeSince = document.visibilityState === "visible" ? Date.now() : null;
|
|
1841
2335
|
}
|
|
@@ -1849,27 +2343,15 @@ function attachPageExit(client) {
|
|
|
1849
2343
|
function onPageHide() {
|
|
1850
2344
|
emitSegment(currentPath2, true);
|
|
1851
2345
|
}
|
|
1852
|
-
const
|
|
1853
|
-
const originalReplaceState = history.replaceState.bind(history);
|
|
1854
|
-
function patchedPushState(...args) {
|
|
1855
|
-
originalPushState(...args);
|
|
1856
|
-
setTimeout(onNavigate, 0);
|
|
1857
|
-
}
|
|
1858
|
-
function patchedReplaceState(...args) {
|
|
1859
|
-
originalReplaceState(...args);
|
|
1860
|
-
setTimeout(onNavigate, 0);
|
|
1861
|
-
}
|
|
1862
|
-
history.pushState = patchedPushState;
|
|
1863
|
-
history.replaceState = patchedReplaceState;
|
|
1864
|
-
window.addEventListener("popstate", onNavigate);
|
|
2346
|
+
const unsubscribeNav = onHistoryChange(onNavigate);
|
|
1865
2347
|
window.addEventListener("scroll", onScroll, { passive: true });
|
|
1866
2348
|
document.addEventListener("visibilitychange", onVisibilityChange);
|
|
1867
2349
|
window.addEventListener("pagehide", onPageHide);
|
|
2350
|
+
gate.rebaseline();
|
|
1868
2351
|
return () => {
|
|
1869
2352
|
if (rafId !== null) cancelAnimationFrame(rafId);
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
window.removeEventListener("popstate", onNavigate);
|
|
2353
|
+
gate.cancel();
|
|
2354
|
+
unsubscribeNav();
|
|
1873
2355
|
window.removeEventListener("scroll", onScroll);
|
|
1874
2356
|
document.removeEventListener("visibilitychange", onVisibilityChange);
|
|
1875
2357
|
window.removeEventListener("pagehide", onPageHide);
|
|
@@ -2084,6 +2566,14 @@ function fireRecordedConversions(firing, input, recorded, sale, currency) {
|
|
|
2084
2566
|
const txnBase = input.external_id ?? sale.id;
|
|
2085
2567
|
for (const item of recorded) {
|
|
2086
2568
|
if (!item.service) continue;
|
|
2569
|
+
if ("fireConversion" in firing) {
|
|
2570
|
+
firing.fireConversion(item.service, {
|
|
2571
|
+
value: item.amount_cents != null ? fromMinor(item.amount_cents, currency) : void 0,
|
|
2572
|
+
currency,
|
|
2573
|
+
transactionId: `${txnBase}:${item.service}`
|
|
2574
|
+
});
|
|
2575
|
+
continue;
|
|
2576
|
+
}
|
|
2087
2577
|
const config = firing.getFiring(item.service);
|
|
2088
2578
|
if (!config) continue;
|
|
2089
2579
|
const cents = item.amount_cents ?? config.value_cents ?? null;
|
|
@@ -2126,6 +2616,14 @@ function createSalesClient(config) {
|
|
|
2126
2616
|
// recordSale is the intent-revealing alias — same behavior, clearer call site.
|
|
2127
2617
|
recordSale: record,
|
|
2128
2618
|
trackConversion(key, options) {
|
|
2619
|
+
if (config.firing && "fireConversion" in config.firing) {
|
|
2620
|
+
config.firing.fireConversion(key, {
|
|
2621
|
+
value: options?.value ?? void 0,
|
|
2622
|
+
currency: options?.currency ?? config.defaultCurrency ?? void 0,
|
|
2623
|
+
transactionId: options?.transactionId ?? null
|
|
2624
|
+
});
|
|
2625
|
+
return;
|
|
2626
|
+
}
|
|
2129
2627
|
const firing = config.firing?.getFiring(key);
|
|
2130
2628
|
if (!firing) return;
|
|
2131
2629
|
const currency = firing.currency ?? options?.currency ?? config.defaultCurrency ?? null;
|
|
@@ -2631,14 +3129,24 @@ var ANIMATION_KEYFRAMES = `
|
|
|
2631
3129
|
|
|
2632
3130
|
// src/AdPlatformTracking.tsx
|
|
2633
3131
|
var import_script = __toESM(require("next/script"));
|
|
3132
|
+
var import_react3 = require("react");
|
|
2634
3133
|
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
2635
3134
|
function AdPlatformTracking({
|
|
2636
3135
|
gtagId,
|
|
2637
3136
|
gtagIds,
|
|
3137
|
+
trackingConfig,
|
|
3138
|
+
standalonePageView = false,
|
|
2638
3139
|
metaPixelId,
|
|
2639
3140
|
metaPixelIds
|
|
2640
3141
|
}) {
|
|
2641
|
-
const
|
|
3142
|
+
const trackingConfigKey = trackingConfig ? `${trackingConfig.cdnUrl}:${trackingConfig.businessId}:${trackingConfig.environment}` : "";
|
|
3143
|
+
(0, import_react3.useEffect)(() => {
|
|
3144
|
+
if (!trackingConfig) return;
|
|
3145
|
+
const runtime = getTrackingConfigRuntime(trackingConfig);
|
|
3146
|
+
if (standalonePageView) runtime.queuePageView();
|
|
3147
|
+
else void runtime.revalidate();
|
|
3148
|
+
}, [trackingConfigKey, standalonePageView]);
|
|
3149
|
+
const gtagAll = trackingConfig ? [] : gtagIds ? Object.values(gtagIds).filter((id) => id != null) : gtagId ? [gtagId] : [];
|
|
2642
3150
|
const metaAll = metaPixelIds ? Object.values(metaPixelIds).filter((id) => id != null) : metaPixelId ? [metaPixelId] : [];
|
|
2643
3151
|
const gtagLoaderId = gtagAll[0];
|
|
2644
3152
|
const gtagInitScript = gtagAll.length === 1 ? createGtagInitScript(gtagAll[0]) : createGtagInitScriptMulti(gtagAll);
|
|
@@ -2685,20 +3193,20 @@ function GoogleAdsTracking(props) {
|
|
|
2685
3193
|
}
|
|
2686
3194
|
|
|
2687
3195
|
// src/factory.tsx
|
|
2688
|
-
var
|
|
3196
|
+
var import_navigation3 = require("next/navigation");
|
|
2689
3197
|
|
|
2690
3198
|
// package.json
|
|
2691
|
-
var version = "0.
|
|
3199
|
+
var version = "0.18.0";
|
|
2692
3200
|
|
|
2693
3201
|
// src/factory.tsx
|
|
2694
|
-
var
|
|
3202
|
+
var import_react5 = require("react");
|
|
2695
3203
|
|
|
2696
3204
|
// ../tracking-core/src/phone-react.tsx
|
|
2697
|
-
var
|
|
3205
|
+
var import_react4 = require("react");
|
|
2698
3206
|
var import_jsx_runtime4 = require("react/jsx-runtime");
|
|
2699
3207
|
var _phoneConfigContext;
|
|
2700
3208
|
function phoneConfigContext() {
|
|
2701
|
-
return _phoneConfigContext ?? (_phoneConfigContext = (0,
|
|
3209
|
+
return _phoneConfigContext ?? (_phoneConfigContext = (0, import_react4.createContext)(null));
|
|
2702
3210
|
}
|
|
2703
3211
|
function PhoneConfigProvider({
|
|
2704
3212
|
value,
|
|
@@ -2708,7 +3216,7 @@ function PhoneConfigProvider({
|
|
|
2708
3216
|
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Ctx.Provider, { value, children });
|
|
2709
3217
|
}
|
|
2710
3218
|
function usePhoneConfig() {
|
|
2711
|
-
const ctx = (0,
|
|
3219
|
+
const ctx = (0, import_react4.useContext)(phoneConfigContext());
|
|
2712
3220
|
return {
|
|
2713
3221
|
defaultCountry: ctx?.defaultCountry ?? DEFAULT_PHONE_COUNTRY,
|
|
2714
3222
|
display: ctx?.display ?? "national"
|
|
@@ -2719,10 +3227,10 @@ function usePhoneField(opts = {}) {
|
|
|
2719
3227
|
const country = opts.country ?? cfg.defaultCountry;
|
|
2720
3228
|
const display = opts.display ?? cfg.display;
|
|
2721
3229
|
const { onValueChange } = opts;
|
|
2722
|
-
const [value, setValue] = (0,
|
|
2723
|
-
const [touched, setTouched] = (0,
|
|
2724
|
-
const parsed = (0,
|
|
2725
|
-
const onChange = (0,
|
|
3230
|
+
const [value, setValue] = (0, import_react4.useState)(() => formatPhoneAsTyped(opts.defaultValue ?? "", country));
|
|
3231
|
+
const [touched, setTouched] = (0, import_react4.useState)(false);
|
|
3232
|
+
const parsed = (0, import_react4.useMemo)(() => parsePhone(value, country), [value, country]);
|
|
3233
|
+
const onChange = (0, import_react4.useCallback)(
|
|
2726
3234
|
(event) => {
|
|
2727
3235
|
const next = formatPhoneAsTyped(event.target.value, country);
|
|
2728
3236
|
setValue(next);
|
|
@@ -2730,7 +3238,7 @@ function usePhoneField(opts = {}) {
|
|
|
2730
3238
|
},
|
|
2731
3239
|
[country, onValueChange]
|
|
2732
3240
|
);
|
|
2733
|
-
const onBlur = (0,
|
|
3241
|
+
const onBlur = (0, import_react4.useCallback)(
|
|
2734
3242
|
(_event) => {
|
|
2735
3243
|
setTouched(true);
|
|
2736
3244
|
setValue((current) => {
|
|
@@ -2750,11 +3258,11 @@ function usePhoneField(opts = {}) {
|
|
|
2750
3258
|
inputProps: { value, onChange, onBlur, type: "tel", inputMode: "tel", autoComplete: "tel" }
|
|
2751
3259
|
};
|
|
2752
3260
|
}
|
|
2753
|
-
var PhoneField = (0,
|
|
3261
|
+
var PhoneField = (0, import_react4.forwardRef)(function PhoneField2({ country, value, defaultValue, onChange, onE164Change, ...rest }, ref) {
|
|
2754
3262
|
const cfg = usePhoneConfig();
|
|
2755
3263
|
const resolvedCountry = country ?? cfg.defaultCountry;
|
|
2756
3264
|
const isControlled = value !== void 0;
|
|
2757
|
-
const [internal, setInternal] = (0,
|
|
3265
|
+
const [internal, setInternal] = (0, import_react4.useState)(
|
|
2758
3266
|
() => formatPhoneAsTyped(defaultValue ?? "", resolvedCountry)
|
|
2759
3267
|
);
|
|
2760
3268
|
const handleChange = (event) => {
|
|
@@ -2782,15 +3290,25 @@ var PhoneField = (0, import_react3.forwardRef)(function PhoneField2({ country, v
|
|
|
2782
3290
|
// src/factory.tsx
|
|
2783
3291
|
var import_jsx_runtime5 = require("react/jsx-runtime");
|
|
2784
3292
|
function PageViewTracker({ fire }) {
|
|
2785
|
-
const pathname = (0,
|
|
2786
|
-
const searchParams = (0,
|
|
2787
|
-
(0,
|
|
3293
|
+
const pathname = (0, import_navigation3.usePathname)();
|
|
3294
|
+
const searchParams = (0, import_navigation3.useSearchParams)();
|
|
3295
|
+
(0, import_react5.useEffect)(() => {
|
|
2788
3296
|
fire();
|
|
2789
3297
|
}, [pathname, searchParams, fire]);
|
|
2790
3298
|
return null;
|
|
2791
3299
|
}
|
|
2792
3300
|
function createTracking(options) {
|
|
2793
|
-
const {
|
|
3301
|
+
const {
|
|
3302
|
+
apiKey,
|
|
3303
|
+
endpoint,
|
|
3304
|
+
triggers,
|
|
3305
|
+
environment,
|
|
3306
|
+
gtagIds,
|
|
3307
|
+
debug,
|
|
3308
|
+
phone,
|
|
3309
|
+
conversionConfig,
|
|
3310
|
+
trackingConfig
|
|
3311
|
+
} = options;
|
|
2794
3312
|
if (!apiKey || !endpoint) {
|
|
2795
3313
|
if (apiKey || endpoint) {
|
|
2796
3314
|
console.warn(
|
|
@@ -2810,12 +3328,12 @@ function createTracking(options) {
|
|
|
2810
3328
|
useTracking: () => noopClient
|
|
2811
3329
|
};
|
|
2812
3330
|
}
|
|
2813
|
-
const TrackingContext = (0,
|
|
3331
|
+
const TrackingContext = (0, import_react5.createContext)(null);
|
|
2814
3332
|
const specificPageVisitConfig = triggers.automatic.specific_page_visit ?? null;
|
|
2815
3333
|
const timeOnSiteConfig = triggers.automatic.time_on_site ?? null;
|
|
2816
3334
|
function TrackingProvider({ children }) {
|
|
2817
|
-
const pathname = (0,
|
|
2818
|
-
const client = (0,
|
|
3335
|
+
const pathname = (0, import_navigation3.usePathname)();
|
|
3336
|
+
const client = (0, import_react5.useMemo)(
|
|
2819
3337
|
() => createTypedClient(
|
|
2820
3338
|
getOrCreateTrackingClient({
|
|
2821
3339
|
apiKey,
|
|
@@ -2833,18 +3351,18 @@ function createTracking(options) {
|
|
|
2833
3351
|
),
|
|
2834
3352
|
[]
|
|
2835
3353
|
);
|
|
2836
|
-
const conversionStore = (0,
|
|
2837
|
-
() => conversionConfig ? resolveConversionConfig({
|
|
3354
|
+
const conversionStore = (0, import_react5.useMemo)(
|
|
3355
|
+
() => trackingConfig ? getTrackingConfigRuntime(trackingConfig) : conversionConfig ? resolveConversionConfig({
|
|
2838
3356
|
cdnUrl: conversionConfig.cdnUrl,
|
|
2839
3357
|
baked: conversionConfig.baked
|
|
2840
3358
|
}) : null,
|
|
2841
3359
|
[]
|
|
2842
3360
|
);
|
|
2843
|
-
const wrapClient = (0,
|
|
3361
|
+
const wrapClient = (0, import_react5.useMemo)(() => {
|
|
2844
3362
|
const autoFire = conversionStore ? createConversionAutoFire(conversionStore) : null;
|
|
2845
3363
|
return (c) => autoFire ? withConversionAutoFire(c, autoFire) : c;
|
|
2846
3364
|
}, [conversionStore]);
|
|
2847
|
-
const firePageView = (0,
|
|
3365
|
+
const firePageView = (0, import_react5.useCallback)(() => {
|
|
2848
3366
|
const rawClient = getOrCreateTrackingClient({
|
|
2849
3367
|
apiKey,
|
|
2850
3368
|
endpoint,
|
|
@@ -2856,9 +3374,10 @@ function createTracking(options) {
|
|
|
2856
3374
|
debug
|
|
2857
3375
|
});
|
|
2858
3376
|
fireManualPageView(wrapClient(rawClient));
|
|
3377
|
+
if (trackingConfig) getTrackingConfigRuntime(trackingConfig).queuePageView();
|
|
2859
3378
|
}, [wrapClient]);
|
|
2860
|
-
const firedPagesRef = (0,
|
|
2861
|
-
(0,
|
|
3379
|
+
const firedPagesRef = (0, import_react5.useRef)(/* @__PURE__ */ new Set());
|
|
3380
|
+
(0, import_react5.useEffect)(() => {
|
|
2862
3381
|
if (!specificPageVisitConfig || pathname === null) return;
|
|
2863
3382
|
const rawClient = getOrCreateTrackingClient({
|
|
2864
3383
|
apiKey,
|
|
@@ -2888,8 +3407,8 @@ function createTracking(options) {
|
|
|
2888
3407
|
}
|
|
2889
3408
|
}, [pathname]);
|
|
2890
3409
|
const multiPageSessionConfig = triggers.automatic.multi_page_session ?? null;
|
|
2891
|
-
const mpsLastPathRef = (0,
|
|
2892
|
-
(0,
|
|
3410
|
+
const mpsLastPathRef = (0, import_react5.useRef)(null);
|
|
3411
|
+
(0, import_react5.useEffect)(() => {
|
|
2893
3412
|
if (!multiPageSessionConfig || pathname === null) return;
|
|
2894
3413
|
if (pathname === mpsLastPathRef.current) return;
|
|
2895
3414
|
mpsLastPathRef.current = pathname;
|
|
@@ -2936,7 +3455,7 @@ function createTracking(options) {
|
|
|
2936
3455
|
} catch {
|
|
2937
3456
|
}
|
|
2938
3457
|
}, [pathname]);
|
|
2939
|
-
(0,
|
|
3458
|
+
(0, import_react5.useEffect)(() => {
|
|
2940
3459
|
const rawClient = getOrCreateTrackingClient({
|
|
2941
3460
|
apiKey,
|
|
2942
3461
|
endpoint,
|
|
@@ -2979,12 +3498,12 @@ function createTracking(options) {
|
|
|
2979
3498
|
});
|
|
2980
3499
|
}, []);
|
|
2981
3500
|
return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(TrackingContext.Provider, { value: client, children: [
|
|
2982
|
-
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
3501
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_react5.Suspense, { fallback: null, children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(PageViewTracker, { fire: firePageView }) }),
|
|
2983
3502
|
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(PhoneConfigProvider, { value: phone ?? null, children })
|
|
2984
3503
|
] });
|
|
2985
3504
|
}
|
|
2986
3505
|
function useTracking() {
|
|
2987
|
-
const client = (0,
|
|
3506
|
+
const client = (0, import_react5.useContext)(TrackingContext);
|
|
2988
3507
|
if (client === null) {
|
|
2989
3508
|
throw new Error(
|
|
2990
3509
|
"useTracking must be called inside a <TrackingProvider> returned by createTracking()"
|
|
@@ -3001,6 +3520,7 @@ function createTracking(options) {
|
|
|
3001
3520
|
ConsentBanner,
|
|
3002
3521
|
DEFAULT_DECLINE_TTL_DAYS,
|
|
3003
3522
|
DEFAULT_PHONE_COUNTRY,
|
|
3523
|
+
EVENT_SEMANTICS,
|
|
3004
3524
|
GoogleAdsTracking,
|
|
3005
3525
|
NAMED_RANGES,
|
|
3006
3526
|
PhoneField,
|
|
@@ -3021,6 +3541,8 @@ function createTracking(options) {
|
|
|
3021
3541
|
fromMinor,
|
|
3022
3542
|
getConsentChoice,
|
|
3023
3543
|
getConsentState,
|
|
3544
|
+
getEventSemantics,
|
|
3545
|
+
getTrackingConfigRuntime,
|
|
3024
3546
|
onConsentChange,
|
|
3025
3547
|
optIn,
|
|
3026
3548
|
optOut,
|