@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.d.cts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { I as InteractionType, X as XeniaConfig, a as XeniaEvent, M as MessageCta, b as MessageTemplateFn, c as MessageComponentRenderer, W as WidgetFormSubmit, d as WidgetSubmitResponse, e as XeniaAction, f as MessagePlacement, C as ConsentState } from '../actions-
|
|
2
|
-
export { A as AnalyticsContext, g as AnalyticsEventName, h as AnalyticsIdentity, i as AnalyticsIngestResponse, j as
|
|
1
|
+
import { I as InteractionType, X as XeniaConfig, a as XeniaEvent, M as MessageCta, b as MessageTemplateFn, c as MessageComponentRenderer, W as WidgetFormSubmit, d as WidgetSubmitResponse, e as XeniaAction, f as MessagePlacement, C as ConsentState } from '../actions-BcdlokGO.cjs';
|
|
2
|
+
export { A as AnalyticsContext, g as AnalyticsEventName, h as AnalyticsIdentity, i as AnalyticsIngestResponse, j as AppliedSwap, k as ClientEvent, l as CmsAdapter, m as ContentSwap, n as IdentifyRequest, o as IdentifyResponse, p as Identity, J as JsonValue, q as MessageMountContext, r as MessageRenderContext, P as PagePersonalizeOptions, s as PageRef, t as PersonalizationSwap, R as ResolveBatchItem, u as ResolveBatchResult, v as ResolveOptions, w as ResolveResult, T as TrackOptions, x as WebhookResponse, y as WidgetPrize, z as WidgetSegment, B as WidgetType, D as XeniaClient, E as XeniaClientOptions, F as XeniaMarker, G as createXeniaClient, H as extractContentSwaps, K as parseActions, L as pendingTargets, N as personalizeContent, O as personalizePage, Q as spliceSlots } from '../actions-BcdlokGO.cjs';
|
|
3
3
|
|
|
4
4
|
interface Parsed {
|
|
5
5
|
eventId: string;
|
package/dist/client/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { I as InteractionType, X as XeniaConfig, a as XeniaEvent, M as MessageCta, b as MessageTemplateFn, c as MessageComponentRenderer, W as WidgetFormSubmit, d as WidgetSubmitResponse, e as XeniaAction, f as MessagePlacement, C as ConsentState } from '../actions-
|
|
2
|
-
export { A as AnalyticsContext, g as AnalyticsEventName, h as AnalyticsIdentity, i as AnalyticsIngestResponse, j as
|
|
1
|
+
import { I as InteractionType, X as XeniaConfig, a as XeniaEvent, M as MessageCta, b as MessageTemplateFn, c as MessageComponentRenderer, W as WidgetFormSubmit, d as WidgetSubmitResponse, e as XeniaAction, f as MessagePlacement, C as ConsentState } from '../actions-BcdlokGO.js';
|
|
2
|
+
export { A as AnalyticsContext, g as AnalyticsEventName, h as AnalyticsIdentity, i as AnalyticsIngestResponse, j as AppliedSwap, k as ClientEvent, l as CmsAdapter, m as ContentSwap, n as IdentifyRequest, o as IdentifyResponse, p as Identity, J as JsonValue, q as MessageMountContext, r as MessageRenderContext, P as PagePersonalizeOptions, s as PageRef, t as PersonalizationSwap, R as ResolveBatchItem, u as ResolveBatchResult, v as ResolveOptions, w as ResolveResult, T as TrackOptions, x as WebhookResponse, y as WidgetPrize, z as WidgetSegment, B as WidgetType, D as XeniaClient, E as XeniaClientOptions, F as XeniaMarker, G as createXeniaClient, H as extractContentSwaps, K as parseActions, L as pendingTargets, N as personalizeContent, O as personalizePage, Q as spliceSlots } from '../actions-BcdlokGO.js';
|
|
3
3
|
|
|
4
4
|
interface Parsed {
|
|
5
5
|
eventId: string;
|
package/dist/client/index.js
CHANGED
|
@@ -540,6 +540,25 @@ var XeniaApi = class {
|
|
|
540
540
|
});
|
|
541
541
|
return data;
|
|
542
542
|
}
|
|
543
|
+
/**
|
|
544
|
+
* Resolve personalized CMS entries by id for one visitor (≤200) —
|
|
545
|
+
* `POST /api/personalization/resolve-entries`. The page resolver
|
|
546
|
+
* ({@link personalizeContent}) calls this once per round with the runtime
|
|
547
|
+
* targets it found; callers rarely need it directly.
|
|
548
|
+
*/
|
|
549
|
+
async resolveEntries(entryIds, opts = {}) {
|
|
550
|
+
const { data } = await this.http.request({
|
|
551
|
+
method: "POST",
|
|
552
|
+
path: "/api/personalization/resolve-entries",
|
|
553
|
+
headers: await this.authHeaders(),
|
|
554
|
+
body: compact({
|
|
555
|
+
entryIds,
|
|
556
|
+
data: opts.data,
|
|
557
|
+
locale: opts.locale
|
|
558
|
+
})
|
|
559
|
+
});
|
|
560
|
+
return data;
|
|
561
|
+
}
|
|
543
562
|
};
|
|
544
563
|
|
|
545
564
|
// src/core/cookie-names.ts
|
|
@@ -654,6 +673,233 @@ function toEventId(raw) {
|
|
|
654
673
|
return digest;
|
|
655
674
|
}
|
|
656
675
|
|
|
676
|
+
// src/core/personalize-content.ts
|
|
677
|
+
var DEFAULT_TIMEOUT_MS = 300;
|
|
678
|
+
var DEFAULT_MAX_ROUNDS = 3;
|
|
679
|
+
var copyOf = (v) => v && typeof v === "object" && typeof structuredClone === "function" ? structuredClone(v) : v;
|
|
680
|
+
async function personalizeContent(payload, options) {
|
|
681
|
+
const { cms } = options;
|
|
682
|
+
const maxRounds = options.maxRounds ?? DEFAULT_MAX_ROUNDS;
|
|
683
|
+
const deadline = Date.now() + (options.timeoutMs ?? DEFAULT_TIMEOUT_MS);
|
|
684
|
+
const applied = [];
|
|
685
|
+
const report = (swap) => {
|
|
686
|
+
applied.push(swap);
|
|
687
|
+
try {
|
|
688
|
+
options.onApplied?.(swap);
|
|
689
|
+
} catch {
|
|
690
|
+
}
|
|
691
|
+
};
|
|
692
|
+
let content = payload;
|
|
693
|
+
try {
|
|
694
|
+
if (!options.inPlace && typeof structuredClone === "function") content = structuredClone(content);
|
|
695
|
+
content = cms.prepare ? cms.prepare(content) : content;
|
|
696
|
+
} catch (error) {
|
|
697
|
+
return { content: payload, applied, complete: false, error };
|
|
698
|
+
}
|
|
699
|
+
const box = { root: content };
|
|
700
|
+
const settled = /* @__PURE__ */ new WeakSet();
|
|
701
|
+
const decided = /* @__PURE__ */ new Map();
|
|
702
|
+
let frontier = [{ parent: box, key: "root", value: box.root }];
|
|
703
|
+
const settle = (target, d, round) => {
|
|
704
|
+
settled.add(target.entry);
|
|
705
|
+
cms.clearMarker?.(target.entry);
|
|
706
|
+
const items = d.items.map(copyOf);
|
|
707
|
+
if (!d.decision || items.length === 0) {
|
|
708
|
+
return deferredSlots(target, cms);
|
|
709
|
+
}
|
|
710
|
+
for (const item of items) if (item && typeof item === "object") settled.add(item);
|
|
711
|
+
const op = d.decision.op ?? target.marker.op ?? "replace";
|
|
712
|
+
const next = apply(target, op, d.decision.field ?? target.marker.field, items, cms);
|
|
713
|
+
report({
|
|
714
|
+
target: target.id,
|
|
715
|
+
kind: "runtime",
|
|
716
|
+
op,
|
|
717
|
+
entryIds: items.map((e) => cms.entryId(e)).filter((id) => !!id),
|
|
718
|
+
contentKey: d.decision.contentKey,
|
|
719
|
+
bucket: d.decision.bucket,
|
|
720
|
+
round
|
|
721
|
+
});
|
|
722
|
+
return next;
|
|
723
|
+
};
|
|
724
|
+
for (let round = 0; ; round++) {
|
|
725
|
+
const pending = walk(frontier, cms, settled, round, report, (t) => {
|
|
726
|
+
const d = decided.get(t.id);
|
|
727
|
+
return d ? settle(t, d, round) : void 0;
|
|
728
|
+
});
|
|
729
|
+
if (pending.size === 0) return { content: box.root, applied, complete: true };
|
|
730
|
+
if (!options.resolve || round >= maxRounds) {
|
|
731
|
+
return { content: box.root, applied, complete: !options.resolve };
|
|
732
|
+
}
|
|
733
|
+
progress(options, { content: box.root, round, pending: [...pending.keys()] });
|
|
734
|
+
let decisions;
|
|
735
|
+
let entriesById;
|
|
736
|
+
let queried;
|
|
737
|
+
try {
|
|
738
|
+
decisions = await withDeadline(options.resolve([...pending.keys()]), deadline);
|
|
739
|
+
({ entriesById, queried } = await withDeadline(loadReplacements(decisions, cms, options.locale), deadline));
|
|
740
|
+
} catch (error) {
|
|
741
|
+
return { content: box.root, applied, complete: false, error: error === TIMEOUT ? void 0 : error };
|
|
742
|
+
}
|
|
743
|
+
const byTarget = new Map(decisions.map((d) => [d.target, d]));
|
|
744
|
+
frontier = [];
|
|
745
|
+
for (const target of pending.values()) {
|
|
746
|
+
const decision = byTarget.get(target.id);
|
|
747
|
+
const items = decision ? decision.ids ? decision.ids.map((id) => entriesById.get(id)).filter((e) => e !== void 0) : queried.get(decision) ?? [] : [];
|
|
748
|
+
const d = { decision, items };
|
|
749
|
+
decided.set(target.id, d);
|
|
750
|
+
frontier.push(...settle(target, d, round));
|
|
751
|
+
}
|
|
752
|
+
progress(options, { content: box.root, round, pending: [] });
|
|
753
|
+
}
|
|
754
|
+
}
|
|
755
|
+
function progress(options, p) {
|
|
756
|
+
try {
|
|
757
|
+
options.onProgress?.(p);
|
|
758
|
+
} catch {
|
|
759
|
+
}
|
|
760
|
+
}
|
|
761
|
+
function walk(frontier, cms, settled, round, report, reuse) {
|
|
762
|
+
const pending = /* @__PURE__ */ new Map();
|
|
763
|
+
const seen = /* @__PURE__ */ new WeakSet();
|
|
764
|
+
const deferred = /* @__PURE__ */ new WeakSet();
|
|
765
|
+
const queue = [...frontier];
|
|
766
|
+
while (queue.length > 0) {
|
|
767
|
+
const slot = queue.shift();
|
|
768
|
+
const value = slot.value;
|
|
769
|
+
if (!value || typeof value !== "object" || deferred.has(value)) continue;
|
|
770
|
+
const id = settled.has(value) ? void 0 : cms.entryId(value);
|
|
771
|
+
const marker = id ? cms.marker(value) : null;
|
|
772
|
+
if (id && marker?.kind === "runtime") {
|
|
773
|
+
const again = reuse({ id, marker, entry: value, slots: [slot] });
|
|
774
|
+
if (again) {
|
|
775
|
+
queue.push(...again);
|
|
776
|
+
continue;
|
|
777
|
+
}
|
|
778
|
+
const existing = pending.get(id);
|
|
779
|
+
if (existing) {
|
|
780
|
+
existing.slots.push(slot);
|
|
781
|
+
} else {
|
|
782
|
+
pending.set(id, { id, marker, entry: value, slots: [slot] });
|
|
783
|
+
if (marker.op === "items" && marker.field) {
|
|
784
|
+
const held = cms.getField(value, marker.field);
|
|
785
|
+
if (held && typeof held === "object") deferred.add(held);
|
|
786
|
+
queue.push(...children(value));
|
|
787
|
+
}
|
|
788
|
+
}
|
|
789
|
+
continue;
|
|
790
|
+
}
|
|
791
|
+
if (id && marker?.kind === "static") {
|
|
792
|
+
const variants = cms.staticVariants(value).filter((v) => v && typeof v === "object");
|
|
793
|
+
settled.add(value);
|
|
794
|
+
cms.clearMarker?.(value);
|
|
795
|
+
if (variants.length > 0) {
|
|
796
|
+
for (const v of variants) settled.add(v);
|
|
797
|
+
const op = marker.op ?? "replace";
|
|
798
|
+
const target = { entry: value, slots: [slot] };
|
|
799
|
+
queue.push(...apply(target, op, marker.field, variants, cms));
|
|
800
|
+
report({
|
|
801
|
+
target: id,
|
|
802
|
+
kind: "static",
|
|
803
|
+
op,
|
|
804
|
+
entryIds: variants.map((v) => cms.entryId(v)).filter((x) => !!x),
|
|
805
|
+
round
|
|
806
|
+
});
|
|
807
|
+
continue;
|
|
808
|
+
}
|
|
809
|
+
}
|
|
810
|
+
if (seen.has(value)) continue;
|
|
811
|
+
seen.add(value);
|
|
812
|
+
queue.push(...children(value));
|
|
813
|
+
}
|
|
814
|
+
return pending;
|
|
815
|
+
}
|
|
816
|
+
function apply(target, op, field, items, cms) {
|
|
817
|
+
if (op === "items") {
|
|
818
|
+
if (field) {
|
|
819
|
+
cms.setField(target.entry, field, items);
|
|
820
|
+
return [{ parent: target.entry, key: field, value: cms.getField(target.entry, field) }];
|
|
821
|
+
}
|
|
822
|
+
if (cms.mergeItems) {
|
|
823
|
+
cms.mergeItems(target.entry, items[0]);
|
|
824
|
+
return target.slots.map((s) => ({ ...s, value: target.entry }));
|
|
825
|
+
}
|
|
826
|
+
op = "replace";
|
|
827
|
+
}
|
|
828
|
+
const next = [];
|
|
829
|
+
for (const slot of target.slots) {
|
|
830
|
+
const placed = op === "expand" && Array.isArray(slot.parent) ? items : [items[0]];
|
|
831
|
+
if (Array.isArray(slot.parent)) {
|
|
832
|
+
const index = slot.parent.indexOf(target.entry);
|
|
833
|
+
if (index < 0) continue;
|
|
834
|
+
slot.parent.splice(index, 1, ...placed);
|
|
835
|
+
} else {
|
|
836
|
+
slot.parent[slot.key] = placed[0];
|
|
837
|
+
}
|
|
838
|
+
for (const value of placed) next.push({ parent: slot.parent, key: slot.key, value });
|
|
839
|
+
}
|
|
840
|
+
return next;
|
|
841
|
+
}
|
|
842
|
+
function deferredSlots(target, cms) {
|
|
843
|
+
if (target.marker.op === "items" && target.marker.field) {
|
|
844
|
+
return [{ parent: target.entry, key: target.marker.field, value: cms.getField(target.entry, target.marker.field) }];
|
|
845
|
+
}
|
|
846
|
+
return target.slots.map((s) => ({ ...s, value: target.entry }));
|
|
847
|
+
}
|
|
848
|
+
function children(value) {
|
|
849
|
+
if (Array.isArray(value)) return value.map((v, i) => ({ parent: value, key: i, value: v }));
|
|
850
|
+
return Object.keys(value).map((k) => ({ parent: value, key: k, value: value[k] }));
|
|
851
|
+
}
|
|
852
|
+
async function loadReplacements(decisions, cms, locale) {
|
|
853
|
+
const ids = [...new Set(decisions.flatMap((d) => d.ids ?? []))];
|
|
854
|
+
const queries = decisions.filter((d) => !d.ids && d.query);
|
|
855
|
+
const [fetched, ...results] = await Promise.all([
|
|
856
|
+
ids.length > 0 ? cms.fetchEntries(ids, locale) : Promise.resolve([]),
|
|
857
|
+
...queries.map((d) => cms.queryEntries(d.query, locale).catch(() => []))
|
|
858
|
+
]);
|
|
859
|
+
const entriesById = /* @__PURE__ */ new Map();
|
|
860
|
+
for (const entry of fetched) {
|
|
861
|
+
const id = cms.entryId(entry);
|
|
862
|
+
if (id) entriesById.set(id, entry);
|
|
863
|
+
}
|
|
864
|
+
const queried = /* @__PURE__ */ new Map();
|
|
865
|
+
queries.forEach((d, i) => queried.set(d, results[i]));
|
|
866
|
+
return { entriesById, queried };
|
|
867
|
+
}
|
|
868
|
+
var TIMEOUT = /* @__PURE__ */ Symbol("timeout");
|
|
869
|
+
function withDeadline(promise, deadline) {
|
|
870
|
+
const remaining = deadline - Date.now();
|
|
871
|
+
if (remaining <= 0) return Promise.reject(TIMEOUT);
|
|
872
|
+
let timer;
|
|
873
|
+
return Promise.race([
|
|
874
|
+
promise,
|
|
875
|
+
new Promise((_, reject) => {
|
|
876
|
+
timer = setTimeout(() => reject(TIMEOUT), remaining);
|
|
877
|
+
})
|
|
878
|
+
]).finally(() => clearTimeout(timer));
|
|
879
|
+
}
|
|
880
|
+
function pendingTargets(payload, cms) {
|
|
881
|
+
try {
|
|
882
|
+
let content = typeof structuredClone === "function" ? structuredClone(payload) : payload;
|
|
883
|
+
content = cms.prepare ? cms.prepare(content) : content;
|
|
884
|
+
const root = { parent: { root: content }, key: "root", value: content };
|
|
885
|
+
return [...walk([root], cms, /* @__PURE__ */ new WeakSet(), 0, () => {
|
|
886
|
+
}, () => void 0).keys()];
|
|
887
|
+
} catch {
|
|
888
|
+
return [];
|
|
889
|
+
}
|
|
890
|
+
}
|
|
891
|
+
async function personalizePage(ref, options) {
|
|
892
|
+
let page;
|
|
893
|
+
try {
|
|
894
|
+
if (!options.cms.fetchPage) throw new Error("This CMS adapter cannot fetch pages; use personalizeContent(page)");
|
|
895
|
+
page = await options.cms.fetchPage(ref, options.locale);
|
|
896
|
+
} catch (error) {
|
|
897
|
+
return { content: null, applied: [], complete: false, error };
|
|
898
|
+
}
|
|
899
|
+
if (page == null) return { content: null, applied: [], complete: true };
|
|
900
|
+
return personalizeContent(page, options);
|
|
901
|
+
}
|
|
902
|
+
|
|
657
903
|
// src/core/response-store.ts
|
|
658
904
|
var MemoryResponseStore = class {
|
|
659
905
|
map = /* @__PURE__ */ new Map();
|
|
@@ -983,6 +1229,20 @@ var AutoTracker = class {
|
|
|
983
1229
|
}
|
|
984
1230
|
};
|
|
985
1231
|
|
|
1232
|
+
// src/server/cookies.ts
|
|
1233
|
+
function parseCookies(header) {
|
|
1234
|
+
const out = {};
|
|
1235
|
+
if (!header) return out;
|
|
1236
|
+
for (const part of header.split(";")) {
|
|
1237
|
+
const idx = part.indexOf("=");
|
|
1238
|
+
if (idx === -1) continue;
|
|
1239
|
+
const key = part.slice(0, idx).trim();
|
|
1240
|
+
const value = part.slice(idx + 1).trim();
|
|
1241
|
+
if (key) out[key] = decodeURIComponent(value);
|
|
1242
|
+
}
|
|
1243
|
+
return out;
|
|
1244
|
+
}
|
|
1245
|
+
|
|
986
1246
|
// src/client/identity.ts
|
|
987
1247
|
var SESSION_TIMEOUT_MS = 30 * 60 * 1e3;
|
|
988
1248
|
var Identity = class {
|
|
@@ -1030,10 +1290,19 @@ var Identity = class {
|
|
|
1030
1290
|
} catch {
|
|
1031
1291
|
}
|
|
1032
1292
|
}
|
|
1293
|
+
/** The visitor id already in the cookie — e.g. minted by `xeniaProxy()` on the first request. */
|
|
1294
|
+
readVisitorCookie() {
|
|
1295
|
+
if (!this.cookie || typeof document === "undefined") return null;
|
|
1296
|
+
try {
|
|
1297
|
+
return parseCookies(document.cookie)[this.cookieName()] || null;
|
|
1298
|
+
} catch {
|
|
1299
|
+
return null;
|
|
1300
|
+
}
|
|
1301
|
+
}
|
|
1033
1302
|
// ── Device id ────────────────────────────────────────────────────────────
|
|
1034
1303
|
/**
|
|
1035
|
-
* Resolve a stable device id: localStorage cache →
|
|
1036
|
-
* browser fingerprint. Cached for the session and across reloads.
|
|
1304
|
+
* Resolve a stable device id: localStorage cache → the visitor cookie a server already set →
|
|
1305
|
+
* MediaDevices → SHA-256 browser fingerprint. Cached for the session and across reloads.
|
|
1037
1306
|
*/
|
|
1038
1307
|
async resolveDeviceId() {
|
|
1039
1308
|
if (this.deviceId) {
|
|
@@ -1050,7 +1319,8 @@ var Identity = class {
|
|
|
1050
1319
|
}
|
|
1051
1320
|
} catch {
|
|
1052
1321
|
}
|
|
1053
|
-
let id =
|
|
1322
|
+
let id = this.readVisitorCookie();
|
|
1323
|
+
if (!id) id = await this.mediaDeviceId();
|
|
1054
1324
|
if (!id) id = await this.fingerprint();
|
|
1055
1325
|
this.deviceId = id;
|
|
1056
1326
|
try {
|
|
@@ -1905,7 +2175,10 @@ var XeniaClient = class {
|
|
|
1905
2175
|
renderer;
|
|
1906
2176
|
analyticsQueue;
|
|
1907
2177
|
tracker = null;
|
|
1908
|
-
|
|
2178
|
+
/** The in-flight (or finished) boot; shared so concurrent `init()` calls start one tracker. */
|
|
2179
|
+
booting = null;
|
|
2180
|
+
/** Bumped by `destroy()`, so a boot still waiting on the device id knows to stop. */
|
|
2181
|
+
generation = 0;
|
|
1909
2182
|
consentState;
|
|
1910
2183
|
listeners = /* @__PURE__ */ new Map();
|
|
1911
2184
|
constructor(options) {
|
|
@@ -1935,10 +2208,14 @@ var XeniaClient = class {
|
|
|
1935
2208
|
});
|
|
1936
2209
|
}
|
|
1937
2210
|
/** Boot the client: resolve device id and start auto-tracking. Idempotent. */
|
|
1938
|
-
|
|
1939
|
-
if (
|
|
2211
|
+
init() {
|
|
2212
|
+
if (!isBrowser()) return Promise.resolve();
|
|
2213
|
+
return this.booting ??= this.boot();
|
|
2214
|
+
}
|
|
2215
|
+
async boot() {
|
|
2216
|
+
const generation = this.generation;
|
|
1940
2217
|
await this.identity.resolveDeviceId();
|
|
1941
|
-
this.
|
|
2218
|
+
if (generation !== this.generation) return;
|
|
1942
2219
|
if (this.analyticsAllowed()) this.analyticsQueue.start();
|
|
1943
2220
|
if (this.options.autoTrack !== false) {
|
|
1944
2221
|
this.tracker = new AutoTracker({
|
|
@@ -1974,7 +2251,8 @@ var XeniaClient = class {
|
|
|
1974
2251
|
this.tracker = null;
|
|
1975
2252
|
this.renderer.destroy();
|
|
1976
2253
|
this.analyticsQueue.stop();
|
|
1977
|
-
this.
|
|
2254
|
+
this.booting = null;
|
|
2255
|
+
this.generation++;
|
|
1978
2256
|
}
|
|
1979
2257
|
// ── Identity ───────────────────────────────────────────────────────────────
|
|
1980
2258
|
/**
|
|
@@ -2103,6 +2381,27 @@ var XeniaClient = class {
|
|
|
2103
2381
|
async resolveBatch(items, opts) {
|
|
2104
2382
|
return this.api.resolveBatch(items, this.withVisitor(opts));
|
|
2105
2383
|
}
|
|
2384
|
+
/**
|
|
2385
|
+
* Personalize a fetched CMS page: static swaps are applied from the payload, runtime targets are
|
|
2386
|
+
* decided by Xenia in one `resolve-entries` call per nesting level, and replacements are loaded
|
|
2387
|
+
* through `opts.cms` (e.g. `contentfulAdapter(...)`). Never throws — anything undecided within
|
|
2388
|
+
* `opts.timeoutMs` keeps its base content.
|
|
2389
|
+
*/
|
|
2390
|
+
async personalizeContent(page, opts) {
|
|
2391
|
+
return personalizeContent(page, { ...opts, resolve: this.entryResolver(opts) });
|
|
2392
|
+
}
|
|
2393
|
+
/**
|
|
2394
|
+
* Fetch a page through `opts.cms` and personalize it — the app passes only the page's identity
|
|
2395
|
+
* (entry id, or `{ contentType, fields: { slug } }`). Never throws: a failed fetch is `content:
|
|
2396
|
+
* null` with `error`, a missing page `content: null`.
|
|
2397
|
+
*/
|
|
2398
|
+
async personalizePage(ref, opts) {
|
|
2399
|
+
return personalizePage(ref, { ...opts, resolve: this.entryResolver(opts) });
|
|
2400
|
+
}
|
|
2401
|
+
entryResolver(opts) {
|
|
2402
|
+
const resolveOpts = this.withVisitor({ data: opts.data, locale: opts.locale });
|
|
2403
|
+
return async (ids) => (await this.api.resolveEntries(ids, resolveOpts)).decisions ?? [];
|
|
2404
|
+
}
|
|
2106
2405
|
/** The SDK-owned visitor anchor: the `identify()`/`config.visitorId` id, else the device id. */
|
|
2107
2406
|
resolveVisitorId() {
|
|
2108
2407
|
return this.identity.getVisitorId() ?? this.identity.getDeviceId() ?? void 0;
|
|
@@ -2181,8 +2480,8 @@ var XeniaClient = class {
|
|
|
2181
2480
|
const response = await this.api.webhook(triggerId, body, opts?.method ?? "POST");
|
|
2182
2481
|
this.responses.set(triggerId, response);
|
|
2183
2482
|
this.emit("response", { triggerId, response });
|
|
2184
|
-
const
|
|
2185
|
-
if (
|
|
2483
|
+
const apply2 = opts?.apply ?? this.options.autoApplyActions !== false;
|
|
2484
|
+
if (apply2 && response.actions.length) {
|
|
2186
2485
|
this.applyActions(response.actions);
|
|
2187
2486
|
}
|
|
2188
2487
|
return response;
|
|
@@ -2236,8 +2535,8 @@ var XeniaClient = class {
|
|
|
2236
2535
|
compact({ ...ambient, fields })
|
|
2237
2536
|
);
|
|
2238
2537
|
this.emit("response", { triggerId: ref, response });
|
|
2239
|
-
const
|
|
2240
|
-
if (
|
|
2538
|
+
const apply2 = opts?.apply ?? this.options.autoApplyActions !== false;
|
|
2539
|
+
if (apply2 && response.actions.length) this.applyActions(response.actions);
|
|
2241
2540
|
return response;
|
|
2242
2541
|
}
|
|
2243
2542
|
// ── Imperative render helpers ─────────────────────────────────────────────────
|
|
@@ -2355,6 +2654,6 @@ function extractContentSwaps(body) {
|
|
|
2355
2654
|
return Array.isArray(swaps) ? swaps : void 0;
|
|
2356
2655
|
}
|
|
2357
2656
|
|
|
2358
|
-
export { ActionRenderer, AnalyticsQueue, AutoTracker, Identity, TokenProvider, WIDGET_RENDERERS, XeniaClient, collectFields, computeSpinRotation, consentCookie, consentCookieName, createXeniaClient, extractContentSwaps, hideContent, parseActions, parseDataAttribute, readSlotMeta, resolveTarget, showContent, spliceSlots, swapContent, wireFormSubmit };
|
|
2657
|
+
export { ActionRenderer, AnalyticsQueue, AutoTracker, Identity, TokenProvider, WIDGET_RENDERERS, XeniaClient, collectFields, computeSpinRotation, consentCookie, consentCookieName, createXeniaClient, extractContentSwaps, hideContent, parseActions, parseDataAttribute, pendingTargets, personalizeContent, personalizePage, readSlotMeta, resolveTarget, showContent, spliceSlots, swapContent, wireFormSubmit };
|
|
2359
2658
|
//# sourceMappingURL=index.js.map
|
|
2360
2659
|
//# sourceMappingURL=index.js.map
|