@xenia-martech/sdk-js 1.2.0-dev.2 → 1.2.0-dev.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (82) hide show
  1. package/README.md +112 -3
  2. package/dist/{actions-BcdlokGO.d.cts → actions-C18Kv-4w.d.cts} +50 -2
  3. package/dist/{actions-BcdlokGO.d.ts → actions-C18Kv-4w.d.ts} +50 -2
  4. package/dist/{api-CktVQ6Gl.d.ts → api-0IycTYJR.d.cts} +4 -2
  5. package/dist/{api-BEPW0Uan.d.cts → api-COXWLL5L.d.ts} +4 -2
  6. package/dist/{chunk-3PYPPIUM.cjs → chunk-2DR5AVPX.cjs} +11 -46
  7. package/dist/chunk-2DR5AVPX.cjs.map +1 -0
  8. package/dist/chunk-BQ5PJCZ7.js +179 -0
  9. package/dist/chunk-BQ5PJCZ7.js.map +1 -0
  10. package/dist/{chunk-DWFVDNU5.js → chunk-GWRDZIVA.js} +5 -37
  11. package/dist/chunk-GWRDZIVA.js.map +1 -0
  12. package/dist/{chunk-7NGJC7CF.js → chunk-LW5AEJ6K.js} +20 -183
  13. package/dist/chunk-LW5AEJ6K.js.map +1 -0
  14. package/dist/chunk-N5M4PI7C.cjs +192 -0
  15. package/dist/chunk-N5M4PI7C.cjs.map +1 -0
  16. package/dist/chunk-OMMIRSPK.js +336 -0
  17. package/dist/chunk-OMMIRSPK.js.map +1 -0
  18. package/dist/chunk-QEYONKWY.js +42 -0
  19. package/dist/chunk-QEYONKWY.js.map +1 -0
  20. package/dist/{chunk-IBNIRT7J.cjs → chunk-RWE2OCQL.cjs} +20 -194
  21. package/dist/chunk-RWE2OCQL.cjs.map +1 -0
  22. package/dist/chunk-WHGK55JP.cjs +46 -0
  23. package/dist/chunk-WHGK55JP.cjs.map +1 -0
  24. package/dist/chunk-YLMMTKQL.cjs +358 -0
  25. package/dist/chunk-YLMMTKQL.cjs.map +1 -0
  26. package/dist/client/index.cjs +320 -31
  27. package/dist/client/index.cjs.map +1 -1
  28. package/dist/client/index.d.cts +15 -4
  29. package/dist/client/index.d.ts +15 -4
  30. package/dist/client/index.js +320 -31
  31. package/dist/client/index.js.map +1 -1
  32. package/dist/client/react/index.cjs +731 -34
  33. package/dist/client/react/index.cjs.map +1 -1
  34. package/dist/client/react/index.d.cts +32 -3
  35. package/dist/client/react/index.d.ts +32 -3
  36. package/dist/client/react/index.js +732 -37
  37. package/dist/client/react/index.js.map +1 -1
  38. package/dist/cms/contentful.cjs +2 -1
  39. package/dist/cms/contentful.cjs.map +1 -1
  40. package/dist/cms/contentful.d.cts +2 -1
  41. package/dist/cms/contentful.d.ts +2 -1
  42. package/dist/cms/contentful.js +2 -1
  43. package/dist/cms/contentful.js.map +1 -1
  44. package/dist/consent-Cemn91j3.d.ts +248 -0
  45. package/dist/consent-DgWdjsSy.d.cts +248 -0
  46. package/dist/{cookie-names-CLqtmGBM.d.ts → cookie-names-BvUaW3uo.d.ts} +1 -1
  47. package/dist/{cookie-names-BACctagz.d.cts → cookie-names-CnL65Oly.d.cts} +1 -1
  48. package/dist/edge/index.cjs +280 -0
  49. package/dist/edge/index.cjs.map +1 -0
  50. package/dist/edge/index.d.cts +61 -0
  51. package/dist/edge/index.d.ts +61 -0
  52. package/dist/edge/index.js +191 -0
  53. package/dist/edge/index.js.map +1 -0
  54. package/dist/index.cjs +41 -40
  55. package/dist/index.d.cts +5 -4
  56. package/dist/index.d.ts +5 -4
  57. package/dist/index.js +2 -1
  58. package/dist/next/index.cjs +320 -15
  59. package/dist/next/index.cjs.map +1 -1
  60. package/dist/next/index.d.cts +180 -6
  61. package/dist/next/index.d.ts +180 -6
  62. package/dist/next/index.js +309 -9
  63. package/dist/next/index.js.map +1 -1
  64. package/dist/personalize-content-c2DDAV6g.d.ts +140 -0
  65. package/dist/personalize-content-ghZPKlf1.d.cts +140 -0
  66. package/dist/server/index.cjs +34 -32
  67. package/dist/server/index.cjs.map +1 -1
  68. package/dist/server/index.d.cts +6 -5
  69. package/dist/server/index.d.ts +6 -5
  70. package/dist/server/index.js +5 -3
  71. package/dist/server/index.js.map +1 -1
  72. package/dist/{personalize-content-Clc1oMYY.d.ts → types-DgVHS4Qw.d.cts} +7 -127
  73. package/dist/{personalize-content-Clc1oMYY.d.cts → types-DgVHS4Qw.d.ts} +7 -127
  74. package/dist/xenia-sdk.iife.js +3 -3
  75. package/dist/xenia-sdk.iife.js.map +1 -1
  76. package/dist/{xenia-server-o_wDOcei.d.cts → xenia-server-C3O-cWQT.d.cts} +3 -2
  77. package/dist/{xenia-server-BWhWD0La.d.ts → xenia-server-DV8Sjmoo.d.ts} +3 -2
  78. package/package.json +14 -1
  79. package/dist/chunk-3PYPPIUM.cjs.map +0 -1
  80. package/dist/chunk-7NGJC7CF.js.map +0 -1
  81. package/dist/chunk-DWFVDNU5.js.map +0 -1
  82. package/dist/chunk-IBNIRT7J.cjs.map +0 -1
