@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.
Files changed (96) hide show
  1. package/README.md +70 -6
  2. package/dist/{actions-35V37IT0.d.cts → actions--1_9Z8ig.d.cts} +13 -2
  3. package/dist/{actions-35V37IT0.d.ts → actions--1_9Z8ig.d.ts} +13 -2
  4. package/dist/{api-0IycTYJR.d.cts → api-DbUxT38T.d.ts} +1 -1
  5. package/dist/{api-COXWLL5L.d.ts → api-DgzJ5s-k.d.cts} +1 -1
  6. package/dist/chunk-DPXINHQR.cjs +34 -0
  7. package/dist/chunk-DPXINHQR.cjs.map +1 -0
  8. package/dist/chunk-FSJMHXS6.js +432 -0
  9. package/dist/chunk-FSJMHXS6.js.map +1 -0
  10. package/dist/chunk-GXJLU3RE.cjs +448 -0
  11. package/dist/chunk-GXJLU3RE.cjs.map +1 -0
  12. package/dist/{chunk-2DR5AVPX.cjs → chunk-I5EHLRSR.cjs} +11 -10
  13. package/dist/chunk-I5EHLRSR.cjs.map +1 -0
  14. package/dist/{chunk-LW5AEJ6K.js → chunk-MEULT5X6.js} +4 -241
  15. package/dist/chunk-MEULT5X6.js.map +1 -0
  16. package/dist/{chunk-GWRDZIVA.js → chunk-NDRRSRTU.js} +4 -3
  17. package/dist/chunk-NDRRSRTU.js.map +1 -0
  18. package/dist/chunk-RC46KXDM.js +32 -0
  19. package/dist/chunk-RC46KXDM.js.map +1 -0
  20. package/dist/{chunk-RWE2OCQL.cjs → chunk-SU25SELD.cjs} +4 -244
  21. package/dist/chunk-SU25SELD.cjs.map +1 -0
  22. package/dist/{chunk-WHGK55JP.cjs → chunk-UUIKUU5W.cjs} +5 -5
  23. package/dist/{chunk-WHGK55JP.cjs.map → chunk-UUIKUU5W.cjs.map} +1 -1
  24. package/dist/{chunk-QEYONKWY.js → chunk-UVTFGQR6.js} +3 -3
  25. package/dist/{chunk-QEYONKWY.js.map → chunk-UVTFGQR6.js.map} +1 -1
  26. package/dist/{chunk-OMMIRSPK.js → chunk-V4EGU3PM.js} +48 -11
  27. package/dist/chunk-V4EGU3PM.js.map +1 -0
  28. package/dist/{chunk-YLMMTKQL.cjs → chunk-WMGCQPHY.cjs} +52 -13
  29. package/dist/chunk-WMGCQPHY.cjs.map +1 -0
  30. package/dist/client/index.cjs +26 -10
  31. package/dist/client/index.cjs.map +1 -1
  32. package/dist/client/index.d.cts +2 -2
  33. package/dist/client/index.d.ts +2 -2
  34. package/dist/client/index.js +26 -10
  35. package/dist/client/index.js.map +1 -1
  36. package/dist/client/react/index.cjs +73 -15
  37. package/dist/client/react/index.cjs.map +1 -1
  38. package/dist/client/react/index.d.cts +9 -3
  39. package/dist/client/react/index.d.ts +9 -3
  40. package/dist/client/react/index.js +73 -15
  41. package/dist/client/react/index.js.map +1 -1
  42. package/dist/cms/contentful.cjs +7 -24
  43. package/dist/cms/contentful.cjs.map +1 -1
  44. package/dist/cms/contentful.d.cts +5 -4
  45. package/dist/cms/contentful.d.ts +5 -4
  46. package/dist/cms/contentful.js +4 -23
  47. package/dist/cms/contentful.js.map +1 -1
  48. package/dist/cms/contentstack.cjs +120 -0
  49. package/dist/cms/contentstack.cjs.map +1 -0
  50. package/dist/cms/contentstack.d.cts +41 -0
  51. package/dist/cms/contentstack.d.ts +41 -0
  52. package/dist/cms/contentstack.js +117 -0
  53. package/dist/cms/contentstack.js.map +1 -0
  54. package/dist/{consent-DRCV0N0d.d.ts → consent-Dtiqzz2B.d.cts} +71 -11
  55. package/dist/{consent-BgAETCkL.d.cts → consent-byYiDMFr.d.ts} +71 -11
  56. package/dist/{cookie-names-DU3MtcNG.d.ts → cookie-names-B4hda2rX.d.cts} +1 -1
  57. package/dist/{cookie-names-DFItnSLK.d.cts → cookie-names-DioZXuV0.d.ts} +1 -1
  58. package/dist/edge/index.cjs +122 -29
  59. package/dist/edge/index.cjs.map +1 -1
  60. package/dist/edge/index.d.cts +42 -6
  61. package/dist/edge/index.d.ts +42 -6
  62. package/dist/edge/index.js +92 -9
  63. package/dist/edge/index.js.map +1 -1
  64. package/dist/index.cjs +31 -31
  65. package/dist/index.d.cts +4 -5
  66. package/dist/index.d.ts +4 -5
  67. package/dist/index.js +2 -2
  68. package/dist/next/index.cjs +39 -34
  69. package/dist/next/index.cjs.map +1 -1
  70. package/dist/next/index.d.cts +2 -3
  71. package/dist/next/index.d.ts +2 -3
  72. package/dist/next/index.js +13 -8
  73. package/dist/next/index.js.map +1 -1
  74. package/dist/{types-DgVHS4Qw.d.ts → personalize-content-D6xeBI7-.d.cts} +150 -1
  75. package/dist/{types-DgVHS4Qw.d.cts → personalize-content-D6xeBI7-.d.ts} +150 -1
  76. package/dist/server/index.cjs +30 -30
  77. package/dist/server/index.d.cts +5 -6
  78. package/dist/server/index.d.ts +5 -6
  79. package/dist/server/index.js +5 -5
  80. package/dist/xenia-sdk.iife.js +3 -3
  81. package/dist/xenia-sdk.iife.js.map +1 -1
  82. package/dist/{xenia-server-Cf24mL0c.d.ts → xenia-server-BcgeUHX4.d.ts} +2 -3
  83. package/dist/{xenia-server-D-bx14lw.d.cts → xenia-server-lv6LtJZP.d.cts} +2 -3
  84. package/package.json +14 -1
  85. package/dist/chunk-2DR5AVPX.cjs.map +0 -1
  86. package/dist/chunk-BQ5PJCZ7.js +0 -179
  87. package/dist/chunk-BQ5PJCZ7.js.map +0 -1
  88. package/dist/chunk-GWRDZIVA.js.map +0 -1
  89. package/dist/chunk-LW5AEJ6K.js.map +0 -1
  90. package/dist/chunk-N5M4PI7C.cjs +0 -192
  91. package/dist/chunk-N5M4PI7C.cjs.map +0 -1
  92. package/dist/chunk-OMMIRSPK.js.map +0 -1
  93. package/dist/chunk-RWE2OCQL.cjs.map +0 -1
  94. package/dist/chunk-YLMMTKQL.cjs.map +0 -1
  95. package/dist/personalize-content-Cwc2AomC.d.ts +0 -141
  96. package/dist/personalize-content-CxSdplMV.d.cts +0 -141
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/cms/contentstack.ts"],"names":[],"mappings":";;;AAoCA,IAAM,aAAA,GAAgB,GAAA;AAEf,SAAS,QAAQ,MAAA,EAAyB;AAC/C,EAAA,MAAM,CAAA,GAAA,CAAK,MAAA,IAAU,IAAA,EAAM,IAAA,GAAO,WAAA,EAAY;AAC9C,EAAA,OAAO,CAAA,KAAM,QAAQ,CAAA,KAAM,IAAA,IAAQ,MAAM,EAAA,GAAK,qBAAA,GAAwB,GAAG,CAAC,CAAA,qBAAA,CAAA;AAC5E;AAEO,SAAS,oBAAoB,OAAA,EAAwD;AAC1F,EAAA,MAAM,WAAA,GAAc,QAAQ,WAAA,IAAe,OAAA;AAC3C,EAAA,MAAM,KAAA,GAAQ,IAAA,CAAK,GAAA,CAAI,IAAA,CAAK,GAAA,CAAI,QAAQ,YAAA,IAAgB,CAAA,EAAG,CAAC,CAAA,EAAG,CAAC,CAAA;AAChE,EAAA,MAAM,IAAA,GAAO,OAAA,CAAQ,IAAA,IAAQ,OAAA,CAAQ,QAAQ,MAAM,CAAA;AAEnD,EAAA,MAAM,OAAA,GAAU,OACd,WAAA,EACA,KAAA,EACA,OACA,MAAA,KACqB;AACrB,IAAA,MAAM,MAAA,GAAS,IAAI,eAAA,CAAgB;AAAA,MACjC,aAAa,OAAA,CAAQ,WAAA;AAAA,MACrB,WAAA,EAAa,MAAA;AAAA,MACb,iBAAA,EAAmB,OAAO,KAAK,CAAA;AAAA,MAC/B,GAAI,MAAA,GAAS,EAAE,MAAA,KAAW,EAAC;AAAA,MAC3B,GAAI,MAAA,CAAO,IAAA,CAAK,KAAK,CAAA,CAAE,MAAA,GAAS,EAAE,KAAA,EAAO,IAAA,CAAK,SAAA,CAAU,KAAK,CAAA,KAAM,EAAC;AAAA,MACpE,GAAG;AAAA,KACJ,CAAA;AACD,IAAA,MAAM,GAAA,GAAM,WAAW,IAAI,CAAA,kBAAA,EAAqB,mBAAmB,WAAW,CAAC,YAAY,MAAM,CAAA,CAAA;AACjG,IAAA,MAAM,GAAA,GAAM,MAAA,CAAO,OAAA,CAAQ,KAAA,IAAS,OAAO,GAAA,EAAK;AAAA,MAC9C,OAAA,EAAS;AAAA,QACP,SAAS,OAAA,CAAQ,MAAA;AAAA,QACjB,cAAc,OAAA,CAAQ,aAAA;AAAA,QACtB,GAAI,QAAQ,MAAA,GAAS,EAAE,QAAQ,OAAA,CAAQ,MAAA,KAAW;AAAC;AACrD,KACD,CAAA;AACD,IAAA,IAAI,CAAC,IAAI,EAAA,EAAI,MAAM,IAAI,KAAA,CAAM,CAAA,iBAAA,EAAoB,GAAA,CAAI,MAAM,CAAA,CAAE,CAAA;AAC7D,IAAA,MAAM,IAAA,GAAQ,MAAM,GAAA,CAAI,IAAA,EAAK;AAE7B,IAAA,OAAA,CAAQ,IAAA,CAAK,OAAA,IAAW,EAAC,EAAG,IAAI,CAAC,CAAA,KAAO,CAAA,CAAE,iBAAA,GAAoB,IAAI,EAAE,GAAG,CAAA,EAAG,iBAAA,EAAmB,aAAc,CAAA;AAAA,EAC7G,CAAA;AAEA,EAAA,OAAO;AAAA,IACL,QAAQ,KAAA,EAAO;AACb,MAAA,MAAM,CAAA,GAAI,KAAA;AAEV,MAAA,OAAO,KAAK,OAAO,CAAA,CAAE,GAAA,KAAQ,QAAA,IAAY,OAAO,CAAA,CAAE,QAAA,KAAa,QAAA,IAAY,EAAE,cAAc,CAAA,CAAA,IAAM,EAAE,QAAA,IAAY,CAAA,CAAA,GAC3G,EAAE,GAAA,GACF,MAAA;AAAA,IACN,CAAA;AAAA,IAEA,QAAQ,CAAC,KAAA,KAAU,WAAA,CAAY,KAAA,CAAM,WAAW,CAAC,CAAA;AAAA;AAAA,IAGjD,cAAA,EAAgB,MAAM,EAAC;AAAA,IAEvB,QAAA,EAAU,CAAC,KAAA,EAAO,KAAA,KAAU,MAAM,KAAK,CAAA;AAAA,IACvC,QAAA,CAAS,KAAA,EAAO,KAAA,EAAO,KAAA,EAAO;AAC5B,MAAA,KAAA,CAAM,KAAK,CAAA,GAAI,KAAA;AAAA,IACjB,CAAA;AAAA,IAEA,UAAA,CAAW,OAAO,WAAA,EAAa;AAC7B,MAAA,KAAA,MAAW,CAAC,GAAA,EAAK,KAAK,KAAK,MAAA,CAAO,OAAA,CAAQ,WAAW,CAAA,EAAG;AACtD,QAAA,IAAI,KAAA,CAAM,QAAQ,KAAK,CAAA,IAAK,QAAQ,MAAA,EAAQ,KAAA,CAAM,GAAG,CAAA,GAAI,KAAA;AAAA,MAC3D;AAAA,IACF,CAAA;AAAA,IAEA,YAAY,KAAA,EAAO;AACjB,MAAA,OAAO,MAAM,WAAW,CAAA;AAAA,IAC1B,CAAA;AAAA,IAEA,MAAM,YAAA,CAAa,GAAA,EAAK,MAAA,EAAQ,KAAA,EAAO;AACrC,MAAA,MAAM,MAAA,uBAAa,GAAA,EAAsB;AACzC,MAAA,KAAA,MAAW,MAAM,GAAA,EAAK;AACpB,QAAA,MAAM,EAAA,GAAK,QAAQ,EAAE,CAAA;AACrB,QAAA,IAAI,EAAA,EAAI,MAAA,CAAO,GAAA,CAAI,EAAA,EAAI,CAAC,GAAI,MAAA,CAAO,GAAA,CAAI,EAAE,CAAA,IAAK,EAAC,EAAI,EAAE,CAAC,CAAA;AAAA,MACxD;AAGA,MAAA,MAAM,OAAA,GAAU,MAAM,OAAA,CAAQ,GAAA;AAAA,QAC5B,CAAC,GAAG,MAAM,CAAA,CAAE,GAAA;AAAA,UAAI,CAAC,CAAC,EAAA,EAAI,IAAI,CAAA,KACxB,QAAQ,EAAA,EAAI,EAAE,GAAA,EAAK,EAAE,GAAA,EAAK,IAAA,IAAO,EAAG,EAAE,KAAA,EAAO,MAAA,CAAO,IAAA,CAAK,GAAA,CAAI,IAAA,CAAK,MAAA,EAAQ,aAAa,CAAC,CAAA,EAAE,EAAG,MAAM;AAAA;AACrG,OACF;AACA,MAAA,OAAO,QAAQ,IAAA,EAAK;AAAA,IACtB,CAAA;AAAA,IAEA,YAAA,CAAa,OAAmB,MAAA,EAAQ;AACtC,MAAA,MAAM,IAA6B,EAAC;AACpC,MAAA,KAAA,MAAW,CAAA,IAAK,KAAA,CAAM,OAAA,IAAW,EAAC,SAAU,MAAA,CAAO,CAAA,EAAG,WAAA,CAAY,CAAC,CAAC,CAAA;AACpE,MAAA,MAAM,KAAA,GAAgC,EAAE,KAAA,EAAO,MAAA,CAAO,IAAA,CAAK,IAAI,KAAA,CAAM,KAAA,EAAO,aAAa,CAAC,CAAA,EAAE;AAC5F,MAAA,IAAI,MAAM,KAAA,EAAO;AACf,QAAA,MAAM,IAAA,GAAO,KAAA,CAAM,KAAA,CAAM,UAAA,CAAW,GAAG,CAAA;AACvC,QAAA,KAAA,CAAM,IAAA,GAAO,MAAA,GAAS,KAAK,CAAA,GAAI,SAAA,CAAU,IAAA,GAAO,KAAA,CAAM,KAAA,CAAM,KAAA,CAAM,CAAC,CAAA,GAAI,KAAA,CAAM,KAAK,CAAA;AAAA,MACpF;AACA,MAAA,OAAO,OAAA,CAAQ,KAAA,CAAM,WAAA,EAAa,CAAA,EAAG,OAAO,MAAM,CAAA;AAAA,IACpD,CAAA;AAAA,IAEA,MAAM,SAAA,CAAU,GAAA,EAAc,MAAA,EAAQ;AACpC,MAAA,IAAI,OAAO,QAAQ,QAAA,EAAU;AAC3B,QAAA,MAAM,IAAI,MAAM,4FAAuF,CAAA;AAAA,MACzG;AACA,MAAA,MAAM,KAAA,GAAQ,MAAM,OAAA,CAAQ,GAAA,CAAI,aAAa,EAAE,GAAG,GAAA,CAAI,MAAA,EAAO,EAAG,EAAE,KAAA,EAAO,GAAA,IAAO,MAAM,CAAA;AACtF,MAAA,OAAO,KAAA,CAAM,CAAC,CAAA,IAAK,IAAA;AAAA,IACrB;AAAA,GACF;AACF;AAGA,SAAS,UAAU,KAAA,EAAuB;AACxC,EAAA,IAAI,KAAA,CAAM,UAAA,CAAW,MAAM,CAAA,SAAU,KAAA,KAAU,QAAA,GAAW,KAAA,GAAQ,KAAA,CAAM,KAAA,CAAM,CAAC,CAAA,CAAE,OAAA,CAAQ,OAAO,KAAK,CAAA;AACrG,EAAA,OAAO,KAAA,CAAM,OAAA,CAAQ,WAAA,EAAa,EAAE,CAAA;AACtC;AAGA,SAAS,YAAY,CAAA,EAAyC;AAC5D,EAAA,MAAM,IAAA,GAAO,SAAA,CAAU,CAAA,CAAE,KAAK,CAAA;AAC9B,EAAA,MAAM,MAAM,CAAC,CAAA,KAAe,CAAA,CAAE,IAAA,OAAW,EAAA,IAAM,CAAC,MAAA,CAAO,KAAA,CAAM,OAAO,CAAC,CAAC,CAAA,GAAI,MAAA,CAAO,CAAC,CAAA,GAAI,CAAA;AACtF,EAAA,MAAM,IAAA,GAAO,MAAM,CAAA,CAAE,KAAA,CAAM,MAAM,GAAG,CAAA,CAAE,GAAA,CAAI,CAAC,MAAM,CAAA,CAAE,IAAA,EAAM,CAAA,CAAE,OAAO,OAAO,CAAA;AACzE,EAAA,QAAQ,CAAA,CAAE,MAAM,IAAA;AAAM,IACpB,KAAK,IAAA;AACH,MAAA,OAAO,EAAE,CAAC,IAAI,GAAG,EAAE,GAAA,EAAK,CAAA,CAAE,OAAM,EAAE;AAAA,IACpC,KAAK,IAAA;AACH,MAAA,OAAO,EAAE,CAAC,IAAI,GAAG,EAAE,GAAA,EAAK,IAAA,IAAO,EAAE;AAAA,IACnC,KAAK,KAAA;AACH,MAAA,OAAO,EAAE,CAAC,IAAI,GAAG,EAAE,IAAA,EAAM,IAAA,IAAO,EAAE;AAAA,IACpC,KAAK,QAAA;AACH,MAAA,OAAO,EAAE,CAAC,IAAI,GAAG,EAAE,OAAA,EAAS,CAAA,CAAE,KAAA,KAAU,OAAA,EAAQ,EAAE;AAAA,IACpD,KAAK,IAAA;AAAA,IACL,KAAK,KAAA;AAAA,IACL,KAAK,IAAA;AAAA,IACL,KAAK,KAAA;AACH,MAAA,OAAO,EAAE,CAAC,IAAI,GAAG,EAAE,CAAC,CAAA,CAAA,EAAI,CAAA,CAAE,EAAE,EAAE,GAAG,GAAA,CAAI,CAAA,CAAE,KAAK,GAAE,EAAE;AAAA,IAClD,KAAK,OAAA;AACH,MAAA,OAAO,EAAE,CAAC,IAAI,GAAG,EAAE,QAAQ,CAAA,CAAE,KAAA,EAAO,QAAA,EAAU,GAAA,EAAI,EAAE;AAAA,IACtD;AACE,MAAA,OAAO,EAAE,CAAC,IAAI,GAAG,EAAE,KAAA,EAAM;AAAA;AAE/B","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 { P as PersonalizationSwap } from './types-DgVHS4Qw.js';
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. An `extra` that would push it past the payload limit is left out — the
156
- * page still renders its swaps — rather than making a token no page accepts.
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
- /** The variant in a token, or `null` if it is malformed or not signed with `secret`. */
160
- declare function decodeVariant<E = unknown>(token: string, secret: string): Promise<{
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
- } | null>;
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 { createXeniaEdge as l, decodeVariant as m, deviceOf as n, edgeConsent as o, encodeVariant as p, isBot as q, isPageLoad as r, isPrefetch as s, landingContext as t, localTime as u, memoryDecisionCache as v, seasonOf as w, swapGroups as x, viewerGeo as y };
249
- export type { CachedDecision as C, DecideInput 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, DecideOutcome as a, DecideResult as b, DecisionCache as c, DecisionSource as d, EdgeDecision as e, EdgeFacts as f, EdgeMode as g, ManifestKey as h, ProfileFacts as i, ViewerGeo as j, XeniaEdgeConfig as k };
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 { P as PersonalizationSwap } from './types-DgVHS4Qw.cjs';
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. An `extra` that would push it past the payload limit is left out — the
156
- * page still renders its swaps — rather than making a token no page accepts.
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
- /** The variant in a token, or `null` if it is malformed or not signed with `secret`. */
160
- declare function decodeVariant<E = unknown>(token: string, secret: string): Promise<{
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
- } | null>;
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 { createXeniaEdge as l, decodeVariant as m, deviceOf as n, edgeConsent as o, encodeVariant as p, isBot as q, isPageLoad as r, isPrefetch as s, landingContext as t, localTime as u, memoryDecisionCache as v, seasonOf as w, swapGroups as x, viewerGeo as y };
249
- export type { CachedDecision as C, DecideInput 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, DecideOutcome as a, DecideResult as b, DecisionCache as c, DecisionSource as d, EdgeDecision as e, EdgeFacts as f, EdgeMode as g, ManifestKey as h, ProfileFacts as i, ViewerGeo as j, XeniaEdgeConfig as k };
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 { r as RetryConfig, C as CircuitBreakerConfig, H as HttpMethod, J as JsonValue, X as XeniaAction, d as ConsentState } from './types-DgVHS4Qw.js';
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 { r as RetryConfig, C as CircuitBreakerConfig, H as HttpMethod, J as JsonValue, X as XeniaAction, d as ConsentState } from './types-DgVHS4Qw.cjs';
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;
@@ -1,8 +1,8 @@
1
1
  'use strict';
