@xenia-martech/sdk-js 1.2.0-dev.7 → 1.2.0-dev.9
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 +70 -6
- package/dist/{actions-35V37IT0.d.cts → actions--1_9Z8ig.d.cts} +13 -2
- package/dist/{actions-35V37IT0.d.ts → actions--1_9Z8ig.d.ts} +13 -2
- package/dist/{api-0IycTYJR.d.cts → api-DbUxT38T.d.ts} +1 -1
- package/dist/{api-COXWLL5L.d.ts → api-DgzJ5s-k.d.cts} +1 -1
- package/dist/chunk-DPXINHQR.cjs +34 -0
- package/dist/chunk-DPXINHQR.cjs.map +1 -0
- package/dist/chunk-FSJMHXS6.js +432 -0
- package/dist/chunk-FSJMHXS6.js.map +1 -0
- package/dist/chunk-GXJLU3RE.cjs +448 -0
- package/dist/chunk-GXJLU3RE.cjs.map +1 -0
- package/dist/{chunk-2DR5AVPX.cjs → chunk-I5EHLRSR.cjs} +11 -10
- package/dist/chunk-I5EHLRSR.cjs.map +1 -0
- package/dist/{chunk-LW5AEJ6K.js → chunk-MEULT5X6.js} +4 -241
- package/dist/chunk-MEULT5X6.js.map +1 -0
- package/dist/{chunk-GWRDZIVA.js → chunk-NDRRSRTU.js} +4 -3
- package/dist/chunk-NDRRSRTU.js.map +1 -0
- package/dist/chunk-RC46KXDM.js +32 -0
- package/dist/chunk-RC46KXDM.js.map +1 -0
- package/dist/{chunk-RWE2OCQL.cjs → chunk-SU25SELD.cjs} +4 -244
- package/dist/chunk-SU25SELD.cjs.map +1 -0
- package/dist/{chunk-WHGK55JP.cjs → chunk-UUIKUU5W.cjs} +5 -5
- package/dist/{chunk-WHGK55JP.cjs.map → chunk-UUIKUU5W.cjs.map} +1 -1
- package/dist/{chunk-QEYONKWY.js → chunk-UVTFGQR6.js} +3 -3
- package/dist/{chunk-QEYONKWY.js.map → chunk-UVTFGQR6.js.map} +1 -1
- package/dist/{chunk-OMMIRSPK.js → chunk-V4EGU3PM.js} +48 -11
- package/dist/chunk-V4EGU3PM.js.map +1 -0
- package/dist/{chunk-YLMMTKQL.cjs → chunk-WMGCQPHY.cjs} +52 -13
- package/dist/chunk-WMGCQPHY.cjs.map +1 -0
- package/dist/client/index.cjs +26 -10
- 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 +26 -10
- package/dist/client/index.js.map +1 -1
- package/dist/client/react/index.cjs +73 -15
- package/dist/client/react/index.cjs.map +1 -1
- package/dist/client/react/index.d.cts +9 -3
- package/dist/client/react/index.d.ts +9 -3
- package/dist/client/react/index.js +73 -15
- package/dist/client/react/index.js.map +1 -1
- package/dist/cms/contentful.cjs +7 -24
- package/dist/cms/contentful.cjs.map +1 -1
- package/dist/cms/contentful.d.cts +5 -4
- package/dist/cms/contentful.d.ts +5 -4
- package/dist/cms/contentful.js +4 -23
- package/dist/cms/contentful.js.map +1 -1
- package/dist/cms/contentstack.cjs +120 -0
- package/dist/cms/contentstack.cjs.map +1 -0
- package/dist/cms/contentstack.d.cts +41 -0
- package/dist/cms/contentstack.d.ts +41 -0
- package/dist/cms/contentstack.js +117 -0
- package/dist/cms/contentstack.js.map +1 -0
- package/dist/{consent-DRCV0N0d.d.ts → consent-Dtiqzz2B.d.cts} +71 -11
- package/dist/{consent-BgAETCkL.d.cts → consent-byYiDMFr.d.ts} +71 -11
- package/dist/{cookie-names-DU3MtcNG.d.ts → cookie-names-B4hda2rX.d.cts} +1 -1
- package/dist/{cookie-names-DFItnSLK.d.cts → cookie-names-DioZXuV0.d.ts} +1 -1
- package/dist/edge/index.cjs +122 -29
- package/dist/edge/index.cjs.map +1 -1
- package/dist/edge/index.d.cts +42 -6
- package/dist/edge/index.d.ts +42 -6
- package/dist/edge/index.js +92 -9
- package/dist/edge/index.js.map +1 -1
- package/dist/index.cjs +31 -31
- package/dist/index.d.cts +4 -5
- package/dist/index.d.ts +4 -5
- package/dist/index.js +2 -2
- package/dist/next/index.cjs +39 -34
- package/dist/next/index.cjs.map +1 -1
- package/dist/next/index.d.cts +2 -3
- package/dist/next/index.d.ts +2 -3
- package/dist/next/index.js +13 -8
- package/dist/next/index.js.map +1 -1
- package/dist/{types-DgVHS4Qw.d.ts → personalize-content-D6xeBI7-.d.cts} +150 -1
- package/dist/{types-DgVHS4Qw.d.cts → personalize-content-D6xeBI7-.d.ts} +150 -1
- package/dist/server/index.cjs +30 -30
- package/dist/server/index.d.cts +5 -6
- package/dist/server/index.d.ts +5 -6
- package/dist/server/index.js +5 -5
- package/dist/xenia-sdk.iife.js +3 -3
- package/dist/xenia-sdk.iife.js.map +1 -1
- package/dist/{xenia-server-Cf24mL0c.d.ts → xenia-server-BcgeUHX4.d.ts} +2 -3
- package/dist/{xenia-server-D-bx14lw.d.cts → xenia-server-lv6LtJZP.d.cts} +2 -3
- package/package.json +14 -1
- package/dist/chunk-2DR5AVPX.cjs.map +0 -1
- package/dist/chunk-BQ5PJCZ7.js +0 -179
- package/dist/chunk-BQ5PJCZ7.js.map +0 -1
- package/dist/chunk-GWRDZIVA.js.map +0 -1
- package/dist/chunk-LW5AEJ6K.js.map +0 -1
- package/dist/chunk-N5M4PI7C.cjs +0 -192
- package/dist/chunk-N5M4PI7C.cjs.map +0 -1
- package/dist/chunk-OMMIRSPK.js.map +0 -1
- package/dist/chunk-RWE2OCQL.cjs.map +0 -1
- package/dist/chunk-YLMMTKQL.cjs.map +0 -1
- package/dist/personalize-content-Cwc2AomC.d.ts +0 -141
- package/dist/personalize-content-CxSdplMV.d.cts +0 -141
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/cms/contentstack.ts"],"names":[],"mappings":"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","file":"contentstack.js","sourcesContent":["/**\n * Contentstack adapter for {@link personalizeContent} / {@link personalizePage}.\n *\n * Talks to the Content Delivery API with plain `fetch` (no SDK dependency). Pages and replacements\n * are fetched with `include_all=true`, so references come back as nested entries at any depth up to\n * `includeDepth` — the page resolver walks them like any other content.\n *\n * Expects the one field Xenia adds to personalized content types: `xenia`, a single-line text field\n * holding the marker JSON. Static replacements are named in the marker (`ids` + `ct`) and fetched\n * from the delivery API in the same round as runtime ones — no Xenia call for them.\n */\n\nimport type { CmsAdapter, PageRef } from \"../core/personalize-content\";\nimport type { EntryFilter, EntryQuery } from \"../core/types\";\nimport { parseMarker } from \"./marker\";\n\nexport interface ContentstackAdapterOptions {\n /** The stack API key. */\n apiKey: string;\n deliveryToken: string;\n environment: string;\n /** `us` (default), `eu`, `au`, `azure-na`, `azure-eu`, `gcp-na`, `gcp-eu`. Ignored with `host`. */\n region?: string;\n /** Delivery host, e.g. `eu-cdn.contentstack.com` (overrides `region`). */\n host?: string;\n branch?: string;\n /** Reference depth fetched with pages and replacements (1-5). Default 3 — raise it for deeper pages. */\n includeDepth?: number;\n /** The marker field uid, if your content types name it differently. */\n markerField?: string;\n fetch?: typeof fetch;\n}\n\n/** A Contentstack entry as the delivery API returns it (fields sit on the entry itself). */\ntype Entry = Record<string, unknown> & { uid: string; _version?: number; _content_type_uid?: string };\n\nconst MAX_PER_QUERY = 100;\n\nexport function cdnHost(region?: string): string {\n const r = (region ?? \"us\").trim().toLowerCase();\n return r === \"us\" || r === \"na\" || r === \"\" ? \"cdn.contentstack.io\" : `${r}-cdn.contentstack.com`;\n}\n\nexport function contentstackAdapter(options: ContentstackAdapterOptions): CmsAdapter<Entry> {\n const markerField = options.markerField ?? \"xenia\";\n const depth = Math.min(Math.max(options.includeDepth ?? 3, 1), 5);\n const host = options.host ?? cdnHost(options.region);\n\n const entries = async (\n contentType: string,\n query: Record<string, unknown>,\n extra: Record<string, string>,\n locale?: string,\n ): Promise<Entry[]> => {\n const params = new URLSearchParams({\n environment: options.environment,\n include_all: \"true\",\n include_all_depth: String(depth),\n ...(locale ? { locale } : {}),\n ...(Object.keys(query).length ? { query: JSON.stringify(query) } : {}),\n ...extra,\n });\n const url = `https://${host}/v3/content_types/${encodeURIComponent(contentType)}/entries?${params}`;\n const res = await (options.fetch ?? fetch)(url, {\n headers: {\n api_key: options.apiKey,\n access_token: options.deliveryToken,\n ...(options.branch ? { branch: options.branch } : {}),\n },\n });\n if (!res.ok) throw new Error(`Contentstack CDA ${res.status}`);\n const body = (await res.json()) as { entries?: Entry[] };\n // A top-level entry has no `_content_type_uid`; stamp it so the walker and analytics can tell.\n return (body.entries ?? []).map((e) => (e._content_type_uid ? e : { ...e, _content_type_uid: contentType }));\n };\n\n return {\n entryId(value) {\n const v = value as Partial<Entry> | null;\n // Entries carry a version; reference stubs ({uid, _content_type_uid}) don't, assets have a file.\n return v && typeof v.uid === \"string\" && typeof v._version === \"number\" && !(\"filename\" in v) && !(\"is_dir\" in v)\n ? v.uid\n : undefined;\n },\n\n marker: (entry) => parseMarker(entry[markerField]),\n\n // Static replacements travel in the marker (`ids`), never as pinned links.\n staticVariants: () => [],\n\n getField: (entry, field) => entry[field],\n setField(entry, field, value) {\n entry[field] = value;\n },\n\n mergeItems(entry, replacement) {\n for (const [key, value] of Object.entries(replacement)) {\n if (Array.isArray(value) && key !== \"tags\") entry[key] = value;\n }\n },\n\n clearMarker(entry) {\n delete entry[markerField];\n },\n\n async fetchEntries(ids, locale, types) {\n const byType = new Map<string, string[]>();\n for (const id of ids) {\n const ct = types?.[id];\n if (ct) byType.set(ct, [...(byType.get(ct) ?? []), id]);\n }\n // ponytail: an id with no known content type is skipped (Contentstack can't fetch it); Xenia\n // sends the type for every replacement it knows, so this only hits swaps saved without one.\n const results = await Promise.all(\n [...byType].map(([ct, list]) =>\n entries(ct, { uid: { $in: list } }, { limit: String(Math.min(list.length, MAX_PER_QUERY)) }, locale),\n ),\n );\n return results.flat();\n },\n\n queryEntries(query: EntryQuery, locale) {\n const q: Record<string, unknown> = {};\n for (const f of query.filters ?? []) Object.assign(q, filterQuery(f));\n const extra: Record<string, string> = { limit: String(Math.min(query.limit, MAX_PER_QUERY)) };\n if (query.order) {\n const desc = query.order.startsWith(\"-\");\n extra[desc ? \"desc\" : \"asc\"] = fieldPath(desc ? query.order.slice(1) : query.order);\n }\n return entries(query.contentType, q, extra, locale);\n },\n\n async fetchPage(ref: PageRef, locale) {\n if (typeof ref === \"string\") {\n throw new Error(\"contentstackAdapter: a page ref needs its content type — pass { contentType, fields }\");\n }\n const found = await entries(ref.contentType, { ...ref.fields }, { limit: \"1\" }, locale);\n return found[0] ?? null;\n },\n };\n}\n\n/** CMS-neutral field path → Contentstack's (`fields.` / `sys.` prefixes from shared presets dropped). */\nfunction fieldPath(field: string): string {\n if (field.startsWith(\"sys.\")) return field === \"sys.id\" ? \"uid\" : field.slice(4).replace(/At$/, \"_at\");\n return field.replace(/^fields\\./, \"\");\n}\n\n/** CMS-neutral filter → Contentstack query operator (`{category: {$in: [...]}}`). */\nfunction filterQuery(f: EntryFilter): Record<string, unknown> {\n const path = fieldPath(f.field);\n const num = (v: string) => (v.trim() !== \"\" && !Number.isNaN(Number(v)) ? Number(v) : v);\n const list = () => f.value.split(\",\").map((v) => v.trim()).filter(Boolean);\n switch (f.op ?? \"eq\") {\n case \"ne\":\n return { [path]: { $ne: f.value } };\n case \"in\":\n return { [path]: { $in: list() } };\n case \"nin\":\n return { [path]: { $nin: list() } };\n case \"exists\":\n return { [path]: { $exists: f.value !== \"false\" } };\n case \"lt\":\n case \"lte\":\n case \"gt\":\n case \"gte\":\n return { [path]: { [`$${f.op}`]: num(f.value) } };\n case \"match\":\n return { [path]: { $regex: f.value, $options: \"i\" } };\n default:\n return { [path]: f.value };\n }\n}\n"]}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { t as PersonalizationSwap, i as EntryDecision } from './personalize-content-D6xeBI7-.cjs';
|
|
2
2
|
|
|
3
3
|
/** Wire types of Xenia's `/api/personalization/edge` endpoints (see the backend's personalization-edge-api.md). */
|
|
4
4
|
|
|
@@ -38,6 +38,11 @@ type ProfileFacts = Record<string, unknown>;
|
|
|
38
38
|
interface DecideResult {
|
|
39
39
|
version: string;
|
|
40
40
|
decisions: EdgeDecision[];
|
|
41
|
+
/**
|
|
42
|
+
* Multi-level CMS personalization: per personalized content key, the decision for every target
|
|
43
|
+
* of the key at every level — the page resolves nested targets from these, no further Xenia call.
|
|
44
|
+
*/
|
|
45
|
+
entryDecisions?: Record<string, EntryDecision[]>;
|
|
41
46
|
profileVersion?: number;
|
|
42
47
|
profile?: ProfileFacts | null;
|
|
43
48
|
}
|
|
@@ -57,14 +62,35 @@ type DecisionSource = "cache" | "xenia" | "timeout" | "error" | "no-manifest";
|
|
|
57
62
|
/** The visitor's location in Xenia's `ViewerGeo` shape — the only keys `/decide` accepts in `facts.geo`. */
|
|
58
63
|
type ViewerGeo = Partial<Record<"country" | "countryName" | "region" | "regionName" | "city" | "postalCode" | "timeZone" | "latitude" | "longitude" | "source", string>>;
|
|
59
64
|
|
|
65
|
+
/**
|
|
66
|
+
* Proxy-side Xenia client for the hybrid personalization flow:
|
|
67
|
+
*
|
|
68
|
+
* 1. `match()` — synchronous, in-memory: does any journey target this page?
|
|
69
|
+
* Answered from the manifest, refreshed in the background every
|
|
70
|
+
* `manifestTtlMs`. Pages no journey targets never call Xenia.
|
|
71
|
+
* 2. `decide()` — for targeted pages only: a per-visitor decision cache hit
|
|
72
|
+
* costs nothing; a miss calls Xenia's `/decide` with a hard `budgetMs`
|
|
73
|
+
* deadline. Timeouts and errors resolve to "no decision" (base content),
|
|
74
|
+
* never an exception — Xenia can't slow down or break a page.
|
|
75
|
+
*
|
|
76
|
+
* Runtime-agnostic (only `fetch`, `AbortSignal`, `Map`). The decision cache is
|
|
77
|
+
* per process; swap in `cache` for a shared store (Redis, edge KV).
|
|
78
|
+
*/
|
|
79
|
+
|
|
60
80
|
/** What a visitor's cached decision holds: the decisions and the profile they were made with. */
|
|
61
81
|
type CachedDecision = {
|
|
62
82
|
decisions: EdgeDecision[];
|
|
83
|
+
entryDecisions?: Record<string, EntryDecision[]>;
|
|
63
84
|
profile?: ProfileFacts | null;
|
|
64
85
|
};
|
|
86
|
+
/**
|
|
87
|
+
* Where visitors' decisions are kept between page views. In memory per process by default; a shared
|
|
88
|
+
* store (Redis, edge KV) may answer asynchronously — a store that fails or throws reads as a miss and
|
|
89
|
+
* never breaks a page view, and writes never hold up the response.
|
|
90
|
+
*/
|
|
65
91
|
type DecisionCache = {
|
|
66
|
-
get(key: string): CachedDecision | undefined
|
|
67
|
-
set(key: string, value: CachedDecision, ttlMs: number): void
|
|
92
|
+
get(key: string): CachedDecision | undefined | Promise<CachedDecision | undefined>;
|
|
93
|
+
set(key: string, value: CachedDecision, ttlMs: number): void | Promise<void>;
|
|
68
94
|
};
|
|
69
95
|
type XeniaEdgeConfig = {
|
|
70
96
|
/** Xenia engine base URL, e.g. `https://engine.xeniamartech.com/xenia`. */
|
|
@@ -114,6 +140,8 @@ type DecideInput = {
|
|
|
114
140
|
};
|
|
115
141
|
type DecideOutcome = {
|
|
116
142
|
decisions: EdgeDecision[];
|
|
143
|
+
/** Multi-level entry decisions per content key (see `DecideResult.entryDecisions`). */
|
|
144
|
+
entryDecisions?: Record<string, EntryDecision[]>;
|
|
117
145
|
/** The profile Xenia decided with (also on cache hits), when profiles are on. */
|
|
118
146
|
profile?: ProfileFacts | null;
|
|
119
147
|
source: DecisionSource;
|
|
@@ -122,6 +150,16 @@ type DecideOutcome = {
|
|
|
122
150
|
};
|
|
123
151
|
/** Bounded in-memory LRU with per-entry expiry. */
|
|
124
152
|
declare function memoryDecisionCache(maxEntries?: number): DecisionCache;
|
|
153
|
+
/**
|
|
154
|
+
* A per-process memory cache in front of a shared store: a returning visitor is answered from memory
|
|
155
|
+
* when this instance served them, else from `shared` (another instance did), and only then by Xenia.
|
|
156
|
+
* Reads from `shared` give up after `timeoutMs` (default 50) — a slow store must not cost more than
|
|
157
|
+
* the decision it saves.
|
|
158
|
+
*/
|
|
159
|
+
declare function tieredDecisionCache(shared: DecisionCache, opts?: {
|
|
160
|
+
memory?: DecisionCache;
|
|
161
|
+
timeoutMs?: number;
|
|
162
|
+
}): DecisionCache;
|
|
125
163
|
declare function createXeniaEdge(config: XeniaEdgeConfig): {
|
|
126
164
|
match: (host: string | null, path: string) => Promise<ManifestKey[] | null>;
|
|
127
165
|
decide: (keys: ManifestKey[], input: DecideInput) => Promise<DecideOutcome>;
|
|
@@ -152,15 +190,37 @@ interface SwapGroup {
|
|
|
152
190
|
/** The swap groups of the personalized decisions, in a stable order (stable cache keys). */
|
|
153
191
|
declare function swapGroups(decisions: EdgeDecision[]): SwapGroup[];
|
|
154
192
|
/**
|
|
155
|
-
* The token for a variant.
|
|
156
|
-
*
|
|
193
|
+
* The token for a variant. `entries` are the multi-level entry decisions of its personalized keys
|
|
194
|
+
* (`DecideOutcome.entryDecisions`), so the page resolves nested CMS targets without calling Xenia.
|
|
195
|
+
* They travel compressed (`ez`), which fits several times as many decisions.
|
|
196
|
+
*
|
|
197
|
+
* Whatever would still push it past the payload limit is left out — first `extra`, then `entries`
|
|
198
|
+
* (counted in `ed`: the page serves those targets' base content and reports itself incomplete) —
|
|
199
|
+
* rather than making a token no page accepts.
|
|
157
200
|
*/
|
|
158
|
-
declare function encodeVariant(groups: SwapGroup[], secret: string, extra?: unknown): Promise<string>;
|
|
159
|
-
/**
|
|
160
|
-
|
|
201
|
+
declare function encodeVariant(groups: SwapGroup[], secret: string, extra?: unknown, entries?: EntryDecision[]): Promise<string>;
|
|
202
|
+
/**
|
|
203
|
+
* The multi-level entry decisions of the personalized `decisions` — what {@link encodeVariant}
|
|
204
|
+
* carries as `entries`.
|
|
205
|
+
*/
|
|
206
|
+
declare function variantEntries(decisions: EdgeDecision[], entryDecisions: Record<string, EntryDecision[]> | undefined): EntryDecision[];
|
|
207
|
+
/**
|
|
208
|
+
* A page resolver (`personalizeContent({ resolve })`) that answers from a variant token's entries:
|
|
209
|
+
* every level of the page's content key decided up front at the edge, so no Xenia call.
|
|
210
|
+
*/
|
|
211
|
+
declare function variantEntryResolver(entries: EntryDecision[] | undefined, contentKey: string): (ids: string[]) => Promise<EntryDecision[]>;
|
|
212
|
+
/**
|
|
213
|
+
* A decoded variant token. `entriesDropped` counts multi-level decisions that didn't fit in the
|
|
214
|
+
* token: their targets show base content.
|
|
215
|
+
*/
|
|
216
|
+
interface DecodedVariant<E = unknown> {
|
|
161
217
|
groups: SwapGroup[];
|
|
162
218
|
extra?: E;
|
|
163
|
-
|
|
219
|
+
entries?: EntryDecision[];
|
|
220
|
+
entriesDropped?: number;
|
|
221
|
+
}
|
|
222
|
+
/** The variant in a token, or `null` if it is malformed or not signed with `secret`. */
|
|
223
|
+
declare function decodeVariant<E = unknown>(token: string, secret: string): Promise<DecodedVariant<E> | null>;
|
|
164
224
|
|
|
165
225
|
/**
|
|
166
226
|
* Facts about a request that journey rules read — where the visitor is, on what device, where they
|
|
@@ -245,5 +305,5 @@ declare function edgeConsent(headers: Headers, opts?: {
|
|
|
245
305
|
respectDnt?: boolean;
|
|
246
306
|
}): EdgeConsent;
|
|
247
307
|
|
|
248
|
-
export {
|
|
249
|
-
export type { CachedDecision as C,
|
|
308
|
+
export { variantEntries as A, variantEntryResolver as B, viewerGeo as F, createXeniaEdge as m, decodeVariant as n, deviceOf as o, edgeConsent as p, encodeVariant as q, isBot as r, isPageLoad as s, isPrefetch as t, landingContext as u, localTime as v, memoryDecisionCache as w, seasonOf as x, swapGroups as y, tieredDecisionCache as z };
|
|
309
|
+
export type { CachedDecision as C, DecodedVariant as D, EdgeConsent as E, GeoHeader as G, LocalTime as L, Manifest as M, PublicManifest as P, SwapGroup as S, TrailView as T, VariantSwap as V, XeniaEdge as X, DecideInput as a, DecideOutcome as b, DecideResult as c, DecisionCache as d, DecisionSource as e, EdgeDecision as f, EdgeFacts as g, EdgeMode as h, ManifestKey as i, ProfileFacts as j, ViewerGeo as k, XeniaEdgeConfig as l };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { t as PersonalizationSwap, i as EntryDecision } from './personalize-content-D6xeBI7-.js';
|
|
2
2
|
|
|
3
3
|
/** Wire types of Xenia's `/api/personalization/edge` endpoints (see the backend's personalization-edge-api.md). */
|
|
4
4
|
|
|
@@ -38,6 +38,11 @@ type ProfileFacts = Record<string, unknown>;
|
|
|
38
38
|
interface DecideResult {
|
|
39
39
|
version: string;
|
|
40
40
|
decisions: EdgeDecision[];
|
|
41
|
+
/**
|
|
42
|
+
* Multi-level CMS personalization: per personalized content key, the decision for every target
|
|
43
|
+
* of the key at every level — the page resolves nested targets from these, no further Xenia call.
|
|
44
|
+
*/
|
|
45
|
+
entryDecisions?: Record<string, EntryDecision[]>;
|
|
41
46
|
profileVersion?: number;
|
|
42
47
|
profile?: ProfileFacts | null;
|
|
43
48
|
}
|
|
@@ -57,14 +62,35 @@ type DecisionSource = "cache" | "xenia" | "timeout" | "error" | "no-manifest";
|
|
|
57
62
|
/** The visitor's location in Xenia's `ViewerGeo` shape — the only keys `/decide` accepts in `facts.geo`. */
|
|
58
63
|
type ViewerGeo = Partial<Record<"country" | "countryName" | "region" | "regionName" | "city" | "postalCode" | "timeZone" | "latitude" | "longitude" | "source", string>>;
|
|
59
64
|
|
|
65
|
+
/**
|
|
66
|
+
* Proxy-side Xenia client for the hybrid personalization flow:
|
|
67
|
+
*
|
|
68
|
+
* 1. `match()` — synchronous, in-memory: does any journey target this page?
|
|
69
|
+
* Answered from the manifest, refreshed in the background every
|
|
70
|
+
* `manifestTtlMs`. Pages no journey targets never call Xenia.
|
|
71
|
+
* 2. `decide()` — for targeted pages only: a per-visitor decision cache hit
|
|
72
|
+
* costs nothing; a miss calls Xenia's `/decide` with a hard `budgetMs`
|
|
73
|
+
* deadline. Timeouts and errors resolve to "no decision" (base content),
|
|
74
|
+
* never an exception — Xenia can't slow down or break a page.
|
|
75
|
+
*
|
|
76
|
+
* Runtime-agnostic (only `fetch`, `AbortSignal`, `Map`). The decision cache is
|
|
77
|
+
* per process; swap in `cache` for a shared store (Redis, edge KV).
|
|
78
|
+
*/
|
|
79
|
+
|
|
60
80
|
/** What a visitor's cached decision holds: the decisions and the profile they were made with. */
|
|
61
81
|
type CachedDecision = {
|
|
62
82
|
decisions: EdgeDecision[];
|
|
83
|
+
entryDecisions?: Record<string, EntryDecision[]>;
|
|
63
84
|
profile?: ProfileFacts | null;
|
|
64
85
|
};
|
|
86
|
+
/**
|
|
87
|
+
* Where visitors' decisions are kept between page views. In memory per process by default; a shared
|
|
88
|
+
* store (Redis, edge KV) may answer asynchronously — a store that fails or throws reads as a miss and
|
|
89
|
+
* never breaks a page view, and writes never hold up the response.
|
|
90
|
+
*/
|
|
65
91
|
type DecisionCache = {
|
|
66
|
-
get(key: string): CachedDecision | undefined
|
|
67
|
-
set(key: string, value: CachedDecision, ttlMs: number): void
|
|
92
|
+
get(key: string): CachedDecision | undefined | Promise<CachedDecision | undefined>;
|
|
93
|
+
set(key: string, value: CachedDecision, ttlMs: number): void | Promise<void>;
|
|
68
94
|
};
|
|
69
95
|
type XeniaEdgeConfig = {
|
|
70
96
|
/** Xenia engine base URL, e.g. `https://engine.xeniamartech.com/xenia`. */
|
|
@@ -114,6 +140,8 @@ type DecideInput = {
|
|
|
114
140
|
};
|
|
115
141
|
type DecideOutcome = {
|
|
116
142
|
decisions: EdgeDecision[];
|
|
143
|
+
/** Multi-level entry decisions per content key (see `DecideResult.entryDecisions`). */
|
|
144
|
+
entryDecisions?: Record<string, EntryDecision[]>;
|
|
117
145
|
/** The profile Xenia decided with (also on cache hits), when profiles are on. */
|
|
118
146
|
profile?: ProfileFacts | null;
|
|
119
147
|
source: DecisionSource;
|
|
@@ -122,6 +150,16 @@ type DecideOutcome = {
|
|
|
122
150
|
};
|
|
123
151
|
/** Bounded in-memory LRU with per-entry expiry. */
|
|
124
152
|
declare function memoryDecisionCache(maxEntries?: number): DecisionCache;
|
|
153
|
+
/**
|
|
154
|
+
* A per-process memory cache in front of a shared store: a returning visitor is answered from memory
|
|
155
|
+
* when this instance served them, else from `shared` (another instance did), and only then by Xenia.
|
|
156
|
+
* Reads from `shared` give up after `timeoutMs` (default 50) — a slow store must not cost more than
|
|
157
|
+
* the decision it saves.
|
|
158
|
+
*/
|
|
159
|
+
declare function tieredDecisionCache(shared: DecisionCache, opts?: {
|
|
160
|
+
memory?: DecisionCache;
|
|
161
|
+
timeoutMs?: number;
|
|
162
|
+
}): DecisionCache;
|
|
125
163
|
declare function createXeniaEdge(config: XeniaEdgeConfig): {
|
|
126
164
|
match: (host: string | null, path: string) => Promise<ManifestKey[] | null>;
|
|
127
165
|
decide: (keys: ManifestKey[], input: DecideInput) => Promise<DecideOutcome>;
|
|
@@ -152,15 +190,37 @@ interface SwapGroup {
|
|
|
152
190
|
/** The swap groups of the personalized decisions, in a stable order (stable cache keys). */
|
|
153
191
|
declare function swapGroups(decisions: EdgeDecision[]): SwapGroup[];
|
|
154
192
|
/**
|
|
155
|
-
* The token for a variant.
|
|
156
|
-
*
|
|
193
|
+
* The token for a variant. `entries` are the multi-level entry decisions of its personalized keys
|
|
194
|
+
* (`DecideOutcome.entryDecisions`), so the page resolves nested CMS targets without calling Xenia.
|
|
195
|
+
* They travel compressed (`ez`), which fits several times as many decisions.
|
|
196
|
+
*
|
|
197
|
+
* Whatever would still push it past the payload limit is left out — first `extra`, then `entries`
|
|
198
|
+
* (counted in `ed`: the page serves those targets' base content and reports itself incomplete) —
|
|
199
|
+
* rather than making a token no page accepts.
|
|
157
200
|
*/
|
|
158
|
-
declare function encodeVariant(groups: SwapGroup[], secret: string, extra?: unknown): Promise<string>;
|
|
159
|
-
/**
|
|
160
|
-
|
|
201
|
+
declare function encodeVariant(groups: SwapGroup[], secret: string, extra?: unknown, entries?: EntryDecision[]): Promise<string>;
|
|
202
|
+
/**
|
|
203
|
+
* The multi-level entry decisions of the personalized `decisions` — what {@link encodeVariant}
|
|
204
|
+
* carries as `entries`.
|
|
205
|
+
*/
|
|
206
|
+
declare function variantEntries(decisions: EdgeDecision[], entryDecisions: Record<string, EntryDecision[]> | undefined): EntryDecision[];
|
|
207
|
+
/**
|
|
208
|
+
* A page resolver (`personalizeContent({ resolve })`) that answers from a variant token's entries:
|
|
209
|
+
* every level of the page's content key decided up front at the edge, so no Xenia call.
|
|
210
|
+
*/
|
|
211
|
+
declare function variantEntryResolver(entries: EntryDecision[] | undefined, contentKey: string): (ids: string[]) => Promise<EntryDecision[]>;
|
|
212
|
+
/**
|
|
213
|
+
* A decoded variant token. `entriesDropped` counts multi-level decisions that didn't fit in the
|
|
214
|
+
* token: their targets show base content.
|
|
215
|
+
*/
|
|
216
|
+
interface DecodedVariant<E = unknown> {
|
|
161
217
|
groups: SwapGroup[];
|
|
162
218
|
extra?: E;
|
|
163
|
-
|
|
219
|
+
entries?: EntryDecision[];
|
|
220
|
+
entriesDropped?: number;
|
|
221
|
+
}
|
|
222
|
+
/** The variant in a token, or `null` if it is malformed or not signed with `secret`. */
|
|
223
|
+
declare function decodeVariant<E = unknown>(token: string, secret: string): Promise<DecodedVariant<E> | null>;
|
|
164
224
|
|
|
165
225
|
/**
|
|
166
226
|
* Facts about a request that journey rules read — where the visitor is, on what device, where they
|
|
@@ -245,5 +305,5 @@ declare function edgeConsent(headers: Headers, opts?: {
|
|
|
245
305
|
respectDnt?: boolean;
|
|
246
306
|
}): EdgeConsent;
|
|
247
307
|
|
|
248
|
-
export {
|
|
249
|
-
export type { CachedDecision as C,
|
|
308
|
+
export { variantEntries as A, variantEntryResolver as B, viewerGeo as F, createXeniaEdge as m, decodeVariant as n, deviceOf as o, edgeConsent as p, encodeVariant as q, isBot as r, isPageLoad as s, isPrefetch as t, landingContext as u, localTime as v, memoryDecisionCache as w, seasonOf as x, swapGroups as y, tieredDecisionCache as z };
|
|
309
|
+
export type { CachedDecision as C, DecodedVariant as D, EdgeConsent as E, GeoHeader as G, LocalTime as L, Manifest as M, PublicManifest as P, SwapGroup as S, TrailView as T, VariantSwap as V, XeniaEdge as X, DecideInput as a, DecideOutcome as b, DecideResult as c, DecisionCache as d, DecisionSource as e, EdgeDecision as f, EdgeFacts as g, EdgeMode as h, ManifestKey as i, ProfileFacts as j, ViewerGeo as k, XeniaEdgeConfig as l };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { y as RetryConfig, g as CircuitBreakerConfig, H as HttpMethod, J as JsonValue, K as XeniaAction, h as ConsentState } from './personalize-content-D6xeBI7-.cjs';
|
|
2
2
|
|
|
3
3
|
/** Returns true when running in a browser environment (SSR-safe). */
|
|
4
4
|
declare function isBrowser(): boolean;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { y as RetryConfig, g as CircuitBreakerConfig, H as HttpMethod, J as JsonValue, K as XeniaAction, h as ConsentState } from './personalize-content-D6xeBI7-.js';
|
|
2
2
|
|
|
3
3
|
/** Returns true when running in a browser environment (SSR-safe). */
|
|
4
4
|
declare function isBrowser(): boolean;
|
package/dist/edge/index.cjs
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var
|
|
4
|
-
require('../chunk-
|
|
5
|
-
require('../chunk-
|
|
3
|
+
var chunkWMGCQPHY_cjs = require('../chunk-WMGCQPHY.cjs');
|
|
4
|
+
require('../chunk-UUIKUU5W.cjs');
|
|
5
|
+
var chunkGXJLU3RE_cjs = require('../chunk-GXJLU3RE.cjs');
|
|
6
6
|
|
|
7
7
|
// src/edge/matcher.ts
|
|
8
8
|
function normalizePath(path) {
|
|
@@ -61,6 +61,43 @@ function memoryDecisionCache(maxEntries = 1e4) {
|
|
|
61
61
|
}
|
|
62
62
|
};
|
|
63
63
|
}
|
|
64
|
+
async function readCache(cache, key) {
|
|
65
|
+
try {
|
|
66
|
+
return await cache.get(key);
|
|
67
|
+
} catch {
|
|
68
|
+
return void 0;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
function writeCache(cache, key, value, ttlMs) {
|
|
72
|
+
try {
|
|
73
|
+
void Promise.resolve(cache.set(key, value, ttlMs)).catch(() => {
|
|
74
|
+
});
|
|
75
|
+
} catch {
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
function tieredDecisionCache(shared, opts = {}) {
|
|
79
|
+
const memory = opts.memory ?? memoryDecisionCache();
|
|
80
|
+
const timeoutMs = opts.timeoutMs ?? 50;
|
|
81
|
+
return {
|
|
82
|
+
async get(key) {
|
|
83
|
+
const local = await readCache(memory, key);
|
|
84
|
+
if (local) return local;
|
|
85
|
+
let timer;
|
|
86
|
+
const hit = await Promise.race([
|
|
87
|
+
readCache(shared, key),
|
|
88
|
+
new Promise((resolve) => {
|
|
89
|
+
timer = setTimeout(() => resolve(void 0), timeoutMs);
|
|
90
|
+
})
|
|
91
|
+
]).finally(() => clearTimeout(timer));
|
|
92
|
+
if (hit) writeCache(memory, key, hit, 15e3);
|
|
93
|
+
return hit;
|
|
94
|
+
},
|
|
95
|
+
set(key, value, ttlMs) {
|
|
96
|
+
writeCache(memory, key, value, ttlMs);
|
|
97
|
+
writeCache(shared, key, value, ttlMs);
|
|
98
|
+
}
|
|
99
|
+
};
|
|
100
|
+
}
|
|
64
101
|
var stableJson = (facts) => JSON.stringify(
|
|
65
102
|
Object.keys(facts ?? {}).sort().map((k) => [k, facts[k]])
|
|
66
103
|
);
|
|
@@ -140,8 +177,10 @@ function createXeniaEdge(config) {
|
|
|
140
177
|
const contentKeys = keys.map((k) => k.contentKey).filter(Boolean).sort();
|
|
141
178
|
const scope = contentKeys.length ? contentKeys.join(",") : keys.flatMap((k) => k.pages).sort().join(",");
|
|
142
179
|
const cacheKey = [version, input.visitorId, scope, stableJson(input.cacheFacts)].join("|");
|
|
143
|
-
const cached = cache
|
|
144
|
-
if (cached)
|
|
180
|
+
const cached = await readCache(cache, cacheKey);
|
|
181
|
+
if (cached) {
|
|
182
|
+
return { decisions: cached.decisions, entryDecisions: cached.entryDecisions, profile: cached.profile, source: "cache", ms: 0 };
|
|
183
|
+
}
|
|
145
184
|
const pending = inflight.get(cacheKey);
|
|
146
185
|
if (pending) return pending;
|
|
147
186
|
const call = (async () => {
|
|
@@ -168,12 +207,12 @@ function createXeniaEdge(config) {
|
|
|
168
207
|
const result = await res.json();
|
|
169
208
|
if (!Array.isArray(result?.decisions)) throw new Error("decide: reply has no decisions list");
|
|
170
209
|
const decisions = result.decisions.filter((d) => d && Array.isArray(d.swaps));
|
|
171
|
-
const answer = { decisions, profile: result.profile };
|
|
172
|
-
cache
|
|
210
|
+
const answer = { decisions, entryDecisions: result.entryDecisions, profile: result.profile };
|
|
211
|
+
writeCache(cache, cacheKey, answer, input.ttlMs?.(answer) ?? (provisional(result.profile) ? provisionalDecisionTtlMs : decisionTtlMs));
|
|
173
212
|
if (manifest && result.version && result.version !== manifest.version) {
|
|
174
213
|
void refreshManifest();
|
|
175
214
|
}
|
|
176
|
-
return { decisions, profile: result.profile, source: "xenia", ms: Date.now() - started };
|
|
215
|
+
return { decisions, entryDecisions: result.entryDecisions, profile: result.profile, source: "xenia", ms: Date.now() - started };
|
|
177
216
|
} catch (e) {
|
|
178
217
|
const timedOut = e instanceof DOMException && e.name === "TimeoutError";
|
|
179
218
|
if (!timedOut) onError(e, "decide");
|
|
@@ -188,93 +227,147 @@ function createXeniaEdge(config) {
|
|
|
188
227
|
return { match, decide, refreshManifest };
|
|
189
228
|
}
|
|
190
229
|
|
|
230
|
+
// src/edge/variant-page.ts
|
|
231
|
+
async function personalizeVariantContent(content, variant, options) {
|
|
232
|
+
const entries = variant?.entries ?? [];
|
|
233
|
+
const keys = options.contentKey ? [options.contentKey] : [...new Set(entries.map((e) => e.contentKey))];
|
|
234
|
+
let result = { content, applied: [], complete: true };
|
|
235
|
+
for (const contentKey of keys) {
|
|
236
|
+
const next = await chunkGXJLU3RE_cjs.personalizeContent(result.content, {
|
|
237
|
+
...options,
|
|
238
|
+
contentKey,
|
|
239
|
+
resolve: chunkWMGCQPHY_cjs.variantEntryResolver(entries, contentKey),
|
|
240
|
+
// Each key's run copies the page unless asked not to; after the first, ours is already a copy.
|
|
241
|
+
inPlace: result.content !== content || options.inPlace
|
|
242
|
+
});
|
|
243
|
+
result = {
|
|
244
|
+
content: next.content,
|
|
245
|
+
applied: [...result.applied, ...next.applied],
|
|
246
|
+
complete: result.complete && next.complete,
|
|
247
|
+
...next.error !== void 0 || result.error !== void 0 ? { error: next.error ?? result.error } : {}
|
|
248
|
+
};
|
|
249
|
+
}
|
|
250
|
+
return variant?.entriesDropped ? { ...result, complete: false } : result;
|
|
251
|
+
}
|
|
252
|
+
async function personalizeVariantPage(token, ref, options) {
|
|
253
|
+
const { secret, ...rest } = options;
|
|
254
|
+
const variant = await chunkWMGCQPHY_cjs.decodeVariant(safeDecode(token), secret);
|
|
255
|
+
let page;
|
|
256
|
+
try {
|
|
257
|
+
if (!rest.cms.fetchPage) throw new Error("This CMS adapter cannot fetch pages; use personalizeVariantContent(page)");
|
|
258
|
+
page = await rest.cms.fetchPage(ref, rest.locale);
|
|
259
|
+
} catch (error) {
|
|
260
|
+
return { content: null, applied: [], complete: false, error, variant };
|
|
261
|
+
}
|
|
262
|
+
if (page == null) return { content: null, applied: [], complete: true, variant };
|
|
263
|
+
return { ...await personalizeVariantContent(page, variant, { ...rest, inPlace: true }), variant };
|
|
264
|
+
}
|
|
265
|
+
function safeDecode(token) {
|
|
266
|
+
try {
|
|
267
|
+
return decodeURIComponent(token);
|
|
268
|
+
} catch {
|
|
269
|
+
return token;
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
|
|
191
273
|
Object.defineProperty(exports, "MAX_TRAIL", {
|
|
192
274
|
enumerable: true,
|
|
193
|
-
get: function () { return
|
|
275
|
+
get: function () { return chunkWMGCQPHY_cjs.MAX_TRAIL; }
|
|
194
276
|
});
|
|
195
277
|
Object.defineProperty(exports, "TRAIL_MAX_AGE_S", {
|
|
196
278
|
enumerable: true,
|
|
197
|
-
get: function () { return
|
|
279
|
+
get: function () { return chunkWMGCQPHY_cjs.TRAIL_MAX_AGE_S; }
|
|
198
280
|
});
|
|
199
281
|
Object.defineProperty(exports, "decodeVariant", {
|
|
200
282
|
enumerable: true,
|
|
201
|
-
get: function () { return
|
|
283
|
+
get: function () { return chunkWMGCQPHY_cjs.decodeVariant; }
|
|
202
284
|
});
|
|
203
285
|
Object.defineProperty(exports, "deviceOf", {
|
|
204
286
|
enumerable: true,
|
|
205
|
-
get: function () { return
|
|
287
|
+
get: function () { return chunkWMGCQPHY_cjs.deviceOf; }
|
|
206
288
|
});
|
|
207
289
|
Object.defineProperty(exports, "edgeConsent", {
|
|
208
290
|
enumerable: true,
|
|
209
|
-
get: function () { return
|
|
291
|
+
get: function () { return chunkWMGCQPHY_cjs.edgeConsent; }
|
|
210
292
|
});
|
|
211
293
|
Object.defineProperty(exports, "encodeVariant", {
|
|
212
294
|
enumerable: true,
|
|
213
|
-
get: function () { return
|
|
295
|
+
get: function () { return chunkWMGCQPHY_cjs.encodeVariant; }
|
|
214
296
|
});
|
|
215
297
|
Object.defineProperty(exports, "fetchManifest", {
|
|
216
298
|
enumerable: true,
|
|
217
|
-
get: function () { return
|
|
299
|
+
get: function () { return chunkWMGCQPHY_cjs.fetchManifest; }
|
|
218
300
|
});
|
|
219
301
|
Object.defineProperty(exports, "isBot", {
|
|
220
302
|
enumerable: true,
|
|
221
|
-
get: function () { return
|
|
303
|
+
get: function () { return chunkWMGCQPHY_cjs.isBot; }
|
|
222
304
|
});
|
|
223
305
|
Object.defineProperty(exports, "isPageLoad", {
|
|
224
306
|
enumerable: true,
|
|
225
|
-
get: function () { return
|
|
307
|
+
get: function () { return chunkWMGCQPHY_cjs.isPageLoad; }
|
|
226
308
|
});
|
|
227
309
|
Object.defineProperty(exports, "isPrefetch", {
|
|
228
310
|
enumerable: true,
|
|
229
|
-
get: function () { return
|
|
311
|
+
get: function () { return chunkWMGCQPHY_cjs.isPrefetch; }
|
|
230
312
|
});
|
|
231
313
|
Object.defineProperty(exports, "landingContext", {
|
|
232
314
|
enumerable: true,
|
|
233
|
-
get: function () { return
|
|
315
|
+
get: function () { return chunkWMGCQPHY_cjs.landingContext; }
|
|
234
316
|
});
|
|
235
317
|
Object.defineProperty(exports, "localTime", {
|
|
236
318
|
enumerable: true,
|
|
237
|
-
get: function () { return
|
|
319
|
+
get: function () { return chunkWMGCQPHY_cjs.localTime; }
|
|
238
320
|
});
|
|
239
321
|
Object.defineProperty(exports, "readTrail", {
|
|
240
322
|
enumerable: true,
|
|
241
|
-
get: function () { return
|
|
323
|
+
get: function () { return chunkWMGCQPHY_cjs.readTrail; }
|
|
242
324
|
});
|
|
243
325
|
Object.defineProperty(exports, "seasonOf", {
|
|
244
326
|
enumerable: true,
|
|
245
|
-
get: function () { return
|
|
327
|
+
get: function () { return chunkWMGCQPHY_cjs.seasonOf; }
|
|
246
328
|
});
|
|
247
329
|
Object.defineProperty(exports, "swapGroups", {
|
|
248
330
|
enumerable: true,
|
|
249
|
-
get: function () { return
|
|
331
|
+
get: function () { return chunkWMGCQPHY_cjs.swapGroups; }
|
|
250
332
|
});
|
|
251
333
|
Object.defineProperty(exports, "trailCookieName", {
|
|
252
334
|
enumerable: true,
|
|
253
|
-
get: function () { return
|
|
335
|
+
get: function () { return chunkWMGCQPHY_cjs.trailCookieName; }
|
|
254
336
|
});
|
|
255
337
|
Object.defineProperty(exports, "trailCookieValue", {
|
|
256
338
|
enumerable: true,
|
|
257
|
-
get: function () { return
|
|
339
|
+
get: function () { return chunkWMGCQPHY_cjs.trailCookieValue; }
|
|
258
340
|
});
|
|
259
341
|
Object.defineProperty(exports, "trimManifest", {
|
|
260
342
|
enumerable: true,
|
|
261
|
-
get: function () { return
|
|
343
|
+
get: function () { return chunkWMGCQPHY_cjs.trimManifest; }
|
|
344
|
+
});
|
|
345
|
+
Object.defineProperty(exports, "variantEntries", {
|
|
346
|
+
enumerable: true,
|
|
347
|
+
get: function () { return chunkWMGCQPHY_cjs.variantEntries; }
|
|
348
|
+
});
|
|
349
|
+
Object.defineProperty(exports, "variantEntryResolver", {
|
|
350
|
+
enumerable: true,
|
|
351
|
+
get: function () { return chunkWMGCQPHY_cjs.variantEntryResolver; }
|
|
262
352
|
});
|
|
263
353
|
Object.defineProperty(exports, "verifyWebhook", {
|
|
264
354
|
enumerable: true,
|
|
265
|
-
get: function () { return
|
|
355
|
+
get: function () { return chunkWMGCQPHY_cjs.verifyWebhook; }
|
|
266
356
|
});
|
|
267
357
|
Object.defineProperty(exports, "viewerGeo", {
|
|
268
358
|
enumerable: true,
|
|
269
|
-
get: function () { return
|
|
359
|
+
get: function () { return chunkWMGCQPHY_cjs.viewerGeo; }
|
|
270
360
|
});
|
|
271
361
|
Object.defineProperty(exports, "withView", {
|
|
272
362
|
enumerable: true,
|
|
273
|
-
get: function () { return
|
|
363
|
+
get: function () { return chunkWMGCQPHY_cjs.withView; }
|
|
274
364
|
});
|
|
275
365
|
exports.createXeniaEdge = createXeniaEdge;
|
|
276
366
|
exports.matchesAny = matchesAny;
|
|
277
367
|
exports.memoryDecisionCache = memoryDecisionCache;
|
|
278
368
|
exports.normalizePath = normalizePath;
|
|
369
|
+
exports.personalizeVariantContent = personalizeVariantContent;
|
|
370
|
+
exports.personalizeVariantPage = personalizeVariantPage;
|
|
371
|
+
exports.tieredDecisionCache = tieredDecisionCache;
|
|
279
372
|
//# sourceMappingURL=index.cjs.map
|
|
280
373
|
//# sourceMappingURL=index.cjs.map
|