@@ -0,0 +1,140 @@
1
+ import { h as EntrySwapOp, g as EntryQuery, e as EntryDecision, p as ResolveOptions } from './types-DgVHS4Qw.cjs';
2
+
3
+ /**
4
+ * CMS page personalization — walk a fetched CMS page breadth-first, swap the
5
+ * entries Xenia marked as personalized, and do it in as few round trips as
6
+ * the page's nesting allows.
7
+ *
8
+ * Rules (see the "Rounds" section of the feature plan):
9
+ * - **Static** targets carry their replacement in the payload (`xeniaVariants`)
10
+ * and are swapped inline — no network at all.
11
+ * - **Runtime** targets are a barrier: their id is collected, their subtree is
12
+ * NOT walked (it may be replaced). One `resolve-entries` call decides every
13
+ * target found in a round, one parallel CMS fetch loads every replacement,
14
+ * and the next round walks whatever now sits at each position.
15
+ * - Never throws: on error or when `timeoutMs` runs out, whatever is not yet
16
+ * decided keeps its base content.
17
+ *
18
+ * CMS specifics live behind {@link CmsAdapter}; built-in adapters ship as
19
+ * `@xenia-martech/sdk-js/cms/contentful` (and later `/cms/contentstack`). Any
20
+ * other CMS works by implementing the interface.
21
+ */
22
+
23
+ /** Any value inside a CMS payload — the walker is shape-agnostic; adapters give it meaning. */
24
+ type Node = any;
25
+ /** The parsed `xenia` marker field of a personalized entry. */
26
+ interface XeniaMarker {
27
+ kind: "static" | "runtime";
28
+ op?: EntrySwapOp;
29
+ /** Array field replaced by `op: "items"`. */
30
+ field?: string;
31
+ limit?: number;
32
+ /**
33
+ * The content keys (Personalization Resolve identifiers) that personalize this entry. A page only
34
+ * acts on markers that list its `contentKey`; a marker without keys is ignored.
35
+ */
36
+ keys?: string[];
37
+ }
38
+ /** What the page resolver needs to know about a CMS's entries. `N` is the entry shape. */
39
+ interface CmsAdapter<N extends object = Node> {
40
+ /** The entry id when `value` is a CMS entry (not a link stub, asset or plain object). */
41
+ entryId(value: unknown): string | undefined;
42
+ /** The entry's Xenia marker, or null when it is not personalized. */
43
+ marker(entry: N): XeniaMarker | null;
44
+ /** Static replacements pinned on the entry (its resolved `xeniaVariants`). */
45
+ staticVariants(entry: N): N[];
46
+ getField(entry: N, field: string): unknown;
47
+ setField(entry: N, field: string, value: unknown): void;
48
+ /** Legacy `items` without a field: copy the replacement's array fields onto the entry. */
49
+ mergeItems?(entry: N, replacement: N): void;
50
+ /**
51
+ * Remove the marker from an entry that has been dealt with (kept base, or had its items replaced),
52
+ * so personalizing the output again — e.g. the browser continuing a server render — never decides
53
+ * it twice. Optional; without it a re-run re-asks Xenia about those entries.
54
+ */
55
+ clearMarker?(entry: N): void;
56
+ /** Normalize a fetched payload before walking (e.g. resolve links). Defaults to identity. */
57
+ prepare?(payload: unknown): unknown;
58
+ /** Load entries by id in ONE call; order does not matter. */
59
+ fetchEntries(ids: string[], locale?: string): Promise<N[]>;
60
+ /** Run a content-type query in ONE call, in the query's order. */
61
+ queryEntries(query: EntryQuery, locale?: string): Promise<N[]>;
62
+ /**
63
+ * Load one page with its whole tree (links resolved), or null when there is none. Optional; it
64
+ * is what lets `personalizePage(ref)` do the fetch, so apps never fetch the page themselves.
65
+ */
66
+ fetchPage?(ref: PageRef, locale?: string): Promise<N | null>;
67
+ }
68
+ /** Identifies a page in the CMS: its entry id, or a content type plus exact field values (a slug). */
69
+ type PageRef = string | {
70
+ contentType: string;
71
+ fields: Record<string, string | number | boolean>;
72
+ };
73
+ /** Resolves a round's runtime targets — `XeniaApi.resolveEntries` bound to the visitor. */
74
+ type EntryResolver = (entryIds: string[]) => Promise<EntryDecision[]>;
75
+ /** Reported for every swap actually applied (for impressions / analytics). */
76
+ interface AppliedSwap {
77
+ target: string;
78
+ kind: "static" | "runtime";
79
+ op: EntrySwapOp;
80
+ /** Ids of the entries now at the target's position (or in its field). */
81
+ entryIds: string[];
82
+ contentKey?: string;
83
+ bucket?: string;
84
+ round: number;
85
+ }
86
+ interface PersonalizeContentOptions {
87
+ cms: CmsAdapter;
88
+ /** Decides runtime targets. Omit to apply static swaps only (zero network). */
89
+ resolve?: EntryResolver;
90
+ locale?: string;
91
+ /**
92
+ * The page's Personalization Resolve identifier (required). Only that journey personalizes the
93
+ * page: a marker that doesn't list this key is plain content, and the backend decides for this key
94
+ * alone — another journey targeting the same section is neither consulted nor run.
95
+ */
96
+ contentKey: string;
97
+ /** Max network rounds (nesting depth of runtime targets). Default 3. */
98
+ maxRounds?: number;
99
+ /** Total budget for all rounds, in ms. Undecided targets keep their base. Default 300. */
100
+ timeoutMs?: number;
101
+ /** Mutate the payload in place instead of personalizing a copy. Default false. */
102
+ inPlace?: boolean;
103
+ onApplied?(swap: AppliedSwap): void;
104
+ /**
105
+ * Called when a round starts waiting on the network (`pending` = targets being decided) and when
106
+ * it has been applied (`pending` empty). `content` is the live, partially personalized tree —
107
+ * lets a UI render level by level instead of waiting for every round.
108
+ */
109
+ onProgress?(progress: {
110
+ content: unknown;
111
+ round: number;
112
+ pending: string[];
113
+ }): void;
114
+ }
115
+ /** `personalizeContent` options as taken by `XeniaClient` / `XeniaServer`, which supply `resolve`. */
116
+ type PagePersonalizeOptions = Omit<PersonalizeContentOptions, "resolve"> & Pick<ResolveOptions, "data" | "identity" | "context">;
117
+ interface PersonalizeContentResult<T> {
118
+ content: T;
119
+ applied: AppliedSwap[];
120
+ /** False when the budget ran out, a call failed, or `maxRounds` was hit with targets left. */
121
+ complete: boolean;
122
+ error?: unknown;
123
+ }
124
+ declare function personalizeContent<T>(payload: T, options: PersonalizeContentOptions): Promise<PersonalizeContentResult<T>>;
125
+ /**
126
+ * The entries Xenia still has to decide on this page — runtime targets reachable without crossing
127
+ * another undecided one. Synchronous and network-free (it reads the `xenia` markers), so a page can
128
+ * render a skeleton in exactly those places from the first frame instead of flashing base content.
129
+ * Nested targets inside a pending one are found once it is decided.
130
+ */
131
+ declare function pendingTargets(payload: unknown, adapter: CmsAdapter, contentKey: string): string[];
132
+ /**
133
+ * Fetch a page through `options.cms` and personalize it — {@link personalizeContent} without the
134
+ * fetch in app code. Never throws: a failed fetch comes back as `content: null` with `error`; a page
135
+ * that does not exist as `content: null`, `complete: true`. The page fetch is not counted against
136
+ * `timeoutMs` — that budget is for personalization only.
137
+ */
138
+ declare function personalizePage<T>(ref: PageRef, options: PersonalizeContentOptions): Promise<PersonalizeContentResult<T | null>>;
139
+
140
+ export { type AppliedSwap as A, type CmsAdapter as C, type EntryResolver as E, type PagePersonalizeOptions as P, type XeniaMarker as X, type PageRef as a, type PersonalizeContentOptions as b, type PersonalizeContentResult as c, personalizeContent as d, personalizePage as e, pendingTargets as p };
@@ -1,12 +1,14 @@
1
1
  'use strict';
2
2
 
3
3
  require('../chunk-PKWANLT7.cjs');
4
- var chunk3PYPPIUM_cjs = require('../chunk-3PYPPIUM.cjs');
5
- var chunkIBNIRT7J_cjs = require('../chunk-IBNIRT7J.cjs');
4
+ var chunk2DR5AVPX_cjs = require('../chunk-2DR5AVPX.cjs');
5
+ var chunkRWE2OCQL_cjs = require('../chunk-RWE2OCQL.cjs');
6
+ var chunkWHGK55JP_cjs = require('../chunk-WHGK55JP.cjs');
7
+ var chunkN5M4PI7C_cjs = require('../chunk-N5M4PI7C.cjs');
6
8
 
7
9
  // src/server/token.ts