2
2
 
3
- var chunkYLMMTKQL_cjs = require('../chunk-YLMMTKQL.cjs');
4
- require('../chunk-WHGK55JP.cjs');
5
- require('../chunk-N5M4PI7C.cjs');
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.get(cacheKey);
144
- if (cached) return { decisions: cached.decisions, profile: cached.profile, source: "cache", ms: 0 };
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.set(cacheKey, answer, input.ttlMs?.(answer) ?? (provisional(result.profile) ? provisionalDecisionTtlMs : decisionTtlMs));
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 chunkYLMMTKQL_cjs.MAX_TRAIL; }
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 chunkYLMMTKQL_cjs.TRAIL_MAX_AGE_S; }
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 chunkYLMMTKQL_cjs.decodeVariant; }
283
+ get: function () { return chunkWMGCQPHY_cjs.decodeVariant; }
202
284
  });
203
285
  Object.defineProperty(exports, "deviceOf", {
204
286
  enumerable: true,
205
- get: function () { return chunkYLMMTKQL_cjs.deviceOf; }
287
+ get: function () { return chunkWMGCQPHY_cjs.deviceOf; }
206
288
  });
207
289
  Object.defineProperty(exports, "edgeConsent", {
208
290
  enumerable: true,
209
- get: function () { return chunkYLMMTKQL_cjs.edgeConsent; }
291
+ get: function () { return chunkWMGCQPHY_cjs.edgeConsent; }
210
292
  });
