@xenia-martech/sdk-js 1.2.0-dev.7 → 1.2.0-dev.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -872,7 +872,7 @@ export const proxy = (request: NextRequest) =>
872
872
  | `onDecision(info)` | Every exit (`skipped`, `not-targeted`, `no-consent`, `decided`, `error`) with the context, facts, outcome and response — debug output, extra cookies. |
873
873
  | `onError(error)` | Anything that threw — a hook, the edge client. The page is served with base content either way. |
874
874
 
875
- Two route factories serve the rest: `createManifestRoute({ token, baseUrl, revalidate })` for the public manifest copy (`export const GET`, with `export const revalidate = …` beside it), and `createRevalidateRoute({ secret, paths: ["/api/xenia/manifest"], onManifestChanged })` for Xenia's storefront webhook (`export const POST`), which regenerates the manifest the moment a journey changes.
875
+ Two route factories serve the rest: `createManifestRoute({ token, baseUrl, revalidate })` for the public manifest copy (`export const GET`, with `export const revalidate = …` beside it), and `createRevalidateRoute({ secret, paths: ["/api/xenia/manifest"], onManifestChanged })` for Xenia's storefront webhook (`export const POST`), which regenerates the manifest the moment a journey changes. Each build and server process fetches its own copy, so a deploy never serves a manifest cached by an earlier build. Point `createXeniaEdge`'s `manifestUrl` at that route: its copy lists pages, not content keys, so the proxy asks `/decide` by path and Xenia decides every live journey on it, including one published since the last manifest refresh. Read Xenia's full manifest instead and the proxy asks only for the keys it already knew.
876
876
 
877
877
  The page behind the rewrite reads the swaps back with `decodeVariant(token, secret)` — `null` for a token that is malformed or not signed with your secret, so a crafted URL can never render arbitrary entries — and applies them to its CMS content. The token holds only the swaps (plus your optional `extra`), identical for every visitor in the same variant, so each variant renders once and is then served from your ISR/CDN cache.
878
878
 
