@xenia-martech/sdk-js 1.1.0 → 1.2.0-dev.2
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 +50 -0
- package/assets/coverage.json +1 -0
- package/dist/{actions-CKeiofxJ.d.cts → actions-BcdlokGO.d.cts} +192 -4
- package/dist/{actions-CKeiofxJ.d.ts → actions-BcdlokGO.d.ts} +192 -4
- package/dist/api-BEPW0Uan.d.cts +88 -0
- package/dist/api-CktVQ6Gl.d.ts +88 -0
- package/dist/{chunk-U2WBBOYT.cjs → chunk-3PYPPIUM.cjs} +35 -10
- package/dist/chunk-3PYPPIUM.cjs.map +1 -0
- package/dist/{chunk-FYSEKFIY.js → chunk-7NGJC7CF.js} +249 -3
- package/dist/chunk-7NGJC7CF.js.map +1 -0
- package/dist/{chunk-WYAMPEVV.js → chunk-DWFVDNU5.js} +28 -3
- package/dist/chunk-DWFVDNU5.js.map +1 -0
- package/dist/{chunk-JEKVN7HK.cjs → chunk-IBNIRT7J.cjs} +251 -2
- package/dist/chunk-IBNIRT7J.cjs.map +1 -0
- package/dist/{chunk-TLTG3BTC.js → chunk-OUZU4Z5B.js} +2 -2
- package/dist/{chunk-TLTG3BTC.js.map → chunk-OUZU4Z5B.js.map} +1 -1
- package/dist/{chunk-5D7SJIO4.cjs → chunk-PKWANLT7.cjs} +2 -2
- package/dist/{chunk-5D7SJIO4.cjs.map → chunk-PKWANLT7.cjs.map} +1 -1
- package/dist/client/index.cjs +314 -12
- package/dist/client/index.cjs.map +1 -1
- package/dist/client/index.d.cts +2 -2
- package/dist/client/index.d.ts +2 -2
- package/dist/client/index.js +312 -13
- package/dist/client/index.js.map +1 -1
- package/dist/client/react/index.cjs +402 -16
- package/dist/client/react/index.cjs.map +1 -1
- package/dist/client/react/index.d.cts +53 -3
- package/dist/client/react/index.d.ts +53 -3
- package/dist/client/react/index.js +396 -17
- package/dist/client/react/index.js.map +1 -1
- package/dist/cms/contentful.cjs +138 -0
- package/dist/cms/contentful.cjs.map +1 -0
- package/dist/cms/contentful.d.cts +57 -0
- package/dist/cms/contentful.d.ts +57 -0
- package/dist/cms/contentful.js +134 -0
- package/dist/cms/contentful.js.map +1 -0
- package/dist/{cookie-names-QYMiEMAq.d.cts → cookie-names-BACctagz.d.cts} +1 -1
- package/dist/{cookie-names-8FxbGKGS.d.ts → cookie-names-CLqtmGBM.d.ts} +1 -1
- package/dist/index.cjs +35 -23
- package/dist/index.d.cts +4 -3
- package/dist/index.d.ts +4 -3
- package/dist/index.js +2 -2
- package/dist/next/index.cjs +41 -9
- package/dist/next/index.cjs.map +1 -1
- package/dist/next/index.d.cts +32 -3
- package/dist/next/index.d.ts +32 -3
- package/dist/next/index.js +14 -3
- package/dist/next/index.js.map +1 -1
- package/dist/{api-URhxxi4x.d.cts → personalize-content-Clc1oMYY.d.cts} +152 -69
- package/dist/{api-URhxxi4x.d.ts → personalize-content-Clc1oMYY.d.ts} +152 -69
- package/dist/server/index.cjs +29 -17
- package/dist/server/index.d.cts +5 -4
- package/dist/server/index.d.ts +5 -4
- package/dist/server/index.js +4 -4
- package/dist/xenia-sdk.iife.js +3 -3
- package/dist/xenia-sdk.iife.js.map +1 -1
- package/dist/{xenia-server-Cti94W49.d.cts → xenia-server-BWhWD0La.d.ts} +15 -1
- package/dist/{xenia-server-DDtQp6Wi.d.ts → xenia-server-o_wDOcei.d.cts} +15 -1
- package/package.json +15 -1
- package/dist/chunk-FYSEKFIY.js.map +0 -1
- package/dist/chunk-JEKVN7HK.cjs.map +0 -1
- package/dist/chunk-U2WBBOYT.cjs.map +0 -1
- package/dist/chunk-WYAMPEVV.js.map +0 -1
package/dist/client/index.cjs
CHANGED
|
@@ -542,6 +542,25 @@ var XeniaApi = class {
|
|
|
542
542
|
});
|
|
543
543
|
return data;
|
|
544
544
|
}
|
|
545
|
+
/**
|
|
546
|
+
* Resolve personalized CMS entries by id for one visitor (≤200) —
|
|
547
|
+
* `POST /api/personalization/resolve-entries`. The page resolver
|
|
548
|
+
* ({@link personalizeContent}) calls this once per round with the runtime
|
|
549
|
+
* targets it found; callers rarely need it directly.
|
|
550
|
+
*/
|
|
551
|
+
async resolveEntries(entryIds, opts = {}) {
|
|
552
|
+
const { data } = await this.http.request({
|
|
553
|
+
method: "POST",
|
|
554
|
+
path: "/api/personalization/resolve-entries",
|
|
555
|
+
headers: await this.authHeaders(),
|
|
556
|
+
body: compact({
|
|
557
|
+
entryIds,
|
|
558
|
+
data: opts.data,
|
|
559
|
+
locale: opts.locale
|
|
560
|
+
})
|
|
561
|
+
});
|
|
562
|
+
return data;
|
|
563
|
+
}
|
|
545
564
|
};
|
|
546
565
|
|
|
547
566
|
// src/core/cookie-names.ts
|
|
@@ -656,6 +675,233 @@ function toEventId(raw) {
|
|
|
656
675
|
return digest;
|
|
657
676
|
}
|
|
658
677
|
|
|
678
|
+
// src/core/personalize-content.ts
|
|
679
|
+
var DEFAULT_TIMEOUT_MS = 300;
|
|
680
|
+
var DEFAULT_MAX_ROUNDS = 3;
|
|
681
|
+
var copyOf = (v) => v && typeof v === "object" && typeof structuredClone === "function" ? structuredClone(v) : v;
|
|
682
|
+
async function personalizeContent(payload, options) {
|
|
683
|
+
const { cms } = options;
|
|
684
|
+
const maxRounds = options.maxRounds ?? DEFAULT_MAX_ROUNDS;
|
|
685
|
+
const deadline = Date.now() + (options.timeoutMs ?? DEFAULT_TIMEOUT_MS);
|
|
686
|
+
const applied = [];
|
|
687
|
+
const report = (swap) => {
|
|
688
|
+
applied.push(swap);
|
|
689
|
+
try {
|
|
690
|
+
options.onApplied?.(swap);
|
|
691
|
+
} catch {
|
|
692
|
+
}
|
|
693
|
+
};
|
|
694
|
+
let content = payload;
|
|
695
|
+
try {
|
|
696
|
+
if (!options.inPlace && typeof structuredClone === "function") content = structuredClone(content);
|
|
697
|
+
content = cms.prepare ? cms.prepare(content) : content;
|
|
698
|
+
} catch (error) {
|
|
699
|
+
return { content: payload, applied, complete: false, error };
|
|
700
|
+
}
|
|
701
|
+
const box = { root: content };
|
|
702
|
+
const settled = /* @__PURE__ */ new WeakSet();
|
|
703
|
+
const decided = /* @__PURE__ */ new Map();
|
|
704
|
+
let frontier = [{ parent: box, key: "root", value: box.root }];
|
|
705
|
+
const settle = (target, d, round) => {
|
|
706
|
+
settled.add(target.entry);
|
|
707
|
+
cms.clearMarker?.(target.entry);
|
|
708
|
+
const items = d.items.map(copyOf);
|
|
709
|
+
if (!d.decision || items.length === 0) {
|
|
710
|
+
return deferredSlots(target, cms);
|
|
711
|
+
}
|
|
712
|
+
for (const item of items) if (item && typeof item === "object") settled.add(item);
|
|
713
|
+
const op = d.decision.op ?? target.marker.op ?? "replace";
|
|
714
|
+
const next = apply(target, op, d.decision.field ?? target.marker.field, items, cms);
|
|
715
|
+
report({
|
|
716
|
+
target: target.id,
|
|
717
|
+
kind: "runtime",
|
|
718
|
+
op,
|
|
719
|
+
entryIds: items.map((e) => cms.entryId(e)).filter((id) => !!id),
|
|
720
|
+
contentKey: d.decision.contentKey,
|
|
721
|
+
bucket: d.decision.bucket,
|
|
722
|
+
round
|
|
723
|
+
});
|
|
724
|
+
return next;
|
|
725
|
+
};
|
|
726
|
+
for (let round = 0; ; round++) {
|
|
727
|
+
const pending = walk(frontier, cms, settled, round, report, (t) => {
|
|
728
|
+
const d = decided.get(t.id);
|
|
729
|
+
return d ? settle(t, d, round) : void 0;
|
|
730
|
+
});
|
|
731
|
+
if (pending.size === 0) return { content: box.root, applied, complete: true };
|
|
732
|
+
if (!options.resolve || round >= maxRounds) {
|
|
733
|
+
return { content: box.root, applied, complete: !options.resolve };
|
|
734
|
+
}
|
|
735
|
+
progress(options, { content: box.root, round, pending: [...pending.keys()] });
|
|
736
|
+
let decisions;
|
|
737
|
+
let entriesById;
|
|
738
|
+
let queried;
|
|
739
|
+
try {
|
|
740
|
+
decisions = await withDeadline(options.resolve([...pending.keys()]), deadline);
|
|
741
|
+
({ entriesById, queried } = await withDeadline(loadReplacements(decisions, cms, options.locale), deadline));
|
|
742
|
+
} catch (error) {
|
|
743
|
+
return { content: box.root, applied, complete: false, error: error === TIMEOUT ? void 0 : error };
|
|
744
|
+
}
|
|
745
|
+
const byTarget = new Map(decisions.map((d) => [d.target, d]));
|
|
746
|
+
frontier = [];
|
|
747
|
+
for (const target of pending.values()) {
|
|
748
|
+
const decision = byTarget.get(target.id);
|
|
749
|
+
const items = decision ? decision.ids ? decision.ids.map((id) => entriesById.get(id)).filter((e) => e !== void 0) : queried.get(decision) ?? [] : [];
|
|
750
|
+
const d = { decision, items };
|
|
751
|
+
decided.set(target.id, d);
|
|
752
|
+
frontier.push(...settle(target, d, round));
|
|
753
|
+
}
|
|
754
|
+
progress(options, { content: box.root, round, pending: [] });
|
|
755
|
+
}
|
|
756
|
+
}
|
|
757
|
+
function progress(options, p) {
|
|
758
|
+
try {
|
|
759
|
+
options.onProgress?.(p);
|
|
760
|
+
} catch {
|
|
761
|
+
}
|
|
762
|
+
}
|
|
763
|
+
function walk(frontier, cms, settled, round, report, reuse) {
|
|
764
|
+
const pending = /* @__PURE__ */ new Map();
|
|
765
|
+
const seen = /* @__PURE__ */ new WeakSet();
|
|
766
|
+
const deferred = /* @__PURE__ */ new WeakSet();
|
|
767
|
+
const queue = [...frontier];
|
|
768
|
+
while (queue.length > 0) {
|
|
769
|
+
const slot = queue.shift();
|
|
770
|
+
const value = slot.value;
|
|
771
|
+
if (!value || typeof value !== "object" || deferred.has(value)) continue;
|
|
772
|
+
const id = settled.has(value) ? void 0 : cms.entryId(value);
|
|
773
|
+
const marker = id ? cms.marker(value) : null;
|
|
774
|
+
if (id && marker?.kind === "runtime") {
|
|
775
|
+
const again = reuse({ id, marker, entry: value, slots: [slot] });
|
|
776
|
+
if (again) {
|
|
777
|
+
queue.push(...again);
|
|
778
|
+
continue;
|
|
779
|
+
}
|
|
780
|
+
const existing = pending.get(id);
|
|
781
|
+
if (existing) {
|
|
782
|
+
existing.slots.push(slot);
|
|
783
|
+
} else {
|
|
784
|
+
pending.set(id, { id, marker, entry: value, slots: [slot] });
|
|
785
|
+
if (marker.op === "items" && marker.field) {
|
|
786
|
+
const held = cms.getField(value, marker.field);
|
|
787
|
+
if (held && typeof held === "object") deferred.add(held);
|
|
788
|
+
queue.push(...children(value));
|
|
789
|
+
}
|
|
790
|
+
}
|
|
791
|
+
continue;
|
|
792
|
+
}
|
|
793
|
+
if (id && marker?.kind === "static") {
|
|
794
|
+
const variants = cms.staticVariants(value).filter((v) => v && typeof v === "object");
|
|
795
|
+
settled.add(value);
|
|
796
|
+
cms.clearMarker?.(value);
|
|
797
|
+
if (variants.length > 0) {
|
|
798
|
+
for (const v of variants) settled.add(v);
|
|
799
|
+
const op = marker.op ?? "replace";
|
|
800
|
+
const target = { entry: value, slots: [slot] };
|
|
801
|
+
queue.push(...apply(target, op, marker.field, variants, cms));
|
|
802
|
+
report({
|
|
803
|
+
target: id,
|
|
804
|
+
kind: "static",
|
|
805
|
+
op,
|
|
806
|
+
entryIds: variants.map((v) => cms.entryId(v)).filter((x) => !!x),
|
|
807
|
+
round
|
|
808
|
+
});
|
|
809
|
+
continue;
|
|
810
|
+
}
|
|
811
|
+
}
|
|
812
|
+
if (seen.has(value)) continue;
|
|
813
|
+
seen.add(value);
|
|
814
|
+
queue.push(...children(value));
|
|
815
|
+
}
|
|
816
|
+
return pending;
|
|
817
|
+
}
|
|
818
|
+
function apply(target, op, field, items, cms) {
|
|
819
|
+
if (op === "items") {
|
|
820
|
+
if (field) {
|
|
821
|
+
cms.setField(target.entry, field, items);
|
|
822
|
+
return [{ parent: target.entry, key: field, value: cms.getField(target.entry, field) }];
|
|
823
|
+
}
|
|
824
|
+
if (cms.mergeItems) {
|
|
825
|
+
cms.mergeItems(target.entry, items[0]);
|
|
826
|
+
return target.slots.map((s) => ({ ...s, value: target.entry }));
|
|
827
|
+
}
|
|
828
|
+
op = "replace";
|
|
829
|
+
}
|
|
830
|
+
const next = [];
|
|
831
|
+
for (const slot of target.slots) {
|
|
832
|
+
const placed = op === "expand" && Array.isArray(slot.parent) ? items : [items[0]];
|
|
833
|
+
if (Array.isArray(slot.parent)) {
|
|
834
|
+
const index = slot.parent.indexOf(target.entry);
|
|
835
|
+
if (index < 0) continue;
|
|
836
|
+
slot.parent.splice(index, 1, ...placed);
|
|
837
|
+
} else {
|
|
838
|
+
slot.parent[slot.key] = placed[0];
|
|
839
|
+
}
|
|
840
|
+
for (const value of placed) next.push({ parent: slot.parent, key: slot.key, value });
|
|
841
|
+
}
|
|
842
|
+
return next;
|
|
843
|
+
}
|
|
844
|
+
function deferredSlots(target, cms) {
|
|
845
|
+
if (target.marker.op === "items" && target.marker.field) {
|
|
846
|
+
return [{ parent: target.entry, key: target.marker.field, value: cms.getField(target.entry, target.marker.field) }];
|
|
847
|
+
}
|
|
848
|
+
return target.slots.map((s) => ({ ...s, value: target.entry }));
|
|
849
|
+
}
|
|
850
|
+
function children(value) {
|
|
851
|
+
if (Array.isArray(value)) return value.map((v, i) => ({ parent: value, key: i, value: v }));
|
|
852
|
+
return Object.keys(value).map((k) => ({ parent: value, key: k, value: value[k] }));
|
|
853
|
+
}
|
|
854
|
+
async function loadReplacements(decisions, cms, locale) {
|
|
855
|
+
const ids = [...new Set(decisions.flatMap((d) => d.ids ?? []))];
|
|
856
|
+
const queries = decisions.filter((d) => !d.ids && d.query);
|
|
857
|
+
const [fetched, ...results] = await Promise.all([
|
|
858
|
+
ids.length > 0 ? cms.fetchEntries(ids, locale) : Promise.resolve([]),
|
|
859
|
+
...queries.map((d) => cms.queryEntries(d.query, locale).catch(() => []))
|
|
860
|
+
]);
|
|
861
|
+
const entriesById = /* @__PURE__ */ new Map();
|
|
862
|
+
for (const entry of fetched) {
|
|
863
|
+
const id = cms.entryId(entry);
|
|
864
|
+
if (id) entriesById.set(id, entry);
|
|
865
|
+
}
|
|
866
|
+
const queried = /* @__PURE__ */ new Map();
|
|
867
|
+
queries.forEach((d, i) => queried.set(d, results[i]));
|
|
868
|
+
return { entriesById, queried };
|
|
869
|
+
}
|
|
870
|
+
var TIMEOUT = /* @__PURE__ */ Symbol("timeout");
|
|
871
|
+
function withDeadline(promise, deadline) {
|
|
872
|
+
const remaining = deadline - Date.now();
|
|
873
|
+
if (remaining <= 0) return Promise.reject(TIMEOUT);
|
|
874
|
+
let timer;
|
|
875
|
+
return Promise.race([
|
|
876
|
+
promise,
|
|
877
|
+
new Promise((_, reject) => {
|
|
878
|
+
timer = setTimeout(() => reject(TIMEOUT), remaining);
|
|
879
|
+
})
|
|
880
|
+
]).finally(() => clearTimeout(timer));
|
|
881
|
+
}
|
|
882
|
+
function pendingTargets(payload, cms) {
|
|
883
|
+
try {
|
|
884
|
+
let content = typeof structuredClone === "function" ? structuredClone(payload) : payload;
|
|
885
|
+
content = cms.prepare ? cms.prepare(content) : content;
|
|
886
|
+
const root = { parent: { root: content }, key: "root", value: content };
|
|
887
|
+
return [...walk([root], cms, /* @__PURE__ */ new WeakSet(), 0, () => {
|
|
888
|
+
}, () => void 0).keys()];
|
|
889
|
+
} catch {
|
|
890
|
+
return [];
|
|
891
|
+
}
|
|
892
|
+
}
|
|
893
|
+
async function personalizePage(ref, options) {
|
|
894
|
+
let page;
|
|
895
|
+
try {
|
|
896
|
+
if (!options.cms.fetchPage) throw new Error("This CMS adapter cannot fetch pages; use personalizeContent(page)");
|
|
897
|
+
page = await options.cms.fetchPage(ref, options.locale);
|
|
898
|
+
} catch (error) {
|
|
899
|
+
return { content: null, applied: [], complete: false, error };
|
|
900
|
+
}
|
|
901
|
+
if (page == null) return { content: null, applied: [], complete: true };
|
|
902
|
+
return personalizeContent(page, options);
|
|
903
|
+
}
|
|
904
|
+
|
|
659
905
|
// src/core/response-store.ts
|
|
660
906
|
var MemoryResponseStore = class {
|
|
661
907
|
map = /* @__PURE__ */ new Map();
|
|
@@ -985,6 +1231,20 @@ var AutoTracker = class {
|
|
|
985
1231
|
}
|
|
986
1232
|
};
|
|
987
1233
|
|
|
1234
|
+
// src/server/cookies.ts
|
|
1235
|
+
function parseCookies(header) {
|
|
1236
|
+
const out = {};
|
|
1237
|
+
if (!header) return out;
|
|
1238
|
+
for (const part of header.split(";")) {
|
|
1239
|
+
const idx = part.indexOf("=");
|
|
1240
|
+
if (idx === -1) continue;
|
|
1241
|
+
const key = part.slice(0, idx).trim();
|
|
1242
|
+
const value = part.slice(idx + 1).trim();
|
|
1243
|
+
if (key) out[key] = decodeURIComponent(value);
|
|
1244
|
+
}
|
|
1245
|
+
return out;
|
|
1246
|
+
}
|
|
1247
|
+
|
|
988
1248
|
// src/client/identity.ts
|
|
989
1249
|
var SESSION_TIMEOUT_MS = 30 * 60 * 1e3;
|
|
990
1250
|
var Identity = class {
|
|
@@ -1032,10 +1292,19 @@ var Identity = class {
|
|
|
1032
1292
|
} catch {
|
|
1033
1293
|
}
|
|
1034
1294
|
}
|
|
1295
|
+
/** The visitor id already in the cookie — e.g. minted by `xeniaProxy()` on the first request. */
|
|
1296
|
+
readVisitorCookie() {
|
|
1297
|
+
if (!this.cookie || typeof document === "undefined") return null;
|
|
1298
|
+
try {
|
|
1299
|
+
return parseCookies(document.cookie)[this.cookieName()] || null;
|
|
1300
|
+
} catch {
|
|
1301
|
+
return null;
|
|
1302
|
+
}
|
|
1303
|
+
}
|
|
1035
1304
|
// ── Device id ────────────────────────────────────────────────────────────
|
|
1036
1305
|
/**
|
|
1037
|
-
* Resolve a stable device id: localStorage cache →
|
|
1038
|
-
* browser fingerprint. Cached for the session and across reloads.
|
|
1306
|
+
* Resolve a stable device id: localStorage cache → the visitor cookie a server already set →
|
|
1307
|
+
* MediaDevices → SHA-256 browser fingerprint. Cached for the session and across reloads.
|
|
1039
1308
|
*/
|
|
1040
1309
|
async resolveDeviceId() {
|
|
1041
1310
|
if (this.deviceId) {
|
|
@@ -1052,7 +1321,8 @@ var Identity = class {
|
|
|
1052
1321
|
}
|
|
1053
1322
|
} catch {
|
|
1054
1323
|
}
|
|
1055
|
-
let id =
|
|
1324
|
+
let id = this.readVisitorCookie();
|
|
1325
|
+
if (!id) id = await this.mediaDeviceId();
|
|
1056
1326
|
if (!id) id = await this.fingerprint();
|
|
1057
1327
|
this.deviceId = id;
|
|
1058
1328
|
try {
|
|
@@ -1907,7 +2177,10 @@ var XeniaClient = class {
|
|
|
1907
2177
|
renderer;
|
|
1908
2178
|
analyticsQueue;
|
|
1909
2179
|
tracker = null;
|
|
1910
|
-
|
|
2180
|
+
/** The in-flight (or finished) boot; shared so concurrent `init()` calls start one tracker. */
|
|
2181
|
+
booting = null;
|
|
2182
|
+
/** Bumped by `destroy()`, so a boot still waiting on the device id knows to stop. */
|
|
2183
|
+
generation = 0;
|
|
1911
2184
|
consentState;
|
|
1912
2185
|
listeners = /* @__PURE__ */ new Map();
|
|
1913
2186
|
constructor(options) {
|
|
@@ -1937,10 +2210,14 @@ var XeniaClient = class {
|
|
|
1937
2210
|
});
|
|
1938
2211
|
}
|
|
1939
2212
|
/** Boot the client: resolve device id and start auto-tracking. Idempotent. */
|
|
1940
|
-
|
|
1941
|
-
if (
|
|
2213
|
+
init() {
|
|
2214
|
+
if (!isBrowser()) return Promise.resolve();
|
|
2215
|
+
return this.booting ??= this.boot();
|
|
2216
|
+
}
|
|
2217
|
+
async boot() {
|
|
2218
|
+
const generation = this.generation;
|
|
1942
2219
|
await this.identity.resolveDeviceId();
|
|
1943
|
-
this.
|
|
2220
|
+
if (generation !== this.generation) return;
|
|
1944
2221
|
if (this.analyticsAllowed()) this.analyticsQueue.start();
|
|
1945
2222
|
if (this.options.autoTrack !== false) {
|
|
1946
2223
|
this.tracker = new AutoTracker({
|
|
@@ -1976,7 +2253,8 @@ var XeniaClient = class {
|
|
|
1976
2253
|
this.tracker = null;
|
|
1977
2254
|
this.renderer.destroy();
|
|
1978
2255
|
this.analyticsQueue.stop();
|
|
1979
|
-
this.
|
|
2256
|
+
this.booting = null;
|
|
2257
|
+
this.generation++;
|
|
1980
2258
|
}
|
|
1981
2259
|
// ── Identity ───────────────────────────────────────────────────────────────
|
|
1982
2260
|
/**
|
|
@@ -2105,6 +2383,27 @@ var XeniaClient = class {
|
|
|
2105
2383
|
async resolveBatch(items, opts) {
|
|
2106
2384
|
return this.api.resolveBatch(items, this.withVisitor(opts));
|
|
2107
2385
|
}
|
|
2386
|
+
/**
|
|
2387
|
+
* Personalize a fetched CMS page: static swaps are applied from the payload, runtime targets are
|
|
2388
|
+
* decided by Xenia in one `resolve-entries` call per nesting level, and replacements are loaded
|
|
2389
|
+
* through `opts.cms` (e.g. `contentfulAdapter(...)`). Never throws — anything undecided within
|
|
2390
|
+
* `opts.timeoutMs` keeps its base content.
|
|
2391
|
+
*/
|
|
2392
|
+
async personalizeContent(page, opts) {
|
|
2393
|
+
return personalizeContent(page, { ...opts, resolve: this.entryResolver(opts) });
|
|
2394
|
+
}
|
|
2395
|
+
/**
|
|
2396
|
+
* Fetch a page through `opts.cms` and personalize it — the app passes only the page's identity
|
|
2397
|
+
* (entry id, or `{ contentType, fields: { slug } }`). Never throws: a failed fetch is `content:
|
|
2398
|
+
* null` with `error`, a missing page `content: null`.
|
|
2399
|
+
*/
|
|
2400
|
+
async personalizePage(ref, opts) {
|
|
2401
|
+
return personalizePage(ref, { ...opts, resolve: this.entryResolver(opts) });
|
|
2402
|
+
}
|
|
2403
|
+
entryResolver(opts) {
|
|
2404
|
+
const resolveOpts = this.withVisitor({ data: opts.data, locale: opts.locale });
|
|
2405
|
+
return async (ids) => (await this.api.resolveEntries(ids, resolveOpts)).decisions ?? [];
|
|
2406
|
+
}
|
|
2108
2407
|
/** The SDK-owned visitor anchor: the `identify()`/`config.visitorId` id, else the device id. */
|
|
2109
2408
|
resolveVisitorId() {
|
|
2110
2409
|
return this.identity.getVisitorId() ?? this.identity.getDeviceId() ?? void 0;
|
|
@@ -2183,8 +2482,8 @@ var XeniaClient = class {
|
|
|
2183
2482
|
const response = await this.api.webhook(triggerId, body, opts?.method ?? "POST");
|
|
2184
2483
|
this.responses.set(triggerId, response);
|
|
2185
2484
|
this.emit("response", { triggerId, response });
|
|
2186
|
-
const
|
|
2187
|
-
if (
|
|
2485
|
+
const apply2 = opts?.apply ?? this.options.autoApplyActions !== false;
|
|
2486
|
+
if (apply2 && response.actions.length) {
|
|
2188
2487
|
this.applyActions(response.actions);
|
|
2189
2488
|
}
|
|
2190
2489
|
return response;
|
|
@@ -2238,8 +2537,8 @@ var XeniaClient = class {
|
|
|
2238
2537
|
compact({ ...ambient, fields })
|
|
2239
2538
|
);
|
|
2240
2539
|
this.emit("response", { triggerId: ref, response });
|
|
2241
|
-
const
|
|
2242
|
-
if (
|
|
2540
|
+
const apply2 = opts?.apply ?? this.options.autoApplyActions !== false;
|
|
2541
|
+
if (apply2 && response.actions.length) this.applyActions(response.actions);
|
|
2243
2542
|
return response;
|
|
2244
2543
|
}
|
|
2245
2544
|
// ── Imperative render helpers ─────────────────────────────────────────────────
|
|
@@ -2373,6 +2672,9 @@ exports.extractContentSwaps = extractContentSwaps;
|
|
|
2373
2672
|
exports.hideContent = hideContent;
|
|
2374
2673
|
exports.parseActions = parseActions;
|
|
2375
2674
|
exports.parseDataAttribute = parseDataAttribute;
|
|
2675
|
+
exports.pendingTargets = pendingTargets;
|
|
2676
|
+
exports.personalizeContent = personalizeContent;
|
|
2677
|
+
exports.personalizePage = personalizePage;
|
|
2376
2678
|
exports.readSlotMeta = readSlotMeta;
|
|
2377
2679
|
exports.resolveTarget = resolveTarget;
|
|
2378
2680
|
exports.showContent = showContent;
|