211
293
  Object.defineProperty(exports, "encodeVariant", {
212
294
  enumerable: true,
213
- get: function () { return chunkYLMMTKQL_cjs.encodeVariant; }
295
+ get: function () { return chunkWMGCQPHY_cjs.encodeVariant; }
214
296
  });
215
297
  Object.defineProperty(exports, "fetchManifest", {
216
298
  enumerable: true,
217
- get: function () { return chunkYLMMTKQL_cjs.fetchManifest; }
299
+ get: function () { return chunkWMGCQPHY_cjs.fetchManifest; }
218
300
  });
219
301
  Object.defineProperty(exports, "isBot", {
220
302
  enumerable: true,
221
- get: function () { return chunkYLMMTKQL_cjs.isBot; }
303
+ get: function () { return chunkWMGCQPHY_cjs.isBot; }
222
304
  });
223
305
  Object.defineProperty(exports, "isPageLoad", {
224
306
  enumerable: true,
225
- get: function () { return chunkYLMMTKQL_cjs.isPageLoad; }
307
+ get: function () { return chunkWMGCQPHY_cjs.isPageLoad; }
226
308
  });
227
309
  Object.defineProperty(exports, "isPrefetch", {
228
310
  enumerable: true,
229
- get: function () { return chunkYLMMTKQL_cjs.isPrefetch; }
311
+ get: function () { return chunkWMGCQPHY_cjs.isPrefetch; }
230
312
  });