8
10
  async function refreshSdkToken(opts) {
9
- const http = new chunkIBNIRT7J_cjs.HttpClient({ baseUrl: opts.baseUrl, timeout: opts.timeout });
11
+ const http = new chunkN5M4PI7C_cjs.HttpClient({ baseUrl: opts.baseUrl, timeout: opts.timeout });
10
12
  const { data } = await http.request({
11
13
  method: "POST",
12
14
  path: "/api/personalization/token/refresh",
@@ -39,67 +41,67 @@ function createTokenRoute(config) {
39
41
 
40
42
  Object.defineProperty(exports, "XeniaServer", {
41
43
  enumerable: true,
42
- get: function () { return chunk3PYPPIUM_cjs.XeniaServer; }
44
+ get: function () { return chunk2DR5AVPX_cjs.XeniaServer; }
43
45
  });
44
46
  Object.defineProperty(exports, "createXeniaServer", {
45
47
  enumerable: true,
46
- get: function () { return chunk3PYPPIUM_cjs.createXeniaServer; }
48
+ get: function () { return chunk2DR5AVPX_cjs.createXeniaServer; }
47
49
  });
48
- Object.defineProperty(exports, "getConsent", {
50
+ Object.defineProperty(exports, "XeniaApi", {
49
51
  enumerable: true,
50
- get: function () { return chunk3PYPPIUM_cjs.getConsent; }
52
+ get: function () { return chunkRWE2OCQL_cjs.XeniaApi; }
51
53
  });
52
- Object.defineProperty(exports, "getVisitorId", {
54
+ Object.defineProperty(exports, "parseActions", {
53
55
  enumerable: true,
54
- get: function () { return chunk3PYPPIUM_cjs.getVisitorId; }
56
+ get: function () { return chunkRWE2OCQL_cjs.parseActions; }
55
57
  });
56
- Object.defineProperty(exports, "parseCookies", {
58
+ Object.defineProperty(exports, "pendingTargets", {
57
59
  enumerable: true,
58
- get: function () { return chunk3PYPPIUM_cjs.parseCookies; }
60
+ get: function () { return chunkRWE2OCQL_cjs.pendingTargets; }
59
61
  });
60
- Object.defineProperty(exports, "CONSENT_COOKIE", {
62
+ Object.defineProperty(exports, "personalizeContent", {
61
63
  enumerable: true,
62
- get: function () { return chunkIBNIRT7J_cjs.CONSENT_COOKIE; }
64
+ get: function () { return chunkRWE2OCQL_cjs.personalizeContent; }
63
65
  });
64
- Object.defineProperty(exports, "CircuitOpenError", {
66
+ Object.defineProperty(exports, "personalizePage", {
65
67
  enumerable: true,
66
- get: function () { return chunkIBNIRT7J_cjs.CircuitOpenError; }
68
+ get: function () { return chunkRWE2OCQL_cjs.personalizePage; }
67
69
  });
68
- Object.defineProperty(exports, "VISITOR_COOKIE", {
70
+ Object.defineProperty(exports, "getConsent", {
69
71
  enumerable: true,
70
- get: function () { return chunkIBNIRT7J_cjs.VISITOR_COOKIE; }
72
+ get: function () { return chunkWHGK55JP_cjs.getConsent; }
71
73
  });
72
- Object.defineProperty(exports, "XeniaApi", {
74
+ Object.defineProperty(exports, "getVisitorId", {
73
75
  enumerable: true,
74
- get: function () { return chunkIBNIRT7J_cjs.XeniaApi; }
76
+ get: function () { return chunkWHGK55JP_cjs.getVisitorId; }
75
77
  });
76
- Object.defineProperty(exports, "consentCookie", {
78
+ Object.defineProperty(exports, "parseCookies", {
77
79
  enumerable: true,
78
- get: function () { return chunkIBNIRT7J_cjs.consentCookie; }
80
+ get: function () { return chunkWHGK55JP_cjs.parseCookies; }
79
81
  });
80
- Object.defineProperty(exports, "consentCookieName", {
82
+ Object.defineProperty(exports, "CONSENT_COOKIE", {
81
83
  enumerable: true,
82
- get: function () { return chunkIBNIRT7J_cjs.consentCookieName; }
84
+ get: function () { return chunkN5M4PI7C_cjs.CONSENT_COOKIE; }
83
85
  });
84
- Object.defineProperty(exports, "parseActions", {
86
+ Object.defineProperty(exports, "CircuitOpenError", {
85
87
  enumerable: true,
86
- get: function () { return chunkIBNIRT7J_cjs.parseActions; }
88
+ get: function () { return chunkN5M4PI7C_cjs.CircuitOpenError; }
87
89
  });
88
- Object.defineProperty(exports, "pendingTargets", {
90
+ Object.defineProperty(exports, "VISITOR_COOKIE", {
89
91
  enumerable: true,
90
- get: function () { return chunkIBNIRT7J_cjs.pendingTargets; }
92
+ get: function () { return chunkN5M4PI7C_cjs.VISITOR_COOKIE; }
91
93
  });
92
- Object.defineProperty(exports, "personalizeContent", {
94
+ Object.defineProperty(exports, "consentCookie", {
93
95
  enumerable: true,
94
- get: function () { return chunkIBNIRT7J_cjs.personalizeContent; }
96
+ get: function () { return chunkN5M4PI7C_cjs.consentCookie; }
95
97
  });
96
- Object.defineProperty(exports, "personalizePage", {
98
+ Object.defineProperty(exports, "consentCookieName", {
97
99
  enumerable: true,
98
- get: function () { return chunkIBNIRT7J_cjs.personalizePage; }
100
+ get: function () { return chunkN5M4PI7C_cjs.consentCookieName; }
99
101
  });
100
102
  Object.defineProperty(exports, "visitorCookieName", {
101
103
  enumerable: true,
102
- get: function () { return chunkIBNIRT7J_cjs.visitorCookieName; }
104
+ get: function () { return chunkN5M4PI7C_cjs.visitorCookieName; }
103
105
  });
104
106
  exports.createTokenRoute = createTokenRoute;
105
107
  exports.refreshSdkToken = refreshSdkToken;
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/server/token.ts"],"names":["HttpClient"],"mappings":";;;;;;;AAgCA,eAAsB,gBAAgB,IAAA,EAAoD;AACxF,EAAA,MAAM,IAAA,GAAO,IAAIA,4BAAA,CAAW,EAAE,OAAA,EAAS,KAAK,OAAA,EAAS,OAAA,EAAS,IAAA,CAAK,OAAA,EAAS,CAAA;AAC5E,EAAA,MAAM,EAAE,IAAA,EAAK,GAAI,MAAM,KAAK,OAAA,CAAQ;AAAA,IAClC,MAAA,EAAQ,MAAA;AAAA,IACR,IAAA,EAAM,oCAAA;AAAA,IACN,SAAS,EAAE,aAAA,EAAe,CAAA,OAAA,EAAU,IAAA,CAAK,QAAQ,CAAA,CAAA,EAAG;AAAA,IACpD,IAAA,EAAM,EAAE,UAAA,EAAY,IAAA,CAAK,cAAc,IAAA;AAAK,GAC7C,CAAA;AACD,EAAA,MAAM,GAAA,GAAO,QAAQ,EAAC;AACtB,EAAA,OAAO;AAAA,IACL,KAAA,EAAO,MAAA,CAAO,GAAA,CAAI,KAAA,IAAS,EAAE,CAAA;AAAA,IAC7B,WAAW,OAAO,GAAA,CAAI,SAAA,KAAc,QAAA,GAAW,IAAI,SAAA,GAAY;AAAA,GACjE;AACF;AAiBO,SAAS,iBAAiB,MAAA,EAAsE;AACrG,EAAA,OAAO,YAA+B;AACpC,IAAA,IAAI;AACF,MAAA,MAAM,SAAA,GAAY,MAAM,eAAA,CAAgB,MAAM,CAAA;AAC9C,MAAA,OAAO,IAAI,QAAA,CAAS,IAAA,CAAK,SAAA,CAAU,SAAS,CAAA,EAAG;AAAA,QAC7C,MAAA,EAAQ,GAAA;AAAA,QACR,OAAA,EAAS,EAAE,cAAA,EAAgB,kBAAA,EAAoB,iBAAiB,UAAA;AAAW,OAC5E,CAAA;AAAA,IACH,SAAS,GAAA,EAAK;AACZ,MAAA,MAAM,OAAA,GAAU,GAAA,YAAe,KAAA,GAAQ,GAAA,CAAI,OAAA,GAAU,sBAAA;AACrD,MAAA,OAAO,IAAI,SAAS,IAAA,CAAK,SAAA,CAAU,EAAE,KAAA,EAAO,OAAA,EAAS,CAAA,EAAG;AAAA,QACtD,MAAA,EAAQ,GAAA;AAAA,QACR,OAAA,EAAS,EAAE,cAAA,EAAgB,kBAAA;AAAmB,OAC/C,CAAA;AAAA,IACH;AAAA,EACF,CAAA;AACF","file":"index.cjs","sourcesContent":["import { HttpClient } from \"../core/http-client\";\nimport type { JsonObject } from \"../core/types\";\n\nexport interface RefreshTokenOptions {\n /**\n * Xenia base URL. Defaults to the hosted engine,\n * `https://engine.xeniamartech.com/xenia` — omit unless self-hosting.\n */\n baseUrl?: string;\n /**\n * The personalization SDK token created once in the dashboard (API Tokens →\n * Create token). Stays server-side; the browser never sees it — only the\n * short-lived token this exchanges it for does.\n */\n sdkToken: string;\n /** Lifetime of the returned token, in minutes. Default 15, capped server-side at 60. */\n ttlMinutes?: number;\n timeout?: number;\n}\n\nexport interface RefreshedToken {\n token: string;\n expiresAt?: string;\n}\n\n/**\n * Exchange the long-lived personalization SDK token for a short-lived one with\n * the same tenant/env scope, via `POST /api/personalization/token/refresh`.\n * Self-service — no dashboard credential needed, just the SDK token you\n * already have. Call this from your backend and hand the result to the\n * browser (e.g. from a token route the client SDK calls via `getToken`).\n */\nexport async function refreshSdkToken(opts: RefreshTokenOptions): Promise<RefreshedToken> {\n const http = new HttpClient({ baseUrl: opts.baseUrl, timeout: opts.timeout });\n const { data } = await http.request({\n method: \"POST\",\n path: \"/api/personalization/token/refresh\",\n headers: { Authorization: `Bearer ${opts.sdkToken}` },\n body: { ttlMinutes: opts.ttlMinutes ?? null },\n });\n const obj = (data ?? {}) as JsonObject;\n return {\n token: String(obj.token ?? \"\"),\n expiresAt: typeof obj.expiresAt === \"string\" ? obj.expiresAt : undefined,\n };\n}\n\n/**\n * Build a framework-agnostic request handler that hands the browser SDK a\n * personalization token — mount it at the endpoint `getToken` calls. Needs\n * only the SDK token you already created in the dashboard; mints a fresh\n * short-lived token per request so the long-lived one never reaches the\n * browser.\n *\n * @example\n * ```ts\n * export const GET = createTokenRoute({\n * sdkToken: process.env.XENIA_SDK_TOKEN!,\n * // baseUrl: process.env.XENIA_URL, // only to override the default hosted engine\n * });\n * ```\n */\nexport function createTokenRoute(config: RefreshTokenOptions): (request: Request) => Promise<Response> {\n return async (): Promise<Response> => {\n try {\n const refreshed = await refreshSdkToken(config);\n return new Response(JSON.stringify(refreshed), {\n status: 200,\n headers: { \"Content-Type\": \"application/json\", \"Cache-Control\": \"no-store\" },\n });\n } catch (err) {\n const message = err instanceof Error ? err.message : \"token refresh failed\";\n return new Response(JSON.stringify({ error: message }), {\n status: 500,\n headers: { \"Content-Type\": \"application/json\" },\n });\n }\n };\n}\n"]}
1
+ {"version":3,"sources":["../../src/server/token.ts"],"names":["HttpClient"],"mappings":";;;;;;;;;AAgCA,eAAsB,gBAAgB,IAAA,EAAoD;AACxF,EAAA,MAAM,IAAA,GAAO,IAAIA,4BAAA,CAAW,EAAE,OAAA,EAAS,KAAK,OAAA,EAAS,OAAA,EAAS,IAAA,CAAK,OAAA,EAAS,CAAA;AAC5E,EAAA,MAAM,EAAE,IAAA,EAAK,GAAI,MAAM,KAAK,OAAA,CAAQ;AAAA,IAClC,MAAA,EAAQ,MAAA;AAAA,IACR,IAAA,EAAM,oCAAA;AAAA,IACN,SAAS,EAAE,aAAA,EAAe,CAAA,OAAA,EAAU,IAAA,CAAK,QAAQ,CAAA,CAAA,EAAG;AAAA,IACpD,IAAA,EAAM,EAAE,UAAA,EAAY,IAAA,CAAK,cAAc,IAAA;AAAK,GAC7C,CAAA;AACD,EAAA,MAAM,GAAA,GAAO,QAAQ,EAAC;AACtB,EAAA,OAAO;AAAA,IACL,KAAA,EAAO,MAAA,CAAO,GAAA,CAAI,KAAA,IAAS,EAAE,CAAA;AAAA,IAC7B,WAAW,OAAO,GAAA,CAAI,SAAA,KAAc,QAAA,GAAW,IAAI,SAAA,GAAY;AAAA,GACjE;AACF;AAiBO,SAAS,iBAAiB,MAAA,EAAsE;AACrG,EAAA,OAAO,YAA+B;AACpC,IAAA,IAAI;AACF,MAAA,MAAM,SAAA,GAAY,MAAM,eAAA,CAAgB,MAAM,CAAA;AAC9C,MAAA,OAAO,IAAI,QAAA,CAAS,IAAA,CAAK,SAAA,CAAU,SAAS,CAAA,EAAG;AAAA,QAC7C,MAAA,EAAQ,GAAA;AAAA,QACR,OAAA,EAAS,EAAE,cAAA,EAAgB,kBAAA,EAAoB,iBAAiB,UAAA;AAAW,OAC5E,CAAA;AAAA,IACH,SAAS,GAAA,EAAK;AACZ,MAAA,MAAM,OAAA,GAAU,GAAA,YAAe,KAAA,GAAQ,GAAA,CAAI,OAAA,GAAU,sBAAA;AACrD,MAAA,OAAO,IAAI,SAAS,IAAA,CAAK,SAAA,CAAU,EAAE,KAAA,EAAO,OAAA,EAAS,CAAA,EAAG;AAAA,QACtD,MAAA,EAAQ,GAAA;AAAA,QACR,OAAA,EAAS,EAAE,cAAA,EAAgB,kBAAA;AAAmB,OAC/C,CAAA;AAAA,IACH;AAAA,EACF,CAAA;AACF","file":"index.cjs","sourcesContent":["import { HttpClient } from \"../core/http-client\";\nimport type { JsonObject } from \"../core/types\";\n\nexport interface RefreshTokenOptions {\n /**\n * Xenia base URL. Defaults to the hosted engine,\n * `https://engine.xeniamartech.com/xenia` — omit unless self-hosting.\n */\n baseUrl?: string;\n /**\n * The personalization SDK token created once in the dashboard (API Tokens →\n * Create token). Stays server-side; the browser never sees it — only the\n * short-lived token this exchanges it for does.\n */\n sdkToken: string;\n /** Lifetime of the returned token, in minutes. Default 15, capped server-side at 60. */\n ttlMinutes?: number;\n timeout?: number;\n}\n\nexport interface RefreshedToken {\n token: string;\n expiresAt?: string;\n}\n\n/**\n * Exchange the long-lived personalization SDK token for a short-lived one with\n * the same tenant/env scope, via `POST /api/personalization/token/refresh`.\n * Self-service — no dashboard credential needed, just the SDK token you\n * already have. Call this from your backend and hand the result to the\n * browser (e.g. from a token route the client SDK calls via `getToken`).\n */\nexport async function refreshSdkToken(opts: RefreshTokenOptions): Promise<RefreshedToken> {\n const http = new HttpClient({ baseUrl: opts.baseUrl, timeout: opts.timeout });\n const { data } = await http.request({\n method: \"POST\",\n path: \"/api/personalization/token/refresh\",\n headers: { Authorization: `Bearer ${opts.sdkToken}` },\n body: { ttlMinutes: opts.ttlMinutes ?? null },\n });\n const obj = (data ?? {}) as JsonObject;\n return {\n token: String(obj.token ?? \"\"),\n expiresAt: typeof obj.expiresAt === \"string\" ? obj.expiresAt : undefined,\n };\n}\n\n/**\n * Build a framework-agnostic request handler that hands the browser SDK a\n * personalization token — mount it at the endpoint `getToken` calls. Needs\n * only the SDK token you already created in the dashboard; mints a fresh\n * short-lived token per request so the long-lived one never reaches the\n * browser.\n *\n * @example\n * ```ts\n * export const GET = createTokenRoute({\n * sdkToken: process.env.XENIA_SDK_TOKEN!,\n * // baseUrl: process.env.XENIA_URL, // only to override the default hosted engine\n * });\n * ```\n */\nexport function createTokenRoute(config: RefreshTokenOptions): (request: Request) => Promise<Response> {\n return async (): Promise<Response> => {\n try {\n const refreshed = await refreshSdkToken(config);\n return new Response(JSON.stringify(refreshed), {\n status: 200,\n headers: { \"Content-Type\": \"application/json\", \"Cache-Control\": \"no-store\" },\n });\n } catch (err) {\n const message = err instanceof Error ? err.message : \"token refresh failed\";\n return new Response(JSON.stringify({ error: message }), {\n status: 500,\n headers: { \"Content-Type\": \"application/json\" },\n });\n }\n };\n}\n"]}
@@ -1,8 +1,9 @@
1
- export { S as ServerResolveOptions, a as ServerTrackOptions, b as ServerWebhookOptions, W as WithFallback, X as XeniaServer, c as createXeniaServer } from '../xenia-server-o_wDOcei.cjs';
2
- import { f as ConsentState } from '../personalize-content-Clc1oMYY.cjs';
3
- export { A as AnalyticsContext, a as AnalyticsEventName, b as AnalyticsIdentity, c as AnalyticsIngestResponse, d as AppliedSwap, e as CircuitBreakerConfig, C as CmsAdapter, E as EventContext, l as EventIdentity, I as IdentifyRequest, m as IdentifyResponse, J as JsonValue, P as PagePersonalizeOptions, q as PageRef, r as PersonalizationSwap, t as PersonalizeContentResult, R as ResolveBatchItem, u as ResolveBatchResult, w as ResolveOptions, x as ResolveResult, W as WebhookResponse, L as XeniaConfig, N as XeniaEvent, X as XeniaMarker, O as pendingTargets, Q as personalizeContent, S as personalizePage } from '../personalize-content-Clc1oMYY.cjs';
4
- export { X as XeniaApi } from '../api-BEPW0Uan.cjs';
5
- export { C as CONSENT_COOKIE, a as CircuitOpenError, V as VISITOR_COOKIE, f as consentCookie, g as consentCookieName, p as parseActions, v as visitorCookieName } from '../cookie-names-BACctagz.cjs';
1
+ export { S as ServerResolveOptions, a as ServerTrackOptions, b as ServerWebhookOptions, W as WithFallback, X as XeniaServer, c as createXeniaServer } from '../xenia-server-C3O-cWQT.cjs';
2
+ import { d as ConsentState } from '../types-DgVHS4Qw.cjs';
3
+ export { A as AnalyticsContext, a as AnalyticsEventName, b as AnalyticsIdentity, c as AnalyticsIngestResponse, C as CircuitBreakerConfig, E as EventContext, i as EventIdentity, I as IdentifyRequest, j as IdentifyResponse, J as JsonValue, P as PersonalizationSwap, R as ResolveBatchItem, n as ResolveBatchResult, p as ResolveOptions, q as ResolveResult, W as WebhookResponse, x as XeniaConfig, y as XeniaEvent } from '../types-DgVHS4Qw.cjs';
4
+ export { X as XeniaApi } from '../api-0IycTYJR.cjs';
5
+ export { C as CONSENT_COOKIE, a as CircuitOpenError, V as VISITOR_COOKIE, f as consentCookie, g as consentCookieName, p as parseActions, v as visitorCookieName } from '../cookie-names-CnL65Oly.cjs';
6
+ export { A as AppliedSwap, C as CmsAdapter, P as PagePersonalizeOptions, a as PageRef, c as PersonalizeContentResult, X as XeniaMarker, p as pendingTargets, d as personalizeContent, e as personalizePage } from '../personalize-content-ghZPKlf1.cjs';
6
7
 
7
8
  interface RefreshTokenOptions {
8
9
  /**
@@ -1,8 +1,9 @@
1
- export { S as ServerResolveOptions, a as ServerTrackOptions, b as ServerWebhookOptions, W as WithFallback, X as XeniaServer, c as createXeniaServer } from '../xenia-server-BWhWD0La.js';
2
- import { f as ConsentState } from '../personalize-content-Clc1oMYY.js';
3
- export { A as AnalyticsContext, a as AnalyticsEventName, b as AnalyticsIdentity, c as AnalyticsIngestResponse, d as AppliedSwap, e as CircuitBreakerConfig, C as CmsAdapter, E as EventContext, l as EventIdentity, I as IdentifyRequest, m as IdentifyResponse, J as JsonValue, P as PagePersonalizeOptions, q as PageRef, r as PersonalizationSwap, t as PersonalizeContentResult, R as ResolveBatchItem, u as ResolveBatchResult, w as ResolveOptions, x as ResolveResult, W as WebhookResponse, L as XeniaConfig, N as XeniaEvent, X as XeniaMarker, O as pendingTargets, Q as personalizeContent, S as personalizePage } from '../personalize-content-Clc1oMYY.js';
4
- export { X as XeniaApi } from '../api-CktVQ6Gl.js';
5
- export { C as CONSENT_COOKIE, a as CircuitOpenError, V as VISITOR_COOKIE, f as consentCookie, g as consentCookieName, p as parseActions, v as visitorCookieName } from '../cookie-names-CLqtmGBM.js';
1
+ export { S as ServerResolveOptions, a as ServerTrackOptions, b as ServerWebhookOptions, W as WithFallback, X as XeniaServer, c as createXeniaServer } from '../xenia-server-DV8Sjmoo.js';
2
+ import { d as ConsentState } from '../types-DgVHS4Qw.js';
3
+ export { A as AnalyticsContext, a as AnalyticsEventName, b as AnalyticsIdentity, c as AnalyticsIngestResponse, C as CircuitBreakerConfig, E as EventContext, i as EventIdentity, I as IdentifyRequest, j as IdentifyResponse, J as JsonValue, P as PersonalizationSwap, R as ResolveBatchItem, n as ResolveBatchResult, p as ResolveOptions, q as ResolveResult, W as WebhookResponse, x as XeniaConfig, y as XeniaEvent } from '../types-DgVHS4Qw.js';
4
+ export { X as XeniaApi } from '../api-COXWLL5L.js';
5
+ export { C as CONSENT_COOKIE, a as CircuitOpenError, V as VISITOR_COOKIE, f as consentCookie, g as consentCookieName, p as parseActions, v as visitorCookieName } from '../cookie-names-BvUaW3uo.js';
6
+ export { A as AppliedSwap, C as CmsAdapter, P as PagePersonalizeOptions, a as PageRef, c as PersonalizeContentResult, X as XeniaMarker, p as pendingTargets, d as personalizeContent, e as personalizePage } from '../personalize-content-c2DDAV6g.js';
6
7
 
7
8
  interface RefreshTokenOptions {
8
9
  /**
@@ -1,7 +1,9 @@
1
1
  import '../chunk-OUZU4Z5B.js';
2
- export { XeniaServer, createXeniaServer, getConsent, getVisitorId, parseCookies } from '../chunk-DWFVDNU5.js';
3
- import { HttpClient } from '../chunk-7NGJC7CF.js';
4
- export { CONSENT_COOKIE, CircuitOpenError, VISITOR_COOKIE, XeniaApi, consentCookie, consentCookieName, parseActions, pendingTargets, personalizeContent, personalizePage, visitorCookieName } from '../chunk-7NGJC7CF.js';
2
+ export { XeniaServer, createXeniaServer } from '../chunk-GWRDZIVA.js';
3
+ export { XeniaApi, parseActions, pendingTargets, personalizeContent, personalizePage } from '../chunk-LW5AEJ6K.js';
4
+ export { getConsent, getVisitorId, parseCookies } from '../chunk-QEYONKWY.js';
5
+ import { HttpClient } from '../chunk-BQ5PJCZ7.js';
6
+ export { CONSENT_COOKIE, CircuitOpenError, VISITOR_COOKIE, consentCookie, consentCookieName, visitorCookieName } from '../chunk-BQ5PJCZ7.js';
5
7
 
6
8
  // src/server/token.ts
7
9
  async function refreshSdkToken(opts) {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/server/token.ts"],"names":[],"mappings":";;;;;;AAgCA,eAAsB,gBAAgB,IAAA,EAAoD;AACxF,EAAA,MAAM,IAAA,GAAO,IAAI,UAAA,CAAW,EAAE,OAAA,EAAS,KAAK,OAAA,EAAS,OAAA,EAAS,IAAA,CAAK,OAAA,EAAS,CAAA;AAC5E,EAAA,MAAM,EAAE,IAAA,EAAK,GAAI,MAAM,KAAK,OAAA,CAAQ;AAAA,IAClC,MAAA,EAAQ,MAAA;AAAA,IACR,IAAA,EAAM,oCAAA;AAAA,IACN,SAAS,EAAE,aAAA,EAAe,CAAA,OAAA,EAAU,IAAA,CAAK,QAAQ,CAAA,CAAA,EAAG;AAAA,IACpD,IAAA,EAAM,EAAE,UAAA,EAAY,IAAA,CAAK,cAAc,IAAA;AAAK,GAC7C,CAAA;AACD,EAAA,MAAM,GAAA,GAAO,QAAQ,EAAC;AACtB,EAAA,OAAO;AAAA,IACL,KAAA,EAAO,MAAA,CAAO,GAAA,CAAI,KAAA,IAAS,EAAE,CAAA;AAAA,IAC7B,WAAW,OAAO,GAAA,CAAI,SAAA,KAAc,QAAA,GAAW,IAAI,SAAA,GAAY;AAAA,GACjE;AACF;AAiBO,SAAS,iBAAiB,MAAA,EAAsE;AACrG,EAAA,OAAO,YAA+B;AACpC,IAAA,IAAI;AACF,MAAA,MAAM,SAAA,GAAY,MAAM,eAAA,CAAgB,MAAM,CAAA;AAC9C,MAAA,OAAO,IAAI,QAAA,CAAS,IAAA,CAAK,SAAA,CAAU,SAAS,CAAA,EAAG;AAAA,QAC7C,MAAA,EAAQ,GAAA;AAAA,QACR,OAAA,EAAS,EAAE,cAAA,EAAgB,kBAAA,EAAoB,iBAAiB,UAAA;AAAW,OAC5E,CAAA;AAAA,IACH,SAAS,GAAA,EAAK;AACZ,MAAA,MAAM,OAAA,GAAU,GAAA,YAAe,KAAA,GAAQ,GAAA,CAAI,OAAA,GAAU,sBAAA;AACrD,MAAA,OAAO,IAAI,SAAS,IAAA,CAAK,SAAA,CAAU,EAAE,KAAA,EAAO,OAAA,EAAS,CAAA,EAAG;AAAA,QACtD,MAAA,EAAQ,GAAA;AAAA,QACR,OAAA,EAAS,EAAE,cAAA,EAAgB,kBAAA;AAAmB,OAC/C,CAAA;AAAA,IACH;AAAA,EACF,CAAA;AACF","file":"index.js","sourcesContent":["import { HttpClient } from \"../core/http-client\";\nimport type { JsonObject } from \"../core/types\";\n\nexport interface RefreshTokenOptions {\n /**\n * Xenia base URL. Defaults to the hosted engine,\n * `https://engine.xeniamartech.com/xenia` — omit unless self-hosting.\n */\n baseUrl?: string;\n /**\n * The personalization SDK token created once in the dashboard (API Tokens →\n * Create token). Stays server-side; the browser never sees it — only the\n * short-lived token this exchanges it for does.\n */\n sdkToken: string;\n /** Lifetime of the returned token, in minutes. Default 15, capped server-side at 60. */\n ttlMinutes?: number;\n timeout?: number;\n}\n\nexport interface RefreshedToken {\n token: string;\n expiresAt?: string;\n}\n\n/**\n * Exchange the long-lived personalization SDK token for a short-lived one with\n * the same tenant/env scope, via `POST /api/personalization/token/refresh`.\n * Self-service — no dashboard credential needed, just the SDK token you\n * already have. Call this from your backend and hand the result to the\n * browser (e.g. from a token route the client SDK calls via `getToken`).\n */\nexport async function refreshSdkToken(opts: RefreshTokenOptions): Promise<RefreshedToken> {\n const http = new HttpClient({ baseUrl: opts.baseUrl, timeout: opts.timeout });\n const { data } = await http.request({\n method: \"POST\",\n path: \"/api/personalization/token/refresh\",\n headers: { Authorization: `Bearer ${opts.sdkToken}` },\n body: { ttlMinutes: opts.ttlMinutes ?? null },\n });\n const obj = (data ?? {}) as JsonObject;\n return {\n token: String(obj.token ?? \"\"),\n expiresAt: typeof obj.expiresAt === \"string\" ? obj.expiresAt : undefined,\n };\n}\n\n/**\n * Build a framework-agnostic request handler that hands the browser SDK a\n * personalization token — mount it at the endpoint `getToken` calls. Needs\n * only the SDK token you already created in the dashboard; mints a fresh\n * short-lived token per request so the long-lived one never reaches the\n * browser.\n *\n * @example\n * ```ts\n * export const GET = createTokenRoute({\n * sdkToken: process.env.XENIA_SDK_TOKEN!,\n * // baseUrl: process.env.XENIA_URL, // only to override the default hosted engine\n * });\n * ```\n */\nexport function createTokenRoute(config: RefreshTokenOptions): (request: Request) => Promise<Response> {\n return async (): Promise<Response> => {\n try {\n const refreshed = await refreshSdkToken(config);\n return new Response(JSON.stringify(refreshed), {\n status: 200,\n headers: { \"Content-Type\": \"application/json\", \"Cache-Control\": \"no-store\" },\n });\n } catch (err) {\n const message = err instanceof Error ? err.message : \"token refresh failed\";\n return new Response(JSON.stringify({ error: message }), {\n status: 500,\n headers: { \"Content-Type\": \"application/json\" },\n });\n }\n };\n}\n"]}
1
+ {"version":3,"sources":["../../src/server/token.ts"],"names":[],"mappings":";;;;;;;;AAgCA,eAAsB,gBAAgB,IAAA,EAAoD;AACxF,EAAA,MAAM,IAAA,GAAO,IAAI,UAAA,CAAW,EAAE,OAAA,EAAS,KAAK,OAAA,EAAS,OAAA,EAAS,IAAA,CAAK,OAAA,EAAS,CAAA;AAC5E,EAAA,MAAM,EAAE,IAAA,EAAK,GAAI,MAAM,KAAK,OAAA,CAAQ;AAAA,IAClC,MAAA,EAAQ,MAAA;AAAA,IACR,IAAA,EAAM,oCAAA;AAAA,IACN,SAAS,EAAE,aAAA,EAAe,CAAA,OAAA,EAAU,IAAA,CAAK,QAAQ,CAAA,CAAA,EAAG;AAAA,IACpD,IAAA,EAAM,EAAE,UAAA,EAAY,IAAA,CAAK,cAAc,IAAA;AAAK,GAC7C,CAAA;AACD,EAAA,MAAM,GAAA,GAAO,QAAQ,EAAC;AACtB,EAAA,OAAO;AAAA,IACL,KAAA,EAAO,MAAA,CAAO,GAAA,CAAI,KAAA,IAAS,EAAE,CAAA;AAAA,IAC7B,WAAW,OAAO,GAAA,CAAI,SAAA,KAAc,QAAA,GAAW,IAAI,SAAA,GAAY;AAAA,GACjE;AACF;AAiBO,SAAS,iBAAiB,MAAA,EAAsE;AACrG,EAAA,OAAO,YAA+B;AACpC,IAAA,IAAI;AACF,MAAA,MAAM,SAAA,GAAY,MAAM,eAAA,CAAgB,MAAM,CAAA;AAC9C,MAAA,OAAO,IAAI,QAAA,CAAS,IAAA,CAAK,SAAA,CAAU,SAAS,CAAA,EAAG;AAAA,QAC7C,MAAA,EAAQ,GAAA;AAAA,QACR,OAAA,EAAS,EAAE,cAAA,EAAgB,kBAAA,EAAoB,iBAAiB,UAAA;AAAW,OAC5E,CAAA;AAAA,IACH,SAAS,GAAA,EAAK;AACZ,MAAA,MAAM,OAAA,GAAU,GAAA,YAAe,KAAA,GAAQ,GAAA,CAAI,OAAA,GAAU,sBAAA;AACrD,MAAA,OAAO,IAAI,SAAS,IAAA,CAAK,SAAA,CAAU,EAAE,KAAA,EAAO,OAAA,EAAS,CAAA,EAAG;AAAA,QACtD,MAAA,EAAQ,GAAA;AAAA,QACR,OAAA,EAAS,EAAE,cAAA,EAAgB,kBAAA;AAAmB,OAC/C,CAAA;AAAA,IACH;AAAA,EACF,CAAA;AACF","file":"index.js","sourcesContent":["import { HttpClient } from \"../core/http-client\";\nimport type { JsonObject } from \"../core/types\";\n\nexport interface RefreshTokenOptions {\n /**\n * Xenia base URL. Defaults to the hosted engine,\n * `https://engine.xeniamartech.com/xenia` — omit unless self-hosting.\n */\n baseUrl?: string;\n /**\n * The personalization SDK token created once in the dashboard (API Tokens →\n * Create token). Stays server-side; the browser never sees it — only the\n * short-lived token this exchanges it for does.\n */\n sdkToken: string;\n /** Lifetime of the returned token, in minutes. Default 15, capped server-side at 60. */\n ttlMinutes?: number;\n timeout?: number;\n}\n\nexport interface RefreshedToken {\n token: string;\n expiresAt?: string;\n}\n\n/**\n * Exchange the long-lived personalization SDK token for a short-lived one with\n * the same tenant/env scope, via `POST /api/personalization/token/refresh`.\n * Self-service — no dashboard credential needed, just the SDK token you\n * already have. Call this from your backend and hand the result to the\n * browser (e.g. from a token route the client SDK calls via `getToken`).\n */\nexport async function refreshSdkToken(opts: RefreshTokenOptions): Promise<RefreshedToken> {\n const http = new HttpClient({ baseUrl: opts.baseUrl, timeout: opts.timeout });\n const { data } = await http.request({\n method: \"POST\",\n path: \"/api/personalization/token/refresh\",\n headers: { Authorization: `Bearer ${opts.sdkToken}` },\n body: { ttlMinutes: opts.ttlMinutes ?? null },\n });\n const obj = (data ?? {}) as JsonObject;\n return {\n token: String(obj.token ?? \"\"),\n expiresAt: typeof obj.expiresAt === \"string\" ? obj.expiresAt : undefined,\n };\n}\n\n/**\n * Build a framework-agnostic request handler that hands the browser SDK a\n * personalization token — mount it at the endpoint `getToken` calls. Needs\n * only the SDK token you already created in the dashboard; mints a fresh\n * short-lived token per request so the long-lived one never reaches the\n * browser.\n *\n * @example\n * ```ts\n * export const GET = createTokenRoute({\n * sdkToken: process.env.XENIA_SDK_TOKEN!,\n * // baseUrl: process.env.XENIA_URL, // only to override the default hosted engine\n * });\n * ```\n */\nexport function createTokenRoute(config: RefreshTokenOptions): (request: Request) => Promise<Response> {\n return async (): Promise<Response> => {\n try {\n const refreshed = await refreshSdkToken(config);\n return new Response(JSON.stringify(refreshed), {\n status: 200,\n headers: { \"Content-Type\": \"application/json\", \"Cache-Control\": \"no-store\" },\n });\n } catch (err) {\n const message = err instanceof Error ? err.message : \"token refresh failed\";\n return new Response(JSON.stringify({ error: message }), {\n status: 500,\n headers: { \"Content-Type\": \"application/json\" },\n });\n }\n };\n}\n"]}
@@ -155,6 +155,12 @@ interface VisitorCookieOptions {
155
155
  secure?: boolean;
156
156
  /** Cookie lifetime in days. Default 365. */
157
157
  maxAgeDays?: number;
158
+ /**
159
+ * The cookie wins over the id stored in this browser: when a server (e.g. an edge proxy) changes
160
+ * the visitor — a profile switch, a QA persona — the browser follows instead of writing its old id
161
+ * back. Default false: the stored id wins and the cookie only seeds a first visit.
162
+ */
163
+ authoritative?: boolean;
158
164
  }
159
165
  /** Well-known event fields understood by the backend event-trigger node. */
160
166
  interface EventIdentity {
@@ -614,130 +620,4 @@ type XeniaAction = {
614
620
  url: string;
615
621
  };
616
622
 
617
- /**
618
- * CMS page personalization — walk a fetched CMS page breadth-first, swap the
619
- * entries Xenia marked as personalized, and do it in as few round trips as
620
- * the page's nesting allows.
621
- *
622
- * Rules (see the "Rounds" section of the feature plan):
623
- * - **Static** targets carry their replacement in the payload (`xeniaVariants`)
624
- * and are swapped inline — no network at all.
625
- * - **Runtime** targets are a barrier: their id is collected, their subtree is
626
- * NOT walked (it may be replaced). One `resolve-entries` call decides every
627
- * target found in a round, one parallel CMS fetch loads every replacement,
628
- * and the next round walks whatever now sits at each position.
629
- * - Never throws: on error or when `timeoutMs` runs out, whatever is not yet
630
- * decided keeps its base content.
631
- *
632
- * CMS specifics live behind {@link CmsAdapter}; built-in adapters ship as
633
- * `@xenia-martech/sdk-js/cms/contentful` (and later `/cms/contentstack`). Any
634
- * other CMS works by implementing the interface.
635
- */
636
-
637
- /** Any value inside a CMS payload — the walker is shape-agnostic; adapters give it meaning. */
638
- type Node = any;
639
- /** The parsed `xenia` marker field of a personalized entry. */
640
- interface XeniaMarker {
641
- kind: "static" | "runtime";
642
- op?: EntrySwapOp;
643
- /** Array field replaced by `op: "items"`. */
644
- field?: string;
645
- limit?: number;
646
- }
647
- /** What the page resolver needs to know about a CMS's entries. `N` is the entry shape. */
648
- interface CmsAdapter<N extends object = Node> {
649
- /** The entry id when `value` is a CMS entry (not a link stub, asset or plain object). */
650
- entryId(value: unknown): string | undefined;
651
- /** The entry's Xenia marker, or null when it is not personalized. */
652
- marker(entry: N): XeniaMarker | null;
653
- /** Static replacements pinned on the entry (its resolved `xeniaVariants`). */
654
- staticVariants(entry: N): N[];
655
- getField(entry: N, field: string): unknown;
656
- setField(entry: N, field: string, value: unknown): void;
657
- /** Legacy `items` without a field: copy the replacement's array fields onto the entry. */
658
- mergeItems?(entry: N, replacement: N): void;
659
- /**
660
- * Remove the marker from an entry that has been dealt with (kept base, or had its items replaced),
661
- * so personalizing the output again — e.g. the browser continuing a server render — never decides
662
- * it twice. Optional; without it a re-run re-asks Xenia about those entries.
663
- */
664
- clearMarker?(entry: N): void;
665
- /** Normalize a fetched payload before walking (e.g. resolve links). Defaults to identity. */
666
- prepare?(payload: unknown): unknown;
667
- /** Load entries by id in ONE call; order does not matter. */
668
- fetchEntries(ids: string[], locale?: string): Promise<N[]>;
669
- /** Run a content-type query in ONE call, in the query's order. */
670
- queryEntries(query: EntryQuery, locale?: string): Promise<N[]>;
671
- /**
672
- * Load one page with its whole tree (links resolved), or null when there is none. Optional; it
673
- * is what lets `personalizePage(ref)` do the fetch, so apps never fetch the page themselves.
674
- */
675
- fetchPage?(ref: PageRef, locale?: string): Promise<N | null>;
676
- }
677
- /** Identifies a page in the CMS: its entry id, or a content type plus exact field values (a slug). */
678
- type PageRef = string | {
679
- contentType: string;
680
- fields: Record<string, string | number | boolean>;
681
- };
682
- /** Resolves a round's runtime targets — `XeniaApi.resolveEntries` bound to the visitor. */
683
- type EntryResolver = (entryIds: string[]) => Promise<EntryDecision[]>;
684
- /** Reported for every swap actually applied (for impressions / analytics). */
685
- interface AppliedSwap {
686
- target: string;
687
- kind: "static" | "runtime";
688
- op: EntrySwapOp;
689
- /** Ids of the entries now at the target's position (or in its field). */
690
- entryIds: string[];
691
- contentKey?: string;
692
- bucket?: string;
693
- round: number;
694
- }
695
- interface PersonalizeContentOptions {
696
- cms: CmsAdapter;
697
- /** Decides runtime targets. Omit to apply static swaps only (zero network). */
698
- resolve?: EntryResolver;
699
- locale?: string;
700
- /** Max network rounds (nesting depth of runtime targets). Default 3. */
701
- maxRounds?: number;
702
- /** Total budget for all rounds, in ms. Undecided targets keep their base. Default 300. */
703
- timeoutMs?: number;
704
- /** Mutate the payload in place instead of personalizing a copy. Default false. */
705
- inPlace?: boolean;
706
- onApplied?(swap: AppliedSwap): void;
707
- /**
708
- * Called when a round starts waiting on the network (`pending` = targets being decided) and when
709
- * it has been applied (`pending` empty). `content` is the live, partially personalized tree —
710
- * lets a UI render level by level instead of waiting for every round.
711
- */
712
- onProgress?(progress: {
713
- content: unknown;
714
- round: number;
715
- pending: string[];
716
- }): void;
717
- }
718
- /** `personalizeContent` options as taken by `XeniaClient` / `XeniaServer`, which supply `resolve`. */
719
- type PagePersonalizeOptions = Omit<PersonalizeContentOptions, "resolve"> & Pick<ResolveOptions, "data" | "identity" | "context">;
720
- interface PersonalizeContentResult<T> {
721
- content: T;
722
- applied: AppliedSwap[];
723
- /** False when the budget ran out, a call failed, or `maxRounds` was hit with targets left. */
724
- complete: boolean;
725
- error?: unknown;
726
- }
727
- declare function personalizeContent<T>(payload: T, options: PersonalizeContentOptions): Promise<PersonalizeContentResult<T>>;
728
- /**
729
- * The entries Xenia still has to decide on this page — runtime targets reachable without crossing
730
- * another undecided one. Synchronous and network-free (it reads the `xenia` markers), so a page can
731
- * render a skeleton in exactly those places from the first frame instead of flashing base content.
732
- * Nested targets inside a pending one are found once it is decided.
733
- */
734
- declare function pendingTargets(payload: unknown, cms: CmsAdapter): string[];
735
- /**
736
- * Fetch a page through `options.cms` and personalize it — {@link personalizeContent} without the
737
- * fetch in app code. Never throws: a failed fetch comes back as `content: null` with `error`; a page
738
- * that does not exist as `content: null`, `complete: true`. The page fetch is not counted against
739
- * `timeoutMs` — that budget is for personalization only.
740
- */
741
- declare function personalizePage<T>(ref: PageRef, options: PersonalizeContentOptions): Promise<PersonalizeContentResult<T | null>>;
742
-
743
- export { type AnalyticsContext as A, type WidgetPrize as B, type CmsAdapter as C, type WidgetSegment as D, type EventContext as E, type WidgetSubmitResponse as F, type WidgetType as G, type HttpMethod as H, type IdentifyRequest as I, type JsonValue as J, type XeniaAction as K, type XeniaConfig as L, type MessageCta as M, type XeniaEvent as N, pendingTargets as O, type PagePersonalizeOptions as P, personalizeContent as Q, type ResolveBatchItem as R, personalizePage as S, type TrackOptions as T, type CallerIdentity as U, type VisitorCookieOptions as V, type WebhookResponse as W, type XeniaMarker as X, type AnalyticsEventName as a, type AnalyticsIdentity as b, type AnalyticsIngestResponse as c, type AppliedSwap as d, type CircuitBreakerConfig as e, type ConsentState as f, type EntryDecision as g, type EntryFilter as h, type EntryQuery as i, type EntryResolver as j, type EntrySwapOp as k, type EventIdentity as l, type IdentifyResponse as m, type InteractionType as n, type JsonObject as o, type MessagePlacement as p, type PageRef as q, type PersonalizationSwap as r, type PersonalizeContentOptions as s, type PersonalizeContentResult as t, type ResolveBatchResult as u, type ResolveEntriesResult as v, type ResolveOptions as w, type ResolveResult as x, type RetryConfig as y, type WidgetFormSubmit as z };
623
+ export type { AnalyticsContext as A, CircuitBreakerConfig as C, EventContext as E, HttpMethod as H, IdentifyRequest as I, JsonValue as J, MessageCta as M, PersonalizationSwap as P, ResolveBatchItem as R, TrackOptions as T, VisitorCookieOptions as V, WebhookResponse as W, XeniaAction as X, AnalyticsEventName as a, AnalyticsIdentity as b, AnalyticsIngestResponse as c, ConsentState as d, EntryDecision as e, EntryFilter as f, EntryQuery as g, EntrySwapOp as h, EventIdentity as i, IdentifyResponse as j, InteractionType as k, JsonObject as l, MessagePlacement as m, ResolveBatchResult as n, ResolveEntriesResult as o, ResolveOptions as p, ResolveResult as q, RetryConfig as r, WidgetFormSubmit as s, WidgetPrize as t, WidgetSegment as u, WidgetSubmitResponse as v, WidgetType as w, XeniaConfig as x, XeniaEvent as y, CallerIdentity as z };