@@ -317,9 +317,11 @@ async function decodeVariant(token, secret) {
317
317
  // src/edge/manifest.ts
318
318
  async function fetchManifest(opts) {
319
319
  const base = (opts.baseUrl ?? DEFAULT_BASE_URL).replace(/\/+$/, "");
320
+ const headers = new Headers(opts.init?.headers);
321
+ headers.set("Authorization", `Bearer ${opts.token}`);
320
322
  const res = await (opts.fetch ?? fetch)(`${base}/api/personalization/edge/manifest`, {
321
323
  ...opts.init,
322
- headers: { Authorization: `Bearer ${opts.token}` },
324
+ headers,
323
325
  signal: AbortSignal.timeout(opts.timeoutMs ?? 5e3)
324
326
  });
325
327
  if (!res.ok) throw new Error(`[xenia] manifest ${res.status}`);
@@ -332,5 +334,5 @@ function trimManifest(m) {
332
334
  }
333
335
 
334
336
  export { MAX_TRAIL, TRAIL_MAX_AGE_S, decodeVariant, deviceOf, edgeConsent, encodeVariant, fetchManifest, isBot, isPageLoad, isPrefetch, landingContext, localTime, readTrail, seasonOf, swapGroups, trailCookieName, trailCookieValue, trimManifest, verifyWebhook, viewerGeo, withView };
335
- //# sourceMappingURL=chunk-OMMIRSPK.js.map
336
- //# sourceMappingURL=chunk-OMMIRSPK.js.map
337
+ //# sourceMappingURL=chunk-F3WZFXYK.js.map
338
+ //# sourceMappingURL=chunk-F3WZFXYK.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/edge/consent.ts","../src/edge/request-facts.ts","../src/edge/trail.ts","../src/edge/webhook.ts","../src/edge/variant.ts","../src/edge/manifest.ts"],"names":["enc"],"mappings":"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","file":"chunk-F3WZFXYK.js","sourcesContent":["import { consentCookieName } from \"../core/cookie-names\";\nimport { parseCookies } from \"../server/cookies\";\n\nexport interface EdgeConsent {\n /** Off: don't ask Xenia, serve base content. */\n personalization: boolean;\n /** Off: decide, but keep no trail or click cookies and send no trail. */\n analytics: boolean;\n}\n\n/**\n * What the edge flow may do for this request, from the consent cookie the browser client's\n * `consent()` writes and the visitor's privacy signals:\n * - no consent cookie (or an unreadable one) → no choice made: everything on, as before;\n * - an explicit choice → followed (JSON `{analytics, personalization}`, or the scopes form\n * `analytics,personalization` where an unlisted scope is refused);\n * - `Sec-GPC: 1` / `DNT: 1` → analytics off, like the browser client's `respectDnt` (default on).\n *\n * Unlike `getConsent()`, an absent cookie isn't \"all denied\": the edge can't tell a visitor who\n * refused from one who was never asked, and refusing both would change every first page view.\n */\nexport function edgeConsent(headers: Headers, opts: { prefix?: string; respectDnt?: boolean } = {}): EdgeConsent {\n const raw = parseCookies(headers.get(\"cookie\"))[consentCookieName(opts.prefix)];\n let consent: EdgeConsent = { personalization: true, analytics: true };\n if (raw) {\n try {\n const parsed: unknown = JSON.parse(raw);\n if (parsed && typeof parsed === \"object\") {\n const p = parsed as Record<string, unknown>;\n consent = { personalization: p.personalization !== false, analytics: p.analytics !== false };\n }\n } catch {\n if (/^[a-z]+(\\s*,\\s*[a-z]+)*$/i.test(raw.trim())) {\n const scopes = new Set(raw.split(\",\").map((s) => s.trim()));\n consent = { personalization: scopes.has(\"personalization\"), analytics: scopes.has(\"analytics\") };\n }\n }\n }\n const privacySignal = headers.get(\"sec-gpc\") === \"1\" || headers.get(\"dnt\") === \"1\";\n if (opts.respectDnt !== false && privacySignal) consent = { ...consent, analytics: false };\n return consent;\n}\n","/**\n * Facts about a request that journey rules read — where the visitor is, on what device, where they\n * came from, their local time — derived from a standard `Request` in any runtime.\n */\nimport type { ViewerGeo } from \"./types\";\n\nconst GEO_KEYS = [\n \"country\",\n \"countryName\",\n \"region\",\n \"regionName\",\n \"city\",\n \"postalCode\",\n \"timeZone\",\n \"latitude\",\n \"longitude\",\n \"source\",\n] as const;\n\n/** Any host's geo object (Netlify, Next.js runtimes, our own edge function). */\ntype GeoLike = {\n country?: string | { code?: string; name?: string };\n countryName?: string;\n region?: string;\n subdivision?: { code?: string; name?: string };\n regionName?: string;\n city?: string;\n postalCode?: string;\n timeZone?: string;\n timezone?: string;\n latitude?: string | number;\n longitude?: string | number;\n};\n\n/** Normalizes any host's geo object into Xenia's `ViewerGeo` keys (values over 100 chars dropped). */\nfunction toViewerGeo(g: GeoLike | undefined, source: string): ViewerGeo | undefined {\n if (!g) return undefined;\n const country = typeof g.country === \"string\" ? g.country : g.country?.code;\n if (!country) return undefined;\n const raw: Record<string, unknown> = {\n country: country.toUpperCase(),\n countryName: g.countryName ?? (typeof g.country === \"object\" ? g.country?.name : undefined),\n region: g.region ?? g.subdivision?.code,\n regionName: g.regionName ?? g.subdivision?.name,\n city: g.city,\n postalCode: g.postalCode,\n timeZone: g.timeZone ?? g.timezone,\n latitude: g.latitude?.toString(),\n longitude: g.longitude?.toString(),\n source,\n };\n const geo: ViewerGeo = {};\n for (const k of GEO_KEYS) {\n const v = raw[k];\n if (typeof v === \"string\" && v && v.length <= 100) geo[k] = v;\n }\n return geo;\n}\n\nconst parseJson = (text: string): GeoLike | undefined => {\n try {\n return JSON.parse(text) as GeoLike;\n } catch {\n return undefined;\n }\n};\n\nfunction safeDecode(v: string): string {\n try {\n return decodeURIComponent(v);\n } catch {\n return v;\n }\n}\n\nfunction safeBase64(v: string): string {\n try {\n return atob(v);\n } catch {\n return \"\";\n }\n}\n\n/** Netlify's per-request context, when running in Netlify's runtime. */\nfunction netlifyContext(): { geo?: GeoLike } | undefined {\n try {\n return (globalThis as unknown as { Netlify?: { context?: { geo?: GeoLike } | null } }).Netlify?.context ?? undefined;\n } catch {\n return undefined;\n }\n}\n\nconst COUNTRY_HEADERS = [\"x-country\", \"x-vercel-ip-country\", \"cloudfront-viewer-country\", \"cf-ipcountry\"];\n\n/** The location headers `viewerGeo` can read, when trusted. */\nexport type GeoHeader = \"x-xn-geo\" | \"x-nf-geo\" | \"x-country\" | \"x-vercel-ip-country\" | \"cloudfront-viewer-country\" | \"cf-ipcountry\";\n\nfunction companions(h: Headers, country: string): GeoLike {\n const of = (name: string) => h.get(name) ?? undefined;\n if (country === \"x-vercel-ip-country\") {\n return { region: of(\"x-vercel-ip-country-region\"), city: safeDecode(of(\"x-vercel-ip-city\") ?? \"\") || undefined, timeZone: of(\"x-vercel-ip-timezone\") };\n }\n if (country === \"cloudfront-viewer-country\") {\n return { region: of(\"cloudfront-viewer-country-region\"), city: safeDecode(of(\"cloudfront-viewer-city\") ?? \"\") || undefined, timeZone: of(\"cloudfront-viewer-time-zone\") };\n }\n return {};\n}\n\n/**\n * The visitor's location as the hosting edge resolved it, and which source supplied it (`via`).\n * Geo must come from the storefront's edge, never Xenia's: `/decide` is server to server. Tried in\n * order — whichever the host provides:\n * 1. Netlify's per-request `Netlify.context.geo` (can't be spoofed by the client, unlike a header)\n * 2. `x-xn-geo` (URI-encoded JSON) from the site's own edge function\n * 3. a `request.geo` some runtimes attach\n * 4. Netlify's `x-nf-geo` header (base64 or URI-encoded JSON)\n * 5. `x-country` (Netlify), `x-vercel-ip-*`, `cloudfront-viewer-*`, `cf-ipcountry`\n * 6. `?xn_country=GB` — only with `devOverride`, for testing geo rules without an edge\n *\n * Headers (2, 4 and 5) are read only when named in `trust`: a visitor can send any header, and only\n * the site knows which ones its own host or edge function sets (and overwrites). Name exactly those,\n * e.g. `[\"x-vercel-ip-country\"]` on Vercel, `[\"cf-ipcountry\"]` behind Cloudflare,\n * `[\"x-nf-geo\", \"x-country\"]` on Netlify. 1 and 3 come from the runtime and are always used.\n */\nexport function viewerGeo(\n request: Request,\n opts: { devOverride?: boolean; trust?: readonly GeoHeader[] } = {},\n): { geo?: ViewerGeo; via: string } {\n const trusted = new Set<string>(opts.trust ?? []);\n const fromNetlify = toViewerGeo(netlifyContext()?.geo, \"edge\");\n if (fromNetlify) return { geo: fromNetlify, via: \"Netlify.context\" };\n\n const h = { get: (name: string) => (trusted.has(name) ? request.headers.get(name) : null) };\n const xn = h.get(\"x-xn-geo\");\n const fromXn = xn ? toViewerGeo(parseJson(safeDecode(xn)), \"edge\") : undefined;\n if (fromXn) return { geo: fromXn, via: \"x-xn-geo (edge function)\" };\n\n const fromRuntime = toViewerGeo((request as unknown as { geo?: GeoLike }).geo, \"edge\");\n if (fromRuntime) return { geo: fromRuntime, via: \"request.geo\" };\n\n const nf = h.get(\"x-nf-geo\");\n if (nf) {\n const fromNf = toViewerGeo(parseJson(safeBase64(nf)) ?? parseJson(safeDecode(nf)), \"edge\");\n if (fromNf) return { geo: fromNf, via: \"x-nf-geo\" };\n }\n\n const via = COUNTRY_HEADERS.find((n) => h.get(n));\n const headerCountry = via ? h.get(via) : null;\n if (via && headerCountry && /^[a-z]{2}$/i.test(headerCountry)) {\n const geo = toViewerGeo(\n {\n country: headerCountry,\n // The region, city and time zone of the same provider as the country header.\n ...companions(request.headers, via),\n },\n \"edge\",\n );\n return { geo, via };\n }\n\n const override = new URL(request.url).searchParams.get(\"xn_country\");\n if (opts.devOverride && override && /^[a-z]{2}$/i.test(override)) {\n return { geo: { country: override.toUpperCase(), source: \"dev\" }, via: \"?xn_country (dev)\" };\n }\n return { via: \"none\" };\n}\n\nexport function deviceOf(headers: Headers): \"mobile\" | \"tablet\" | \"desktop\" {\n const ua = headers.get(\"user-agent\") ?? \"\";\n if (/iPad|Tablet/i.test(ua)) return \"tablet\";\n if (headers.get(\"sec-ch-ua-mobile\") === \"?1\" || /Mobi|Android|iPhone/i.test(ua)) return \"mobile\";\n return \"desktop\";\n}\n\nconst BOT = /bot|crawl|spider|slurp|facebookexternalhit|embedly|preview|whatsapp|bingpreview/i;\n\n/** Crawlers and link-preview fetchers: never decided, never recorded. */\nexport function isBot(userAgent: string | null | undefined): boolean {\n return BOT.test(userAgent ?? \"\");\n}\n\n/** Link prefetches pass through a proxy too; they aren't page views. */\nexport function isPrefetch(headers: Headers): boolean {\n return (\n headers.has(\"next-router-prefetch\") ||\n /prefetch/i.test(headers.get(\"purpose\") ?? \"\") ||\n /prefetch/i.test(headers.get(\"sec-purpose\") ?? \"\")\n );\n}\n\n/**\n * Whether this is the browser loading a page (a full page load), by `Sec-Fetch-Dest`. Requests\n * without it (old browsers, curl) count as page loads.\n */\nexport function isPageLoad(headers: Headers): boolean {\n return (headers.get(\"sec-fetch-dest\") ?? \"document\") === \"document\" && !isPrefetch(headers);\n}\n\nconst UTM: Record<string, string> = {\n utm_source: \"utmSource\",\n utm_medium: \"utmMedium\",\n utm_campaign: \"utmCampaign\",\n utm_content: \"utmContent\",\n utm_term: \"utmTerm\",\n utm_id: \"utmId\",\n};\n\n/**\n * This session's landing context — first-touch attribution, so a campaign carries across pages:\n * the UTMs (≤200 chars each) and an external referrer (≤500), refreshed when the visitor arrives\n * from a new campaign. `saved` is the value kept from earlier (a cookie); `changed` says to save it.\n */\nexport function landingContext(\n url: URL,\n referrer: string | null | undefined,\n saved: string | undefined,\n): { ctx: Record<string, string>; changed: boolean } {\n const fromQuery: Record<string, string> = {};\n for (const [param, fact] of Object.entries(UTM)) {\n const v = url.searchParams.get(param);\n if (v) fromQuery[fact] = v.slice(0, 200);\n }\n const external = referrer && !referrer.startsWith(url.origin) ? referrer.slice(0, 500) : null;\n\n let kept: Record<string, string> | null;\n try {\n const parsed: unknown = saved ? JSON.parse(saved) : null;\n kept = parsed && typeof parsed === \"object\" && !Array.isArray(parsed) ? (parsed as Record<string, string>) : null;\n } catch {\n kept = null;\n }\n if (kept && !Object.keys(fromQuery).length) return { ctx: kept, changed: false };\n return { ctx: { ...fromQuery, ...(external ? { referrer: external } : {}) }, changed: true };\n}\n\nconst WEEKDAYS = [\"sunday\", \"monday\", \"tuesday\", \"wednesday\", \"thursday\", \"friday\", \"saturday\"];\n\n/** Meteorological seasons: Dec–Feb winter, Mar–May spring, Jun–Aug summer, Sep–Nov fall. */\nexport function seasonOf(month: number, southernHemisphere = false): string {\n const north = [\"winter\", \"winter\", \"spring\", \"spring\", \"spring\", \"summer\", \"summer\", \"summer\", \"fall\", \"fall\", \"fall\", \"winter\"];\n const season = north[(month - 1 + 12) % 12];\n if (!southernHemisphere) return season;\n return ({ winter: \"summer\", summer: \"winter\", spring: \"fall\", fall: \"spring\" } as Record<string, string>)[season] ?? season;\n}\n\nfunction validTimeZone(tz: string | undefined): string | undefined {\n if (!tz) return undefined;\n try {\n new Intl.DateTimeFormat(\"en-US\", { timeZone: tz });\n return tz;\n } catch {\n return undefined;\n }\n}\n\nexport interface LocalTime {\n local_weekday: string;\n local_month: number;\n local_hour: number;\n is_weekend: boolean;\n season: string;\n}\n\n/** Weekday, month, hour and season where the visitor is (their time zone; UTC when unknown). */\nexport function localTime(geo: ViewerGeo | undefined, now: Date): LocalTime {\n const timeZone = validTimeZone(geo?.timeZone) ?? \"UTC\";\n const parts = Object.fromEntries(\n new Intl.DateTimeFormat(\"en-US\", { timeZone, weekday: \"long\", month: \"numeric\", hour: \"numeric\", hourCycle: \"h23\" })\n .formatToParts(now)\n .map((p) => [p.type, p.value]),\n );\n const weekday = String(parts.weekday ?? WEEKDAYS[now.getUTCDay()]).toLowerCase();\n const month = Number(parts.month) || now.getUTCMonth() + 1;\n return {\n local_weekday: weekday,\n local_month: month,\n local_hour: Number(parts.hour) % 24,\n is_weekend: weekday === \"saturday\" || weekday === \"sunday\",\n season: seasonOf(month, Number(geo?.latitude) < 0),\n };\n}\n","/**\n * The visitor's recent page views, most recent first, kept in a short-lived first-party cookie\n * (`[[path, at], [path, at, seconds], …]`). The proxy sends it with `/decide`, so profile rules see\n * pages whose analytics events haven't been processed yet; the browser adds client-side navigations\n * and the time spent on a page as the visitor leaves it. Shared by both sides.\n */\nimport { DEFAULT_PREFIX } from \"../core/cookie-names\";\nimport type { TrailView } from \"./types\";\n\nexport const MAX_TRAIL = 8;\n/** Cookie lifetime: a visit ends after 30 idle minutes, like the SDK's session. */\nexport const TRAIL_MAX_AGE_S = 30 * 60;\n\nexport function trailCookieName(prefix: string = DEFAULT_PREFIX): string {\n return `${prefix}_trail`;\n}\n\nconst decode = (v: string) => {\n try {\n return decodeURIComponent(v);\n } catch {\n return v;\n }\n};\n\n/** The trail in a cookie value (raw or URI-encoded); anything malformed reads as no trail. */\nexport function readTrail(value: string | undefined | null): TrailView[] {\n if (!value) return [];\n try {\n const parsed: unknown = JSON.parse(decode(value));\n if (!Array.isArray(parsed)) return [];\n return parsed\n .filter((t): t is [string, number, number?] => Array.isArray(t) && typeof t[0] === \"string\" && typeof t[1] === \"number\")\n .slice(0, MAX_TRAIL)\n .map(([path, at, seconds]) =>\n typeof seconds === \"number\" && seconds >= 0 && seconds <= 86_400 ? { path, at, seconds } : { path, at },\n );\n } catch {\n return [];\n }\n}\n\n/**\n * The trail with this view first: a revisit moves to the front and keeps its first time (its old\n * time on page is dropped — this view's isn't known yet). The same array when `path` is already first.\n */\nexport function withView(trail: TrailView[], path: string, now: number = Date.now()): TrailView[] {\n if (trail[0]?.path === path) return trail;\n const earlier = trail.find((t) => t.path === path);\n return [{ path, at: earlier?.at ?? now }, ...trail.filter((t) => t.path !== path)].slice(0, MAX_TRAIL);\n}\n\n/** The cookie value for a trail (not URI-encoded; cookie setters encode it). */\nexport function trailCookieValue(trail: TrailView[]): string {\n return JSON.stringify(\n trail.slice(0, MAX_TRAIL).map((t) => (t.seconds === undefined ? [t.path, t.at] : [t.path, t.at, t.seconds])),\n );\n}\n","const enc = new TextEncoder();\n\nasync function hmacHex(secret: string, payload: string): Promise<string> {\n const key = await crypto.subtle.importKey(\"raw\", enc.encode(secret), { name: \"HMAC\", hash: \"SHA-256\" }, false, [\"sign\"]);\n const mac = new Uint8Array(await crypto.subtle.sign(\"HMAC\", key, enc.encode(payload)));\n return Array.from(mac, (b) => b.toString(16).padStart(2, \"0\")).join(\"\");\n}\n\n/** Constant-time comparison, so a signature can't be guessed byte by byte. */\nexport function sameString(a: string, b: string): boolean {\n if (a.length !== b.length) return false;\n let diff = 0;\n for (let i = 0; i < a.length; i++) diff |= a.charCodeAt(i) ^ b.charCodeAt(i);\n return diff === 0;\n}\n\n/**\n * Whether a storefront webhook from Xenia is genuine and fresh:\n * `X-Xenia-Signature: t=<unix s>,v1=<hex HMAC-SHA256(secret, \"<t>.<body>\")>`. Requests older than\n * `toleranceS` are refused, so a captured request can't be replayed. Pass the raw body text.\n */\nexport async function verifyWebhook(\n input: { headers: Headers; body: string },\n secret: string,\n toleranceS = 300,\n): Promise<boolean> {\n const header = input.headers.get(\"x-xenia-signature\") ?? \"\";\n if (!secret || !header) return false;\n const parts = Object.fromEntries(header.split(\",\").map((p) => p.trim().split(\"=\", 2)));\n const t = Number(parts.t);\n if (!Number.isFinite(t) || Math.abs(Date.now() / 1000 - t) > toleranceS || !parts.v1) return false;\n return sameString(await hmacHex(secret, `${parts.t}.${input.body}`), String(parts.v1).toLowerCase());\n}\n","/**\n * The proxy hands a decision to the page as a signed token in the rewritten URL\n * (`/xv/<token>/women`). The token is just the swaps — identical for every visitor in the same\n * variant — so each variant renders once and is then served from the ISR/CDN cache like any static\n * page. A site may carry its own data along (`extra`), e.g. what the page fills itself from.\n *\n * Signed (HMAC-SHA256) so nobody can craft a URL that renders arbitrary entries. Web Crypto only, so\n * it runs in any runtime.\n */\nimport type { EdgeDecision } from \"./types\";\nimport { sameString } from \"./webhook\";\n\n/**\n * One swap: the base entry's field, the entry shown in its place, and the content key whose\n * decision it is — so the page can report impressions and clicks per journey and entry.\n */\nexport type VariantSwap = [identifier: string, replacementEntryId: string, contentKey?: string];\n\n/** Swaps for one base entry. */\nexport interface SwapGroup {\n base: string;\n swaps: VariantSwap[];\n}\n\n/** The longest payload `decodeVariant` accepts. */\nconst MAX_PAYLOAD = 4096;\n\nconst enc = new TextEncoder();\n\nconst b64url = (bytes: Uint8Array) =>\n btoa(String.fromCharCode(...bytes))\n .replace(/\\+/g, \"-\")\n .replace(/\\//g, \"_\")\n .replace(/=+$/, \"\");\n\nconst fromB64url = (s: string) =>\n Uint8Array.from(atob(s.replace(/-/g, \"+\").replace(/_/g, \"/\")), (c) => c.charCodeAt(0));\n\nconst keys = new Map<string, Promise<CryptoKey>>();\nconst keyFor = (secret: string) => {\n let key = keys.get(secret);\n if (!key) {\n key = crypto.subtle.importKey(\"raw\", enc.encode(secret), { name: \"HMAC\", hash: \"SHA-256\" }, false, [\"sign\"]);\n keys.set(secret, key);\n }\n return key;\n};\n\nasync function sign(secret: string, payload: string): Promise<string> {\n const mac = await crypto.subtle.sign(\"HMAC\", await keyFor(secret), enc.encode(payload));\n return b64url(new Uint8Array(mac).slice(0, 16));\n}\n\n/** The swap groups of the personalized decisions, in a stable order (stable cache keys). */\nexport function swapGroups(decisions: EdgeDecision[]): SwapGroup[] {\n const byBase = new Map<string, Map<string, VariantSwap>>();\n for (const d of decisions) {\n if (!d.personalized || !d.baseEntryId) continue;\n for (const s of d.swaps) {\n if (!s.identifier || !s.replacementEntryId) continue;\n if (!byBase.has(d.baseEntryId)) byBase.set(d.baseEntryId, new Map());\n byBase.get(d.baseEntryId)!.set(s.identifier, [s.identifier, s.replacementEntryId, d.contentKey]);\n }\n }\n return [...byBase.entries()]\n .sort(([a], [b]) => a.localeCompare(b))\n .map(([base, swaps]) => ({ base, swaps: [...swaps.values()].sort(([a], [b]) => a.localeCompare(b)) }));\n}\n\n/**\n * The token for a variant. An `extra` that would push it past the payload limit is left out — the\n * page still renders its swaps — rather than making a token no page accepts.\n */\nexport async function encodeVariant(groups: SwapGroup[], secret: string, extra?: unknown): Promise<string> {\n const g = groups.map((x) => [x.base, x.swaps]);\n const payloadOf = (x: unknown) => b64url(enc.encode(JSON.stringify(x === undefined ? { g } : { g, x })));\n let payload = payloadOf(extra);\n if (payload.length > MAX_PAYLOAD && extra !== undefined) payload = payloadOf(undefined);\n return `${payload}.${await sign(secret, payload)}`;\n}\n\n/** The variant in a token, or `null` if it is malformed or not signed with `secret`. */\nexport async function decodeVariant<E = unknown>(\n token: string,\n secret: string,\n): Promise<{ groups: SwapGroup[]; extra?: E } | null> {\n const [payload, mac, rest] = token.split(\".\");\n if (!payload || !mac || rest !== undefined || payload.length > MAX_PAYLOAD) return null;\n if (!sameString(await sign(secret, payload), mac)) return null;\n try {\n const raw = JSON.parse(new TextDecoder().decode(fromB64url(payload))) as { g: [string, VariantSwap[]][]; x?: E };\n const groups = raw.g.map(([base, swaps]) => ({ base, swaps }));\n return raw.x === undefined ? { groups } : { groups, extra: raw.x };\n } catch {\n return null;\n }\n}\n","import { DEFAULT_BASE_URL } from \"../core/http-client\";\nimport type { Manifest, PublicManifest } from \"./types\";\n\nexport interface FetchManifestOptions {\n /** Xenia engine base URL. Default the hosted engine. */\n baseUrl?: string;\n /** A server-type SDK token. */\n token: string;\n /** Default 5000 ms. */\n timeoutMs?: number;\n fetch?: typeof fetch;\n /** Extra fetch options, e.g. Next.js `{ next: { revalidate } }`. Their headers are kept. */\n init?: RequestInit;\n}\n\n/** Xenia's manifest (`GET /api/personalization/edge/manifest`). Throws on a non-2xx answer. */\nexport async function fetchManifest(opts: FetchManifestOptions): Promise<Manifest> {\n const base = (opts.baseUrl ?? DEFAULT_BASE_URL).replace(/\\/+$/, \"\");\n const headers = new Headers(opts.init?.headers);\n headers.set(\"Authorization\", `Bearer ${opts.token}`);\n const res = await (opts.fetch ?? fetch)(`${base}/api/personalization/edge/manifest`, {\n ...opts.init,\n headers,\n signal: AbortSignal.timeout(opts.timeoutMs ?? 5_000),\n });\n if (!res.ok) throw new Error(`[xenia] manifest ${res.status}`);\n return (await res.json()) as Manifest;\n}\n\n/**\n * The manifest as a storefront may serve it publicly (e.g. from a CDN): the version and the page\n * patterns any live journey targets — no content keys, modes or rules. A proxy reading it decides by\n * path, and Xenia picks the content keys itself.\n */\nexport function trimManifest(m: Manifest): PublicManifest {\n const pages = new Set<string>();\n for (const k of m.keys ?? []) if (k.mode !== \"off\") for (const p of k.pages ?? []) pages.add(p);\n return { version: m.version, pages: [...pages].sort() };\n}\n"]}
@@ -319,9 +319,11 @@ async function decodeVariant(token, secret) {
319
319
  // src/edge/manifest.ts
320
320
  async function fetchManifest(opts) {
321
321
  const base = (opts.baseUrl ?? chunkN5M4PI7C_cjs.DEFAULT_BASE_URL).replace(/\/+$/, "");
322
+ const headers = new Headers(opts.init?.headers);
323
+ headers.set("Authorization", `Bearer ${opts.token}`);
322
324
  const res = await (opts.fetch ?? fetch)(`${base}/api/personalization/edge/manifest`, {
323
325
  ...opts.init,
324
- headers: { Authorization: `Bearer ${opts.token}` },
326
+ headers,
325
327
  signal: AbortSignal.timeout(opts.timeoutMs ?? 5e3)
326
328
  });
327
329
  if (!res.ok) throw new Error(`[xenia] manifest ${res.status}`);
@@ -354,5 +356,5 @@ exports.trimManifest = trimManifest;
354
356
  exports.verifyWebhook = verifyWebhook;
355
357
  exports.viewerGeo = viewerGeo;
356
358
  exports.withView = withView;
357
- //# sourceMappingURL=chunk-YLMMTKQL.cjs.map
358
- //# sourceMappingURL=chunk-YLMMTKQL.cjs.map
359
+ //# sourceMappingURL=chunk-IOY6IPUW.cjs.map
360
+ //# sourceMappingURL=chunk-IOY6IPUW.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/edge/consent.ts","../src/edge/request-facts.ts","../src/edge/trail.ts","../src/edge/webhook.ts","../src/edge/variant.ts","../src/edge/manifest.ts"],"names":["parseCookies","consentCookieName","DEFAULT_PREFIX","enc","DEFAULT_BASE_URL"],"mappings":"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","file":"chunk-IOY6IPUW.cjs","sourcesContent":["import { consentCookieName } from \"../core/cookie-names\";\nimport { parseCookies } from \"../server/cookies\";\n\nexport interface EdgeConsent {\n /** Off: don't ask Xenia, serve base content. */\n personalization: boolean;\n /** Off: decide, but keep no trail or click cookies and send no trail. */\n analytics: boolean;\n}\n\n/**\n * What the edge flow may do for this request, from the consent cookie the browser client's\n * `consent()` writes and the visitor's privacy signals:\n * - no consent cookie (or an unreadable one) → no choice made: everything on, as before;\n * - an explicit choice → followed (JSON `{analytics, personalization}`, or the scopes form\n * `analytics,personalization` where an unlisted scope is refused);\n * - `Sec-GPC: 1` / `DNT: 1` → analytics off, like the browser client's `respectDnt` (default on).\n *\n * Unlike `getConsent()`, an absent cookie isn't \"all denied\": the edge can't tell a visitor who\n * refused from one who was never asked, and refusing both would change every first page view.\n */\nexport function edgeConsent(headers: Headers, opts: { prefix?: string; respectDnt?: boolean } = {}): EdgeConsent {\n const raw = parseCookies(headers.get(\"cookie\"))[consentCookieName(opts.prefix)];\n let consent: EdgeConsent = { personalization: true, analytics: true };\n if (raw) {\n try {\n const parsed: unknown = JSON.parse(raw);\n if (parsed && typeof parsed === \"object\") {\n const p = parsed as Record<string, unknown>;\n consent = { personalization: p.personalization !== false, analytics: p.analytics !== false };\n }\n } catch {\n if (/^[a-z]+(\\s*,\\s*[a-z]+)*$/i.test(raw.trim())) {\n const scopes = new Set(raw.split(\",\").map((s) => s.trim()));\n consent = { personalization: scopes.has(\"personalization\"), analytics: scopes.has(\"analytics\") };\n }\n }\n }\n const privacySignal = headers.get(\"sec-gpc\") === \"1\" || headers.get(\"dnt\") === \"1\";\n if (opts.respectDnt !== false && privacySignal) consent = { ...consent, analytics: false };\n return consent;\n}\n","/**\n * Facts about a request that journey rules read — where the visitor is, on what device, where they\n * came from, their local time — derived from a standard `Request` in any runtime.\n */\nimport type { ViewerGeo } from \"./types\";\n\nconst GEO_KEYS = [\n \"country\",\n \"countryName\",\n \"region\",\n \"regionName\",\n \"city\",\n \"postalCode\",\n \"timeZone\",\n \"latitude\",\n \"longitude\",\n \"source\",\n] as const;\n\n/** Any host's geo object (Netlify, Next.js runtimes, our own edge function). */\ntype GeoLike = {\n country?: string | { code?: string; name?: string };\n countryName?: string;\n region?: string;\n subdivision?: { code?: string; name?: string };\n regionName?: string;\n city?: string;\n postalCode?: string;\n timeZone?: string;\n timezone?: string;\n latitude?: string | number;\n longitude?: string | number;\n};\n\n/** Normalizes any host's geo object into Xenia's `ViewerGeo` keys (values over 100 chars dropped). */\nfunction toViewerGeo(g: GeoLike | undefined, source: string): ViewerGeo | undefined {\n if (!g) return undefined;\n const country = typeof g.country === \"string\" ? g.country : g.country?.code;\n if (!country) return undefined;\n const raw: Record<string, unknown> = {\n country: country.toUpperCase(),\n countryName: g.countryName ?? (typeof g.country === \"object\" ? g.country?.name : undefined),\n region: g.region ?? g.subdivision?.code,\n regionName: g.regionName ?? g.subdivision?.name,\n city: g.city,\n postalCode: g.postalCode,\n timeZone: g.timeZone ?? g.timezone,\n latitude: g.latitude?.toString(),\n longitude: g.longitude?.toString(),\n source,\n };\n const geo: ViewerGeo = {};\n for (const k of GEO_KEYS) {\n const v = raw[k];\n if (typeof v === \"string\" && v && v.length <= 100) geo[k] = v;\n }\n return geo;\n}\n\nconst parseJson = (text: string): GeoLike | undefined => {\n try {\n return JSON.parse(text) as GeoLike;\n } catch {\n return undefined;\n }\n};\n\nfunction safeDecode(v: string): string {\n try {\n return decodeURIComponent(v);\n } catch {\n return v;\n }\n}\n\nfunction safeBase64(v: string): string {\n try {\n return atob(v);\n } catch {\n return \"\";\n }\n}\n\n/** Netlify's per-request context, when running in Netlify's runtime. */\nfunction netlifyContext(): { geo?: GeoLike } | undefined {\n try {\n return (globalThis as unknown as { Netlify?: { context?: { geo?: GeoLike } | null } }).Netlify?.context ?? undefined;\n } catch {\n return undefined;\n }\n}\n\nconst COUNTRY_HEADERS = [\"x-country\", \"x-vercel-ip-country\", \"cloudfront-viewer-country\", \"cf-ipcountry\"];\n\n/** The location headers `viewerGeo` can read, when trusted. */\nexport type GeoHeader = \"x-xn-geo\" | \"x-nf-geo\" | \"x-country\" | \"x-vercel-ip-country\" | \"cloudfront-viewer-country\" | \"cf-ipcountry\";\n\nfunction companions(h: Headers, country: string): GeoLike {\n const of = (name: string) => h.get(name) ?? undefined;\n if (country === \"x-vercel-ip-country\") {\n return { region: of(\"x-vercel-ip-country-region\"), city: safeDecode(of(\"x-vercel-ip-city\") ?? \"\") || undefined, timeZone: of(\"x-vercel-ip-timezone\") };\n }\n if (country === \"cloudfront-viewer-country\") {\n return { region: of(\"cloudfront-viewer-country-region\"), city: safeDecode(of(\"cloudfront-viewer-city\") ?? \"\") || undefined, timeZone: of(\"cloudfront-viewer-time-zone\") };\n }\n return {};\n}\n\n/**\n * The visitor's location as the hosting edge resolved it, and which source supplied it (`via`).\n * Geo must come from the storefront's edge, never Xenia's: `/decide` is server to server. Tried in\n * order — whichever the host provides:\n * 1. Netlify's per-request `Netlify.context.geo` (can't be spoofed by the client, unlike a header)\n * 2. `x-xn-geo` (URI-encoded JSON) from the site's own edge function\n * 3. a `request.geo` some runtimes attach\n * 4. Netlify's `x-nf-geo` header (base64 or URI-encoded JSON)\n * 5. `x-country` (Netlify), `x-vercel-ip-*`, `cloudfront-viewer-*`, `cf-ipcountry`\n * 6. `?xn_country=GB` — only with `devOverride`, for testing geo rules without an edge\n *\n * Headers (2, 4 and 5) are read only when named in `trust`: a visitor can send any header, and only\n * the site knows which ones its own host or edge function sets (and overwrites). Name exactly those,\n * e.g. `[\"x-vercel-ip-country\"]` on Vercel, `[\"cf-ipcountry\"]` behind Cloudflare,\n * `[\"x-nf-geo\", \"x-country\"]` on Netlify. 1 and 3 come from the runtime and are always used.\n */\nexport function viewerGeo(\n request: Request,\n opts: { devOverride?: boolean; trust?: readonly GeoHeader[] } = {},\n): { geo?: ViewerGeo; via: string } {\n const trusted = new Set<string>(opts.trust ?? []);\n const fromNetlify = toViewerGeo(netlifyContext()?.geo, \"edge\");\n if (fromNetlify) return { geo: fromNetlify, via: \"Netlify.context\" };\n\n const h = { get: (name: string) => (trusted.has(name) ? request.headers.get(name) : null) };\n const xn = h.get(\"x-xn-geo\");\n const fromXn = xn ? toViewerGeo(parseJson(safeDecode(xn)), \"edge\") : undefined;\n if (fromXn) return { geo: fromXn, via: \"x-xn-geo (edge function)\" };\n\n const fromRuntime = toViewerGeo((request as unknown as { geo?: GeoLike }).geo, \"edge\");\n if (fromRuntime) return { geo: fromRuntime, via: \"request.geo\" };\n\n const nf = h.get(\"x-nf-geo\");\n if (nf) {\n const fromNf = toViewerGeo(parseJson(safeBase64(nf)) ?? parseJson(safeDecode(nf)), \"edge\");\n if (fromNf) return { geo: fromNf, via: \"x-nf-geo\" };\n }\n\n const via = COUNTRY_HEADERS.find((n) => h.get(n));\n const headerCountry = via ? h.get(via) : null;\n if (via && headerCountry && /^[a-z]{2}$/i.test(headerCountry)) {\n const geo = toViewerGeo(\n {\n country: headerCountry,\n // The region, city and time zone of the same provider as the country header.\n ...companions(request.headers, via),\n },\n \"edge\",\n );\n return { geo, via };\n }\n\n const override = new URL(request.url).searchParams.get(\"xn_country\");\n if (opts.devOverride && override && /^[a-z]{2}$/i.test(override)) {\n return { geo: { country: override.toUpperCase(), source: \"dev\" }, via: \"?xn_country (dev)\" };\n }\n return { via: \"none\" };\n}\n\nexport function deviceOf(headers: Headers): \"mobile\" | \"tablet\" | \"desktop\" {\n const ua = headers.get(\"user-agent\") ?? \"\";\n if (/iPad|Tablet/i.test(ua)) return \"tablet\";\n if (headers.get(\"sec-ch-ua-mobile\") === \"?1\" || /Mobi|Android|iPhone/i.test(ua)) return \"mobile\";\n return \"desktop\";\n}\n\nconst BOT = /bot|crawl|spider|slurp|facebookexternalhit|embedly|preview|whatsapp|bingpreview/i;\n\n/** Crawlers and link-preview fetchers: never decided, never recorded. */\nexport function isBot(userAgent: string | null | undefined): boolean {\n return BOT.test(userAgent ?? \"\");\n}\n\n/** Link prefetches pass through a proxy too; they aren't page views. */\nexport function isPrefetch(headers: Headers): boolean {\n return (\n headers.has(\"next-router-prefetch\") ||\n /prefetch/i.test(headers.get(\"purpose\") ?? \"\") ||\n /prefetch/i.test(headers.get(\"sec-purpose\") ?? \"\")\n );\n}\n\n/**\n * Whether this is the browser loading a page (a full page load), by `Sec-Fetch-Dest`. Requests\n * without it (old browsers, curl) count as page loads.\n */\nexport function isPageLoad(headers: Headers): boolean {\n return (headers.get(\"sec-fetch-dest\") ?? \"document\") === \"document\" && !isPrefetch(headers);\n}\n\nconst UTM: Record<string, string> = {\n utm_source: \"utmSource\",\n utm_medium: \"utmMedium\",\n utm_campaign: \"utmCampaign\",\n utm_content: \"utmContent\",\n utm_term: \"utmTerm\",\n utm_id: \"utmId\",\n};\n\n/**\n * This session's landing context — first-touch attribution, so a campaign carries across pages:\n * the UTMs (≤200 chars each) and an external referrer (≤500), refreshed when the visitor arrives\n * from a new campaign. `saved` is the value kept from earlier (a cookie); `changed` says to save it.\n */\nexport function landingContext(\n url: URL,\n referrer: string | null | undefined,\n saved: string | undefined,\n): { ctx: Record<string, string>; changed: boolean } {\n const fromQuery: Record<string, string> = {};\n for (const [param, fact] of Object.entries(UTM)) {\n const v = url.searchParams.get(param);\n if (v) fromQuery[fact] = v.slice(0, 200);\n }\n const external = referrer && !referrer.startsWith(url.origin) ? referrer.slice(0, 500) : null;\n\n let kept: Record<string, string> | null;\n try {\n const parsed: unknown = saved ? JSON.parse(saved) : null;\n kept = parsed && typeof parsed === \"object\" && !Array.isArray(parsed) ? (parsed as Record<string, string>) : null;\n } catch {\n kept = null;\n }\n if (kept && !Object.keys(fromQuery).length) return { ctx: kept, changed: false };\n return { ctx: { ...fromQuery, ...(external ? { referrer: external } : {}) }, changed: true };\n}\n\nconst WEEKDAYS = [\"sunday\", \"monday\", \"tuesday\", \"wednesday\", \"thursday\", \"friday\", \"saturday\"];\n\n/** Meteorological seasons: Dec–Feb winter, Mar–May spring, Jun–Aug summer, Sep–Nov fall. */\nexport function seasonOf(month: number, southernHemisphere = false): string {\n const north = [\"winter\", \"winter\", \"spring\", \"spring\", \"spring\", \"summer\", \"summer\", \"summer\", \"fall\", \"fall\", \"fall\", \"winter\"];\n const season = north[(month - 1 + 12) % 12];\n if (!southernHemisphere) return season;\n return ({ winter: \"summer\", summer: \"winter\", spring: \"fall\", fall: \"spring\" } as Record<string, string>)[season] ?? season;\n}\n\nfunction validTimeZone(tz: string | undefined): string | undefined {\n if (!tz) return undefined;\n try {\n new Intl.DateTimeFormat(\"en-US\", { timeZone: tz });\n return tz;\n } catch {\n return undefined;\n }\n}\n\nexport interface LocalTime {\n local_weekday: string;\n local_month: number;\n local_hour: number;\n is_weekend: boolean;\n season: string;\n}\n\n/** Weekday, month, hour and season where the visitor is (their time zone; UTC when unknown). */\nexport function localTime(geo: ViewerGeo | undefined, now: Date): LocalTime {\n const timeZone = validTimeZone(geo?.timeZone) ?? \"UTC\";\n const parts = Object.fromEntries(\n new Intl.DateTimeFormat(\"en-US\", { timeZone, weekday: \"long\", month: \"numeric\", hour: \"numeric\", hourCycle: \"h23\" })\n .formatToParts(now)\n .map((p) => [p.type, p.value]),\n );\n const weekday = String(parts.weekday ?? WEEKDAYS[now.getUTCDay()]).toLowerCase();\n const month = Number(parts.month) || now.getUTCMonth() + 1;\n return {\n local_weekday: weekday,\n local_month: month,\n local_hour: Number(parts.hour) % 24,\n is_weekend: weekday === \"saturday\" || weekday === \"sunday\",\n season: seasonOf(month, Number(geo?.latitude) < 0),\n };\n}\n","/**\n * The visitor's recent page views, most recent first, kept in a short-lived first-party cookie\n * (`[[path, at], [path, at, seconds], …]`). The proxy sends it with `/decide`, so profile rules see\n * pages whose analytics events haven't been processed yet; the browser adds client-side navigations\n * and the time spent on a page as the visitor leaves it. Shared by both sides.\n */\nimport { DEFAULT_PREFIX } from \"../core/cookie-names\";\nimport type { TrailView } from \"./types\";\n\nexport const MAX_TRAIL = 8;\n/** Cookie lifetime: a visit ends after 30 idle minutes, like the SDK's session. */\nexport const TRAIL_MAX_AGE_S = 30 * 60;\n\nexport function trailCookieName(prefix: string = DEFAULT_PREFIX): string {\n return `${prefix}_trail`;\n}\n\nconst decode = (v: string) => {\n try {\n return decodeURIComponent(v);\n } catch {\n return v;\n }\n};\n\n/** The trail in a cookie value (raw or URI-encoded); anything malformed reads as no trail. */\nexport function readTrail(value: string | undefined | null): TrailView[] {\n if (!value) return [];\n try {\n const parsed: unknown = JSON.parse(decode(value));\n if (!Array.isArray(parsed)) return [];\n return parsed\n .filter((t): t is [string, number, number?] => Array.isArray(t) && typeof t[0] === \"string\" && typeof t[1] === \"number\")\n .slice(0, MAX_TRAIL)\n .map(([path, at, seconds]) =>\n typeof seconds === \"number\" && seconds >= 0 && seconds <= 86_400 ? { path, at, seconds } : { path, at },\n );\n } catch {\n return [];\n }\n}\n\n/**\n * The trail with this view first: a revisit moves to the front and keeps its first time (its old\n * time on page is dropped — this view's isn't known yet). The same array when `path` is already first.\n */\nexport function withView(trail: TrailView[], path: string, now: number = Date.now()): TrailView[] {\n if (trail[0]?.path === path) return trail;\n const earlier = trail.find((t) => t.path === path);\n return [{ path, at: earlier?.at ?? now }, ...trail.filter((t) => t.path !== path)].slice(0, MAX_TRAIL);\n}\n\n/** The cookie value for a trail (not URI-encoded; cookie setters encode it). */\nexport function trailCookieValue(trail: TrailView[]): string {\n return JSON.stringify(\n trail.slice(0, MAX_TRAIL).map((t) => (t.seconds === undefined ? [t.path, t.at] : [t.path, t.at, t.seconds])),\n );\n}\n","const enc = new TextEncoder();\n\nasync function hmacHex(secret: string, payload: string): Promise<string> {\n const key = await crypto.subtle.importKey(\"raw\", enc.encode(secret), { name: \"HMAC\", hash: \"SHA-256\" }, false, [\"sign\"]);\n const mac = new Uint8Array(await crypto.subtle.sign(\"HMAC\", key, enc.encode(payload)));\n return Array.from(mac, (b) => b.toString(16).padStart(2, \"0\")).join(\"\");\n}\n\n/** Constant-time comparison, so a signature can't be guessed byte by byte. */\nexport function sameString(a: string, b: string): boolean {\n if (a.length !== b.length) return false;\n let diff = 0;\n for (let i = 0; i < a.length; i++) diff |= a.charCodeAt(i) ^ b.charCodeAt(i);\n return diff === 0;\n}\n\n/**\n * Whether a storefront webhook from Xenia is genuine and fresh:\n * `X-Xenia-Signature: t=<unix s>,v1=<hex HMAC-SHA256(secret, \"<t>.<body>\")>`. Requests older than\n * `toleranceS` are refused, so a captured request can't be replayed. Pass the raw body text.\n */\nexport async function verifyWebhook(\n input: { headers: Headers; body: string },\n secret: string,\n toleranceS = 300,\n): Promise<boolean> {\n const header = input.headers.get(\"x-xenia-signature\") ?? \"\";\n if (!secret || !header) return false;\n const parts = Object.fromEntries(header.split(\",\").map((p) => p.trim().split(\"=\", 2)));\n const t = Number(parts.t);\n if (!Number.isFinite(t) || Math.abs(Date.now() / 1000 - t) > toleranceS || !parts.v1) return false;\n return sameString(await hmacHex(secret, `${parts.t}.${input.body}`), String(parts.v1).toLowerCase());\n}\n","/**\n * The proxy hands a decision to the page as a signed token in the rewritten URL\n * (`/xv/<token>/women`). The token is just the swaps — identical for every visitor in the same\n * variant — so each variant renders once and is then served from the ISR/CDN cache like any static\n * page. A site may carry its own data along (`extra`), e.g. what the page fills itself from.\n *\n * Signed (HMAC-SHA256) so nobody can craft a URL that renders arbitrary entries. Web Crypto only, so\n * it runs in any runtime.\n */\nimport type { EdgeDecision } from \"./types\";\nimport { sameString } from \"./webhook\";\n\n/**\n * One swap: the base entry's field, the entry shown in its place, and the content key whose\n * decision it is — so the page can report impressions and clicks per journey and entry.\n */\nexport type VariantSwap = [identifier: string, replacementEntryId: string, contentKey?: string];\n\n/** Swaps for one base entry. */\nexport interface SwapGroup {\n base: string;\n swaps: VariantSwap[];\n}\n\n/** The longest payload `decodeVariant` accepts. */\nconst MAX_PAYLOAD = 4096;\n\nconst enc = new TextEncoder();\n\nconst b64url = (bytes: Uint8Array) =>\n btoa(String.fromCharCode(...bytes))\n .replace(/\\+/g, \"-\")\n .replace(/\\//g, \"_\")\n .replace(/=+$/, \"\");\n\nconst fromB64url = (s: string) =>\n Uint8Array.from(atob(s.replace(/-/g, \"+\").replace(/_/g, \"/\")), (c) => c.charCodeAt(0));\n\nconst keys = new Map<string, Promise<CryptoKey>>();\nconst keyFor = (secret: string) => {\n let key = keys.get(secret);\n if (!key) {\n key = crypto.subtle.importKey(\"raw\", enc.encode(secret), { name: \"HMAC\", hash: \"SHA-256\" }, false, [\"sign\"]);\n keys.set(secret, key);\n }\n return key;\n};\n\nasync function sign(secret: string, payload: string): Promise<string> {\n const mac = await crypto.subtle.sign(\"HMAC\", await keyFor(secret), enc.encode(payload));\n return b64url(new Uint8Array(mac).slice(0, 16));\n}\n\n/** The swap groups of the personalized decisions, in a stable order (stable cache keys). */\nexport function swapGroups(decisions: EdgeDecision[]): SwapGroup[] {\n const byBase = new Map<string, Map<string, VariantSwap>>();\n for (const d of decisions) {\n if (!d.personalized || !d.baseEntryId) continue;\n for (const s of d.swaps) {\n if (!s.identifier || !s.replacementEntryId) continue;\n if (!byBase.has(d.baseEntryId)) byBase.set(d.baseEntryId, new Map());\n byBase.get(d.baseEntryId)!.set(s.identifier, [s.identifier, s.replacementEntryId, d.contentKey]);\n }\n }\n return [...byBase.entries()]\n .sort(([a], [b]) => a.localeCompare(b))\n .map(([base, swaps]) => ({ base, swaps: [...swaps.values()].sort(([a], [b]) => a.localeCompare(b)) }));\n}\n\n/**\n * The token for a variant. An `extra` that would push it past the payload limit is left out — the\n * page still renders its swaps — rather than making a token no page accepts.\n */\nexport async function encodeVariant(groups: SwapGroup[], secret: string, extra?: unknown): Promise<string> {\n const g = groups.map((x) => [x.base, x.swaps]);\n const payloadOf = (x: unknown) => b64url(enc.encode(JSON.stringify(x === undefined ? { g } : { g, x })));\n let payload = payloadOf(extra);\n if (payload.length > MAX_PAYLOAD && extra !== undefined) payload = payloadOf(undefined);\n return `${payload}.${await sign(secret, payload)}`;\n}\n\n/** The variant in a token, or `null` if it is malformed or not signed with `secret`. */\nexport async function decodeVariant<E = unknown>(\n token: string,\n secret: string,\n): Promise<{ groups: SwapGroup[]; extra?: E } | null> {\n const [payload, mac, rest] = token.split(\".\");\n if (!payload || !mac || rest !== undefined || payload.length > MAX_PAYLOAD) return null;\n if (!sameString(await sign(secret, payload), mac)) return null;\n try {\n const raw = JSON.parse(new TextDecoder().decode(fromB64url(payload))) as { g: [string, VariantSwap[]][]; x?: E };\n const groups = raw.g.map(([base, swaps]) => ({ base, swaps }));\n return raw.x === undefined ? { groups } : { groups, extra: raw.x };\n } catch {\n return null;\n }\n}\n","import { DEFAULT_BASE_URL } from \"../core/http-client\";\nimport type { Manifest, PublicManifest } from \"./types\";\n\nexport interface FetchManifestOptions {\n /** Xenia engine base URL. Default the hosted engine. */\n baseUrl?: string;\n /** A server-type SDK token. */\n token: string;\n /** Default 5000 ms. */\n timeoutMs?: number;\n fetch?: typeof fetch;\n /** Extra fetch options, e.g. Next.js `{ next: { revalidate } }`. Their headers are kept. */\n init?: RequestInit;\n}\n\n/** Xenia's manifest (`GET /api/personalization/edge/manifest`). Throws on a non-2xx answer. */\nexport async function fetchManifest(opts: FetchManifestOptions): Promise<Manifest> {\n const base = (opts.baseUrl ?? DEFAULT_BASE_URL).replace(/\\/+$/, \"\");\n const headers = new Headers(opts.init?.headers);\n headers.set(\"Authorization\", `Bearer ${opts.token}`);\n const res = await (opts.fetch ?? fetch)(`${base}/api/personalization/edge/manifest`, {\n ...opts.init,\n headers,\n signal: AbortSignal.timeout(opts.timeoutMs ?? 5_000),\n });\n if (!res.ok) throw new Error(`[xenia] manifest ${res.status}`);\n return (await res.json()) as Manifest;\n}\n\n/**\n * The manifest as a storefront may serve it publicly (e.g. from a CDN): the version and the page\n * patterns any live journey targets — no content keys, modes or rules. A proxy reading it decides by\n * path, and Xenia picks the content keys itself.\n */\nexport function trimManifest(m: Manifest): PublicManifest {\n const pages = new Set<string>();\n for (const k of m.keys ?? []) if (k.mode !== \"off\") for (const p of k.pages ?? []) pages.add(p);\n return { version: m.version, pages: [...pages].sort() };\n}\n"]}
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- var chunkYLMMTKQL_cjs = require('../chunk-YLMMTKQL.cjs');
3
+ var chunkIOY6IPUW_cjs = require('../chunk-IOY6IPUW.cjs');
4
4
  require('../chunk-WHGK55JP.cjs');
5
5
  require('../chunk-N5M4PI7C.cjs');
6
6
 
@@ -190,87 +190,87 @@ function createXeniaEdge(config) {
190
190
 
191
191
  Object.defineProperty(exports, "MAX_TRAIL", {
192
192
  enumerable: true,
193
- get: function () { return chunkYLMMTKQL_cjs.MAX_TRAIL; }
193
+ get: function () { return chunkIOY6IPUW_cjs.MAX_TRAIL; }
194
194
  });
195
195
  Object.defineProperty(exports, "TRAIL_MAX_AGE_S", {
196
196
  enumerable: true,
197
- get: function () { return chunkYLMMTKQL_cjs.TRAIL_MAX_AGE_S; }
197
+ get: function () { return chunkIOY6IPUW_cjs.TRAIL_MAX_AGE_S; }
198
198
  });
199
199
  Object.defineProperty(exports, "decodeVariant", {
200
200
  enumerable: true,
201
- get: function () { return chunkYLMMTKQL_cjs.decodeVariant; }
201
+ get: function () { return chunkIOY6IPUW_cjs.decodeVariant; }
202
202
  });
203
203
  Object.defineProperty(exports, "deviceOf", {
204
204
  enumerable: true,
205
- get: function () { return chunkYLMMTKQL_cjs.deviceOf; }
205
+ get: function () { return chunkIOY6IPUW_cjs.deviceOf; }
206
206
  });
207
207
  Object.defineProperty(exports, "edgeConsent", {
208
208
  enumerable: true,
209
- get: function () { return chunkYLMMTKQL_cjs.edgeConsent; }
209
+ get: function () { return chunkIOY6IPUW_cjs.edgeConsent; }
210
210
  });
211
211
  Object.defineProperty(exports, "encodeVariant", {
212
212
  enumerable: true,
213
- get: function () { return chunkYLMMTKQL_cjs.encodeVariant; }
213
+ get: function () { return chunkIOY6IPUW_cjs.encodeVariant; }
214
214
  });
215
215
  Object.defineProperty(exports, "fetchManifest", {
216
216
  enumerable: true,
217
- get: function () { return chunkYLMMTKQL_cjs.fetchManifest; }
217
+ get: function () { return chunkIOY6IPUW_cjs.fetchManifest; }
218
218
  });
219
219
  Object.defineProperty(exports, "isBot", {
220
220
  enumerable: true,
221
- get: function () { return chunkYLMMTKQL_cjs.isBot; }
221
+ get: function () { return chunkIOY6IPUW_cjs.isBot; }
222
222
  });
223
223
  Object.defineProperty(exports, "isPageLoad", {
224
224
  enumerable: true,
225
- get: function () { return chunkYLMMTKQL_cjs.isPageLoad; }
225
+ get: function () { return chunkIOY6IPUW_cjs.isPageLoad; }
226
226
  });
227
227
  Object.defineProperty(exports, "isPrefetch", {
228
228
  enumerable: true,
229
- get: function () { return chunkYLMMTKQL_cjs.isPrefetch; }
229
+ get: function () { return chunkIOY6IPUW_cjs.isPrefetch; }
230
230
  });
231
231
  Object.defineProperty(exports, "landingContext", {
232
232
  enumerable: true,
233
- get: function () { return chunkYLMMTKQL_cjs.landingContext; }
233
+ get: function () { return chunkIOY6IPUW_cjs.landingContext; }
234
234
  });
235
235
  Object.defineProperty(exports, "localTime", {
236
236
  enumerable: true,
237
- get: function () { return chunkYLMMTKQL_cjs.localTime; }
237
+ get: function () { return chunkIOY6IPUW_cjs.localTime; }
238
238
  });
239
239
  Object.defineProperty(exports, "readTrail", {
240
240
  enumerable: true,
241
- get: function () { return chunkYLMMTKQL_cjs.readTrail; }
241
+ get: function () { return chunkIOY6IPUW_cjs.readTrail; }
242
242
  });
243
243
  Object.defineProperty(exports, "seasonOf", {
244
244
  enumerable: true,
245
- get: function () { return chunkYLMMTKQL_cjs.seasonOf; }
245
+ get: function () { return chunkIOY6IPUW_cjs.seasonOf; }
246
246
  });
247
247
  Object.defineProperty(exports, "swapGroups", {
248
248
  enumerable: true,
249
- get: function () { return chunkYLMMTKQL_cjs.swapGroups; }
249
+ get: function () { return chunkIOY6IPUW_cjs.swapGroups; }
250
250
  });
251
251
  Object.defineProperty(exports, "trailCookieName", {
252
252
  enumerable: true,
253
- get: function () { return chunkYLMMTKQL_cjs.trailCookieName; }
253
+ get: function () { return chunkIOY6IPUW_cjs.trailCookieName; }
254
254
  });
255
255
  Object.defineProperty(exports, "trailCookieValue", {
256
256
  enumerable: true,
257
- get: function () { return chunkYLMMTKQL_cjs.trailCookieValue; }
257
+ get: function () { return chunkIOY6IPUW_cjs.trailCookieValue; }
258
258
  });
259
259
  Object.defineProperty(exports, "trimManifest", {
260
260
  enumerable: true,
261
- get: function () { return chunkYLMMTKQL_cjs.trimManifest; }
261
+ get: function () { return chunkIOY6IPUW_cjs.trimManifest; }
262
262
  });
263
263
  Object.defineProperty(exports, "verifyWebhook", {
264
264
  enumerable: true,
265
- get: function () { return chunkYLMMTKQL_cjs.verifyWebhook; }
265
+ get: function () { return chunkIOY6IPUW_cjs.verifyWebhook; }
266
266
  });
267
267
  Object.defineProperty(exports, "viewerGeo", {
268
268
  enumerable: true,
269
- get: function () { return chunkYLMMTKQL_cjs.viewerGeo; }
269
+ get: function () { return chunkIOY6IPUW_cjs.viewerGeo; }
270
270
  });
271
271
  Object.defineProperty(exports, "withView", {
272
272
  enumerable: true,
273
- get: function () { return chunkYLMMTKQL_cjs.withView; }
273
+ get: function () { return chunkIOY6IPUW_cjs.withView; }
274
274
  });
275
275
  exports.createXeniaEdge = createXeniaEdge;
276
276
  exports.matchesAny = matchesAny;
@@ -22,7 +22,7 @@ interface FetchManifestOptions {
22
22
  /** Default 5000 ms. */
23
23
  timeoutMs?: number;
24
24
  fetch?: typeof fetch;
25
- /** Extra fetch options, e.g. Next.js `{ next: { revalidate } }`. */
25
+ /** Extra fetch options, e.g. Next.js `{ next: { revalidate } }`. Their headers are kept. */
26
26
  init?: RequestInit;
27
27
  }
28
28
  /** Xenia's manifest (`GET /api/personalization/edge/manifest`). Throws on a non-2xx answer. */
@@ -22,7 +22,7 @@ interface FetchManifestOptions {
22
22
  /** Default 5000 ms. */
23
23
  timeoutMs?: number;
24
24
  fetch?: typeof fetch;
25
- /** Extra fetch options, e.g. Next.js `{ next: { revalidate } }`. */
25
+ /** Extra fetch options, e.g. Next.js `{ next: { revalidate } }`. Their headers are kept. */
26
26
  init?: RequestInit;
27
27
  }
28
28
  /** Xenia's manifest (`GET /api/personalization/edge/manifest`). Throws on a non-2xx answer. */
@@ -1,4 +1,4 @@
1
- export { MAX_TRAIL, TRAIL_MAX_AGE_S, decodeVariant, deviceOf, edgeConsent, encodeVariant, fetchManifest, isBot, isPageLoad, isPrefetch, landingContext, localTime, readTrail, seasonOf, swapGroups, trailCookieName, trailCookieValue, trimManifest, verifyWebhook, viewerGeo, withView } from '../chunk-OMMIRSPK.js';
1
+ export { MAX_TRAIL, TRAIL_MAX_AGE_S, decodeVariant, deviceOf, edgeConsent, encodeVariant, fetchManifest, isBot, isPageLoad, isPrefetch, landingContext, localTime, readTrail, seasonOf, swapGroups, trailCookieName, trailCookieValue, trimManifest, verifyWebhook, viewerGeo, withView } from '../chunk-F3WZFXYK.js';
2
2
  import '../chunk-QEYONKWY.js';
3
3
  import '../chunk-BQ5PJCZ7.js';
4
4
 
@@ -2,7 +2,7 @@
2
2
 
3
3
  var chunk2DR5AVPX_cjs = require('../chunk-2DR5AVPX.cjs');
4
4
  var chunkRWE2OCQL_cjs = require('../chunk-RWE2OCQL.cjs');
5
- var chunkYLMMTKQL_cjs = require('../chunk-YLMMTKQL.cjs');
5
+ var chunkIOY6IPUW_cjs = require('../chunk-IOY6IPUW.cjs');
6
6
  var chunkWHGK55JP_cjs = require('../chunk-WHGK55JP.cjs');
7
7
  var chunkN5M4PI7C_cjs = require('../chunk-N5M4PI7C.cjs');
8
8
  var React = require('react');
@@ -171,7 +171,7 @@ function debugEntry(info) {
171
171
  };
172
172
  }
173
173
  function writeDebug(request, response, entry, cookie) {
174
- if (chunkYLMMTKQL_cjs.isPrefetch(request.headers)) return;
174
+ if (chunkIOY6IPUW_cjs.isPrefetch(request.headers)) return;
175
175
  entry = { ...entry, page: entry.page.slice(0, MAX_PAGE) };
176
176
  const pages = readDebugEntries(request.cookies.get(cookie)?.value);
177
177
  delete pages[entry.page];
@@ -194,20 +194,20 @@ var MAX_QUERY = 20;
194
194
  var MAX_QUERY_VALUE = 500;
195
195
  async function xeniaEdgeProxy(request, opts) {
196
196
  if (request.method !== "GET" && request.method !== "HEAD") return server.NextResponse.next();
197
- if (chunkYLMMTKQL_cjs.isBot(request.headers.get("user-agent"))) return server.NextResponse.next();
197
+ if (chunkIOY6IPUW_cjs.isBot(request.headers.get("user-agent"))) return server.NextResponse.next();
198
198
  const started = Date.now();
199
199
  const prefix = opts.prefix ?? chunkN5M4PI7C_cjs.DEFAULT_PREFIX;
200
200
  const vidCookie = chunkN5M4PI7C_cjs.visitorCookieName(prefix);
201
201
  const ctxCookie = `${prefix}_ctx`;
202
- const trailCookie = chunkYLMMTKQL_cjs.trailCookieName(prefix);
202
+ const trailCookie = chunkIOY6IPUW_cjs.trailCookieName(prefix);
203
203
  const { pathname: path } = request.nextUrl;
204
204
  const existingVisitorId = request.cookies.get(vidCookie)?.value;
205
- const landing = chunkYLMMTKQL_cjs.landingContext(request.nextUrl, request.headers.get("referer"), request.cookies.get(ctxCookie)?.value);
205
+ const landing = chunkIOY6IPUW_cjs.landingContext(request.nextUrl, request.headers.get("referer"), request.cookies.get(ctxCookie)?.value);
206
206
  const qaOn = Boolean(opts.qa);
207
207
  const cities = typeof opts.qa === "object" && opts.qa.cities || QA_CITIES;
208
208
  const qaCookie = `${prefix}_qa_ctx`;
209
209
  const { qa, changed: qaChanged } = qaOn ? readQa(request, cities, qaCookie) : { qa: {}, changed: false };
210
- const detected = chunkYLMMTKQL_cjs.viewerGeo(request, { trust: opts.geoHeaders, devOverride: qaOn && process.env.NODE_ENV === "development" });
210
+ const detected = chunkIOY6IPUW_cjs.viewerGeo(request, { trust: opts.geoHeaders, devOverride: qaOn && process.env.NODE_ENV === "development" });
211
211
  const qaCity = qa.geo && Object.hasOwn(cities, qa.geo) ? cities[qa.geo] : void 0;
212
212
  const { geo, via } = qaCity ? { geo: { ...qaCity, source: "qa" }, via: "qa" } : detected;
213
213
  const ctx = {
@@ -219,11 +219,11 @@ async function xeniaEdgeProxy(request, opts) {
219
219
  geo,
220
220
  geoVia: via,
221
221
  ...qaCity && detected.geo ? { detectedGeo: detected.geo } : {},
222
- device: chunkYLMMTKQL_cjs.deviceOf(request.headers),
222
+ device: chunkIOY6IPUW_cjs.deviceOf(request.headers),
223
223
  landing: landing.ctx,
224
- previousTrail: chunkYLMMTKQL_cjs.readTrail(request.cookies.get(trailCookie)?.value),
224
+ previousTrail: chunkIOY6IPUW_cjs.readTrail(request.cookies.get(trailCookie)?.value),
225
225
  trail: [],
226
- consent: chunkYLMMTKQL_cjs.edgeConsent(request.headers, { prefix, respectDnt: opts.respectDnt }),
226
+ consent: chunkIOY6IPUW_cjs.edgeConsent(request.headers, { prefix, respectDnt: opts.respectDnt }),
227
227
  now: qaNow(qa) ?? /* @__PURE__ */ new Date()
228
228
  };
229
229
  const newVisitor = () => ctx.visitorId !== existingVisitorId;
@@ -235,8 +235,8 @@ async function xeniaEdgeProxy(request, opts) {
235
235
  if (landing.changed || ctx.landing !== landing.ctx) {
236
236
  response.cookies.set(ctxCookie, JSON.stringify(ctx.landing), { path: "/", sameSite: "lax", httpOnly: true });
237
237
  }
238
- if (ctx.consent.analytics && chunkYLMMTKQL_cjs.isPageLoad(request.headers) && ctx.trail.length && ctx.trail !== ctx.previousTrail) {
239
- response.cookies.set(trailCookie, chunkYLMMTKQL_cjs.trailCookieValue(ctx.trail), { path: "/", maxAge: chunkYLMMTKQL_cjs.TRAIL_MAX_AGE_S, sameSite: "lax" });
238
+ if (ctx.consent.analytics && chunkIOY6IPUW_cjs.isPageLoad(request.headers) && ctx.trail.length && ctx.trail !== ctx.previousTrail) {
239
+ response.cookies.set(trailCookie, chunkIOY6IPUW_cjs.trailCookieValue(ctx.trail), { path: "/", maxAge: chunkIOY6IPUW_cjs.TRAIL_MAX_AGE_S, sameSite: "lax" });
240
240
  }
241
241
  response.headers.set("Server-Timing", `xenia;dur=${Date.now() - started};desc="${desc}"`);
242
242
  if (qaChanged) rememberQa(response, qa, qaCookie);
@@ -257,7 +257,7 @@ async function xeniaEdgeProxy(request, opts) {
257
257
  }
258
258
  async function decideAndRespond() {
259
259
  await opts.prepare?.(ctx);
260
- ctx.trail = chunkYLMMTKQL_cjs.withView(ctx.previousTrail, path);
260
+ ctx.trail = chunkIOY6IPUW_cjs.withView(ctx.previousTrail, path);
261
261
  if (opts.skip?.(path)) return finish(base(), "skipped", "site page");
262
262
  const query = {};
263
263
  for (const [k, v] of request.nextUrl.searchParams) {
@@ -266,7 +266,7 @@ async function xeniaEdgeProxy(request, opts) {
266
266
  }
267
267
  const facts = {
268
268
  ...ctx.landing,
269
- ...chunkYLMMTKQL_cjs.localTime(ctx.geo, ctx.now),
269
+ ...chunkIOY6IPUW_cjs.localTime(ctx.geo, ctx.now),
270
270
  device: ctx.device,
271
271
  path,
272
272
  url: request.nextUrl.href,
@@ -307,10 +307,10 @@ async function xeniaEdgeProxy(request, opts) {
307
307
  });
308
308
  const side = await pending;
309
309
  const decisions = opts.filterDecisions ? opts.filterDecisions(outcome.decisions, ctx, side, outcome) : outcome.decisions;
310
- const groups = chunkYLMMTKQL_cjs.swapGroups(decisions);
310
+ const groups = chunkIOY6IPUW_cjs.swapGroups(decisions);
311
311
  let url = await opts.rewrite?.(ctx, groups, side, outcome);
312
312
  if (url === void 0 && groups.length) {
313
- const token = await chunkYLMMTKQL_cjs.encodeVariant(groups, opts.variantSecret, await opts.extra?.(ctx, side, outcome));
313
+ const token = await chunkIOY6IPUW_cjs.encodeVariant(groups, opts.variantSecret, await opts.extra?.(ctx, side, outcome));
314
314
  url = request.nextUrl.clone();
315
315
  url.pathname = `${opts.variantPrefix ?? "/xv"}/${token}${path === "/" ? "" : path}`;
316
316
  }
@@ -318,17 +318,21 @@ async function xeniaEdgeProxy(request, opts) {
318
318
  }
319
319
  }
320
320
  var EMPTY = { version: "", pages: [] };
321
+ var PROCESS_KEY = `${Date.now()}-${Math.random().toString(36).slice(2)}`;
321
322
  var MAX_WEBHOOK_BODY = 64 * 1024;
322
323
  function createManifestRoute(opts) {
323
324
  return async () => {
324
325
  if (!opts.token) return Response.json(EMPTY);
325
326
  try {
326
- const manifest = await chunkYLMMTKQL_cjs.fetchManifest({
327
+ const manifest = await chunkIOY6IPUW_cjs.fetchManifest({
327
328
  baseUrl: opts.baseUrl,
328
329
  token: opts.token,
329
- ...opts.revalidate !== void 0 ? { init: { next: { revalidate: opts.revalidate } } } : {}
330
+ init: {
331
+ headers: { "X-Xenia-Cache-Key": PROCESS_KEY },
332
+ ...opts.revalidate !== void 0 && { next: { revalidate: opts.revalidate } }
333
+ }
330
334
  });
331
- return Response.json(chunkYLMMTKQL_cjs.trimManifest(manifest));
335
+ return Response.json(chunkIOY6IPUW_cjs.trimManifest(manifest));
332
336
  } catch (error) {
333
337
  if (process.env.NEXT_PHASE === "phase-production-build") {
334
338
  console.warn("[xenia] manifest unavailable at build:", error instanceof Error ? error.message : error);
@@ -341,7 +345,7 @@ function createManifestRoute(opts) {
341
345
  function createRevalidateRoute(opts) {
342
346
  return async (request) => {
343
347
  const body = await request.text();
344
- if (body.length > MAX_WEBHOOK_BODY || !await chunkYLMMTKQL_cjs.verifyWebhook({ headers: request.headers, body }, opts.secret ?? "")) {
348
+ if (body.length > MAX_WEBHOOK_BODY || !await chunkIOY6IPUW_cjs.verifyWebhook({ headers: request.headers, body }, opts.secret ?? "")) {
345
349
  return Response.json({ error: "invalid signature" }, { status: 401 });
346
350
  }
347
351
  const event = request.headers.get("x-xenia-event") ?? "";