231
313
  Object.defineProperty(exports, "landingContext", {
232
314
  enumerable: true,
233
- get: function () { return chunkYLMMTKQL_cjs.landingContext; }
315
+ get: function () { return chunkWMGCQPHY_cjs.landingContext; }
234
316
  });
235
317
  Object.defineProperty(exports, "localTime", {
236
318
  enumerable: true,
237
- get: function () { return chunkYLMMTKQL_cjs.localTime; }
319
+ get: function () { return chunkWMGCQPHY_cjs.localTime; }
238
320
  });
239
321
  Object.defineProperty(exports, "readTrail", {
240
322
  enumerable: true,
241
- get: function () { return chunkYLMMTKQL_cjs.readTrail; }
323
+ get: function () { return chunkWMGCQPHY_cjs.readTrail; }
242
324
  });
243
325
  Object.defineProperty(exports, "seasonOf", {
244
326
  enumerable: true,
245
- get: function () { return chunkYLMMTKQL_cjs.seasonOf; }
327
+ get: function () { return chunkWMGCQPHY_cjs.seasonOf; }
246
328
  });
247
329
  Object.defineProperty(exports, "swapGroups", {
248
330
  enumerable: true,
249
- get: function () { return chunkYLMMTKQL_cjs.swapGroups; }
331
+ get: function () { return chunkWMGCQPHY_cjs.swapGroups; }
250
332
  });
