@xenia-martech/sdk-js 1.2.0-dev.3 → 1.2.0-dev.4
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 +106 -2
- package/dist/{actions-Bhji05Pe.d.cts → actions-C18Kv-4w.d.cts} +35 -0
- package/dist/{actions-Bhji05Pe.d.ts → actions-C18Kv-4w.d.ts} +35 -0
- package/dist/{api-ByoECRhR.d.ts → api-0IycTYJR.d.cts} +1 -1
- package/dist/{api-BqSvWgBZ.d.cts → api-COXWLL5L.d.ts} +1 -1
- package/dist/{chunk-TRRMMPBH.cjs → chunk-2DR5AVPX.cjs} +10 -45
- package/dist/chunk-2DR5AVPX.cjs.map +1 -0
- package/dist/chunk-BQ5PJCZ7.js +179 -0
- package/dist/chunk-BQ5PJCZ7.js.map +1 -0
- package/dist/{chunk-INM2SPH4.js → chunk-GWRDZIVA.js} +4 -36
- package/dist/chunk-GWRDZIVA.js.map +1 -0
- package/dist/{chunk-TGG5Y4QD.js → chunk-LW5AEJ6K.js} +5 -179
- package/dist/chunk-LW5AEJ6K.js.map +1 -0
- package/dist/chunk-N5M4PI7C.cjs +192 -0
- package/dist/chunk-N5M4PI7C.cjs.map +1 -0
- package/dist/chunk-OMMIRSPK.js +336 -0
- package/dist/chunk-OMMIRSPK.js.map +1 -0
- package/dist/chunk-QEYONKWY.js +42 -0
- package/dist/chunk-QEYONKWY.js.map +1 -0
- package/dist/{chunk-YZG6JR4E.cjs → chunk-RWE2OCQL.cjs} +5 -190
- package/dist/chunk-RWE2OCQL.cjs.map +1 -0
- package/dist/chunk-WHGK55JP.cjs +46 -0
- package/dist/chunk-WHGK55JP.cjs.map +1 -0
- package/dist/chunk-YLMMTKQL.cjs +358 -0
- package/dist/chunk-YLMMTKQL.cjs.map +1 -0
- package/dist/client/index.cjs +304 -26
- package/dist/client/index.cjs.map +1 -1
- package/dist/client/index.d.cts +15 -4
- package/dist/client/index.d.ts +15 -4
- package/dist/client/index.js +304 -26
- package/dist/client/index.js.map +1 -1
- package/dist/client/react/index.cjs +712 -26
- package/dist/client/react/index.cjs.map +1 -1
- package/dist/client/react/index.d.cts +32 -3
- package/dist/client/react/index.d.ts +32 -3
- package/dist/client/react/index.js +713 -29
- package/dist/client/react/index.js.map +1 -1
- package/dist/cms/contentful.d.cts +2 -1
- package/dist/cms/contentful.d.ts +2 -1
- package/dist/consent-Cemn91j3.d.ts +248 -0
- package/dist/consent-DgWdjsSy.d.cts +248 -0
- package/dist/{cookie-names-Bxd-VNyJ.d.ts → cookie-names-BvUaW3uo.d.ts} +1 -1
- package/dist/{cookie-names-RXVfGYPE.d.cts → cookie-names-CnL65Oly.d.cts} +1 -1
- package/dist/edge/index.cjs +280 -0
- package/dist/edge/index.cjs.map +1 -0
- package/dist/edge/index.d.cts +61 -0
- package/dist/edge/index.d.ts +61 -0
- package/dist/edge/index.js +191 -0
- package/dist/edge/index.js.map +1 -0
- package/dist/index.cjs +41 -40
- package/dist/index.d.cts +5 -4
- package/dist/index.d.ts +5 -4
- package/dist/index.js +2 -1
- package/dist/next/index.cjs +320 -15
- package/dist/next/index.cjs.map +1 -1
- package/dist/next/index.d.cts +180 -6
- package/dist/next/index.d.ts +180 -6
- package/dist/next/index.js +309 -9
- package/dist/next/index.js.map +1 -1
- package/dist/personalize-content-c2DDAV6g.d.ts +140 -0
- package/dist/personalize-content-ghZPKlf1.d.cts +140 -0
- package/dist/server/index.cjs +34 -32
- package/dist/server/index.cjs.map +1 -1
- package/dist/server/index.d.cts +6 -5
- package/dist/server/index.d.ts +6 -5
- package/dist/server/index.js +5 -3
- package/dist/server/index.js.map +1 -1
- package/dist/{personalize-content-Bf6YZdkZ.d.cts → types-DgVHS4Qw.d.cts} +7 -138
- package/dist/{personalize-content-Bf6YZdkZ.d.ts → types-DgVHS4Qw.d.ts} +7 -138
- package/dist/xenia-sdk.iife.js +3 -3
- package/dist/xenia-sdk.iife.js.map +1 -1
- package/dist/{xenia-server-MlI3Khya.d.cts → xenia-server-C3O-cWQT.d.cts} +3 -2
- package/dist/{xenia-server-CcjQenru.d.ts → xenia-server-DV8Sjmoo.d.ts} +3 -2
- package/package.json +14 -1
- package/dist/chunk-INM2SPH4.js.map +0 -1
- package/dist/chunk-TGG5Y4QD.js.map +0 -1
- package/dist/chunk-TRRMMPBH.cjs.map +0 -1
- package/dist/chunk-YZG6JR4E.cjs.map +0 -1
package/dist/client/index.cjs
CHANGED
|
@@ -1090,17 +1090,22 @@ function parseDataAttribute(value) {
|
|
|
1090
1090
|
return { eventId, interactionType };
|
|
1091
1091
|
}
|
|
1092
1092
|
function readSlotMeta(el) {
|
|
1093
|
-
const
|
|
1094
|
-
|
|
1095
|
-
const contentEntryId =
|
|
1096
|
-
|
|
1097
|
-
const personalized =
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1093
|
+
const attr = (name) => el.getAttribute(`data-xenia-${name}`) ?? void 0;
|
|
1094
|
+
const contentKey = attr("content-key");
|
|
1095
|
+
const contentEntryId = attr("content-entry-id");
|
|
1096
|
+
if (!contentKey && !contentEntryId) return null;
|
|
1097
|
+
const personalized = attr("personalized");
|
|
1098
|
+
const meta2 = {};
|
|
1099
|
+
const slot = attr("slot");
|
|
1100
|
+
if (slot) meta2.slot = slot;
|
|
1101
|
+
if (contentKey) meta2.contentKey = contentKey;
|
|
1102
|
+
if (contentEntryId) meta2.contentEntryId = contentEntryId;
|
|
1103
|
+
const baseEntryId = attr("base-entry-id");
|
|
1104
|
+
if (baseEntryId) meta2.baseEntryId = baseEntryId;
|
|
1105
|
+
if (personalized != null) meta2.personalized = personalized === "true";
|
|
1106
|
+
const bucket = attr("bucket");
|
|
1107
|
+
if (bucket) meta2.bucket = bucket;
|
|
1108
|
+
return meta2;
|
|
1104
1109
|
}
|
|
1105
1110
|
var AutoTracker = class {
|
|
1106
1111
|
opts;
|
|
@@ -1161,8 +1166,12 @@ var AutoTracker = class {
|
|
|
1161
1166
|
if (this.opts.fireSlot) {
|
|
1162
1167
|
const slot = target.closest("[data-xenia-slot]");
|
|
1163
1168
|
if (slot) {
|
|
1164
|
-
const
|
|
1165
|
-
|
|
1169
|
+
const meta2 = readSlotMeta(slot);
|
|
1170
|
+
const found = target.closest("a, button");
|
|
1171
|
+
const control = found && slot.contains(found) ? found : null;
|
|
1172
|
+
if (!control && this.opts.slotClicks === "controls") return;
|
|
1173
|
+
const label = control ? (control.textContent ?? "").trim().slice(0, 60) : "";
|
|
1174
|
+
if (meta2 && !this.throttled(slot)) this.opts.fireSlot("click", label ? { ...meta2, label } : meta2);
|
|
1166
1175
|
}
|
|
1167
1176
|
}
|
|
1168
1177
|
};
|
|
@@ -1170,24 +1179,24 @@ var AutoTracker = class {
|
|
|
1170
1179
|
}
|
|
1171
1180
|
observeSlot(el) {
|
|
1172
1181
|
if (!this.opts.fireSlot || this.slotObservers.has(el)) return;
|
|
1173
|
-
const
|
|
1174
|
-
if (!
|
|
1182
|
+
const meta2 = readSlotMeta(el);
|
|
1183
|
+
if (!meta2) return;
|
|
1175
1184
|
const fireSlot = this.opts.fireSlot;
|
|
1176
1185
|
const { threshold, rootMargin } = this.opts.viewport;
|
|
1177
1186
|
const observer = new IntersectionObserver(
|
|
1178
1187
|
(entries) => {
|
|
1179
1188
|
for (const entry of entries) {
|
|
1180
|
-
|
|
1181
|
-
if (this.slotImpressed.has(el)) continue;
|
|
1189
|
+
const inView = entry.isIntersecting && (entry.intersectionRatio >= threshold || entry.intersectionRect.height >= window.innerHeight / 2);
|
|
1190
|
+
if (!inView || this.slotImpressed.has(el)) continue;
|
|
1182
1191
|
this.slotImpressed.add(el);
|
|
1183
|
-
fireSlot("impression",
|
|
1192
|
+
fireSlot("impression", meta2);
|
|
1184
1193
|
observer.disconnect();
|
|
1185
1194
|
this.slotObservers.delete(el);
|
|
1186
1195
|
}
|
|
1187
1196
|
},
|
|
1188
|
-
{ threshold, rootMargin }
|
|
1197
|
+
{ threshold: [.../* @__PURE__ */ new Set([0.1, 0.25, threshold])].sort(), rootMargin }
|
|
1189
1198
|
);
|
|
1190
|
-
observer.observe(el);
|
|
1199
|
+
observer.observe(boxOf(el));
|
|
1191
1200
|
this.slotObservers.set(el, observer);
|
|
1192
1201
|
}
|
|
1193
1202
|
observeViewport(el, eventId) {
|
|
@@ -1241,6 +1250,235 @@ var AutoTracker = class {
|
|
|
1241
1250
|
this.mutationObserver.observe(document.body, { childList: true, subtree: true });
|
|
1242
1251
|
}
|
|
1243
1252
|
};
|
|
1253
|
+
function boxOf(el) {
|
|
1254
|
+
if (typeof getComputedStyle !== "function") return el;
|
|
1255
|
+
return getComputedStyle(el).display === "contents" ? el.firstElementChild ?? el : el;
|
|
1256
|
+
}
|
|
1257
|
+
|
|
1258
|
+
// src/edge/trail.ts
|
|
1259
|
+
var MAX_TRAIL = 8;
|
|
1260
|
+
var TRAIL_MAX_AGE_S = 30 * 60;
|
|
1261
|
+
function trailCookieName(prefix = DEFAULT_PREFIX) {
|
|
1262
|
+
return `${prefix}_trail`;
|
|
1263
|
+
}
|
|
1264
|
+
var decode = (v) => {
|
|
1265
|
+
try {
|
|
1266
|
+
return decodeURIComponent(v);
|
|
1267
|
+
} catch {
|
|
1268
|
+
return v;
|
|
1269
|
+
}
|
|
1270
|
+
};
|
|
1271
|
+
function readTrail(value) {
|
|
1272
|
+
if (!value) return [];
|
|
1273
|
+
try {
|
|
1274
|
+
const parsed = JSON.parse(decode(value));
|
|
1275
|
+
if (!Array.isArray(parsed)) return [];
|
|
1276
|
+
return parsed.filter((t) => Array.isArray(t) && typeof t[0] === "string" && typeof t[1] === "number").slice(0, MAX_TRAIL).map(
|
|
1277
|
+
([path, at, seconds]) => typeof seconds === "number" && seconds >= 0 && seconds <= 86400 ? { path, at, seconds } : { path, at }
|
|
1278
|
+
);
|
|
1279
|
+
} catch {
|
|
1280
|
+
return [];
|
|
1281
|
+
}
|
|
1282
|
+
}
|
|
1283
|
+
function withView(trail, path, now = Date.now()) {
|
|
1284
|
+
if (trail[0]?.path === path) return trail;
|
|
1285
|
+
const earlier = trail.find((t) => t.path === path);
|
|
1286
|
+
return [{ path, at: earlier?.at ?? now }, ...trail.filter((t) => t.path !== path)].slice(0, MAX_TRAIL);
|
|
1287
|
+
}
|
|
1288
|
+
function trailCookieValue(trail) {
|
|
1289
|
+
return JSON.stringify(
|
|
1290
|
+
trail.slice(0, MAX_TRAIL).map((t) => t.seconds === void 0 ? [t.path, t.at] : [t.path, t.at, t.seconds])
|
|
1291
|
+
);
|
|
1292
|
+
}
|
|
1293
|
+
|
|
1294
|
+
// src/client/page-tracking.ts
|
|
1295
|
+
var SETTLE_MS = 3e3;
|
|
1296
|
+
var POLL_MS = 50;
|
|
1297
|
+
var MIN_VISIT_MS = 250;
|
|
1298
|
+
var listeners = /* @__PURE__ */ new Set();
|
|
1299
|
+
var restore = null;
|
|
1300
|
+
function watchNavigation(listener) {
|
|
1301
|
+
listeners.add(listener);
|
|
1302
|
+
if (!restore) {
|
|
1303
|
+
const { pushState, replaceState } = history;
|
|
1304
|
+
const notify = () => listeners.forEach((l) => {
|
|
1305
|
+
try {
|
|
1306
|
+
l();
|
|
1307
|
+
} catch {
|
|
1308
|
+
}
|
|
1309
|
+
});
|
|
1310
|
+
const ownPush = function(...args) {
|
|
1311
|
+
pushState.apply(this, args);
|
|
1312
|
+
notify();
|
|
1313
|
+
};
|
|
1314
|
+
const ownReplace = function(...args) {
|
|
1315
|
+
replaceState.apply(this, args);
|
|
1316
|
+
notify();
|
|
1317
|
+
};
|
|
1318
|
+
history.pushState = ownPush;
|
|
1319
|
+
history.replaceState = ownReplace;
|
|
1320
|
+
window.addEventListener("popstate", notify);
|
|
1321
|
+
restore = () => {
|
|
1322
|
+
if (history.pushState !== ownPush || history.replaceState !== ownReplace) return false;
|
|
1323
|
+
history.pushState = pushState;
|
|
1324
|
+
history.replaceState = replaceState;
|
|
1325
|
+
window.removeEventListener("popstate", notify);
|
|
1326
|
+
return true;
|
|
1327
|
+
};
|
|
1328
|
+
}
|
|
1329
|
+
return () => {
|
|
1330
|
+
listeners.delete(listener);
|
|
1331
|
+
if (!listeners.size && restore?.()) restore = null;
|
|
1332
|
+
};
|
|
1333
|
+
}
|
|
1334
|
+
var meta = (name) => document.querySelector(`meta[name="${name}"]`)?.content || void 0;
|
|
1335
|
+
function startPageTracking(client, opts) {
|
|
1336
|
+
let path = location.pathname;
|
|
1337
|
+
let lastTitle = null;
|
|
1338
|
+
let timer;
|
|
1339
|
+
let view = { path, since: visibleNow(), sent: false };
|
|
1340
|
+
function visibleNow() {
|
|
1341
|
+
return document.visibilityState === "visible" ? Date.now() : null;
|
|
1342
|
+
}
|
|
1343
|
+
function sendView() {
|
|
1344
|
+
clearTimeout(timer);
|
|
1345
|
+
const started = Date.now();
|
|
1346
|
+
const tick = () => {
|
|
1347
|
+
const settled = document.title !== "" && document.title !== lastTitle;
|
|
1348
|
+
if (!settled && Date.now() - started < SETTLE_MS) {
|
|
1349
|
+
timer = setTimeout(tick, POLL_MS);
|
|
1350
|
+
return;
|
|
1351
|
+
}
|
|
1352
|
+
const params = new URLSearchParams(location.search);
|
|
1353
|
+
const searchTerm = (params.get("utm_term") ?? params.get("q") ?? "").slice(0, 120);
|
|
1354
|
+
const category = meta("xenia-category");
|
|
1355
|
+
lastTitle = document.title;
|
|
1356
|
+
client.track("pageView", {
|
|
1357
|
+
title: (meta("xenia-title") ?? document.title).slice(0, 160),
|
|
1358
|
+
...searchTerm ? { searchTerm } : {},
|
|
1359
|
+
...category ? { category } : {}
|
|
1360
|
+
});
|
|
1361
|
+
};
|
|
1362
|
+
timer = setTimeout(tick, 0);
|
|
1363
|
+
}
|
|
1364
|
+
function leave() {
|
|
1365
|
+
if (view.sent || view.since === null || Date.now() - view.since < MIN_VISIT_MS) return;
|
|
1366
|
+
view.sent = true;
|
|
1367
|
+
client.track("page_leave", { path: view.path, durationMs: Date.now() - view.since });
|
|
1368
|
+
}
|
|
1369
|
+
const onNavigate = () => {
|
|
1370
|
+
if (location.pathname === path) return;
|
|
1371
|
+
if (opts.pageLeave) leave();
|
|
1372
|
+
path = location.pathname;
|
|
1373
|
+
view = { path, since: visibleNow(), sent: false };
|
|
1374
|
+
if (opts.pageViews) sendView();
|
|
1375
|
+
};
|
|
1376
|
+
const onVisibility = () => {
|
|
1377
|
+
if (document.visibilityState === "hidden") leave();
|
|
1378
|
+
else if (view.since === null) view.since = Date.now();
|
|
1379
|
+
};
|
|
1380
|
+
const unwatch = watchNavigation(onNavigate);
|
|
1381
|
+
if (opts.pageLeave) document.addEventListener("visibilitychange", onVisibility);
|
|
1382
|
+
if (opts.pageViews) sendView();
|
|
1383
|
+
return () => {
|
|
1384
|
+
clearTimeout(timer);
|
|
1385
|
+
unwatch();
|
|
1386
|
+
document.removeEventListener("visibilitychange", onVisibility);
|
|
1387
|
+
};
|
|
1388
|
+
}
|
|
1389
|
+
|
|
1390
|
+
// src/client/engagement.ts
|
|
1391
|
+
var rootOf = (opt, fallback) => typeof opt === "object" && opt.root || fallback;
|
|
1392
|
+
function readCookie(name) {
|
|
1393
|
+
return document.cookie.match(new RegExp(`(?:^|;\\s*)${name}=([^;]*)`))?.[1];
|
|
1394
|
+
}
|
|
1395
|
+
function writeCookie(name, value) {
|
|
1396
|
+
try {
|
|
1397
|
+
document.cookie = `${name}=${encodeURIComponent(value)}; path=/; max-age=${TRAIL_MAX_AGE_S}; samesite=lax`;
|
|
1398
|
+
} catch {
|
|
1399
|
+
}
|
|
1400
|
+
}
|
|
1401
|
+
function sectionOf(link) {
|
|
1402
|
+
if (!link || link.origin !== location.origin) return void 0;
|
|
1403
|
+
return link.pathname.split("/").filter(Boolean)[0];
|
|
1404
|
+
}
|
|
1405
|
+
function cardImage(target, root) {
|
|
1406
|
+
let el = target;
|
|
1407
|
+
for (let depth = 0; el && depth < 5 && el !== root; depth++, el = el.parentElement) {
|
|
1408
|
+
const imgs = [...el.querySelectorAll("img")].filter((i) => i.alt.trim());
|
|
1409
|
+
if (!imgs.length) continue;
|
|
1410
|
+
return new Set(imgs.map((i) => i.alt.trim())).size === 1 ? imgs[0] : null;
|
|
1411
|
+
}
|
|
1412
|
+
return null;
|
|
1413
|
+
}
|
|
1414
|
+
function startEngagement(client, opts) {
|
|
1415
|
+
const trailName = trailCookieName(opts.prefix);
|
|
1416
|
+
const clicksName = `${opts.prefix}_clicks`;
|
|
1417
|
+
const visibleNow = () => document.visibilityState === "visible" ? Date.now() : null;
|
|
1418
|
+
let current = { path: location.pathname, since: visibleNow(), stamped: false };
|
|
1419
|
+
const stamp = () => {
|
|
1420
|
+
if (current.stamped || !opts.trail || !opts.analyticsAllowed() || current.since === null) return;
|
|
1421
|
+
current.stamped = true;
|
|
1422
|
+
const seconds = Math.round((Date.now() - current.since) / 100) / 10;
|
|
1423
|
+
const trail = readTrail(readCookie(trailName));
|
|
1424
|
+
const i = trail.findIndex((t) => t.path === current.path);
|
|
1425
|
+
if (i < 0) return;
|
|
1426
|
+
trail[i] = { ...trail[i], seconds };
|
|
1427
|
+
writeCookie(trailName, trailCookieValue(trail));
|
|
1428
|
+
};
|
|
1429
|
+
const onNavigate = () => {
|
|
1430
|
+
if (location.pathname === current.path) return;
|
|
1431
|
+
stamp();
|
|
1432
|
+
current = { path: location.pathname, since: visibleNow(), stamped: false };
|
|
1433
|
+
if (!opts.trail || !opts.analyticsAllowed()) return;
|
|
1434
|
+
const trail = readTrail(readCookie(trailName));
|
|
1435
|
+
const next = withView(trail, current.path);
|
|
1436
|
+
if (next !== trail) writeCookie(trailName, trailCookieValue(next));
|
|
1437
|
+
};
|
|
1438
|
+
const onVisible = () => {
|
|
1439
|
+
if (document.visibilityState === "visible" && current.since === null) current.since = Date.now();
|
|
1440
|
+
};
|
|
1441
|
+
const onLeaveClick = (e) => {
|
|
1442
|
+
const link = e.target?.closest?.("a[href]");
|
|
1443
|
+
if (link && link.origin === location.origin && link.pathname !== location.pathname) stamp();
|
|
1444
|
+
};
|
|
1445
|
+
const bump = () => {
|
|
1446
|
+
if (!opts.analyticsAllowed()) return;
|
|
1447
|
+
writeCookie(clicksName, String((Number(readCookie(clicksName)) || 0) + 1));
|
|
1448
|
+
};
|
|
1449
|
+
const navRoot = rootOf(opts.navClicks, "header");
|
|
1450
|
+
const contentRoot = rootOf(opts.contentClicks, "main");
|
|
1451
|
+
const onClick = (e) => {
|
|
1452
|
+
const target = e.target;
|
|
1453
|
+
if (!target?.closest) return;
|
|
1454
|
+
const nav = opts.navClicks ? target.closest(`${navRoot} a[href]`) : null;
|
|
1455
|
+
if (nav) {
|
|
1456
|
+
const category2 = sectionOf(nav);
|
|
1457
|
+
if (!category2) return;
|
|
1458
|
+
client.track("nav_click", { category: category2, label: (nav.textContent ?? "").trim().slice(0, 60) });
|
|
1459
|
+
bump();
|
|
1460
|
+
return;
|
|
1461
|
+
}
|
|
1462
|
+
const root = opts.contentClicks ? target.closest(contentRoot) : null;
|
|
1463
|
+
if (!root) return;
|
|
1464
|
+
const img = target.closest("img") ?? cardImage(target, root);
|
|
1465
|
+
const label = (img?.alt ?? "").trim().slice(0, 160);
|
|
1466
|
+
if (!label) return;
|
|
1467
|
+
const category = sectionOf(target.closest("a[href]"));
|
|
1468
|
+
client.track("content_click", category ? { label, category } : { label });
|
|
1469
|
+
bump();
|
|
1470
|
+
};
|
|
1471
|
+
const unwatch = watchNavigation(onNavigate);
|
|
1472
|
+
window.addEventListener("click", onLeaveClick, { capture: true });
|
|
1473
|
+
document.addEventListener("visibilitychange", onVisible);
|
|
1474
|
+
if (opts.contentClicks || opts.navClicks) document.addEventListener("click", onClick, { capture: true });
|
|
1475
|
+
return () => {
|
|
1476
|
+
unwatch();
|
|
1477
|
+
window.removeEventListener("click", onLeaveClick, { capture: true });
|
|
1478
|
+
document.removeEventListener("visibilitychange", onVisible);
|
|
1479
|
+
document.removeEventListener("click", onClick, { capture: true });
|
|
1480
|
+
};
|
|
1481
|
+
}
|
|
1244
1482
|
|
|
1245
1483
|
// src/server/cookies.ts
|
|
1246
1484
|
function parseCookies(header) {
|
|
@@ -1251,7 +1489,12 @@ function parseCookies(header) {
|
|
|
1251
1489
|
if (idx === -1) continue;
|
|
1252
1490
|
const key = part.slice(0, idx).trim();
|
|
1253
1491
|
const value = part.slice(idx + 1).trim();
|
|
1254
|
-
if (key)
|
|
1492
|
+
if (!key) continue;
|
|
1493
|
+
try {
|
|
1494
|
+
out[key] = decodeURIComponent(value);
|
|
1495
|
+
} catch {
|
|
1496
|
+
out[key] = value;
|
|
1497
|
+
}
|
|
1255
1498
|
}
|
|
1256
1499
|
return out;
|
|
1257
1500
|
}
|
|
@@ -1325,6 +1568,14 @@ var Identity = class {
|
|
|
1325
1568
|
const storageKey = this.key("device_id");
|
|
1326
1569
|
try {
|
|
1327
1570
|
const cached = localStorage.getItem(storageKey);
|
|
1571
|
+
const fromServer = this.cookie?.authoritative ? this.readVisitorCookie() : null;
|
|
1572
|
+
const effective = localStorage.getItem(this.key("visitor_id")) ?? cached;
|
|
1573
|
+
if (cached && fromServer && fromServer !== effective) {
|
|
1574
|
+
this.deviceId = fromServer;
|
|
1575
|
+
localStorage.setItem(storageKey, fromServer);
|
|
1576
|
+
if (localStorage.getItem(this.key("visitor_id"))) localStorage.setItem(this.key("visitor_id"), fromServer);
|
|
1577
|
+
return fromServer;
|
|
1578
|
+
}
|
|
1328
1579
|
if (cached) {
|
|
1329
1580
|
this.deviceId = cached;
|
|
1330
1581
|
this.writeVisitorCookie(this.getVisitorId() ?? cached);
|
|
@@ -1504,7 +1755,7 @@ function resolveCookieConfig(opt) {
|
|
|
1504
1755
|
const o = opt === true ? {} : opt;
|
|
1505
1756
|
const sameSite = o.sameSite ?? "Lax";
|
|
1506
1757
|
const secure = sameSite === "None" ? true : o.secure ?? false;
|
|
1507
|
-
return { sameSite, secure, maxAgeDays: o.maxAgeDays ?? 365, domain: o.domain };
|
|
1758
|
+
return { sameSite, secure, maxAgeDays: o.maxAgeDays ?? 365, domain: o.domain, authoritative: o.authoritative ?? false };
|
|
1508
1759
|
}
|
|
1509
1760
|
|
|
1510
1761
|
// src/client/widgets/form.ts
|
|
@@ -2188,6 +2439,8 @@ var XeniaClient = class {
|
|
|
2188
2439
|
renderer;
|
|
2189
2440
|
analyticsQueue;
|
|
2190
2441
|
tracker = null;
|
|
2442
|
+
stopPageTracking = null;
|
|
2443
|
+
stopEngagement = null;
|
|
2191
2444
|
/** The in-flight (or finished) boot; shared so concurrent `init()` calls start one tracker. */
|
|
2192
2445
|
booting = null;
|
|
2193
2446
|
/** Bumped by `destroy()`, so a boot still waiting on the device id knows to stop. */
|
|
@@ -2239,17 +2492,21 @@ var XeniaClient = class {
|
|
|
2239
2492
|
rootMargin: this.options.viewport?.rootMargin,
|
|
2240
2493
|
triggerOnce: this.options.viewport?.triggerOnce ?? true
|
|
2241
2494
|
},
|
|
2495
|
+
slotClicks: this.options.slotClicks,
|
|
2242
2496
|
// Declarative `data-xenia` elements fire journey triggers (immediate).
|
|
2243
2497
|
fire: (eventId, interaction) => this.track(eventId, { interactionType: interaction }, { trigger: true }),
|
|
2244
2498
|
// `[data-xenia-slot]` elements fire analytics-only personalization events.
|
|
2245
|
-
fireSlot: (interaction,
|
|
2499
|
+
fireSlot: (interaction, meta2) => {
|
|
2246
2500
|
this.track(
|
|
2247
2501
|
"customEvent",
|
|
2248
2502
|
{
|
|
2249
2503
|
customEventName: interaction === "impression" ? "personalization_impression" : "personalization_click",
|
|
2250
|
-
contentKey:
|
|
2251
|
-
contentEntryId:
|
|
2252
|
-
baseEntryId:
|
|
2504
|
+
...meta2.contentKey ? { contentKey: meta2.contentKey } : {},
|
|
2505
|
+
contentEntryId: meta2.contentEntryId ?? null,
|
|
2506
|
+
baseEntryId: meta2.baseEntryId ?? null,
|
|
2507
|
+
...meta2.bucket ? { bucket: meta2.bucket } : {},
|
|
2508
|
+
...meta2.slot ? { slot: meta2.slot } : {},
|
|
2509
|
+
...meta2.label ? { label: meta2.label } : {}
|
|
2253
2510
|
},
|
|
2254
2511
|
{ trigger: false }
|
|
2255
2512
|
);
|
|
@@ -2257,11 +2514,32 @@ var XeniaClient = class {
|
|
|
2257
2514
|
});
|
|
2258
2515
|
this.tracker.start();
|
|
2259
2516
|
}
|
|
2517
|
+
const { pageViews = false, pageLeave = false } = this.options;
|
|
2518
|
+
if (pageViews || pageLeave) {
|
|
2519
|
+
this.stopPageTracking = startPageTracking({ track: (name, props) => this.track(name, props) }, { pageViews, pageLeave });
|
|
2520
|
+
}
|
|
2521
|
+
const { trail = false, contentClicks = false, navClicks = false } = this.options;
|
|
2522
|
+
if (trail || contentClicks || navClicks) {
|
|
2523
|
+
this.stopEngagement = startEngagement(
|
|
2524
|
+
{ track: (name, props) => this.track(name, props) },
|
|
2525
|
+
{
|
|
2526
|
+
trail,
|
|
2527
|
+
contentClicks,
|
|
2528
|
+
navClicks,
|
|
2529
|
+
prefix: this.options.prefix ?? "xenia",
|
|
2530
|
+
analyticsAllowed: () => this.analyticsAllowed() && !this.dntBlocked()
|
|
2531
|
+
}
|
|
2532
|
+
);
|
|
2533
|
+
}
|
|
2260
2534
|
}
|
|
2261
2535
|
/** Tear down listeners, observers, and injected UI. */
|
|
2262
2536
|
destroy() {
|
|
2263
2537
|
this.tracker?.stop();
|
|
2264
2538
|
this.tracker = null;
|
|
2539
|
+
this.stopPageTracking?.();
|
|
2540
|
+
this.stopPageTracking = null;
|
|
2541
|
+
this.stopEngagement?.();
|
|
2542
|
+
this.stopEngagement = null;
|
|
2265
2543
|
this.renderer.destroy();
|
|
2266
2544
|
this.analyticsQueue.stop();
|
|
2267
2545
|
this.booting = null;
|