251
333
  Object.defineProperty(exports, "trailCookieName", {
252
334
  enumerable: true,
253
- get: function () { return chunkYLMMTKQL_cjs.trailCookieName; }
335
+ get: function () { return chunkWMGCQPHY_cjs.trailCookieName; }
254
336
  });
255
337
  Object.defineProperty(exports, "trailCookieValue", {
256
338
  enumerable: true,
257
- get: function () { return chunkYLMMTKQL_cjs.trailCookieValue; }
339
+ get: function () { return chunkWMGCQPHY_cjs.trailCookieValue; }
258
340
  });
259
341
  Object.defineProperty(exports, "trimManifest", {
260
342
  enumerable: true,
261
- get: function () { return chunkYLMMTKQL_cjs.trimManifest; }
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 chunkYLMMTKQL_cjs.verifyWebhook; }
355
+ get: function () { return chunkWMGCQPHY_cjs.verifyWebhook; }
266
356
  });
267
357
  Object.defineProperty(exports, "viewerGeo", {
268
358
  enumerable: true,
269
- get: function () { return chunkYLMMTKQL_cjs.viewerGeo; }
359
+ get: function () { return chunkWMGCQPHY_cjs.viewerGeo; }
270
360
  });
271
361
  Object.defineProperty(exports, "withView", {
272
362
  enumerable: true,
273
- get: function () { return chunkYLMMTKQL_cjs.withView; }
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