@sentientui/core 0.25.0 → 0.27.0
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/dist/chunk-TRBPPGFT.mjs +2 -0
- package/dist/chunk-TRBPPGFT.mjs.map +1 -0
- package/dist/{index-BjVvN7cU.d.ts → index-B5b5LlRJ.d.cts} +42 -215
- package/dist/{index-Cd0gMdJv.d.cts → index-NqZbPgmm.d.ts} +42 -215
- package/dist/index-engagement.js +1 -1
- package/dist/index-engagement.js.map +1 -1
- package/dist/index-engagement.mjs +1 -1
- package/dist/index-graph.d.cts +4 -3
- package/dist/index-graph.d.ts +4 -3
- package/dist/index-graph.js +1 -1
- package/dist/index-graph.js.map +1 -1
- package/dist/index-graph.mjs +1 -1
- package/dist/index-graph.mjs.map +1 -1
- package/dist/index-local.d.cts +2 -2
- package/dist/index-local.d.ts +2 -2
- package/dist/index-server.d.cts +9 -2
- package/dist/index-server.d.ts +9 -2
- package/dist/index-server.js +1 -1
- package/dist/index-server.js.map +1 -1
- package/dist/index-server.mjs +1 -1
- package/dist/index-server.mjs.map +1 -1
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1 -1
- package/dist/session-meta-dN92q19_.d.cts +378 -0
- package/dist/session-meta-dN92q19_.d.ts +378 -0
- package/llms.txt +4 -0
- package/package.json +1 -1
- package/dist/chunk-RL5B6M4G.mjs +0 -2
- package/dist/chunk-RL5B6M4G.mjs.map +0 -1
- package/dist/session-meta-C-bMqVeq.d.cts +0 -130
- package/dist/session-meta-C-bMqVeq.d.ts +0 -130
package/dist/index-server.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index-server.ts","../src/session-meta.ts","../src/storage-key.ts","../src/slots.ts","../src/server.ts"],"sourcesContent":["/**\n * Server-only entry (`@sentientui/core/server`). SSR preload helpers that are\n * never needed in the browser — kept out of the lean client bundle.\n */\n\nexport { preloadAssignments, readSessionCookie, preloadDecisions } from './server.js';\nexport type { ServerAssignConfig, ServerAssignments, DecideResult, SlotDeclInput, SlotResult } from './server.js';\n\nexport {\n deriveSessionSegment,\n buildSessionUpsertPayload,\n detectDeviceClass,\n detectTrafficSource,\n detectTimeOfDay,\n extractTrackedParams,\n CLICK_ID_KEYS,\n referrerDomainFromReferer,\n} from './session-meta.js';\nexport type { SessionUpsertPayload } from './session-meta.js';\n","/** Session metadata helpers (browser + Node). No DOM APIs. */\n\n/**\n * Known AI-agent / crawler user-agent tokens. Matched case-insensitively as\n * substrings. This is maintained data — bot lists move monthly. Used both to\n * flag automation on sessions (agentic browsers that leak a token) and by the\n * server middleware / agent-feed route to identify crawler HTTP reads.\n */\nexport const agentUaList: readonly string[] = [\n 'GPTBot',\n 'ChatGPT-User',\n 'OAI-SearchBot',\n 'ClaudeBot',\n 'Claude-User',\n 'Claude-SearchBot',\n 'PerplexityBot',\n 'Perplexity-User',\n 'Google-Extended',\n 'Applebot-Extended',\n 'Meta-ExternalAgent',\n 'Bytespider',\n 'CCBot',\n 'Amazonbot',\n 'cohere-ai',\n 'Diffbot',\n];\n\n/** True when the user-agent contains a known AI-agent / crawler token. */\nexport function uaTokenMatch(userAgent: string): boolean {\n return matchedAgentToken(userAgent) !== null;\n}\n\n/** The first known agent token found in the user-agent, or null. */\nexport function matchedAgentToken(userAgent: string): string | null {\n if (!userAgent) return null;\n const s = userAgent.toLowerCase();\n return agentUaList.find((token) => s.includes(token.toLowerCase())) ?? null;\n}\n\n/**\n * Purpose category for an agent fetch, inferred from its published user-agent:\n * - `user` — a person asked an assistant to read the page, live (…-User UAs)\n * - `search` — indexing for an AI answer engine (…-SearchBot / SearchBot)\n * - `training` — model-training crawl (GPTBot, ClaudeBot, CCBot, …)\n * - `other` — not clearly AI, or an unknown/new token\n */\nexport type AgentIntent = 'user' | 'search' | 'training' | 'other';\n\n/** Intent per agent token. Maintained beside `agentUaList` — when you add or\n * rename a token above, set its intent here (a unit test enforces coverage). */\nexport const AGENT_INTENTS: Record<string, AgentIntent> = {\n 'GPTBot': 'training',\n 'ChatGPT-User': 'user',\n 'OAI-SearchBot': 'search',\n 'ClaudeBot': 'training',\n 'Claude-User': 'user',\n 'Claude-SearchBot': 'search',\n 'PerplexityBot': 'search',\n 'Perplexity-User': 'user',\n 'Google-Extended': 'training',\n 'Applebot-Extended': 'training',\n 'Meta-ExternalAgent': 'training',\n 'Bytespider': 'training',\n 'CCBot': 'training',\n 'Amazonbot': 'other',\n 'cohere-ai': 'training',\n 'Diffbot': 'other',\n};\n\n/** Intent for a matched bot token (case-insensitive); `other` for null/unknown. */\nexport function agentIntent(botName: string | null): AgentIntent {\n if (!botName) return 'other';\n const hit = agentUaList.find((t) => t.toLowerCase() === botName.toLowerCase());\n return (hit && AGENT_INTENTS[hit]) || 'other';\n}\n\n/** `agentUaList` grouped by intent — the \"which crawlers do you classify?\" reference. */\nexport function classifiedAgents(): Record<AgentIntent, string[]> {\n const out: Record<AgentIntent, string[]> = { user: [], search: [], training: [], other: [] };\n for (const t of agentUaList) out[AGENT_INTENTS[t] ?? 'other'].push(t);\n return out;\n}\n\nexport function detectDeviceClass(userAgent: string): string {\n const s = userAgent.toLowerCase();\n if (/ipad|tablet|playbook|kindle|silk/.test(s)) return 'tablet';\n if (/mobi|iphone|ipod|android.*mobile|phone/.test(s)) return 'mobile';\n return 'desktop';\n}\n\nexport function detectTrafficSource(referrer: string, appOrigin?: string): string {\n if (!referrer) return 'direct';\n try {\n const refUrl = new URL(referrer);\n if (appOrigin) {\n try {\n if (new URL(appOrigin).host === refUrl.host) return 'direct';\n } catch {\n /* ignore invalid appOrigin */\n }\n }\n const host = refUrl.hostname.toLowerCase();\n if (/(^|\\.)(google|bing|duckduckgo|yahoo)\\./.test(host)) return 'search';\n // Anchor to the registrable domain (exact host or a subdomain of it) so\n // hosts like `x.company.com`, `t.company.io` or `linkedinsights.com` are not\n // misclassified as social by an unbounded substring match.\n if (/(^|\\.)(twitter\\.com|x\\.com|facebook\\.com|linkedin\\.com|reddit\\.com|t\\.co)$/.test(host)) return 'social';\n return 'referral';\n } catch {\n return 'direct';\n }\n}\n\nexport function referrerDomainFromReferer(referrer: string): string | null {\n if (!referrer) return null;\n try {\n return new URL(referrer).hostname;\n } catch {\n return null;\n }\n}\n\n/**\n * Ad-platform click-ID query params worth keeping. An ALLOWLIST, unlike the\n * `utm_` prefix match: unknown query keys here are unbounded-cardinality junk\n * (session tokens, cache busters) that would bloat the sessions table, so only\n * the IDs the major ad platforms actually append survive.\n *\n * gclid / gbraid / wbraid — Google Ads auto-tagging (search, YouTube, Display;\n * gbraid/wbraid are the iOS-14 privacy variants)\n * fbclid — Meta (Facebook + Instagram). Appended to EVERY outbound Meta\n * click, paid and organic alike — it identifies the platform, never spend.\n * ttclid — TikTok Ads\n * msclkid — Microsoft Ads (Bing)\n * twclid — X/Twitter Ads\n * li_fat_id — LinkedIn Ads\n */\nexport const CLICK_ID_KEYS: readonly string[] = [\n 'gclid',\n 'gbraid',\n 'wbraid',\n 'fbclid',\n 'ttclid',\n 'msclkid',\n 'twclid',\n 'li_fat_id',\n];\n\n/**\n * Splits a URL query into the attribution params the session upsert carries:\n * every `utm_`-prefixed key, plus allowlisted ad click IDs (CLICK_ID_KEYS).\n * Accepts a raw search string (\"?a=b\" or \"a=b\"), a URLSearchParams, or a\n * Next.js `searchParams` object (whose values may be string arrays — the\n * first occurrence wins, matching URLSearchParams iteration order).\n * Node-safe: no DOM APIs.\n */\nexport function extractTrackedParams(\n search: string | URLSearchParams | Record<string, string | string[] | undefined>,\n): { utmParams: Record<string, string>; clickIds: Record<string, string> } {\n const utmParams: Record<string, string> = {};\n const clickIds: Record<string, string> = {};\n try {\n const entries: Iterable<[string, string]> =\n typeof search === 'string' || search instanceof URLSearchParams\n ? new URLSearchParams(search)\n : Object.entries(search).flatMap(([k, v]): Array<[string, string]> => {\n const first = Array.isArray(v) ? v[0] : v;\n return first === undefined ? [] : [[k, first]];\n });\n for (const [k, v] of entries) {\n // First occurrence wins for duplicates — a repeated gclid in a mangled\n // URL must not let the later value silently replace the real one.\n if (k.startsWith('utm_')) {\n if (!(k in utmParams)) utmParams[k] = v;\n } else if (CLICK_ID_KEYS.includes(k)) {\n if (!(k in clickIds)) clickIds[k] = v;\n }\n }\n } catch {\n /* malformed input → empty attribution, never a throw at init() */\n }\n return { utmParams, clickIds };\n}\n\nexport function detectTimeOfDay(d: Date): string {\n const h = d.getHours();\n if (h < 6) return 'night';\n if (h < 12) return 'morning';\n if (h < 18) return 'afternoon';\n return 'evening';\n}\n\nexport type SessionUpsertPayload = {\n sessionId: string;\n ephemeral: boolean;\n utmParams: Record<string, string>;\n /** Allowlisted ad-platform click IDs from the landing URL (CLICK_ID_KEYS). */\n clickIds: Record<string, string>;\n deviceClass: string;\n trafficSource: string;\n referrerDomain: string | null;\n timeOfDay: string;\n dayOfWeek: string;\n /**\n * True when this session is likely driven by automation — either\n * `navigator.webdriver` was set, or the user-agent carried a known agent\n * token. Probabilistic: a flag for metrics + bandit exclusion, never a gate.\n */\n automation: boolean;\n};\n\n/** Bandit segment key: `<device_class>:<traffic_source>`. */\nexport function deriveSessionSegment(opts?: {\n userAgent?: string;\n referer?: string;\n appOrigin?: string;\n}): string {\n const body = buildSessionUpsertPayload('__segment__', opts);\n return `${body.deviceClass}:${body.trafficSource}`;\n}\n\n/**\n * Builds a session upsert body aligned with the browser SDK so SSR assign uses\n * the same segment key (`device:source`) as the client after hydration.\n */\nexport function buildSessionUpsertPayload(\n sessionId: string,\n opts?: {\n userAgent?: string;\n referer?: string;\n appOrigin?: string;\n utmParams?: Record<string, string>;\n clickIds?: Record<string, string>;\n now?: Date;\n /** `navigator.webdriver` value from the browser, when available. */\n webdriver?: boolean;\n },\n): SessionUpsertPayload {\n const ua = opts?.userAgent?.trim() ?? '';\n const referer = opts?.referer?.trim() ?? '';\n const now = opts?.now ?? new Date();\n return {\n sessionId,\n ephemeral: false,\n utmParams: opts?.utmParams ?? {},\n clickIds: opts?.clickIds ?? {},\n deviceClass: ua ? detectDeviceClass(ua) : 'desktop',\n trafficSource: referer\n ? detectTrafficSource(referer, opts?.appOrigin)\n : 'direct',\n referrerDomain: referrerDomainFromReferer(referer),\n timeOfDay: detectTimeOfDay(now),\n dayOfWeek: ['sun', 'mon', 'tue', 'wed', 'thu', 'fri', 'sat'][now.getDay()] ?? 'sun',\n automation: opts?.webdriver === true || uaTokenMatch(ua),\n };\n}\n","/**\n * Per-project browser-storage namespace suffix, derived from the public apiKey.\n *\n * Multiple SentientUI projects (different `pk_` keys) can run on the same exact\n * origin. Storage keyed only by name (`_snt_uid`, `_snt_asgn_*`,\n * `_snt_graph_nodes`) would then collide across projects — the visitor id in\n * particular, whose session row is keyed globally server-side, would let one\n * project's traffic land on another's session. Suffixing every browser key with\n * the apiKey prefix isolates projects, matching the queue's existing\n * `_snt_retry_${apiKey.slice(0,12)}` convention.\n *\n * Returns `''` when no apiKey is available (local mode) so keys stay stable\n * there.\n */\nexport function storageSuffix(apiKey?: string): string {\n return apiKey ? `_${apiKey.slice(0, 12)}` : '';\n}\n\n/**\n * The bare, pre-namespacing session cookie name. Still read as a FALLBACK\n * everywhere the suffixed name is read, so visitors who got their identity\n * before per-project namespacing keep it instead of being minted a fresh one.\n */\nexport const LEGACY_SESSION_COOKIE_NAME = '_snt_uid';\n\n/**\n * The one place the session cookie's name is decided. The writer (session.ts)\n * and every reader (core/server.ts SSR helper, graph sync, react devtools) must\n * call THIS — when namespacing landed, the writer moved to the suffixed name\n * while three readers kept the bare `_snt_uid`, so every SSR request for a\n * returning visitor missed the cookie and minted a fresh orphan session (quota\n * inflation, broken sticky assignments and persona continuity), and graph sync\n * sent `sessionId: undefined`. Deriving both sides from one function makes that\n * drift impossible, and index.test.ts pins the reader against the writer.\n */\nexport function sessionCookieName(apiKey?: string): string {\n return `${LEGACY_SESSION_COOKIE_NAME}${storageSuffix(apiKey)}`;\n}\n","/**\n * Slot declaration helpers shared by the browser client (decide) and the\n * server preload path. Pure wrappers over @sentientui/policy.\n */\nimport {\n canonicalArm,\n slotBaselineArm,\n slotResultFor,\n type SlotDecl,\n type SlotResult,\n} from '@sentientui/policy';\n\n/** SDK-facing slot declaration. `dims` accepts readonly arrays (`as const`). */\nexport type SlotDeclInput = {\n id: string;\n arms?: string[];\n dims?: Record<string, readonly string[]>;\n baseline?: string | Record<string, string>;\n};\n\nexport type { SlotResult };\n\n/**\n * Whitelists the wire fields of a slot declaration. Anything an SDK layer\n * attached (goal configs, refs, …) is stripped so it never reaches the zod\n * schema on the API. Also normalizes readonly arrays to mutable ones.\n */\nexport function toWireSlot(d: SlotDeclInput): SlotDecl {\n return {\n id: d.id,\n ...(d.arms ? { arms: [...d.arms] } : {}),\n ...(d.dims\n ? {\n dims: Object.fromEntries(\n Object.entries(d.dims).map(([dim, values]) => [dim, [...values]]),\n ),\n }\n : {}),\n ...(d.baseline !== undefined ? { baseline: d.baseline } : {}),\n };\n}\n\n/** The declared (or default first-declared) baseline result for a slot. */\nexport function baselineResultFor(d: SlotDeclInput): SlotResult {\n const decl = toWireSlot(d);\n return slotResultFor(decl, slotBaselineArm(decl));\n}\n\n/** Baseline results for a whole declaration list, keyed by slot id. */\nexport function baselineSlots(decls: SlotDeclInput[]): Record<string, SlotResult> {\n const out: Record<string, SlotResult> = {};\n for (const d of decls) out[d.id] = baselineResultFor(d);\n return out;\n}\n\n/**\n * Canonical arm string of a slot result: dims results encode as sorted\n * `dim=value` pairs joined with '|'; arms results are the arm id verbatim.\n */\nexport function armOfResult(result: SlotResult): string {\n return typeof result === 'string' ? result : canonicalArm(result);\n}\n","/**\n * Server-side helpers for SSR variant pre-loading.\n * Pure fetch — no DOM APIs. Safe in Node.js, Edge, and Deno runtimes.\n */\nimport { buildSessionUpsertPayload } from './session-meta.js';\nimport { LEGACY_SESSION_COOKIE_NAME, sessionCookieName } from './storage-key.js';\nimport {\n toWireSlot,\n baselineResultFor,\n baselineSlots,\n type SlotDeclInput,\n type SlotResult,\n} from './slots.js';\n\nexport type { SlotDeclInput, SlotResult };\n\nexport type ServerAssignConfig = {\n /** Public API key (pk_...). */\n apiKey: string;\n /**\n * Base URL of the Sentient API without trailing slash.\n * e.g. 'https://api.yourapp.com/v1'\n */\n baseUrl: string;\n /**\n * Browser origin of your app (e.g. `http://localhost:3001`). Required for `pk_`\n * keys — server-side fetch must send the same `Origin` the API allows.\n */\n origin?: string;\n /** From `User-Agent` request header (Next.js `headers()`). */\n userAgent?: string;\n /** From `Referer` request header. */\n referer?: string;\n utmParams?: Record<string, string>;\n /**\n * Ad-platform click IDs (gclid, fbclid, ttclid, …) from the landing URL.\n * Derive both this and `utmParams` with `extractTrackedParams(searchParams)`\n * — the Referer header never carries the landing page's own query string, so\n * without these two fields SSR-minted sessions have no campaign attribution\n * until the client SDK hydrates.\n */\n clickIds?: Record<string, string>;\n /**\n * Set true when the request carries a tracking opt-out — `DNT: 1` or\n * `Sec-GPC: 1`. When set, the SSR helpers skip the session upsert and the\n * assign/decide call entirely: the page renders defaults/baseline and no\n * session row is minted for the visitor (audit P4). The client SDK already\n * no-ops for these visitors; this keeps the server from minting a session +\n * assignment on every page view before the client can.\n */\n doNotTrack?: boolean;\n /**\n * Declared persona — the role your app already knows for this visitor\n * (e.g. from your auth context during SSR). Same contract as the client\n * option: must be a vocabulary key; unrecognized values are ignored\n * server-side. Never a user id or email.\n */\n persona?: string;\n /**\n * Milliseconds to wait for the API before returning default variants.\n * Prevents slow/cold API from blocking SSR. Defaults to 1000 — the hot path\n * is served from in-process caches and typically returns in well under\n * 150 ms. The full 1 s budget is only reached on a cold start or an API\n * geographically distant from your SSR host, after which defaults render\n * with no layout shift. Lower it if your API is co-located and warm.\n */\n timeoutMs?: number;\n};\n\n/** componentId → assigned variantId */\nexport type ServerAssignments = Record<string, string>;\n\ntype AssignResult = { variantId: string; assignmentTtlMs: number };\n\nconst DEFAULT_TIMEOUT_MS = 1000;\n\nfunction fetchWithTimeout(\n url: string,\n init: RequestInit,\n timeoutMs: number,\n): Promise<Response> {\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), timeoutMs);\n return fetch(url, { ...init, signal: controller.signal }).finally(() =>\n clearTimeout(timer),\n );\n}\n\n/**\n * Fetches variant assignments server-side for all listed components.\n * Returns a map suitable for passing as `initialAssignments` to `<AdaptiveProvider>`.\n *\n * Call from Next.js layout, Server Component, or getServerSideProps.\n *\n * @example\n * ```tsx\n * const assignments = await preloadAssignments(\n * [\n * { id: 'hero', variantIds: ['hero-a', 'hero-b'] },\n * { id: 'cta', variantIds: ['cta-short', 'cta-long'] },\n * ],\n * sessionId, // read from cookies() or req.cookies\n * { apiKey: process.env.NEXT_PUBLIC_SENTIENT_API_KEY!, baseUrl: 'https://api.sentient-ui.com/v1' },\n * );\n * return <AdaptiveProvider ... initialAssignments={assignments}>{children}</AdaptiveProvider>;\n * ```\n */\nexport async function preloadAssignments(\n components: Array<{ id: string; variantIds: string[] }>,\n sessionId: string,\n config: ServerAssignConfig,\n): Promise<ServerAssignments> {\n // Opted-out visitor (DNT/GPC): render defaults, mint no session (audit P4).\n if (config.doNotTrack) return {};\n\n const timeoutMs = config.timeoutMs ?? DEFAULT_TIMEOUT_MS;\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${config.apiKey}`,\n };\n if (config.origin) {\n headers.Origin = config.origin;\n }\n\n // Session metadata must match the browser SDK so assign seeds variant_weights\n // under the same segment (not `unknown:unknown`).\n const sessionBody = {\n ...buildSessionUpsertPayload(sessionId, {\n userAgent: config.userAgent,\n referer: config.referer,\n utmParams: config.utmParams,\n clickIds: config.clickIds,\n appOrigin: config.origin,\n }),\n ...(config.persona ? { persona: config.persona } : {}),\n };\n\n try {\n const res = await fetchWithTimeout(\n `${config.baseUrl}/sessions`,\n { method: 'POST', headers, body: JSON.stringify(sessionBody) },\n timeoutMs,\n );\n if (res.status === 402) {\n console.warn(\n '[SentientUI] Session limit exceeded for this project. The bandit will stop learning until the limit resets. Upgrade at sentient-ui.com/pricing',\n );\n } else if (!res.ok) {\n const body = await res.json().catch(() => ({}));\n console.error(`[SentientUI] preloadAssignments: session upsert failed (${res.status})`, body);\n }\n } catch (err) {\n console.error('[SentientUI] preloadAssignments: session upsert threw', err);\n }\n\n const results = await Promise.allSettled(\n components.map(async ({ id, variantIds }) => {\n const res = await fetchWithTimeout(\n `${config.baseUrl}/assign`,\n { method: 'POST', headers, body: JSON.stringify({ sessionId, componentId: id, variantIds }) },\n timeoutMs,\n );\n if (!res.ok) {\n const body = await res.json().catch(() => ({}));\n console.error(`[SentientUI] preloadAssignments: assign failed for \"${id}\" (${res.status})`, body);\n return null;\n }\n const body = (await res.json()) as AssignResult;\n return { id, variantId: body.variantId };\n }),\n );\n\n const assignments: ServerAssignments = {};\n for (const result of results) {\n if (result.status === 'fulfilled' && result.value) {\n assignments[result.value.id] = result.value.variantId;\n }\n }\n return assignments;\n}\n\n/**\n * Reads the Sentient session cookie from a Next.js `ReadonlyRequestCookies` object\n * (the return value of `cookies()` from `next/headers`), or any object with a\n * `get(name: string)` method. Returns null if the cookie is absent.\n *\n * Pass the project's `apiKey`: the client writes the per-project SUFFIXED name\n * (`sessionCookieName` — see storage-key.ts), so reading only the bare\n * `_snt_uid` missed the cookie on every SSR request for a returning visitor and\n * minted a fresh orphan session each page view (quota inflation, broken sticky\n * assignments and persona continuity). The bare name is still read as a\n * fallback for identities minted before namespacing.\n */\nexport function readSessionCookie(\n cookies: { get(name: string): { value: string } | undefined },\n apiKey?: string,\n): string | null {\n return (\n (apiKey ? cookies.get(sessionCookieName(apiKey))?.value : undefined) ??\n cookies.get(LEGACY_SESSION_COOKIE_NAME)?.value ??\n null\n );\n}\n\nexport type DecideResult = {\n layoutOrder: string[];\n assignments: Record<string, string>;\n /** Slot results keyed by slot id. Baseline-resolved when the API omits/fails them. */\n slots: Record<string, SlotResult>;\n persona: string;\n confidence: number;\n};\n\n/**\n * Single-roundtrip SSR call returning layout order, component assignments,\n * and adaptive-slot results. Falls back to default section order + empty\n * assignments + baseline slots if the API is unavailable. A response without\n * a `slots` field means the server predates slots — every declared slot\n * resolves to its baseline (no retry).\n */\nexport async function preloadDecisions(\n params: {\n sections?: string[];\n components: Array<{ id: string; variantIds?: string[] }>;\n slots?: SlotDeclInput[];\n },\n sessionId: string,\n config: ServerAssignConfig,\n): Promise<DecideResult> {\n const timeoutMs = config.timeoutMs ?? DEFAULT_TIMEOUT_MS;\n const declaredSlots = params.slots ?? [];\n const fallback: DecideResult = {\n layoutOrder: params.sections ?? [],\n assignments: {},\n slots: baselineSlots(declaredSlots),\n persona: 'unknown',\n confidence: 0,\n };\n\n // Opted-out visitor (DNT/GPC): baseline layout/slots, mint no session (audit P4).\n if (config.doNotTrack) return fallback;\n\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${config.apiKey}`,\n };\n if (config.origin) headers.Origin = config.origin;\n\n const sessionBody = {\n ...buildSessionUpsertPayload(sessionId, {\n userAgent: config.userAgent,\n referer: config.referer,\n utmParams: config.utmParams,\n clickIds: config.clickIds,\n appOrigin: config.origin,\n }),\n ...(config.persona ? { persona: config.persona } : {}),\n };\n\n try {\n const sessionRes = await fetchWithTimeout(\n `${config.baseUrl}/sessions`,\n { method: 'POST', headers, body: JSON.stringify(sessionBody) },\n timeoutMs,\n );\n if (!sessionRes.ok) {\n const body = await sessionRes.json().catch(() => ({}));\n console.error(`[SentientUI] preloadDecisions: session upsert failed (${sessionRes.status})`, body);\n }\n } catch (err) {\n console.error('[SentientUI] preloadDecisions: session upsert threw', err);\n }\n\n try {\n const res = await fetchWithTimeout(\n `${config.baseUrl}/decide`,\n {\n method: 'POST',\n headers,\n body: JSON.stringify({\n sessionId,\n sections: (params.sections ?? []).map((id) => ({ id })),\n components: params.components,\n ...(declaredSlots.length > 0 ? { slots: declaredSlots.map(toWireSlot) } : {}),\n ...(config.persona ? { persona: config.persona } : {}),\n }),\n },\n timeoutMs,\n );\n if (!res.ok) {\n const body = await res.json().catch(() => ({}));\n console.error(`[SentientUI] preloadDecisions: decide failed (${res.status})`, body);\n return fallback;\n }\n const data = (await res.json()) as {\n layoutOrder?: string[];\n assignments?: Record<string, string>;\n slots?: Record<string, SlotResult>;\n persona?: string;\n confidence?: number;\n };\n\n const slots: Record<string, SlotResult> = {};\n for (const d of declaredSlots) {\n // `data.slots === undefined` ⇒ server predates slots: baseline, no retry.\n slots[d.id] = data.slots?.[d.id] ?? baselineResultFor(d);\n }\n\n return {\n layoutOrder: data.layoutOrder ?? params.sections ?? [],\n assignments: data.assignments ?? {},\n slots,\n persona: data.persona ?? 'unknown',\n confidence: data.confidence ?? 0,\n };\n } catch (err) {\n console.error('[SentientUI] preloadDecisions: decide threw', err);\n return fallback;\n }\n}\n"],"mappings":"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","names":["index_server_exports","__export","CLICK_ID_KEYS","buildSessionUpsertPayload","deriveSessionSegment","detectDeviceClass","detectTimeOfDay","detectTrafficSource","extractTrackedParams","preloadAssignments","preloadDecisions","readSessionCookie","referrerDomainFromReferer","__toCommonJS","agentUaList","uaTokenMatch","userAgent","matchedAgentToken","_a","s","token","detectDeviceClass","userAgent","s","detectTrafficSource","referrer","appOrigin","refUrl","e","host","referrerDomainFromReferer","CLICK_ID_KEYS","extractTrackedParams","search","utmParams","clickIds","entries","k","v","first","detectTimeOfDay","d","h","deriveSessionSegment","opts","body","buildSessionUpsertPayload","sessionId","_a","_b","_c","_d","_e","_f","_g","_h","ua","referer","now","uaTokenMatch","storageSuffix","apiKey","LEGACY_SESSION_COOKIE_NAME","sessionCookieName","import_policy","toWireSlot","d","__spreadValues","dim","values","baselineResultFor","decl","baselineSlots","decls","out","DEFAULT_TIMEOUT_MS","fetchWithTimeout","url","init","timeoutMs","controller","timer","__spreadProps","__spreadValues","preloadAssignments","components","sessionId","config","_a","headers","sessionBody","buildSessionUpsertPayload","res","body","err","results","id","variantIds","assignments","result","readSessionCookie","cookies","apiKey","_b","_c","_d","sessionCookieName","LEGACY_SESSION_COOKIE_NAME","preloadDecisions","params","_e","_f","_g","_h","_i","_j","_k","declaredSlots","fallback","baselineSlots","sessionRes","toWireSlot","data","slots","d","baselineResultFor"]}
|
|
1
|
+
{"version":3,"sources":["../src/index-server.ts","../src/session-meta.ts","../src/storage-key.ts","../src/slots.ts","../src/server.ts"],"sourcesContent":["/**\n * Server-only entry (`@sentientui/core/server`). SSR preload helpers that are\n * never needed in the browser — kept out of the lean client bundle.\n */\n\nexport { preloadAssignments, readSessionCookie, preloadDecisions } from './server.js';\nexport type { ServerAssignConfig, ServerAssignments, DecideResult, SlotDeclInput, SlotResult } from './server.js';\n\nexport {\n deriveSessionSegment,\n buildSessionUpsertPayload,\n detectDeviceClass,\n detectTrafficSource,\n detectTimeOfDay,\n extractTrackedParams,\n CLICK_ID_KEYS,\n referrerDomainFromReferer,\n} from './session-meta.js';\nexport type { SessionUpsertPayload } from './session-meta.js';\n","/** Session metadata helpers (browser + Node). No DOM APIs. */\n\n/**\n * Known AI-agent / crawler user-agent tokens. Matched case-insensitively as\n * substrings. This is maintained data — bot lists move monthly. Used both to\n * flag automation on sessions (agentic browsers that leak a token) and by the\n * server middleware / agent-feed route to identify crawler HTTP reads.\n */\nexport const agentUaList: readonly string[] = [\n 'GPTBot',\n 'ChatGPT-User',\n 'OAI-SearchBot',\n 'ClaudeBot',\n 'Claude-User',\n 'Claude-SearchBot',\n 'PerplexityBot',\n 'Perplexity-User',\n 'Google-Extended',\n 'Applebot-Extended',\n 'Meta-ExternalAgent',\n 'Bytespider',\n 'CCBot',\n 'Amazonbot',\n 'cohere-ai',\n 'Diffbot',\n];\n\n/** True when the user-agent contains a known AI-agent / crawler token. */\nexport function uaTokenMatch(userAgent: string): boolean {\n return matchedAgentToken(userAgent) !== null;\n}\n\n/** The first known agent token found in the user-agent, or null. */\nexport function matchedAgentToken(userAgent: string): string | null {\n if (!userAgent) return null;\n const s = userAgent.toLowerCase();\n return agentUaList.find((token) => s.includes(token.toLowerCase())) ?? null;\n}\n\n/**\n * Purpose category for an agent fetch, inferred from its published user-agent:\n * - `user` — a person asked an assistant to read the page, live (…-User UAs)\n * - `search` — indexing for an AI answer engine (…-SearchBot / SearchBot)\n * - `training` — model-training crawl (GPTBot, ClaudeBot, CCBot, …)\n * - `other` — not clearly AI, or an unknown/new token\n */\nexport type AgentIntent = 'user' | 'search' | 'training' | 'other';\n\n/** Intent per agent token. Maintained beside `agentUaList` — when you add or\n * rename a token above, set its intent here (a unit test enforces coverage). */\nexport const AGENT_INTENTS: Record<string, AgentIntent> = {\n 'GPTBot': 'training',\n 'ChatGPT-User': 'user',\n 'OAI-SearchBot': 'search',\n 'ClaudeBot': 'training',\n 'Claude-User': 'user',\n 'Claude-SearchBot': 'search',\n 'PerplexityBot': 'search',\n 'Perplexity-User': 'user',\n 'Google-Extended': 'training',\n 'Applebot-Extended': 'training',\n 'Meta-ExternalAgent': 'training',\n 'Bytespider': 'training',\n 'CCBot': 'training',\n 'Amazonbot': 'other',\n 'cohere-ai': 'training',\n 'Diffbot': 'other',\n};\n\n/** Intent for a matched bot token (case-insensitive); `other` for null/unknown. */\nexport function agentIntent(botName: string | null): AgentIntent {\n if (!botName) return 'other';\n const hit = agentUaList.find((t) => t.toLowerCase() === botName.toLowerCase());\n return (hit && AGENT_INTENTS[hit]) || 'other';\n}\n\n/** `agentUaList` grouped by intent — the \"which crawlers do you classify?\" reference. */\nexport function classifiedAgents(): Record<AgentIntent, string[]> {\n const out: Record<AgentIntent, string[]> = { user: [], search: [], training: [], other: [] };\n for (const t of agentUaList) out[AGENT_INTENTS[t] ?? 'other'].push(t);\n return out;\n}\n\nexport function detectDeviceClass(userAgent: string): string {\n const s = userAgent.toLowerCase();\n if (/ipad|tablet|playbook|kindle|silk/.test(s)) return 'tablet';\n if (/mobi|iphone|ipod|android.*mobile|phone/.test(s)) return 'mobile';\n return 'desktop';\n}\n\nexport function detectTrafficSource(referrer: string, appOrigin?: string): string {\n if (!referrer) return 'direct';\n try {\n const refUrl = new URL(referrer);\n if (appOrigin) {\n try {\n if (new URL(appOrigin).host === refUrl.host) return 'direct';\n } catch {\n /* ignore invalid appOrigin */\n }\n }\n const host = refUrl.hostname.toLowerCase();\n if (/(^|\\.)(google|bing|duckduckgo|yahoo)\\./.test(host)) return 'search';\n // Anchor to the registrable domain (exact host or a subdomain of it) so\n // hosts like `x.company.com`, `t.company.io` or `linkedinsights.com` are not\n // misclassified as social by an unbounded substring match.\n if (/(^|\\.)(twitter\\.com|x\\.com|facebook\\.com|linkedin\\.com|reddit\\.com|t\\.co)$/.test(host)) return 'social';\n return 'referral';\n } catch {\n return 'direct';\n }\n}\n\nexport function referrerDomainFromReferer(referrer: string): string | null {\n if (!referrer) return null;\n try {\n return new URL(referrer).hostname;\n } catch {\n return null;\n }\n}\n\n/**\n * Ad-platform click-ID query params worth keeping. An ALLOWLIST, unlike the\n * `utm_` prefix match: unknown query keys here are unbounded-cardinality junk\n * (session tokens, cache busters) that would bloat the sessions table, so only\n * the IDs the major ad platforms actually append survive.\n *\n * gclid / gbraid / wbraid — Google Ads auto-tagging (search, YouTube, Display;\n * gbraid/wbraid are the iOS-14 privacy variants)\n * fbclid — Meta (Facebook + Instagram). Appended to EVERY outbound Meta\n * click, paid and organic alike — it identifies the platform, never spend.\n * ttclid — TikTok Ads\n * msclkid — Microsoft Ads (Bing)\n * twclid — X/Twitter Ads\n * li_fat_id — LinkedIn Ads\n */\nexport const CLICK_ID_KEYS: readonly string[] = [\n 'gclid',\n 'gbraid',\n 'wbraid',\n 'fbclid',\n 'ttclid',\n 'msclkid',\n 'twclid',\n 'li_fat_id',\n];\n\n/**\n * Splits a URL query into the attribution params the session upsert carries:\n * every `utm_`-prefixed key, plus allowlisted ad click IDs (CLICK_ID_KEYS).\n * Accepts a raw search string (\"?a=b\" or \"a=b\"), a URLSearchParams, or a\n * Next.js `searchParams` object (whose values may be string arrays — the\n * first occurrence wins, matching URLSearchParams iteration order).\n * Node-safe: no DOM APIs.\n */\nexport function extractTrackedParams(\n search: string | URLSearchParams | Record<string, string | string[] | undefined>,\n): { utmParams: Record<string, string>; clickIds: Record<string, string> } {\n const utmParams: Record<string, string> = {};\n const clickIds: Record<string, string> = {};\n try {\n const entries: Iterable<[string, string]> =\n typeof search === 'string' || search instanceof URLSearchParams\n ? new URLSearchParams(search)\n : Object.entries(search).flatMap(([k, v]): Array<[string, string]> => {\n const first = Array.isArray(v) ? v[0] : v;\n return first === undefined ? [] : [[k, first]];\n });\n for (const [k, v] of entries) {\n // First occurrence wins for duplicates — a repeated gclid in a mangled\n // URL must not let the later value silently replace the real one.\n if (k.startsWith('utm_')) {\n if (!(k in utmParams)) utmParams[k] = v;\n } else if (CLICK_ID_KEYS.includes(k)) {\n if (!(k in clickIds)) clickIds[k] = v;\n }\n }\n } catch {\n /* malformed input → empty attribution, never a throw at init() */\n }\n return { utmParams, clickIds };\n}\n\nexport function detectTimeOfDay(d: Date): string {\n const h = d.getHours();\n if (h < 6) return 'night';\n if (h < 12) return 'morning';\n if (h < 18) return 'afternoon';\n return 'evening';\n}\n\nexport type SessionUpsertPayload = {\n sessionId: string;\n ephemeral: boolean;\n utmParams: Record<string, string>;\n /** Allowlisted ad-platform click IDs from the landing URL (CLICK_ID_KEYS). */\n clickIds: Record<string, string>;\n deviceClass: string;\n trafficSource: string;\n referrerDomain: string | null;\n timeOfDay: string;\n dayOfWeek: string;\n /**\n * True when this session is likely driven by automation — either\n * `navigator.webdriver` was set, or the user-agent carried a known agent\n * token. Probabilistic: a flag for metrics + bandit exclusion, never a gate.\n */\n automation: boolean;\n};\n\n/** Bandit segment key: `<device_class>:<traffic_source>`. */\nexport function deriveSessionSegment(opts?: {\n userAgent?: string;\n referer?: string;\n appOrigin?: string;\n}): string {\n const body = buildSessionUpsertPayload('__segment__', opts);\n return `${body.deviceClass}:${body.trafficSource}`;\n}\n\n/**\n * Builds a session upsert body aligned with the browser SDK so SSR assign uses\n * the same segment key (`device:source`) as the client after hydration.\n */\nexport function buildSessionUpsertPayload(\n sessionId: string,\n opts?: {\n userAgent?: string;\n referer?: string;\n appOrigin?: string;\n utmParams?: Record<string, string>;\n clickIds?: Record<string, string>;\n now?: Date;\n /** `navigator.webdriver` value from the browser, when available. */\n webdriver?: boolean;\n },\n): SessionUpsertPayload {\n const ua = opts?.userAgent?.trim() ?? '';\n const referer = opts?.referer?.trim() ?? '';\n const now = opts?.now ?? new Date();\n return {\n sessionId,\n ephemeral: false,\n utmParams: opts?.utmParams ?? {},\n clickIds: opts?.clickIds ?? {},\n deviceClass: ua ? detectDeviceClass(ua) : 'desktop',\n trafficSource: referer\n ? detectTrafficSource(referer, opts?.appOrigin)\n : 'direct',\n referrerDomain: referrerDomainFromReferer(referer),\n timeOfDay: detectTimeOfDay(now),\n dayOfWeek: ['sun', 'mon', 'tue', 'wed', 'thu', 'fri', 'sat'][now.getDay()] ?? 'sun',\n automation: opts?.webdriver === true || uaTokenMatch(ua),\n };\n}\n","/**\n * Per-project browser-storage namespace suffix, derived from the public apiKey.\n *\n * Multiple SentientUI projects (different `pk_` keys) can run on the same exact\n * origin. Storage keyed only by name (`_snt_uid`, `_snt_asgn_*`,\n * `_snt_graph_nodes`) would then collide across projects — the visitor id in\n * particular, whose session row is keyed globally server-side, would let one\n * project's traffic land on another's session. Suffixing every browser key with\n * the apiKey prefix isolates projects, matching the queue's existing\n * `_snt_retry_${apiKey.slice(0,12)}` convention.\n *\n * Returns `''` when no apiKey is available (local mode) so keys stay stable\n * there.\n */\nexport function storageSuffix(apiKey?: string): string {\n return apiKey ? `_${apiKey.slice(0, 12)}` : '';\n}\n\n/**\n * The bare, pre-namespacing session cookie name. Still read as a FALLBACK\n * everywhere the suffixed name is read, so visitors who got their identity\n * before per-project namespacing keep it instead of being minted a fresh one.\n */\nexport const LEGACY_SESSION_COOKIE_NAME = '_snt_uid';\n\n/**\n * The one place the session cookie's name is decided. The writer (session.ts)\n * and every reader (core/server.ts SSR helper, graph sync, react devtools) must\n * call THIS — when namespacing landed, the writer moved to the suffixed name\n * while three readers kept the bare `_snt_uid`, so every SSR request for a\n * returning visitor missed the cookie and minted a fresh orphan session (quota\n * inflation, broken sticky assignments and persona continuity), and graph sync\n * sent `sessionId: undefined`. Deriving both sides from one function makes that\n * drift impossible, and index.test.ts pins the reader against the writer.\n */\nexport function sessionCookieName(apiKey?: string): string {\n return `${LEGACY_SESSION_COOKIE_NAME}${storageSuffix(apiKey)}`;\n}\n","/**\n * Slot declaration helpers shared by the browser client (decide) and the\n * server preload path. Pure wrappers over @sentientui/policy.\n */\nimport {\n canonicalArm,\n slotBaselineArm,\n slotResultFor,\n type SlotDecl,\n type SlotResult,\n} from '@sentientui/policy';\n\n/** SDK-facing slot declaration. `dims` accepts readonly arrays (`as const`). */\nexport type SlotDeclInput = {\n id: string;\n arms?: string[];\n dims?: Record<string, readonly string[]>;\n baseline?: string | Record<string, string>;\n};\n\nexport type { SlotResult };\n\n/**\n * Whitelists the wire fields of a slot declaration. Anything an SDK layer\n * attached (goal configs, refs, …) is stripped so it never reaches the zod\n * schema on the API. Also normalizes readonly arrays to mutable ones.\n */\nexport function toWireSlot(d: SlotDeclInput): SlotDecl {\n return {\n id: d.id,\n ...(d.arms ? { arms: [...d.arms] } : {}),\n ...(d.dims\n ? {\n dims: Object.fromEntries(\n Object.entries(d.dims).map(([dim, values]) => [dim, [...values]]),\n ),\n }\n : {}),\n ...(d.baseline !== undefined ? { baseline: d.baseline } : {}),\n };\n}\n\n/** The declared (or default first-declared) baseline result for a slot. */\nexport function baselineResultFor(d: SlotDeclInput): SlotResult {\n const decl = toWireSlot(d);\n return slotResultFor(decl, slotBaselineArm(decl));\n}\n\n/** Baseline results for a whole declaration list, keyed by slot id. */\nexport function baselineSlots(decls: SlotDeclInput[]): Record<string, SlotResult> {\n const out: Record<string, SlotResult> = {};\n for (const d of decls) out[d.id] = baselineResultFor(d);\n return out;\n}\n\n/**\n * Canonical arm string of a slot result: dims results encode as sorted\n * `dim=value` pairs joined with '|'; arms results are the arm id verbatim.\n */\nexport function armOfResult(result: SlotResult): string {\n return typeof result === 'string' ? result : canonicalArm(result);\n}\n","/**\n * Server-side helpers for SSR variant pre-loading.\n * Pure fetch — no DOM APIs. Safe in Node.js, Edge, and Deno runtimes.\n */\nimport { buildSessionUpsertPayload } from './session-meta.js';\nimport { LEGACY_SESSION_COOKIE_NAME, sessionCookieName } from './storage-key.js';\nimport {\n toWireSlot,\n baselineResultFor,\n baselineSlots,\n type SlotDeclInput,\n type SlotResult,\n} from './slots.js';\n\nimport type { SlotConfigEntry } from './snapshot.js';\nimport type { SitePalette } from './blocks.js';\n\nexport type { SlotDeclInput, SlotResult, SlotConfigEntry, SitePalette };\n\nexport type ServerAssignConfig = {\n /** Public API key (pk_...). */\n apiKey: string;\n /**\n * Base URL of the Sentient API without trailing slash.\n * e.g. 'https://api.yourapp.com/v1'\n */\n baseUrl: string;\n /**\n * Browser origin of your app (e.g. `http://localhost:3001`). Required for `pk_`\n * keys — server-side fetch must send the same `Origin` the API allows.\n */\n origin?: string;\n /** From `User-Agent` request header (Next.js `headers()`). */\n userAgent?: string;\n /** From `Referer` request header. */\n referer?: string;\n utmParams?: Record<string, string>;\n /**\n * Ad-platform click IDs (gclid, fbclid, ttclid, …) from the landing URL.\n * Derive both this and `utmParams` with `extractTrackedParams(searchParams)`\n * — the Referer header never carries the landing page's own query string, so\n * without these two fields SSR-minted sessions have no campaign attribution\n * until the client SDK hydrates.\n */\n clickIds?: Record<string, string>;\n /**\n * Set true when the request carries a tracking opt-out — `DNT: 1` or\n * `Sec-GPC: 1`. When set, the SSR helpers skip the session upsert and the\n * assign/decide call entirely: the page renders defaults/baseline and no\n * session row is minted for the visitor (audit P4). The client SDK already\n * no-ops for these visitors; this keeps the server from minting a session +\n * assignment on every page view before the client can.\n */\n doNotTrack?: boolean;\n /**\n * Declared persona — the role your app already knows for this visitor\n * (e.g. from your auth context during SSR). Same contract as the client\n * option: must be a vocabulary key; unrecognized values are ignored\n * server-side. Never a user id or email.\n */\n persona?: string;\n /**\n * Milliseconds to wait for the API before returning default variants.\n * Prevents slow/cold API from blocking SSR. Defaults to 1000 — the hot path\n * is served from in-process caches and typically returns in well under\n * 150 ms. The full 1 s budget is only reached on a cold start or an API\n * geographically distant from your SSR host, after which defaults render\n * with no layout shift. Lower it if your API is co-located and warm.\n */\n timeoutMs?: number;\n};\n\n/** componentId → assigned variantId */\nexport type ServerAssignments = Record<string, string>;\n\ntype AssignResult = { variantId: string; assignmentTtlMs: number };\n\nconst DEFAULT_TIMEOUT_MS = 1000;\n\nfunction fetchWithTimeout(\n url: string,\n init: RequestInit,\n timeoutMs: number,\n): Promise<Response> {\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), timeoutMs);\n return fetch(url, { ...init, signal: controller.signal }).finally(() =>\n clearTimeout(timer),\n );\n}\n\n/**\n * Fetches variant assignments server-side for all listed components.\n * Returns a map suitable for passing as `initialAssignments` to `<AdaptiveProvider>`.\n *\n * Call from Next.js layout, Server Component, or getServerSideProps.\n *\n * @example\n * ```tsx\n * const assignments = await preloadAssignments(\n * [\n * { id: 'hero', variantIds: ['hero-a', 'hero-b'] },\n * { id: 'cta', variantIds: ['cta-short', 'cta-long'] },\n * ],\n * sessionId, // read from cookies() or req.cookies\n * { apiKey: process.env.NEXT_PUBLIC_SENTIENT_API_KEY!, baseUrl: 'https://api.sentient-ui.com/v1' },\n * );\n * return <AdaptiveProvider ... initialAssignments={assignments}>{children}</AdaptiveProvider>;\n * ```\n */\nexport async function preloadAssignments(\n components: Array<{ id: string; variantIds: string[] }>,\n sessionId: string,\n config: ServerAssignConfig,\n): Promise<ServerAssignments> {\n // Opted-out visitor (DNT/GPC): render defaults, mint no session (audit P4).\n if (config.doNotTrack) return {};\n\n const timeoutMs = config.timeoutMs ?? DEFAULT_TIMEOUT_MS;\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${config.apiKey}`,\n };\n if (config.origin) {\n headers.Origin = config.origin;\n }\n\n // Session metadata must match the browser SDK so assign seeds variant_weights\n // under the same segment (not `unknown:unknown`).\n const sessionBody = {\n ...buildSessionUpsertPayload(sessionId, {\n userAgent: config.userAgent,\n referer: config.referer,\n utmParams: config.utmParams,\n clickIds: config.clickIds,\n appOrigin: config.origin,\n }),\n ...(config.persona ? { persona: config.persona } : {}),\n };\n\n try {\n const res = await fetchWithTimeout(\n `${config.baseUrl}/sessions`,\n { method: 'POST', headers, body: JSON.stringify(sessionBody) },\n timeoutMs,\n );\n if (res.status === 402) {\n console.warn(\n '[SentientUI] Session limit exceeded for this project. The bandit will stop learning until the limit resets. Upgrade at sentient-ui.com/pricing',\n );\n } else if (!res.ok) {\n const body = await res.json().catch(() => ({}));\n console.error(`[SentientUI] preloadAssignments: session upsert failed (${res.status})`, body);\n }\n } catch (err) {\n console.error('[SentientUI] preloadAssignments: session upsert threw', err);\n }\n\n const results = await Promise.allSettled(\n components.map(async ({ id, variantIds }) => {\n const res = await fetchWithTimeout(\n `${config.baseUrl}/assign`,\n { method: 'POST', headers, body: JSON.stringify({ sessionId, componentId: id, variantIds }) },\n timeoutMs,\n );\n if (!res.ok) {\n const body = await res.json().catch(() => ({}));\n console.error(`[SentientUI] preloadAssignments: assign failed for \"${id}\" (${res.status})`, body);\n return null;\n }\n const body = (await res.json()) as AssignResult;\n return { id, variantId: body.variantId };\n }),\n );\n\n const assignments: ServerAssignments = {};\n for (const result of results) {\n if (result.status === 'fulfilled' && result.value) {\n assignments[result.value.id] = result.value.variantId;\n }\n }\n return assignments;\n}\n\n/**\n * Reads the Sentient session cookie from a Next.js `ReadonlyRequestCookies` object\n * (the return value of `cookies()` from `next/headers`), or any object with a\n * `get(name: string)` method. Returns null if the cookie is absent.\n *\n * Pass the project's `apiKey`: the client writes the per-project SUFFIXED name\n * (`sessionCookieName` — see storage-key.ts), so reading only the bare\n * `_snt_uid` missed the cookie on every SSR request for a returning visitor and\n * minted a fresh orphan session each page view (quota inflation, broken sticky\n * assignments and persona continuity). The bare name is still read as a\n * fallback for identities minted before namespacing.\n */\nexport function readSessionCookie(\n cookies: { get(name: string): { value: string } | undefined },\n apiKey?: string,\n): string | null {\n return (\n (apiKey ? cookies.get(sessionCookieName(apiKey))?.value : undefined) ??\n cookies.get(LEGACY_SESSION_COOKIE_NAME)?.value ??\n null\n );\n}\n\nexport type DecideResult = {\n layoutOrder: string[];\n assignments: Record<string, string>;\n /** Slot results keyed by slot id. Baseline-resolved when the API omits/fails them. */\n slots: Record<string, SlotResult>;\n persona: string;\n confidence: number;\n /** Registry mode only: content/ops/blocks for server-defined slots. */\n slotConfig?: Record<string, SlotConfigEntry>;\n /** Registry mode only: site palette for block rendering. */\n palette?: SitePalette;\n};\n\n/**\n * Single-roundtrip SSR call returning layout order, component assignments,\n * and adaptive-slot results. Falls back to default section order + empty\n * assignments + baseline slots if the API is unavailable. A response without\n * a `slots` field means the server predates slots — every declared slot\n * resolves to its baseline (no retry).\n */\nexport async function preloadDecisions(\n params: {\n sections?: string[];\n components: Array<{ id: string; variantIds?: string[] }>;\n slots?: SlotDeclInput[];\n /** 'registry' serves the project's published slot_definitions as if declared\n * (and is the only way slotConfig/palette come back). Default 'request'. */\n slotsFrom?: 'request' | 'registry';\n },\n sessionId: string,\n config: ServerAssignConfig,\n): Promise<DecideResult> {\n const timeoutMs = config.timeoutMs ?? DEFAULT_TIMEOUT_MS;\n const declaredSlots = params.slots ?? [];\n const fallback: DecideResult = {\n layoutOrder: params.sections ?? [],\n assignments: {},\n slots: baselineSlots(declaredSlots),\n persona: 'unknown',\n confidence: 0,\n };\n\n // Opted-out visitor (DNT/GPC): baseline layout/slots, mint no session (audit P4).\n if (config.doNotTrack) return fallback;\n\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${config.apiKey}`,\n };\n if (config.origin) headers.Origin = config.origin;\n\n const sessionBody = {\n ...buildSessionUpsertPayload(sessionId, {\n userAgent: config.userAgent,\n referer: config.referer,\n utmParams: config.utmParams,\n clickIds: config.clickIds,\n appOrigin: config.origin,\n }),\n ...(config.persona ? { persona: config.persona } : {}),\n };\n\n try {\n const sessionRes = await fetchWithTimeout(\n `${config.baseUrl}/sessions`,\n { method: 'POST', headers, body: JSON.stringify(sessionBody) },\n timeoutMs,\n );\n if (!sessionRes.ok) {\n const body = await sessionRes.json().catch(() => ({}));\n console.error(`[SentientUI] preloadDecisions: session upsert failed (${sessionRes.status})`, body);\n }\n } catch (err) {\n console.error('[SentientUI] preloadDecisions: session upsert threw', err);\n }\n\n try {\n const res = await fetchWithTimeout(\n `${config.baseUrl}/decide`,\n {\n method: 'POST',\n headers,\n body: JSON.stringify({\n sessionId,\n sections: (params.sections ?? []).map((id) => ({ id })),\n components: params.components,\n ...(declaredSlots.length > 0 ? { slots: declaredSlots.map(toWireSlot) } : {}),\n ...(params.slotsFrom === 'registry' ? { slotsFrom: 'registry' } : {}),\n ...(config.persona ? { persona: config.persona } : {}),\n }),\n },\n timeoutMs,\n );\n if (!res.ok) {\n const body = await res.json().catch(() => ({}));\n console.error(`[SentientUI] preloadDecisions: decide failed (${res.status})`, body);\n return fallback;\n }\n const data = (await res.json()) as {\n layoutOrder?: string[];\n assignments?: Record<string, string>;\n slots?: Record<string, SlotResult>;\n slotConfig?: Record<string, SlotConfigEntry>;\n palette?: SitePalette;\n persona?: string;\n confidence?: number;\n };\n\n const slots: Record<string, SlotResult> = {};\n for (const d of declaredSlots) {\n // `data.slots === undefined` ⇒ server predates slots: baseline, no retry.\n slots[d.id] = data.slots?.[d.id] ?? baselineResultFor(d);\n }\n // Registry mode returns slots the request never declared — union them in\n // (same rule as the browser client), or an SSR page gets results for none\n // of its registry slots.\n for (const [slotId, result] of Object.entries(data.slots ?? {})) {\n if (!(slotId in slots)) slots[slotId] = result;\n }\n\n // Plain-object guard only (readSnapshot's rule): the server total-validated\n // the config at publish; a deep re-validation here would just drift.\n const slotConfigOk =\n data.slotConfig !== undefined && typeof data.slotConfig === 'object' && data.slotConfig !== null && !Array.isArray(data.slotConfig);\n\n return {\n layoutOrder: data.layoutOrder ?? params.sections ?? [],\n assignments: data.assignments ?? {},\n slots,\n persona: data.persona ?? 'unknown',\n confidence: data.confidence ?? 0,\n ...(slotConfigOk ? { slotConfig: data.slotConfig } : {}),\n ...(data.palette ? { palette: data.palette } : {}),\n };\n } catch (err) {\n console.error('[SentientUI] preloadDecisions: decide threw', err);\n return fallback;\n }\n}\n"],"mappings":"4wBAAA,IAAAA,GAAA,GAAAC,GAAAD,GAAA,mBAAAE,EAAA,8BAAAC,EAAA,yBAAAC,EAAA,sBAAAC,EAAA,oBAAAC,EAAA,wBAAAC,EAAA,yBAAAC,EAAA,uBAAAC,EAAA,qBAAAC,EAAA,sBAAAC,EAAA,8BAAAC,IAAA,eAAAC,GAAAb,ICQO,IAAMc,GAAiC,CAC5C,SACA,eACA,gBACA,YACA,cACA,mBACA,gBACA,kBACA,kBACA,oBACA,qBACA,aACA,QACA,YACA,YACA,SACF,EAGO,SAASC,GAAaC,EAA4B,CACvD,OAAOC,GAAkBD,CAAS,IAAM,IAC1C,CAGO,SAASC,GAAkBD,EAAkC,CAjCpE,IAAAE,EAkCE,GAAI,CAACF,EAAW,OAAO,KACvB,IAAMG,EAAIH,EAAU,YAAY,EAChC,OAAOE,EAAAJ,GAAY,KAAMM,GAAUD,EAAE,SAASC,EAAM,YAAY,CAAC,CAAC,IAA3D,KAAAF,EAAgE,IACzE,CA8CO,SAASG,EAAkBC,EAA2B,CAC3D,IAAMC,EAAID,EAAU,YAAY,EAChC,MAAI,mCAAmC,KAAKC,CAAC,EAAU,SACnD,yCAAyC,KAAKA,CAAC,EAAU,SACtD,SACT,CAEO,SAASC,EAAoBC,EAAkBC,EAA4B,CAChF,GAAI,CAACD,EAAU,MAAO,SACtB,GAAI,CACF,IAAME,EAAS,IAAI,IAAIF,CAAQ,EAC/B,GAAIC,EACF,GAAI,CACF,GAAI,IAAI,IAAIA,CAAS,EAAE,OAASC,EAAO,KAAM,MAAO,QACtD,OAAQC,EAAA,CAER,CAEF,IAAMC,EAAOF,EAAO,SAAS,YAAY,EACzC,MAAI,yCAAyC,KAAKE,CAAI,EAAU,SAI5D,6EAA6E,KAAKA,CAAI,EAAU,SAC7F,UACT,OAAQD,EAAA,CACN,MAAO,QACT,CACF,CAEO,SAASE,EAA0BL,EAAiC,CACzE,GAAI,CAACA,EAAU,OAAO,KACtB,GAAI,CACF,OAAO,IAAI,IAAIA,CAAQ,EAAE,QAC3B,OAAQ,GACN,OAAO,IACT,CACF,CAiBO,IAAMM,EAAmC,CAC9C,QACA,SACA,SACA,SACA,SACA,UACA,SACA,WACF,EAUO,SAASC,EACdC,EACyE,CACzE,IAAMC,EAAoC,CAAC,EACrCC,EAAmC,CAAC,EAC1C,GAAI,CACF,IAAMC,EACJ,OAAOH,GAAW,UAAYA,aAAkB,gBAC5C,IAAI,gBAAgBA,CAAM,EAC1B,OAAO,QAAQA,CAAM,EAAE,QAAQ,CAAC,CAACI,EAAGC,CAAC,IAA+B,CAClE,IAAMC,EAAQ,MAAM,QAAQD,CAAC,EAAIA,EAAE,CAAC,EAAIA,EACxC,OAAOC,IAAU,OAAY,CAAC,EAAI,CAAC,CAACF,EAAGE,CAAK,CAAC,CAC/C,CAAC,EACP,OAAW,CAACF,EAAGC,CAAC,IAAKF,EAGfC,EAAE,WAAW,MAAM,EACfA,KAAKH,IAAYA,EAAUG,CAAC,EAAIC,GAC7BP,EAAc,SAASM,CAAC,IAC3BA,KAAKF,IAAWA,EAASE,CAAC,EAAIC,GAG1C,OAAQV,EAAA,CAER,CACA,MAAO,CAAE,UAAAM,EAAW,SAAAC,CAAS,CAC/B,CAEO,SAASK,EAAgBC,EAAiB,CAC/C,IAAMC,EAAID,EAAE,SAAS,EACrB,OAAIC,EAAI,EAAU,QACdA,EAAI,GAAW,UACfA,EAAI,GAAW,YACZ,SACT,CAsBO,SAASC,EAAqBC,EAI1B,CACT,IAAMC,EAAOC,EAA0B,cAAeF,CAAI,EAC1D,MAAO,GAAGC,EAAK,WAAW,IAAIA,EAAK,aAAa,EAClD,CAMO,SAASC,EACdC,EACAH,EAUsB,CA7OxB,IAAAI,EAAAC,EAAAC,EAAAC,EAAAC,EAAAC,EAAAC,EAAAC,EA8OE,IAAMC,GAAKP,GAAAD,EAAAJ,GAAA,YAAAA,EAAM,YAAN,YAAAI,EAAiB,SAAjB,KAAAC,EAA2B,GAChCQ,GAAUN,GAAAD,EAAAN,GAAA,YAAAA,EAAM,UAAN,YAAAM,EAAe,SAAf,KAAAC,EAAyB,GACnCO,GAAMN,EAAAR,GAAA,YAAAA,EAAM,MAAN,KAAAQ,EAAa,IAAI,KAC7B,MAAO,CACL,UAAAL,EACA,UAAW,GACX,WAAWM,EAAAT,GAAA,YAAAA,EAAM,YAAN,KAAAS,EAAmB,CAAC,EAC/B,UAAUC,EAAAV,GAAA,YAAAA,EAAM,WAAN,KAAAU,EAAkB,CAAC,EAC7B,YAAaE,EAAKnC,EAAkBmC,CAAE,EAAI,UAC1C,cAAeC,EACXjC,EAAoBiC,EAASb,GAAA,YAAAA,EAAM,SAAS,EAC5C,SACJ,eAAgBd,EAA0B2B,CAAO,EACjD,UAAWjB,EAAgBkB,CAAG,EAC9B,WAAWH,EAAA,CAAC,MAAO,MAAO,MAAO,MAAO,MAAO,MAAO,KAAK,EAAEG,EAAI,OAAO,CAAC,IAA9D,KAAAH,EAAmE,MAC9E,YAAYX,GAAA,YAAAA,EAAM,aAAc,IAAQe,GAAaH,CAAE,CACzD,CACF,CCjPO,SAASI,GAAcC,EAAyB,CACrD,OAAOA,EAAS,IAAIA,EAAO,MAAM,EAAG,EAAE,CAAC,GAAK,EAC9C,CAOO,IAAMC,EAA6B,WAYnC,SAASC,EAAkBF,EAAyB,CACzD,MAAO,GAAGC,CAA0B,GAAGF,GAAcC,CAAM,CAAC,EAC9D,CCjCA,IAAAG,EAMO,8BAiBA,SAASC,EAAWC,EAA4B,CACrD,OAAOC,MAAA,CACL,GAAID,EAAE,IACFA,EAAE,KAAO,CAAE,KAAM,CAAC,GAAGA,EAAE,IAAI,CAAE,EAAI,CAAC,GAClCA,EAAE,KACF,CACE,KAAM,OAAO,YACX,OAAO,QAAQA,EAAE,IAAI,EAAE,IAAI,CAAC,CAACE,EAAKC,CAAM,IAAM,CAACD,EAAK,CAAC,GAAGC,CAAM,CAAC,CAAC,CAClE,CACF,EACA,CAAC,GACDH,EAAE,WAAa,OAAY,CAAE,SAAUA,EAAE,QAAS,EAAI,CAAC,EAE/D,CAGO,SAASI,EAAkBJ,EAA8B,CAC9D,IAAMK,EAAON,EAAWC,CAAC,EACzB,SAAO,iBAAcK,KAAM,mBAAgBA,CAAI,CAAC,CAClD,CAGO,SAASC,EAAcC,EAAoD,CAChF,IAAMC,EAAkC,CAAC,EACzC,QAAWR,KAAKO,EAAOC,EAAIR,EAAE,EAAE,EAAII,EAAkBJ,CAAC,EACtD,OAAOQ,CACT,CCwBA,IAAMC,EAAqB,IAE3B,SAASC,EACPC,EACAC,EACAC,EACmB,CACnB,IAAMC,EAAa,IAAI,gBACjBC,EAAQ,WAAW,IAAMD,EAAW,MAAM,EAAGD,CAAS,EAC5D,OAAO,MAAMF,EAAKK,EAAAC,EAAA,GAAKL,GAAL,CAAW,OAAQE,EAAW,MAAO,EAAC,EAAE,QAAQ,IAChE,aAAaC,CAAK,CACpB,CACF,CAqBA,eAAsBG,EACpBC,EACAC,EACAC,EAC4B,CAlH9B,IAAAC,EAoHE,GAAID,EAAO,WAAY,MAAO,CAAC,EAE/B,IAAMR,GAAYS,EAAAD,EAAO,YAAP,KAAAC,EAAoBb,EAChCc,EAAkC,CACtC,eAAgB,mBAChB,cAAe,UAAUF,EAAO,MAAM,EACxC,EACIA,EAAO,SACTE,EAAQ,OAASF,EAAO,QAK1B,IAAMG,EAAcP,IAAA,GACfQ,EAA0BL,EAAW,CACtC,UAAWC,EAAO,UAClB,QAASA,EAAO,QAChB,UAAWA,EAAO,UAClB,SAAUA,EAAO,SACjB,UAAWA,EAAO,MACpB,CAAC,GACGA,EAAO,QAAU,CAAE,QAASA,EAAO,OAAQ,EAAI,CAAC,GAGtD,GAAI,CACF,IAAMK,EAAM,MAAMhB,EAChB,GAAGW,EAAO,OAAO,YACjB,CAAE,OAAQ,OAAQ,QAAAE,EAAS,KAAM,KAAK,UAAUC,CAAW,CAAE,EAC7DX,CACF,EACA,GAAIa,EAAI,SAAW,IACjB,QAAQ,KACN,gJACF,UACS,CAACA,EAAI,GAAI,CAClB,IAAMC,EAAO,MAAMD,EAAI,KAAK,EAAE,MAAM,KAAO,CAAC,EAAE,EAC9C,QAAQ,MAAM,2DAA2DA,EAAI,MAAM,IAAKC,CAAI,CAC9F,CACF,OAASC,EAAK,CACZ,QAAQ,MAAM,wDAAyDA,CAAG,CAC5E,CAEA,IAAMC,EAAU,MAAM,QAAQ,WAC5BV,EAAW,IAAI,MAAO,CAAE,GAAAW,EAAI,WAAAC,CAAW,IAAM,CAC3C,IAAML,EAAM,MAAMhB,EAChB,GAAGW,EAAO,OAAO,UACjB,CAAE,OAAQ,OAAQ,QAAAE,EAAS,KAAM,KAAK,UAAU,CAAE,UAAAH,EAAW,YAAaU,EAAI,WAAAC,CAAW,CAAC,CAAE,EAC5FlB,CACF,EACA,GAAI,CAACa,EAAI,GAAI,CACX,IAAMC,EAAO,MAAMD,EAAI,KAAK,EAAE,MAAM,KAAO,CAAC,EAAE,EAC9C,eAAQ,MAAM,uDAAuDI,CAAE,MAAMJ,EAAI,MAAM,IAAKC,CAAI,EACzF,IACT,CACA,IAAMA,EAAQ,MAAMD,EAAI,KAAK,EAC7B,MAAO,CAAE,GAAAI,EAAI,UAAWH,EAAK,SAAU,CACzC,CAAC,CACH,EAEMK,EAAiC,CAAC,EACxC,QAAWC,KAAUJ,EACfI,EAAO,SAAW,aAAeA,EAAO,QAC1CD,EAAYC,EAAO,MAAM,EAAE,EAAIA,EAAO,MAAM,WAGhD,OAAOD,CACT,CAcO,SAASE,EACdC,EACAC,EACe,CAvMjB,IAAAd,EAAAe,EAAAC,EAAAC,EAwME,OACGA,GAAAD,EAAAF,GAASd,EAAAa,EAAQ,IAAIK,EAAkBJ,CAAM,CAAC,IAArC,YAAAd,EAAwC,MAAQ,SAAzD,KAAAgB,GACDD,EAAAF,EAAQ,IAAIM,CAA0B,IAAtC,YAAAJ,EAAyC,QADxC,KAAAE,EAED,IAEJ,CAsBA,eAAsBG,EACpBC,EAQAvB,EACAC,EACuB,CA9OzB,IAAAC,EAAAe,EAAAC,EAAAC,EAAAK,EAAAC,EAAAC,EAAAC,EAAAC,EAAAC,EAAAC,EAAAC,EA+OE,IAAMtC,GAAYS,EAAAD,EAAO,YAAP,KAAAC,EAAoBb,EAChC2C,GAAgBf,EAAAM,EAAO,QAAP,KAAAN,EAAgB,CAAC,EACjCgB,EAAyB,CAC7B,aAAaf,EAAAK,EAAO,WAAP,KAAAL,EAAmB,CAAC,EACjC,YAAa,CAAC,EACd,MAAOgB,EAAcF,CAAa,EAClC,QAAS,UACT,WAAY,CACd,EAGA,GAAI/B,EAAO,WAAY,OAAOgC,EAE9B,IAAM9B,EAAkC,CACtC,eAAgB,mBAChB,cAAe,UAAUF,EAAO,MAAM,EACxC,EACIA,EAAO,SAAQE,EAAQ,OAASF,EAAO,QAE3C,IAAMG,EAAcP,IAAA,GACfQ,EAA0BL,EAAW,CACtC,UAAWC,EAAO,UAClB,QAASA,EAAO,QAChB,UAAWA,EAAO,UAClB,SAAUA,EAAO,SACjB,UAAWA,EAAO,MACpB,CAAC,GACGA,EAAO,QAAU,CAAE,QAASA,EAAO,OAAQ,EAAI,CAAC,GAGtD,GAAI,CACF,IAAMkC,EAAa,MAAM7C,EACvB,GAAGW,EAAO,OAAO,YACjB,CAAE,OAAQ,OAAQ,QAAAE,EAAS,KAAM,KAAK,UAAUC,CAAW,CAAE,EAC7DX,CACF,EACA,GAAI,CAAC0C,EAAW,GAAI,CAClB,IAAM5B,EAAO,MAAM4B,EAAW,KAAK,EAAE,MAAM,KAAO,CAAC,EAAE,EACrD,QAAQ,MAAM,yDAAyDA,EAAW,MAAM,IAAK5B,CAAI,CACnG,CACF,OAASC,EAAK,CACZ,QAAQ,MAAM,sDAAuDA,CAAG,CAC1E,CAEA,GAAI,CACF,IAAMF,EAAM,MAAMhB,EAChB,GAAGW,EAAO,OAAO,UACjB,CACE,OAAQ,OACR,QAAAE,EACA,KAAM,KAAK,UAAUN,MAAA,CACnB,UAAAG,EACA,WAAWmB,EAAAI,EAAO,WAAP,KAAAJ,EAAmB,CAAC,GAAG,IAAKT,IAAQ,CAAE,GAAAA,CAAG,EAAE,EACtD,WAAYa,EAAO,YACfS,EAAc,OAAS,EAAI,CAAE,MAAOA,EAAc,IAAII,CAAU,CAAE,EAAI,CAAC,GACvEb,EAAO,YAAc,WAAa,CAAE,UAAW,UAAW,EAAI,CAAC,GAC/DtB,EAAO,QAAU,CAAE,QAASA,EAAO,OAAQ,EAAI,CAAC,EACrD,CACH,EACAR,CACF,EACA,GAAI,CAACa,EAAI,GAAI,CACX,IAAMC,EAAO,MAAMD,EAAI,KAAK,EAAE,MAAM,KAAO,CAAC,EAAE,EAC9C,eAAQ,MAAM,iDAAiDA,EAAI,MAAM,IAAKC,CAAI,EAC3E0B,CACT,CACA,IAAMI,EAAQ,MAAM/B,EAAI,KAAK,EAUvBgC,EAAoC,CAAC,EAC3C,QAAWC,KAAKP,EAEdM,EAAMC,EAAE,EAAE,GAAId,GAAAD,EAAAa,EAAK,QAAL,YAAAb,EAAae,EAAE,MAAf,KAAAd,EAAsBe,EAAkBD,CAAC,EAKzD,OAAW,CAACE,EAAQ5B,CAAM,IAAK,OAAO,SAAQa,EAAAW,EAAK,QAAL,KAAAX,EAAc,CAAC,CAAC,EACtDe,KAAUH,IAAQA,EAAMG,CAAM,EAAI5B,GAK1C,IAAM6B,EACJL,EAAK,aAAe,QAAa,OAAOA,EAAK,YAAe,UAAYA,EAAK,aAAe,MAAQ,CAAC,MAAM,QAAQA,EAAK,UAAU,EAEpI,OAAOxC,IAAA,CACL,aAAa+B,GAAAD,EAAAU,EAAK,cAAL,KAAAV,EAAoBJ,EAAO,WAA3B,KAAAK,EAAuC,CAAC,EACrD,aAAaC,EAAAQ,EAAK,cAAL,KAAAR,EAAoB,CAAC,EAClC,MAAAS,EACA,SAASR,EAAAO,EAAK,UAAL,KAAAP,EAAgB,UACzB,YAAYC,EAAAM,EAAK,aAAL,KAAAN,EAAmB,GAC3BW,EAAe,CAAE,WAAYL,EAAK,UAAW,EAAI,CAAC,GAClDA,EAAK,QAAU,CAAE,QAASA,EAAK,OAAQ,EAAI,CAAC,EAEpD,OAAS7B,EAAK,CACZ,eAAQ,MAAM,8CAA+CA,CAAG,EACzDyB,CACT,CACF","names":["index_server_exports","__export","CLICK_ID_KEYS","buildSessionUpsertPayload","deriveSessionSegment","detectDeviceClass","detectTimeOfDay","detectTrafficSource","extractTrackedParams","preloadAssignments","preloadDecisions","readSessionCookie","referrerDomainFromReferer","__toCommonJS","agentUaList","uaTokenMatch","userAgent","matchedAgentToken","_a","s","token","detectDeviceClass","userAgent","s","detectTrafficSource","referrer","appOrigin","refUrl","e","host","referrerDomainFromReferer","CLICK_ID_KEYS","extractTrackedParams","search","utmParams","clickIds","entries","k","v","first","detectTimeOfDay","d","h","deriveSessionSegment","opts","body","buildSessionUpsertPayload","sessionId","_a","_b","_c","_d","_e","_f","_g","_h","ua","referer","now","uaTokenMatch","storageSuffix","apiKey","LEGACY_SESSION_COOKIE_NAME","sessionCookieName","import_policy","toWireSlot","d","__spreadValues","dim","values","baselineResultFor","decl","baselineSlots","decls","out","DEFAULT_TIMEOUT_MS","fetchWithTimeout","url","init","timeoutMs","controller","timer","__spreadProps","__spreadValues","preloadAssignments","components","sessionId","config","_a","headers","sessionBody","buildSessionUpsertPayload","res","body","err","results","id","variantIds","assignments","result","readSessionCookie","cookies","apiKey","_b","_c","_d","sessionCookieName","LEGACY_SESSION_COOKIE_NAME","preloadDecisions","params","_e","_f","_g","_h","_i","_j","_k","_l","declaredSlots","fallback","baselineSlots","sessionRes","toWireSlot","data","slots","d","baselineResultFor","slotId","slotConfigOk"]}
|
package/dist/index-server.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{b as
|
|
1
|
+
import{b as T,c as w,j as N,k as M,l as _,m as F,n as K,o as B,p as J,q as A,r as D,s as U,t as j}from"./chunk-2NVSFI4V.mjs";import{a,b as k}from"./chunk-TMCGHANO.mjs";var x=1e3;function I(r,l,e){let i=new AbortController,n=setTimeout(()=>i.abort(),e);return fetch(r,k(a({},l),{signal:i.signal})).finally(()=>clearTimeout(n))}async function L(r,l,e){var m;if(e.doNotTrack)return{};let i=(m=e.timeoutMs)!=null?m:x,n={"Content-Type":"application/json",Authorization:`Bearer ${e.apiKey}`};e.origin&&(n.Origin=e.origin);let d=a(a({},A(l,{userAgent:e.userAgent,referer:e.referer,utmParams:e.utmParams,clickIds:e.clickIds,appOrigin:e.origin})),e.persona?{persona:e.persona}:{});try{let t=await I(`${e.baseUrl}/sessions`,{method:"POST",headers:n,body:JSON.stringify(d)},i);if(t.status===402)console.warn("[SentientUI] Session limit exceeded for this project. The bandit will stop learning until the limit resets. Upgrade at sentient-ui.com/pricing");else if(!t.ok){let p=await t.json().catch(()=>({}));console.error(`[SentientUI] preloadAssignments: session upsert failed (${t.status})`,p)}}catch(t){console.error("[SentientUI] preloadAssignments: session upsert threw",t)}let g=await Promise.allSettled(r.map(async({id:t,variantIds:p})=>{let u=await I(`${e.baseUrl}/assign`,{method:"POST",headers:n,body:JSON.stringify({sessionId:l,componentId:t,variantIds:p})},i);if(!u.ok){let f=await u.json().catch(()=>({}));return console.error(`[SentientUI] preloadAssignments: assign failed for "${t}" (${u.status})`,f),null}let y=await u.json();return{id:t,variantId:y.variantId}})),S={};for(let t of g)t.status==="fulfilled"&&t.value&&(S[t.value.id]=t.value.variantId);return S}function q(r,l){var e,i,n,d;return(d=(n=l?(e=r.get(w(l)))==null?void 0:e.value:void 0)!=null?n:(i=r.get(T))==null?void 0:i.value)!=null?d:null}async function z(r,l,e){var m,t,p,u,y,f,R,C,h,O,v,P;let i=(m=e.timeoutMs)!=null?m:x,n=(t=r.slots)!=null?t:[],d={layoutOrder:(p=r.sections)!=null?p:[],assignments:{},slots:j(n),persona:"unknown",confidence:0};if(e.doNotTrack)return d;let g={"Content-Type":"application/json",Authorization:`Bearer ${e.apiKey}`};e.origin&&(g.Origin=e.origin);let S=a(a({},A(l,{userAgent:e.userAgent,referer:e.referer,utmParams:e.utmParams,clickIds:e.clickIds,appOrigin:e.origin})),e.persona?{persona:e.persona}:{});try{let o=await I(`${e.baseUrl}/sessions`,{method:"POST",headers:g,body:JSON.stringify(S)},i);if(!o.ok){let s=await o.json().catch(()=>({}));console.error(`[SentientUI] preloadDecisions: session upsert failed (${o.status})`,s)}}catch(o){console.error("[SentientUI] preloadDecisions: session upsert threw",o)}try{let o=await I(`${e.baseUrl}/decide`,{method:"POST",headers:g,body:JSON.stringify(a(a(a({sessionId:l,sections:((u=r.sections)!=null?u:[]).map(c=>({id:c})),components:r.components},n.length>0?{slots:n.map(D)}:{}),r.slotsFrom==="registry"?{slotsFrom:"registry"}:{}),e.persona?{persona:e.persona}:{}))},i);if(!o.ok){let c=await o.json().catch(()=>({}));return console.error(`[SentientUI] preloadDecisions: decide failed (${o.status})`,c),d}let s=await o.json(),b={};for(let c of n)b[c.id]=(f=(y=s.slots)==null?void 0:y[c.id])!=null?f:U(c);for(let[c,$]of Object.entries((R=s.slots)!=null?R:{}))c in b||(b[c]=$);let E=s.slotConfig!==void 0&&typeof s.slotConfig=="object"&&s.slotConfig!==null&&!Array.isArray(s.slotConfig);return a(a({layoutOrder:(h=(C=s.layoutOrder)!=null?C:r.sections)!=null?h:[],assignments:(O=s.assignments)!=null?O:{},slots:b,persona:(v=s.persona)!=null?v:"unknown",confidence:(P=s.confidence)!=null?P:0},E?{slotConfig:s.slotConfig}:{}),s.palette?{palette:s.palette}:{})}catch(o){return console.error("[SentientUI] preloadDecisions: decide threw",o),d}}export{F as CLICK_ID_KEYS,A as buildSessionUpsertPayload,J as deriveSessionSegment,N as detectDeviceClass,B as detectTimeOfDay,M as detectTrafficSource,K as extractTrackedParams,L as preloadAssignments,z as preloadDecisions,q as readSessionCookie,_ as referrerDomainFromReferer};
|
|
2
2
|
//# sourceMappingURL=index-server.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/server.ts"],"sourcesContent":["/**\n * Server-side helpers for SSR variant pre-loading.\n * Pure fetch — no DOM APIs. Safe in Node.js, Edge, and Deno runtimes.\n */\nimport { buildSessionUpsertPayload } from './session-meta.js';\nimport { LEGACY_SESSION_COOKIE_NAME, sessionCookieName } from './storage-key.js';\nimport {\n toWireSlot,\n baselineResultFor,\n baselineSlots,\n type SlotDeclInput,\n type SlotResult,\n} from './slots.js';\n\nexport type { SlotDeclInput, SlotResult };\n\nexport type ServerAssignConfig = {\n /** Public API key (pk_...). */\n apiKey: string;\n /**\n * Base URL of the Sentient API without trailing slash.\n * e.g. 'https://api.yourapp.com/v1'\n */\n baseUrl: string;\n /**\n * Browser origin of your app (e.g. `http://localhost:3001`). Required for `pk_`\n * keys — server-side fetch must send the same `Origin` the API allows.\n */\n origin?: string;\n /** From `User-Agent` request header (Next.js `headers()`). */\n userAgent?: string;\n /** From `Referer` request header. */\n referer?: string;\n utmParams?: Record<string, string>;\n /**\n * Ad-platform click IDs (gclid, fbclid, ttclid, …) from the landing URL.\n * Derive both this and `utmParams` with `extractTrackedParams(searchParams)`\n * — the Referer header never carries the landing page's own query string, so\n * without these two fields SSR-minted sessions have no campaign attribution\n * until the client SDK hydrates.\n */\n clickIds?: Record<string, string>;\n /**\n * Set true when the request carries a tracking opt-out — `DNT: 1` or\n * `Sec-GPC: 1`. When set, the SSR helpers skip the session upsert and the\n * assign/decide call entirely: the page renders defaults/baseline and no\n * session row is minted for the visitor (audit P4). The client SDK already\n * no-ops for these visitors; this keeps the server from minting a session +\n * assignment on every page view before the client can.\n */\n doNotTrack?: boolean;\n /**\n * Declared persona — the role your app already knows for this visitor\n * (e.g. from your auth context during SSR). Same contract as the client\n * option: must be a vocabulary key; unrecognized values are ignored\n * server-side. Never a user id or email.\n */\n persona?: string;\n /**\n * Milliseconds to wait for the API before returning default variants.\n * Prevents slow/cold API from blocking SSR. Defaults to 1000 — the hot path\n * is served from in-process caches and typically returns in well under\n * 150 ms. The full 1 s budget is only reached on a cold start or an API\n * geographically distant from your SSR host, after which defaults render\n * with no layout shift. Lower it if your API is co-located and warm.\n */\n timeoutMs?: number;\n};\n\n/** componentId → assigned variantId */\nexport type ServerAssignments = Record<string, string>;\n\ntype AssignResult = { variantId: string; assignmentTtlMs: number };\n\nconst DEFAULT_TIMEOUT_MS = 1000;\n\nfunction fetchWithTimeout(\n url: string,\n init: RequestInit,\n timeoutMs: number,\n): Promise<Response> {\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), timeoutMs);\n return fetch(url, { ...init, signal: controller.signal }).finally(() =>\n clearTimeout(timer),\n );\n}\n\n/**\n * Fetches variant assignments server-side for all listed components.\n * Returns a map suitable for passing as `initialAssignments` to `<AdaptiveProvider>`.\n *\n * Call from Next.js layout, Server Component, or getServerSideProps.\n *\n * @example\n * ```tsx\n * const assignments = await preloadAssignments(\n * [\n * { id: 'hero', variantIds: ['hero-a', 'hero-b'] },\n * { id: 'cta', variantIds: ['cta-short', 'cta-long'] },\n * ],\n * sessionId, // read from cookies() or req.cookies\n * { apiKey: process.env.NEXT_PUBLIC_SENTIENT_API_KEY!, baseUrl: 'https://api.sentient-ui.com/v1' },\n * );\n * return <AdaptiveProvider ... initialAssignments={assignments}>{children}</AdaptiveProvider>;\n * ```\n */\nexport async function preloadAssignments(\n components: Array<{ id: string; variantIds: string[] }>,\n sessionId: string,\n config: ServerAssignConfig,\n): Promise<ServerAssignments> {\n // Opted-out visitor (DNT/GPC): render defaults, mint no session (audit P4).\n if (config.doNotTrack) return {};\n\n const timeoutMs = config.timeoutMs ?? DEFAULT_TIMEOUT_MS;\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${config.apiKey}`,\n };\n if (config.origin) {\n headers.Origin = config.origin;\n }\n\n // Session metadata must match the browser SDK so assign seeds variant_weights\n // under the same segment (not `unknown:unknown`).\n const sessionBody = {\n ...buildSessionUpsertPayload(sessionId, {\n userAgent: config.userAgent,\n referer: config.referer,\n utmParams: config.utmParams,\n clickIds: config.clickIds,\n appOrigin: config.origin,\n }),\n ...(config.persona ? { persona: config.persona } : {}),\n };\n\n try {\n const res = await fetchWithTimeout(\n `${config.baseUrl}/sessions`,\n { method: 'POST', headers, body: JSON.stringify(sessionBody) },\n timeoutMs,\n );\n if (res.status === 402) {\n console.warn(\n '[SentientUI] Session limit exceeded for this project. The bandit will stop learning until the limit resets. Upgrade at sentient-ui.com/pricing',\n );\n } else if (!res.ok) {\n const body = await res.json().catch(() => ({}));\n console.error(`[SentientUI] preloadAssignments: session upsert failed (${res.status})`, body);\n }\n } catch (err) {\n console.error('[SentientUI] preloadAssignments: session upsert threw', err);\n }\n\n const results = await Promise.allSettled(\n components.map(async ({ id, variantIds }) => {\n const res = await fetchWithTimeout(\n `${config.baseUrl}/assign`,\n { method: 'POST', headers, body: JSON.stringify({ sessionId, componentId: id, variantIds }) },\n timeoutMs,\n );\n if (!res.ok) {\n const body = await res.json().catch(() => ({}));\n console.error(`[SentientUI] preloadAssignments: assign failed for \"${id}\" (${res.status})`, body);\n return null;\n }\n const body = (await res.json()) as AssignResult;\n return { id, variantId: body.variantId };\n }),\n );\n\n const assignments: ServerAssignments = {};\n for (const result of results) {\n if (result.status === 'fulfilled' && result.value) {\n assignments[result.value.id] = result.value.variantId;\n }\n }\n return assignments;\n}\n\n/**\n * Reads the Sentient session cookie from a Next.js `ReadonlyRequestCookies` object\n * (the return value of `cookies()` from `next/headers`), or any object with a\n * `get(name: string)` method. Returns null if the cookie is absent.\n *\n * Pass the project's `apiKey`: the client writes the per-project SUFFIXED name\n * (`sessionCookieName` — see storage-key.ts), so reading only the bare\n * `_snt_uid` missed the cookie on every SSR request for a returning visitor and\n * minted a fresh orphan session each page view (quota inflation, broken sticky\n * assignments and persona continuity). The bare name is still read as a\n * fallback for identities minted before namespacing.\n */\nexport function readSessionCookie(\n cookies: { get(name: string): { value: string } | undefined },\n apiKey?: string,\n): string | null {\n return (\n (apiKey ? cookies.get(sessionCookieName(apiKey))?.value : undefined) ??\n cookies.get(LEGACY_SESSION_COOKIE_NAME)?.value ??\n null\n );\n}\n\nexport type DecideResult = {\n layoutOrder: string[];\n assignments: Record<string, string>;\n /** Slot results keyed by slot id. Baseline-resolved when the API omits/fails them. */\n slots: Record<string, SlotResult>;\n persona: string;\n confidence: number;\n};\n\n/**\n * Single-roundtrip SSR call returning layout order, component assignments,\n * and adaptive-slot results. Falls back to default section order + empty\n * assignments + baseline slots if the API is unavailable. A response without\n * a `slots` field means the server predates slots — every declared slot\n * resolves to its baseline (no retry).\n */\nexport async function preloadDecisions(\n params: {\n sections?: string[];\n components: Array<{ id: string; variantIds?: string[] }>;\n slots?: SlotDeclInput[];\n },\n sessionId: string,\n config: ServerAssignConfig,\n): Promise<DecideResult> {\n const timeoutMs = config.timeoutMs ?? DEFAULT_TIMEOUT_MS;\n const declaredSlots = params.slots ?? [];\n const fallback: DecideResult = {\n layoutOrder: params.sections ?? [],\n assignments: {},\n slots: baselineSlots(declaredSlots),\n persona: 'unknown',\n confidence: 0,\n };\n\n // Opted-out visitor (DNT/GPC): baseline layout/slots, mint no session (audit P4).\n if (config.doNotTrack) return fallback;\n\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${config.apiKey}`,\n };\n if (config.origin) headers.Origin = config.origin;\n\n const sessionBody = {\n ...buildSessionUpsertPayload(sessionId, {\n userAgent: config.userAgent,\n referer: config.referer,\n utmParams: config.utmParams,\n clickIds: config.clickIds,\n appOrigin: config.origin,\n }),\n ...(config.persona ? { persona: config.persona } : {}),\n };\n\n try {\n const sessionRes = await fetchWithTimeout(\n `${config.baseUrl}/sessions`,\n { method: 'POST', headers, body: JSON.stringify(sessionBody) },\n timeoutMs,\n );\n if (!sessionRes.ok) {\n const body = await sessionRes.json().catch(() => ({}));\n console.error(`[SentientUI] preloadDecisions: session upsert failed (${sessionRes.status})`, body);\n }\n } catch (err) {\n console.error('[SentientUI] preloadDecisions: session upsert threw', err);\n }\n\n try {\n const res = await fetchWithTimeout(\n `${config.baseUrl}/decide`,\n {\n method: 'POST',\n headers,\n body: JSON.stringify({\n sessionId,\n sections: (params.sections ?? []).map((id) => ({ id })),\n components: params.components,\n ...(declaredSlots.length > 0 ? { slots: declaredSlots.map(toWireSlot) } : {}),\n ...(config.persona ? { persona: config.persona } : {}),\n }),\n },\n timeoutMs,\n );\n if (!res.ok) {\n const body = await res.json().catch(() => ({}));\n console.error(`[SentientUI] preloadDecisions: decide failed (${res.status})`, body);\n return fallback;\n }\n const data = (await res.json()) as {\n layoutOrder?: string[];\n assignments?: Record<string, string>;\n slots?: Record<string, SlotResult>;\n persona?: string;\n confidence?: number;\n };\n\n const slots: Record<string, SlotResult> = {};\n for (const d of declaredSlots) {\n // `data.slots === undefined` ⇒ server predates slots: baseline, no retry.\n slots[d.id] = data.slots?.[d.id] ?? baselineResultFor(d);\n }\n\n return {\n layoutOrder: data.layoutOrder ?? params.sections ?? [],\n assignments: data.assignments ?? {},\n slots,\n persona: data.persona ?? 'unknown',\n confidence: data.confidence ?? 0,\n };\n } catch (err) {\n console.error('[SentientUI] preloadDecisions: decide threw', err);\n return fallback;\n }\n}\n"],"mappings":"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","names":["DEFAULT_TIMEOUT_MS","fetchWithTimeout","url","init","timeoutMs","controller","timer","__spreadProps","__spreadValues","preloadAssignments","components","sessionId","config","_a","headers","sessionBody","buildSessionUpsertPayload","res","body","err","results","id","variantIds","assignments","result","readSessionCookie","cookies","apiKey","_b","_c","_d","sessionCookieName","LEGACY_SESSION_COOKIE_NAME","preloadDecisions","params","_e","_f","_g","_h","_i","_j","_k","declaredSlots","fallback","baselineSlots","sessionRes","toWireSlot","data","slots","d","baselineResultFor"]}
|
|
1
|
+
{"version":3,"sources":["../src/server.ts"],"sourcesContent":["/**\n * Server-side helpers for SSR variant pre-loading.\n * Pure fetch — no DOM APIs. Safe in Node.js, Edge, and Deno runtimes.\n */\nimport { buildSessionUpsertPayload } from './session-meta.js';\nimport { LEGACY_SESSION_COOKIE_NAME, sessionCookieName } from './storage-key.js';\nimport {\n toWireSlot,\n baselineResultFor,\n baselineSlots,\n type SlotDeclInput,\n type SlotResult,\n} from './slots.js';\n\nimport type { SlotConfigEntry } from './snapshot.js';\nimport type { SitePalette } from './blocks.js';\n\nexport type { SlotDeclInput, SlotResult, SlotConfigEntry, SitePalette };\n\nexport type ServerAssignConfig = {\n /** Public API key (pk_...). */\n apiKey: string;\n /**\n * Base URL of the Sentient API without trailing slash.\n * e.g. 'https://api.yourapp.com/v1'\n */\n baseUrl: string;\n /**\n * Browser origin of your app (e.g. `http://localhost:3001`). Required for `pk_`\n * keys — server-side fetch must send the same `Origin` the API allows.\n */\n origin?: string;\n /** From `User-Agent` request header (Next.js `headers()`). */\n userAgent?: string;\n /** From `Referer` request header. */\n referer?: string;\n utmParams?: Record<string, string>;\n /**\n * Ad-platform click IDs (gclid, fbclid, ttclid, …) from the landing URL.\n * Derive both this and `utmParams` with `extractTrackedParams(searchParams)`\n * — the Referer header never carries the landing page's own query string, so\n * without these two fields SSR-minted sessions have no campaign attribution\n * until the client SDK hydrates.\n */\n clickIds?: Record<string, string>;\n /**\n * Set true when the request carries a tracking opt-out — `DNT: 1` or\n * `Sec-GPC: 1`. When set, the SSR helpers skip the session upsert and the\n * assign/decide call entirely: the page renders defaults/baseline and no\n * session row is minted for the visitor (audit P4). The client SDK already\n * no-ops for these visitors; this keeps the server from minting a session +\n * assignment on every page view before the client can.\n */\n doNotTrack?: boolean;\n /**\n * Declared persona — the role your app already knows for this visitor\n * (e.g. from your auth context during SSR). Same contract as the client\n * option: must be a vocabulary key; unrecognized values are ignored\n * server-side. Never a user id or email.\n */\n persona?: string;\n /**\n * Milliseconds to wait for the API before returning default variants.\n * Prevents slow/cold API from blocking SSR. Defaults to 1000 — the hot path\n * is served from in-process caches and typically returns in well under\n * 150 ms. The full 1 s budget is only reached on a cold start or an API\n * geographically distant from your SSR host, after which defaults render\n * with no layout shift. Lower it if your API is co-located and warm.\n */\n timeoutMs?: number;\n};\n\n/** componentId → assigned variantId */\nexport type ServerAssignments = Record<string, string>;\n\ntype AssignResult = { variantId: string; assignmentTtlMs: number };\n\nconst DEFAULT_TIMEOUT_MS = 1000;\n\nfunction fetchWithTimeout(\n url: string,\n init: RequestInit,\n timeoutMs: number,\n): Promise<Response> {\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), timeoutMs);\n return fetch(url, { ...init, signal: controller.signal }).finally(() =>\n clearTimeout(timer),\n );\n}\n\n/**\n * Fetches variant assignments server-side for all listed components.\n * Returns a map suitable for passing as `initialAssignments` to `<AdaptiveProvider>`.\n *\n * Call from Next.js layout, Server Component, or getServerSideProps.\n *\n * @example\n * ```tsx\n * const assignments = await preloadAssignments(\n * [\n * { id: 'hero', variantIds: ['hero-a', 'hero-b'] },\n * { id: 'cta', variantIds: ['cta-short', 'cta-long'] },\n * ],\n * sessionId, // read from cookies() or req.cookies\n * { apiKey: process.env.NEXT_PUBLIC_SENTIENT_API_KEY!, baseUrl: 'https://api.sentient-ui.com/v1' },\n * );\n * return <AdaptiveProvider ... initialAssignments={assignments}>{children}</AdaptiveProvider>;\n * ```\n */\nexport async function preloadAssignments(\n components: Array<{ id: string; variantIds: string[] }>,\n sessionId: string,\n config: ServerAssignConfig,\n): Promise<ServerAssignments> {\n // Opted-out visitor (DNT/GPC): render defaults, mint no session (audit P4).\n if (config.doNotTrack) return {};\n\n const timeoutMs = config.timeoutMs ?? DEFAULT_TIMEOUT_MS;\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${config.apiKey}`,\n };\n if (config.origin) {\n headers.Origin = config.origin;\n }\n\n // Session metadata must match the browser SDK so assign seeds variant_weights\n // under the same segment (not `unknown:unknown`).\n const sessionBody = {\n ...buildSessionUpsertPayload(sessionId, {\n userAgent: config.userAgent,\n referer: config.referer,\n utmParams: config.utmParams,\n clickIds: config.clickIds,\n appOrigin: config.origin,\n }),\n ...(config.persona ? { persona: config.persona } : {}),\n };\n\n try {\n const res = await fetchWithTimeout(\n `${config.baseUrl}/sessions`,\n { method: 'POST', headers, body: JSON.stringify(sessionBody) },\n timeoutMs,\n );\n if (res.status === 402) {\n console.warn(\n '[SentientUI] Session limit exceeded for this project. The bandit will stop learning until the limit resets. Upgrade at sentient-ui.com/pricing',\n );\n } else if (!res.ok) {\n const body = await res.json().catch(() => ({}));\n console.error(`[SentientUI] preloadAssignments: session upsert failed (${res.status})`, body);\n }\n } catch (err) {\n console.error('[SentientUI] preloadAssignments: session upsert threw', err);\n }\n\n const results = await Promise.allSettled(\n components.map(async ({ id, variantIds }) => {\n const res = await fetchWithTimeout(\n `${config.baseUrl}/assign`,\n { method: 'POST', headers, body: JSON.stringify({ sessionId, componentId: id, variantIds }) },\n timeoutMs,\n );\n if (!res.ok) {\n const body = await res.json().catch(() => ({}));\n console.error(`[SentientUI] preloadAssignments: assign failed for \"${id}\" (${res.status})`, body);\n return null;\n }\n const body = (await res.json()) as AssignResult;\n return { id, variantId: body.variantId };\n }),\n );\n\n const assignments: ServerAssignments = {};\n for (const result of results) {\n if (result.status === 'fulfilled' && result.value) {\n assignments[result.value.id] = result.value.variantId;\n }\n }\n return assignments;\n}\n\n/**\n * Reads the Sentient session cookie from a Next.js `ReadonlyRequestCookies` object\n * (the return value of `cookies()` from `next/headers`), or any object with a\n * `get(name: string)` method. Returns null if the cookie is absent.\n *\n * Pass the project's `apiKey`: the client writes the per-project SUFFIXED name\n * (`sessionCookieName` — see storage-key.ts), so reading only the bare\n * `_snt_uid` missed the cookie on every SSR request for a returning visitor and\n * minted a fresh orphan session each page view (quota inflation, broken sticky\n * assignments and persona continuity). The bare name is still read as a\n * fallback for identities minted before namespacing.\n */\nexport function readSessionCookie(\n cookies: { get(name: string): { value: string } | undefined },\n apiKey?: string,\n): string | null {\n return (\n (apiKey ? cookies.get(sessionCookieName(apiKey))?.value : undefined) ??\n cookies.get(LEGACY_SESSION_COOKIE_NAME)?.value ??\n null\n );\n}\n\nexport type DecideResult = {\n layoutOrder: string[];\n assignments: Record<string, string>;\n /** Slot results keyed by slot id. Baseline-resolved when the API omits/fails them. */\n slots: Record<string, SlotResult>;\n persona: string;\n confidence: number;\n /** Registry mode only: content/ops/blocks for server-defined slots. */\n slotConfig?: Record<string, SlotConfigEntry>;\n /** Registry mode only: site palette for block rendering. */\n palette?: SitePalette;\n};\n\n/**\n * Single-roundtrip SSR call returning layout order, component assignments,\n * and adaptive-slot results. Falls back to default section order + empty\n * assignments + baseline slots if the API is unavailable. A response without\n * a `slots` field means the server predates slots — every declared slot\n * resolves to its baseline (no retry).\n */\nexport async function preloadDecisions(\n params: {\n sections?: string[];\n components: Array<{ id: string; variantIds?: string[] }>;\n slots?: SlotDeclInput[];\n /** 'registry' serves the project's published slot_definitions as if declared\n * (and is the only way slotConfig/palette come back). Default 'request'. */\n slotsFrom?: 'request' | 'registry';\n },\n sessionId: string,\n config: ServerAssignConfig,\n): Promise<DecideResult> {\n const timeoutMs = config.timeoutMs ?? DEFAULT_TIMEOUT_MS;\n const declaredSlots = params.slots ?? [];\n const fallback: DecideResult = {\n layoutOrder: params.sections ?? [],\n assignments: {},\n slots: baselineSlots(declaredSlots),\n persona: 'unknown',\n confidence: 0,\n };\n\n // Opted-out visitor (DNT/GPC): baseline layout/slots, mint no session (audit P4).\n if (config.doNotTrack) return fallback;\n\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${config.apiKey}`,\n };\n if (config.origin) headers.Origin = config.origin;\n\n const sessionBody = {\n ...buildSessionUpsertPayload(sessionId, {\n userAgent: config.userAgent,\n referer: config.referer,\n utmParams: config.utmParams,\n clickIds: config.clickIds,\n appOrigin: config.origin,\n }),\n ...(config.persona ? { persona: config.persona } : {}),\n };\n\n try {\n const sessionRes = await fetchWithTimeout(\n `${config.baseUrl}/sessions`,\n { method: 'POST', headers, body: JSON.stringify(sessionBody) },\n timeoutMs,\n );\n if (!sessionRes.ok) {\n const body = await sessionRes.json().catch(() => ({}));\n console.error(`[SentientUI] preloadDecisions: session upsert failed (${sessionRes.status})`, body);\n }\n } catch (err) {\n console.error('[SentientUI] preloadDecisions: session upsert threw', err);\n }\n\n try {\n const res = await fetchWithTimeout(\n `${config.baseUrl}/decide`,\n {\n method: 'POST',\n headers,\n body: JSON.stringify({\n sessionId,\n sections: (params.sections ?? []).map((id) => ({ id })),\n components: params.components,\n ...(declaredSlots.length > 0 ? { slots: declaredSlots.map(toWireSlot) } : {}),\n ...(params.slotsFrom === 'registry' ? { slotsFrom: 'registry' } : {}),\n ...(config.persona ? { persona: config.persona } : {}),\n }),\n },\n timeoutMs,\n );\n if (!res.ok) {\n const body = await res.json().catch(() => ({}));\n console.error(`[SentientUI] preloadDecisions: decide failed (${res.status})`, body);\n return fallback;\n }\n const data = (await res.json()) as {\n layoutOrder?: string[];\n assignments?: Record<string, string>;\n slots?: Record<string, SlotResult>;\n slotConfig?: Record<string, SlotConfigEntry>;\n palette?: SitePalette;\n persona?: string;\n confidence?: number;\n };\n\n const slots: Record<string, SlotResult> = {};\n for (const d of declaredSlots) {\n // `data.slots === undefined` ⇒ server predates slots: baseline, no retry.\n slots[d.id] = data.slots?.[d.id] ?? baselineResultFor(d);\n }\n // Registry mode returns slots the request never declared — union them in\n // (same rule as the browser client), or an SSR page gets results for none\n // of its registry slots.\n for (const [slotId, result] of Object.entries(data.slots ?? {})) {\n if (!(slotId in slots)) slots[slotId] = result;\n }\n\n // Plain-object guard only (readSnapshot's rule): the server total-validated\n // the config at publish; a deep re-validation here would just drift.\n const slotConfigOk =\n data.slotConfig !== undefined && typeof data.slotConfig === 'object' && data.slotConfig !== null && !Array.isArray(data.slotConfig);\n\n return {\n layoutOrder: data.layoutOrder ?? params.sections ?? [],\n assignments: data.assignments ?? {},\n slots,\n persona: data.persona ?? 'unknown',\n confidence: data.confidence ?? 0,\n ...(slotConfigOk ? { slotConfig: data.slotConfig } : {}),\n ...(data.palette ? { palette: data.palette } : {}),\n };\n } catch (err) {\n console.error('[SentientUI] preloadDecisions: decide threw', err);\n return fallback;\n }\n}\n"],"mappings":"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","names":["DEFAULT_TIMEOUT_MS","fetchWithTimeout","url","init","timeoutMs","controller","timer","__spreadProps","__spreadValues","preloadAssignments","components","sessionId","config","_a","headers","sessionBody","buildSessionUpsertPayload","res","body","err","results","id","variantIds","assignments","result","readSessionCookie","cookies","apiKey","_b","_c","_d","sessionCookieName","LEGACY_SESSION_COOKIE_NAME","preloadDecisions","params","_e","_f","_g","_h","_i","_j","_k","_l","declaredSlots","fallback","baselineSlots","sessionRes","toWireSlot","data","slots","d","baselineResultFor","slotId","slotConfigOk"]}
|
package/dist/index.d.cts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export { A as
|
|
2
|
-
export { A as
|
|
1
|
+
export { A as AGENT_INTENTS, a as AgentIntent, B as BLOCK_ALIGNS, b as BLOCK_EMPHASES, c as BLOCK_FITS, d as BLOCK_GAPS, e as BLOCK_GRID_COLUMNS, f as BLOCK_HEADING_LEVELS, g as BLOCK_JUSTIFIES, h as BLOCK_RATIOS, i as BLOCK_SIZES, j as BLOCK_TEXT_ALIGNS, k as BLOCK_TONES, l as BLOCK_WEIGHTS, m as BadgeBlock, n as BlockAlign, o as BlockEmphasis, p as BlockFit, q as BlockGap, r as BlockJustify, s as BlockNode, t as BlockRatio, u as BlockSize, v as BlockTextAlign, w as BlockTone, x as BlockWeight, y as ButtonBlock, C as CLICK_ID_KEYS, z as CompoundLocator, D as DecisionSnapshot, F as FORM_FIELD_KINDS, E as FORM_INPUT_TYPES, G as FormBlock, H as FormField, I as GridBlock, J as HeadingBlock, K as ImageBlock, L as LinkBlock, M as MAX_BLOCK_ARMS, N as MAX_BLOCK_CHILDREN, O as MAX_BLOCK_DEPTH, P as MAX_BLOCK_NODES, Q as MAX_BLOCK_TEXT_LEN, R as MAX_FORM_FIELDS, S as MAX_FORM_SELECT_OPTIONS, T as SNAPSHOT_STORAGE_KEY_PREFIX, V as SitePalette, W as SlotConfigEntry, X as SlotDeclInput, Y as SlotOps, Z as SpacerBlock, _ as StackBlock, $ as TextBlock, a0 as agentIntent, a1 as agentUaList, a2 as armOfResult, a3 as baselineResultFor, a4 as baselineSlots, a6 as classifiedAgents, a7 as containsFormBlock, a8 as deriveSessionSegment, a9 as detectDeviceClass, aa as detectTimeOfDay, ab as detectTrafficSource, ac as extractTrackedParams, ad as matchedAgentToken, ae as readSnapshot, af as referrerDomainFromReferer, ag as renderPrePaintScript, ah as toWireSlot, ai as uaTokenMatch, aj as writeSnapshot } from './session-meta-dN92q19_.cjs';
|
|
2
|
+
export { A as AssignResult, a as Assignment, C as ComponentGoalOptions, c as ComponentWeightEntry, D as DecideInput, d as DecideOutcome, e as EventType, G as GoalDefinition, f as GoalOptions, h as GraphConfig, i as GraphSnapshot, L as LEGACY_SESSION_COOKIE_NAME, j as LOCAL_MODE_BANNER, M as MicroSignalEmitter, k as MicroSignalType, P as PROD_KEYLESS_ERROR, Q as QueueConfig, S as SectionMapEntry, m as SentientClient, n as SentientConfig, o as SentientEvent, p as SessionConfig, q as SessionManager, W as WeightEntry, _ as _registerConsentUpgradeInit, r as attachMicroSignalDetectors, s as grantConsent, t as init, u as isDoNotTrackEnabled, w as sessionCookieName } from './index-B5b5LlRJ.cjs';
|
|
3
3
|
export { SlotResult } from '@sentientui/policy';
|
package/dist/index.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export { A as
|
|
2
|
-
export { A as
|
|
1
|
+
export { A as AGENT_INTENTS, a as AgentIntent, B as BLOCK_ALIGNS, b as BLOCK_EMPHASES, c as BLOCK_FITS, d as BLOCK_GAPS, e as BLOCK_GRID_COLUMNS, f as BLOCK_HEADING_LEVELS, g as BLOCK_JUSTIFIES, h as BLOCK_RATIOS, i as BLOCK_SIZES, j as BLOCK_TEXT_ALIGNS, k as BLOCK_TONES, l as BLOCK_WEIGHTS, m as BadgeBlock, n as BlockAlign, o as BlockEmphasis, p as BlockFit, q as BlockGap, r as BlockJustify, s as BlockNode, t as BlockRatio, u as BlockSize, v as BlockTextAlign, w as BlockTone, x as BlockWeight, y as ButtonBlock, C as CLICK_ID_KEYS, z as CompoundLocator, D as DecisionSnapshot, F as FORM_FIELD_KINDS, E as FORM_INPUT_TYPES, G as FormBlock, H as FormField, I as GridBlock, J as HeadingBlock, K as ImageBlock, L as LinkBlock, M as MAX_BLOCK_ARMS, N as MAX_BLOCK_CHILDREN, O as MAX_BLOCK_DEPTH, P as MAX_BLOCK_NODES, Q as MAX_BLOCK_TEXT_LEN, R as MAX_FORM_FIELDS, S as MAX_FORM_SELECT_OPTIONS, T as SNAPSHOT_STORAGE_KEY_PREFIX, V as SitePalette, W as SlotConfigEntry, X as SlotDeclInput, Y as SlotOps, Z as SpacerBlock, _ as StackBlock, $ as TextBlock, a0 as agentIntent, a1 as agentUaList, a2 as armOfResult, a3 as baselineResultFor, a4 as baselineSlots, a6 as classifiedAgents, a7 as containsFormBlock, a8 as deriveSessionSegment, a9 as detectDeviceClass, aa as detectTimeOfDay, ab as detectTrafficSource, ac as extractTrackedParams, ad as matchedAgentToken, ae as readSnapshot, af as referrerDomainFromReferer, ag as renderPrePaintScript, ah as toWireSlot, ai as uaTokenMatch, aj as writeSnapshot } from './session-meta-dN92q19_.js';
|
|
2
|
+
export { A as AssignResult, a as Assignment, C as ComponentGoalOptions, c as ComponentWeightEntry, D as DecideInput, d as DecideOutcome, e as EventType, G as GoalDefinition, f as GoalOptions, h as GraphConfig, i as GraphSnapshot, L as LEGACY_SESSION_COOKIE_NAME, j as LOCAL_MODE_BANNER, M as MicroSignalEmitter, k as MicroSignalType, P as PROD_KEYLESS_ERROR, Q as QueueConfig, S as SectionMapEntry, m as SentientClient, n as SentientConfig, o as SentientEvent, p as SessionConfig, q as SessionManager, W as WeightEntry, _ as _registerConsentUpgradeInit, r as attachMicroSignalDetectors, s as grantConsent, t as init, u as isDoNotTrackEnabled, w as sessionCookieName } from './index-NqZbPgmm.js';
|
|
3
3
|
export { SlotResult } from '@sentientui/policy';
|
package/dist/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";var kt=Object.create;var fe=Object.defineProperty,bt=Object.defineProperties,Et=Object.getOwnPropertyDescriptor,It=Object.getOwnPropertyDescriptors,Ct=Object.getOwnPropertyNames,Ze=Object.getOwnPropertySymbols,_t=Object.getPrototypeOf,tt=Object.prototype.hasOwnProperty,At=Object.prototype.propertyIsEnumerable;var et=(e,t,n)=>t in e?fe(e,t,{enumerable:!0,configurable:!0,writable:!0,value:n}):e[t]=n,b=(e,t)=>{for(var n in t||(t={}))tt.call(t,n)&&et(e,n,t[n]);if(Ze)for(var n of Ze(t))At.call(t,n)&&et(e,n,t[n]);return e},se=(e,t)=>bt(e,It(t));var Rt=(e,t)=>{for(var n in t)fe(e,n,{get:t[n],enumerable:!0})},nt=(e,t,n,l)=>{if(t&&typeof t=="object"||typeof t=="function")for(let d of Ct(t))!tt.call(e,d)&&d!==n&&fe(e,d,{get:()=>t[d],enumerable:!(l=Et(t,d))||l.enumerable});return e};var Tt=(e,t,n)=>(n=e!=null?kt(_t(e)):{},nt(t||!e||!e.__esModule?fe(n,"default",{value:e,enumerable:!0}):n,e)),Ot=e=>nt(fe({},"__esModule",{value:!0}),e);var bn={};Rt(bn,{AGENT_INTENTS:()=>Be,BLOCK_ALIGNS:()=>qt,BLOCK_EMPHASES:()=>rn,BLOCK_FITS:()=>an,BLOCK_GAPS:()=>Yt,BLOCK_GRID_COLUMNS:()=>ln,BLOCK_HEADING_LEVELS:()=>cn,BLOCK_JUSTIFIES:()=>Zt,BLOCK_RATIOS:()=>sn,BLOCK_SIZES:()=>en,BLOCK_TEXT_ALIGNS:()=>on,BLOCK_TONES:()=>nn,BLOCK_WEIGHTS:()=>tn,CLICK_ID_KEYS:()=>je,LEGACY_SESSION_COOKIE_NAME:()=>Ge,LOCAL_MODE_BANNER:()=>He,MAX_BLOCK_ARMS:()=>gn,MAX_BLOCK_CHILDREN:()=>pn,MAX_BLOCK_DEPTH:()=>un,MAX_BLOCK_NODES:()=>dn,MAX_BLOCK_TEXT_LEN:()=>fn,PROD_KEYLESS_ERROR:()=>Ke,SNAPSHOT_STORAGE_KEY_PREFIX:()=>te,_registerConsentUpgradeInit:()=>mn,agentIntent:()=>ct,agentUaList:()=>ye,armOfResult:()=>Le,attachMicroSignalDetectors:()=>yt,baselineResultFor:()=>le,baselineSlots:()=>pt,classifiedAgents:()=>dt,deriveSessionSegment:()=>ut,detectDeviceClass:()=>Se,detectTimeOfDay:()=>xe,detectTrafficSource:()=>ve,extractTrackedParams:()=>Pe,grantConsent:()=>xn,init:()=>xt,isDoNotTrackEnabled:()=>Ve,matchedAgentToken:()=>We,readSnapshot:()=>ce,referrerDomainFromReferer:()=>we,renderPrePaintScript:()=>gt,sessionCookieName:()=>Ce,toWireSlot:()=>ke,uaTokenMatch:()=>he,writeSnapshot:()=>de});module.exports=Ot(bn);function Ie(){try{if(typeof crypto!="undefined"&&typeof crypto.randomUUID=="function")return crypto.randomUUID()}catch(e){}try{if(typeof crypto!="undefined"&&typeof crypto.getRandomValues=="function"){let e=new Uint8Array(16);crypto.getRandomValues(e),e[6]=e[6]&15|64,e[8]=e[8]&63|128;let t="";for(let n=0;n<16;n++)t+=e[n].toString(16).padStart(2,"0"),(n===3||n===5||n===7||n===9)&&(t+="-");return t}}catch(e){}return"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,e=>{let t=Math.random()*16|0;return(e==="x"?t:t&3|8).toString(16)})}function me(e){return e?`_${e.slice(0,12)}`:""}var Ge="_snt_uid";function Ce(e){return`${Ge}${me(e)}`}var Bt="_snt_uid",Pt=365,_e="_snt_uid";function Lt(){return Ie()}function rt(e){try{let t=document.cookie.match(new RegExp(`(?:^|; )${e}=([^;]*)`));return t?decodeURIComponent(t[1]):null}catch(t){return null}}function Dt(e,t,n){try{document.cookie=`${e}=${encodeURIComponent(t)}; max-age=${n}; SameSite=strict; path=/`}catch(l){}}function Ue(e){try{return localStorage.getItem(e)}catch(t){return null}}function ot(e,t){try{return localStorage.setItem(e,t),!0}catch(n){return!1}}function st(e){try{return sessionStorage.getItem(e)}catch(t){return null}}function Mt(e,t){try{return sessionStorage.setItem(e,t),!0}catch(n){return!1}}function Kt(e){try{sessionStorage.removeItem(e)}catch(t){}}function Nt(e){try{return document.cookie=`${e}_probe=1; max-age=1; SameSite=strict; path=/`,document.cookie.indexOf(`${e}_probe=1`)!==-1}catch(t){return!1}}function Gt(e){try{localStorage.removeItem(e)}catch(t){}}function Ut(e){try{document.cookie=`${e}=; max-age=0; SameSite=strict; path=/`}catch(t){}}var $t={getSessionId:()=>null,isEphemeral:()=>!1,destroy:()=>{}};function Ae(e){var E,R,O,I,G,$,C;if(typeof window=="undefined")return $t;let t=me(e==null?void 0:e.apiKey),n=(E=e==null?void 0:e.cookieName)!=null?E:Ce(e==null?void 0:e.apiKey),l=`${_e}${t}`,d=`${_e}_tomb${t}`,c=((R=e==null?void 0:e.cookieTTLDays)!=null?R:Pt)*24*60*60,a=w=>w&&w.length>0?w:null,p=()=>{var w,P;return t&&!(e!=null&&e.cookieName)&&Ue(d)===null?(P=(w=a(rt(Bt)))!=null?w:a(Ue(_e)))!=null?P:a(st(_e)):null},s=(C=($=(G=(I=(O=a(rt(n)))!=null?O:a(Ue(l)))!=null?I:a(st(l)))!=null?G:p())!=null?$:a(e==null?void 0:e.ssrSessionId))!=null?C:Lt();Dt(n,s,c);let r=ot(l,s),o=Nt(n),m=r?!1:Mt(l,s),S=!r&&!o&&!m;return{getSessionId:()=>s,isEphemeral:()=>S,destroy:()=>{s=null,Ut(n),Gt(l),Kt(l),t&&!(e!=null&&e.cookieName)&&ot(d,"1")}}}function X(e){return Math.min(6e4,1e3*2**Math.min(e,6))}function ie(e){if(e.ok===!0)return"delivered";let{status:t}=e;return typeof t!="number"?"retry":t>=200&&t<300?"delivered":t>=400&&t<500&&t!==429?"dropped":"retry"}function Re(e,t){try{let n=localStorage.getItem(e);if(!n)return[];let l=JSON.parse(n);return Array.isArray(l)?(localStorage.removeItem(e),l.slice(-t)):[]}catch(n){return[]}}function Y(e,t,n){try{let l=(()=>{try{let i=localStorage.getItem(n);if(!i)return[];let c=JSON.parse(i);return Array.isArray(c)?c:[]}catch(i){return[]}})(),d=new Map;for(let i of l)d.set(i.id,i);for(let i of e)d.set(i.id,i);localStorage.setItem(n,JSON.stringify([...d.values()].slice(-t)))}catch(l){}}function Te(e,t){try{let n=localStorage.getItem(t);if(!n)return;let l=JSON.parse(n);if(!Array.isArray(l))return;let d=new Set(e),i=l.filter(c=>!d.has(c.id));if(i.length===l.length)return;i.length===0?localStorage.removeItem(t):localStorage.setItem(t,JSON.stringify(i))}catch(n){}}var Ft=500,Wt=56*1024;function $e(e){return`_snt_retry_${e.slice(0,12)}`}var jt={push:()=>{},flush:()=>{},destroy:()=>{}};function it(e){var q,K,ne;if(typeof window=="undefined")return jt;let t=(q=e.flushIntervalMs)!=null?q:5e3,n=(K=e.maxBatchSize)!=null?K:20,l=(ne=e.maxRetrySize)!=null?ne:100,d=e.ingestUrl,i=e.apiKey,c=$e(i),a=[],p=new Set,s=[],r=f=>{for(let x of f)o.delete(x),!p.has(x)&&(p.add(x),s.push(x));for(;s.length>Ft;){let x=s.shift();x&&p.delete(x)}Te(f,c)},o=new Set,m=()=>{for(;a.length>l;){let f=a.shift();f&&o.delete(f.id)}},S=f=>{p.has(f.id)||o.has(f.id)||(o.add(f.id),a.push(f),m())},E=f=>{for(let x of f)p.has(x.id)||(o.add(x.id),a.push(x));m()},R=Re(c,l);for(let f of R)S(f);let O=0,I=0,G=(f,x=!0)=>{if(f.length===0)return;let j=JSON.stringify(f),Z=f.map(z=>z.id),F;try{F=fetch(d,{method:"POST",keepalive:!0,body:j,headers:{"Content-Type":"application/json",Authorization:`Bearer ${i}`}})}catch(z){Y(f,l,c),E(f),I++,O=Date.now()+X(I);return}let ue=z=>{if(ie(z)!=="retry"){if(!z.ok&&x){let re=f.filter(Q=>Q.eventType!=="pageview");if(re.length>0&&re.length<f.length){r(f.filter(Q=>Q.eventType==="pageview").map(Q=>Q.id)),G(re,!1);return}}r(Z),I=0,O=0;return}Y(f,l,c),E(f),I++,O=Date.now()+X(I)};F instanceof Promise?F.then(ue).catch(()=>{Y(f,l,c),E(f),I++,O=Date.now()+X(I)}):ue(F)},$=typeof TextEncoder!="undefined"?new TextEncoder:null,C=f=>$?$.encode(f).length:f.length,w=f=>{let x=[],j=2;for(let Z of f){let F=C(JSON.stringify(Z))+1;if(x.length>0&&j+F>Wt||x.length>=n)break;x.push(Z),j+=F}return x},P=()=>{try{if(Date.now()<O)return;for(;a.length>0&&!(Date.now()<O);){let f=a.filter(j=>!p.has(j.id));if(a.length=0,f.length===0)break;let x=w(f);if(x.length===0)break;x.length<f.length&&a.push(...f.slice(x.length)),G(x)}}catch(f){}},y=!0,A=null;A=setInterval(()=>{y&&P()},t);let M=()=>{document.visibilityState==="hidden"&&P()},W=()=>{P()};return document.addEventListener("visibilitychange",M),window.addEventListener("pagehide",W),{push(f){S(f),a.length>=n&&P()},flush:P,destroy(){y=!1,A!==null&&(clearInterval(A),A=null),document.removeEventListener("visibilitychange",M),window.removeEventListener("pagehide",W),P()}}}var Ht=200;function Fe(e){return`_snt_goal_retry_${e.slice(0,12)}`}var zt={send:()=>{},flush:()=>{},destroy:()=>{}};function at(e){var C,w,P;if(typeof window=="undefined")return zt;let t=(C=e.flushIntervalMs)!=null?C:5e3,n=(w=e.maxRetrySize)!=null?w:100,l=(P=e.maxPerFlush)!=null?P:5,d=Fe(e.apiKey),i=[],c=new Set,a=new Set,p=[],s=0,r=0,o=!1,m=y=>{for(c.delete(y.id),a.has(y.id)||(a.add(y.id),p.push(y.id));p.length>Ht;){let A=p.shift();A&&a.delete(A)}Te([y.id],d)},S=y=>{Y([y],n,d),!a.has(y.id)&&!c.has(y.id)&&(c.add(y.id),i.push(y)),Date.now()>=s&&(r++,s=Date.now()+X(r))},E=y=>{let A;try{A=fetch(e.url,{method:"POST",keepalive:!0,body:y.body,headers:e.headers})}catch(W){S(y);return}let M=W=>{var K;let q=ie(W);if(q==="retry"){S(y);return}q==="dropped"&&((K=e.onDrop)==null||K.call(e,y,W.status)),m(y),r=0,s=0};A instanceof Promise?A.then(M).catch(()=>S(y)):M(A)},R=()=>{try{if(o||Date.now()<s)return;let y=0;for(;i.length>0&&y<l&&!(Date.now()<s);){let A=i.shift();c.delete(A.id),!a.has(A.id)&&(y++,E(A))}}catch(y){}},O=Re(d,n);for(let y of O)c.has(y.id)||(c.add(y.id),i.push(y));O.length>0&&Y(O,n,d);let I=setInterval(R,t),G=()=>{document.visibilityState==="hidden"&&R()},$=()=>R();return document.addEventListener("visibilitychange",G),window.addEventListener("pagehide",$),{send(y){if(!o&&!(a.has(y.id)||c.has(y.id))){if(Date.now()<s){Y([y],n,d),c.add(y.id),i.push(y);return}E(y)}},flush:R,destroy(){clearInterval(I),document.removeEventListener("visibilitychange",G),window.removeEventListener("pagehide",$),R(),o=!0}}}var Qt=1800*1e3;function Oe(e,t){return`${encodeURIComponent(e)}:${encodeURIComponent(t)}`}function lt(e=Qt,t){let n=new Map,l=`_snt_asgn${me(t)}_`,d=(s,r)=>`${l}${encodeURIComponent(s)}:${encodeURIComponent(r)}`,i=s=>{let r=s.slice(l.length),o=r.indexOf(":");if(o<0)return null;try{return{componentId:decodeURIComponent(r.slice(0,o)),segment:decodeURIComponent(r.slice(o+1))}}catch(m){return null}},c=()=>{try{let s=[];for(let r=0;r<localStorage.length;r++){let o=localStorage.key(r);o!=null&&o.startsWith(l)&&s.push(o)}return s}catch(s){return[]}},a=s=>s.assignedAt+(s.ttlMs&&s.ttlMs>0?s.ttlMs:e)<Date.now();return typeof window!="undefined"&&(()=>{for(let s of c())try{let r=localStorage.getItem(s);if(!r)continue;let o=JSON.parse(r);if(a(o)){localStorage.removeItem(s);continue}let m=i(s);if(!m)continue;n.set(Oe(m.componentId,m.segment),o)}catch(r){}})(),{get(s,r){let o=n.get(Oe(s,r));return o?a(o)?(n.delete(Oe(s,r)),null):o:null},set(s,r,o){let m=Oe(s,r);n.set(m,o);try{localStorage.setItem(d(s,r),JSON.stringify(o))}catch(S){}},clear(){n.clear();for(let s of c())try{localStorage.removeItem(s)}catch(r){}}}}var ye=["GPTBot","ChatGPT-User","OAI-SearchBot","ClaudeBot","Claude-User","Claude-SearchBot","PerplexityBot","Perplexity-User","Google-Extended","Applebot-Extended","Meta-ExternalAgent","Bytespider","CCBot","Amazonbot","cohere-ai","Diffbot"];function he(e){return We(e)!==null}function We(e){var n;if(!e)return null;let t=e.toLowerCase();return(n=ye.find(l=>t.includes(l.toLowerCase())))!=null?n:null}var Be={GPTBot:"training","ChatGPT-User":"user","OAI-SearchBot":"search",ClaudeBot:"training","Claude-User":"user","Claude-SearchBot":"search",PerplexityBot:"search","Perplexity-User":"user","Google-Extended":"training","Applebot-Extended":"training","Meta-ExternalAgent":"training",Bytespider:"training",CCBot:"training",Amazonbot:"other","cohere-ai":"training",Diffbot:"other"};function ct(e){if(!e)return"other";let t=ye.find(n=>n.toLowerCase()===e.toLowerCase());return t&&Be[t]||"other"}function dt(){var t;let e={user:[],search:[],training:[],other:[]};for(let n of ye)e[(t=Be[n])!=null?t:"other"].push(n);return e}function Se(e){let t=e.toLowerCase();return/ipad|tablet|playbook|kindle|silk/.test(t)?"tablet":/mobi|iphone|ipod|android.*mobile|phone/.test(t)?"mobile":"desktop"}function ve(e,t){if(!e)return"direct";try{let n=new URL(e);if(t)try{if(new URL(t).host===n.host)return"direct"}catch(d){}let l=n.hostname.toLowerCase();return/(^|\.)(google|bing|duckduckgo|yahoo)\./.test(l)?"search":/(^|\.)(twitter\.com|x\.com|facebook\.com|linkedin\.com|reddit\.com|t\.co)$/.test(l)?"social":"referral"}catch(n){return"direct"}}function we(e){if(!e)return null;try{return new URL(e).hostname}catch(t){return null}}var je=["gclid","gbraid","wbraid","fbclid","ttclid","msclkid","twclid","li_fat_id"];function Pe(e){let t={},n={};try{let l=typeof e=="string"||e instanceof URLSearchParams?new URLSearchParams(e):Object.entries(e).flatMap(([d,i])=>{let c=Array.isArray(i)?i[0]:i;return c===void 0?[]:[[d,c]]});for(let[d,i]of l)d.startsWith("utm_")?d in t||(t[d]=i):je.includes(d)&&(d in n||(n[d]=i))}catch(l){}return{utmParams:t,clickIds:n}}function xe(e){let t=e.getHours();return t<6?"night":t<12?"morning":t<18?"afternoon":"evening"}function ut(e){let t=Jt("__segment__",e);return`${t.deviceClass}:${t.trafficSource}`}function Jt(e,t){var i,c,a,p,s,r,o,m;let n=(c=(i=t==null?void 0:t.userAgent)==null?void 0:i.trim())!=null?c:"",l=(p=(a=t==null?void 0:t.referer)==null?void 0:a.trim())!=null?p:"",d=(s=t==null?void 0:t.now)!=null?s:new Date;return{sessionId:e,ephemeral:!1,utmParams:(r=t==null?void 0:t.utmParams)!=null?r:{},clickIds:(o=t==null?void 0:t.clickIds)!=null?o:{},deviceClass:n?Se(n):"desktop",trafficSource:l?ve(l,t==null?void 0:t.appOrigin):"direct",referrerDomain:we(l),timeOfDay:xe(d),dayOfWeek:(m=["sun","mon","tue","wed","thu","fri","sat"][d.getDay()])!=null?m:"sun",automation:(t==null?void 0:t.webdriver)===!0||he(n)}}var ae=require("@sentientui/policy");function ke(e){return b(b(b({id:e.id},e.arms?{arms:[...e.arms]}:{}),e.dims?{dims:Object.fromEntries(Object.entries(e.dims).map(([t,n])=>[t,[...n]]))}:{}),e.baseline!==void 0?{baseline:e.baseline}:{})}function le(e){let t=ke(e);return(0,ae.slotResultFor)(t,(0,ae.slotBaselineArm)(t))}function pt(e){let t={};for(let n of e)t[n.id]=le(n);return t}function Le(e){return typeof e=="string"?e:(0,ae.canonicalArm)(e)}var te="_snt_snap:",Vt=["low","medium","high"];function ce(e){try{let t=localStorage.getItem(te+e);if(!t)return null;let n=JSON.parse(t);return!n||typeof n!="object"||n.v!==1||typeof n.persona!="string"||typeof n.band!="string"||!Vt.includes(n.band)||typeof n.slots!="object"||n.slots===null||Array.isArray(n.slots)||!(n.layoutOrder===null||Array.isArray(n.layoutOrder))||typeof n.savedAt!="number"||!(n.slotConfig===void 0||typeof n.slotConfig=="object"&&n.slotConfig!==null&&!Array.isArray(n.slotConfig))?null:n}catch(t){return null}}function de(e,t){try{localStorage.setItem(te+e,JSON.stringify(t))}catch(n){}}function gt(e){return"(function(){try{var r=localStorage.getItem("+JSON.stringify(te+e).replace(/</g,"\\u003c")+');if(!r)return;var s=JSON.parse(r);if(!s||s.v!==1||typeof s.persona!=="string"||typeof s.band!=="string")return;var d=document.documentElement;if(d.hasAttribute("data-sentient-persona"))return;d.setAttribute("data-sentient-persona",s.persona);d.setAttribute("data-sentient-confidence",s.band);}catch(e){}})();'}var Qe=require("@sentientui/policy");var Me=require("@sentientui/policy");var Ke="[sentient] No API key configured \u2014 nothing is being learned. Set NEXT_PUBLIC_SENTIENT_API_KEY or pass localMode: true for local development.",He="[sentient] Local mode \u2014 decisions are simulated on-device and nothing is sent over the network. Add an API key to learn from real traffic.",ft=!1,De=!1;function Xt(){var e;try{return(e=new URLSearchParams(window.location.search).get("sentient_persona"))!=null?e:void 0}catch(t){return}}function mt(e){var s;let t=Ae({ssrSessionId:e.ssrSessionId,apiKey:e.apiKey}),n=(s=t.getSessionId())!=null?s:"local",l=Xt(),d=import("@sentientui/core/local").then(r=>{let o=r;return o.LOCAL_ENGINE_AVAILABLE?(ft||(ft=!0,console.info(He)),o):(De||(De=!0,console.error(Ke)),null)}).catch(()=>(De||(De=!0,console.error(Ke)),null)),i=null,c={low:.15,medium:.5,high:.85},a=(()=>{var o;if(e.initialPersona)return b({},e.initialPersona);let r=ce(e.apiKey||"local");return r?{persona:r.persona,confidence:(o=c[r.band])!=null?o:.15}:null})();function p(r){let o=document.documentElement;o.dataset.sentientPersona===void 0&&(o.dataset.sentientPersona=r.persona,o.dataset.sentientConfidence=(0,Me.confidenceBand)(r.confidence))}return{isLocal:!0,async decide(r){var S,E,R;let o=await d;if(!o)return null;let m=o.createLocalEngine({sessionId:n,forcedPersona:l}).decide(r);return i=se(b({},m),{layoutOrder:(E=(S=m.layoutOrder)!=null?S:i==null?void 0:i.layoutOrder)!=null?E:null,slots:b(b({},(R=i==null?void 0:i.slots)!=null?R:{}),m.slots)}),de(e.apiKey||"local",{v:1,persona:i.persona,band:(0,Me.confidenceBand)(i.confidence),slots:i.slots,layoutOrder:i.layoutOrder,savedAt:Date.now()}),p(m),m},getSlotResult(r){var o,m,S;return(S=(m=i==null?void 0:i.slots[r])!=null?m:(o=e.initialSlots)==null?void 0:o[r])!=null?S:null},getPersona(){let r=i!=null?i:a;return r?{persona:r.persona,confidence:r.confidence,band:(0,Me.confidenceBand)(r.confidence)}:null},async assign(r,o){var E;let m=await d;return!m||!o||o.length===0?o!=null&&o[0]?{variantId:o[0],assignmentTtlMs:0}:null:{variantId:(E=m.createLocalEngine({sessionId:n,forcedPersona:l}).decide({components:[{id:r,variantIds:o}]}).assignments[r])!=null?E:o[0],assignmentTtlMs:0}},track:()=>{},goal:()=>{},componentGoal:()=>{},identify:()=>{},getAssignment:()=>null,fetchWeights:()=>Promise.resolve([]),getGraph:()=>({pageNodes:[],capturedAt:0}),dispose:()=>{},destroy:()=>t.destroy()}}var Yt=["none","sm","md","lg"],qt=["start","center","end","stretch"],Zt=["start","center","end","between"],en=["sm","md","lg"],tn=["normal","medium","bold"],nn=["default","muted","accent"],rn=["primary","secondary","ghost"],on=["left","center","right"],sn=["auto","square","landscape","wide"],an=["cover","contain"],ln=[2,3,4],cn=[2,3,4],dn=64,un=5,pn=12,gn=4,fn=500;function yt(e,t,n,l){let d=[];{let a=!1,p=[],s=()=>{if(a)return;let r=Date.now();for(p.push(r);p.length>0&&r-p[0]>500;)p.shift();p.length>=3&&(a=!0,e("rage_click"))};t.addEventListener("click",s),d.push(()=>t.removeEventListener("click",s))}{let i=!1,c=a=>{if(i||!(a.target instanceof Node)||!t.contains(a.target)&&t!==a.target)return;i=!0;let p=typeof window!="undefined"?window.getSelection():null,s=p?p.toString().length:0;e("text_copy",{selectionLength:s})};document.addEventListener("copy",c),d.push(()=>document.removeEventListener("copy",c))}{let i=!1,c=!1,a=null,p=()=>{a!==null&&(clearTimeout(a),a=null)},s=()=>{i||!c||(p(),a=setTimeout(()=>{!i&&c&&(i=!0,e("scroll_hesitation"))},3e3))},r=()=>{p(),s()},o=S=>{for(let E of S)c=E.intersectionRatio>.3,c?s():p()},m=new IntersectionObserver(o,{threshold:[.3]});m.observe(t),window.addEventListener("scroll",r,{passive:!0}),d.push(()=>{m.disconnect(),window.removeEventListener("scroll",r),p()})}if((l==null?void 0:l.tabLoss)!==!1){let i=!1,c=n!=null?n:Date.now(),a=()=>{if(i||document.visibilityState!=="hidden")return;let p=Date.now()-c;p<15e3&&(i=!0,e("tab_loss",{timeOnPage:p}))};document.addEventListener("visibilitychange",a),d.push(()=>document.removeEventListener("visibilitychange",a))}return()=>{for(let i of d)i()}}var ht="https://api.sentient-ui.com/v1/events",N=new Map,St=null;function mn(e,t){let n=N.get(e);n&&n.upgrade&&(n.reinit=t)}function yn(e){return"value"in e||"currency"in e||"externalId"in e||"metadata"in e||"weight"in e||"stepIndex"in e}var hn=3;function ze(){return Ie()}function Ne(){var e;if(typeof window!="undefined")return((e=window.location)==null?void 0:e.pathname)||void 0}function Sn(){let e=new WeakMap,t=new Map,n=!1,l=(a,p,s)=>{let r=a.get(p);return s===null?r?!0:(a.set(p,new Set),!1):r?r.has(s)?!0:(r.add(s),!1):(a.set(p,new Set([s])),!1)},d=typeof window!="undefined"&&"event"in window,i=()=>{if(!d)return;let a=window.event;return typeof Event=="function"&&a instanceof Event?a:void 0},c=()=>{if(d){Promise.resolve().then(()=>{n=!1,t.clear()});return}let a=!1,p=()=>{a||(a=!0,n=!1,t.clear())};if(setTimeout(p,0),typeof MessageChannel=="function"){let s=new MessageChannel;s.port1.onmessage=()=>{s.port1.close(),s.port2.close(),p()},s.port2.postMessage(0)}};return{firedBefore(a,p){let s=i();if(s){let o=e.get(s);return o||(o=new Map,e.set(s,o)),l(o,a,p)}let r=l(t,a,p);return!r&&!n&&(n=!0,c()),r}}}var vt=new Set;function vn(e,t,n){let l=typeof window=="undefined"?null:window.history;if(!l)return()=>{};let d=!1,i,c=()=>{if(d)return;let s=Ne();!s||s===i||(i=s,e.track({projectId:t,componentId:"__page__",eventType:"pageview",payload:{}}),n(`${t}:${s}`))},a=[];for(let s of["pushState","replaceState"]){let r=l[s],o=function(...m){let S=r.apply(this,m);return c(),S};l[s]=o,a.push([s,r,o])}window.addEventListener("popstate",c);let p=Ne();return p&&vt.has(`${t}:${p}`)?i=p:c(),()=>{if(!d){d=!0,window.removeEventListener("popstate",c);for(let[s,r,o]of a)l[s]===o&&(l[s]=r)}}}var be={track:()=>{},goal:()=>{},componentGoal:()=>{},identify:()=>{},getAssignment:()=>null,assign:()=>Promise.resolve(null),decide:()=>Promise.resolve(null),getSlotResult:()=>null,getPersona:()=>null,fetchWeights:()=>Promise.resolve([]),getGraph:()=>({pageNodes:[],capturedAt:0}),dispose:()=>{},destroy:()=>{}};function wn(){try{return Pe(window.location.search)}catch(e){return{utmParams:{},clickIds:{}}}}function wt(e){return e.replace(/\/events\/?$/,"")}function Ve(){return typeof navigator!="undefined"&&navigator.globalPrivacyControl===!0?!0:[typeof navigator!="undefined"?navigator.doNotTrack:void 0,typeof window!="undefined"?window.doNotTrack:void 0,typeof navigator!="undefined"?navigator.msDoNotTrack:void 0].some(t=>t==="1"||t==="yes")}function xn(e){var p;if(typeof window=="undefined")return;let t=e!=null?e:St;if(!t){console.warn("[sentient] grantConsent() called before init()");return}let n=N.get(t);if(!n){console.warn("[sentient] grantConsent() called before init()");return}let{config:l,upgrade:d,reinit:i}=n;if(!d){n.upgradeBlockedReason&&console.warn(n.upgradeBlockedReason);return}if(l.respectDoNotTrack!==!1&&Ve())return;let c=(i!=null?i:xt)(se(b({},l),{consent:!0}));d(c);let a=(p=N.get(t))==null?void 0:p.dispose;N.set(t,{config:se(b({},l),{consent:!0}),upgrade:null,dispose:a})}function kn(e){var s;let t=e.preConsentBehavior==="statistical_winner",n=wt((s=e.ingestUrl)!=null?s:ht),l={"Content-Type":"application/json",Authorization:`Bearer ${e.apiKey}`},d=new Map,i=new Map,c={track:()=>{},goal:()=>{},componentGoal:()=>{},identify:()=>{},getAssignment:()=>null,fetchWeights:()=>Promise.resolve([]),assign(r,o,m){if(!t)return Promise.resolve(null);let S=d.get(r);return S?Promise.resolve(S):Je(i,r,async()=>{try{let E=new URLSearchParams({componentId:r});for(let G of o!=null?o:[])E.append("variantIds[]",G);let R=await fetch(`${n}/winner?${E.toString()}`,{headers:l});if(!R.ok)return o!=null&&o[0]?{variantId:o[0],assignmentTtlMs:0}:null;let I={variantId:(await R.json()).variantId,assignmentTtlMs:0};return d.set(r,I),I}catch(E){return o!=null&&o[0]?{variantId:o[0],assignmentTtlMs:0}:null}})},decide:()=>Promise.resolve(null),getSlotResult:()=>null,getPersona:()=>null,getGraph:()=>({pageNodes:[],capturedAt:0}),dispose:()=>{},destroy:()=>{}},a={track:r=>c.track(r),goal:((r,o,m,S)=>c.goal(r,o,m,S)),componentGoal:(r,o,m)=>c.componentGoal(r,o,m),identify:r=>c.identify(r),getAssignment:(r,o)=>c.getAssignment(r,o),assign:(r,o,m,S)=>c.assign(r,o,m,S),decide:r=>c.decide(r),getSlotResult:r=>c.getSlotResult(r),getPersona:()=>c.getPersona(),fetchWeights:()=>c.fetchWeights(),getGraph:()=>c.getGraph(),dispose:()=>c.dispose(),destroy:()=>c.destroy()};function p(r){c=r}return{proxy:a,setInner:p}}function Je(e,t,n){let l=e.get(t);if(l)return l;let d=(async()=>{try{return await n()}finally{e.delete(t)}})();return e.set(t,d),d}function xt(e){var x,j,Z,F,ue,z,re,Q,Xe;if(typeof window=="undefined")return be;St=e.apiKey||"local";let t=N.get(e.apiKey||"local");if(t!=null&&t.dispose)try{t.dispose()}catch(u){}let n=e.respectDoNotTrack!==!1&&Ve(),l=e.consent===!1||n,d=typeof e.apiKey=="string"&&e.apiKey.startsWith("pk_");if(e.localMode===!0||!d&&e.localMode!==!1)return N.set(e.apiKey||"local",{config:e,upgrade:null,upgradeBlockedReason:"[sentient] grantConsent(): this client is keyless/local \u2014 there is no hosted client to upgrade to. Configure a pk_ API key to enable tracking."}),l?be:mt(e);if(l){if(!e.apiKey||!e.apiKey.startsWith("pk_"))return e.preConsentBehavior==="statistical_winner"&&console.warn("[sentient] init() called with an invalid apiKey \u2014 expected a pk_ public key. SDK disabled."),N.set(e.apiKey||"local",{config:e,upgrade:null,upgradeBlockedReason:"[sentient] grantConsent(): the client was initialized with an invalid apiKey (expected a pk_ public key) \u2014 consent cannot enable tracking."}),be;let{proxy:u,setInner:g}=kn(e);return N.set(e.apiKey,{config:e,upgrade:n?null:g}),u}if(!e.apiKey||!e.apiKey.startsWith("pk_"))return console.warn("[sentient] init() called with an invalid apiKey \u2014 expected a pk_ public key. SDK disabled."),be;if(e.ingestUrl==="")return console.warn("[sentient] init() called with an empty ingestUrl. SDK disabled."),be;let i=(x=e.ingestUrl)!=null?x:ht,c=Date.now(),a=Ae({ssrSessionId:e.ssrSessionId,apiKey:e.apiKey}),p=lt(void 0,e.apiKey),s=it({ingestUrl:i,apiKey:e.apiKey}),r=wt(i),o={"Content-Type":"application/json",Authorization:`Bearer ${e.apiKey}`},m=new Set,S=at({url:`${r}/goals`,apiKey:e.apiKey,headers:o,onDrop:(u,g)=>{if(!e.debug){if(m.has(g))return;m.add(g)}console.warn(`[sentient] goal dropped (HTTP ${g}) \u2014 this will not be retried. `+(g===400?"The session was not found: call init() and let the session upsert complete before firing goals.":g===401||g===403?"Check the API key and that this origin is on the project allowlist.":"See the response status for the cause."),u)}}),E=Se((j=navigator.userAgent)!=null?j:""),R=window.location.origin,O=ve((Z=document.referrer)!=null?Z:"",R),I=(F=e.sessionSegment)!=null?F:`${E}:${O}`,G=new Map,$=new Map,C=new Map,w=null,P=u=>{for(let g of u)C.has(g.id)||C.set(g.id,le(g))};if(e.initialSlots)for(let[u,g]of Object.entries(e.initialSlots))C.set(u,g);let y=ce(e.apiKey);if(y)for(let[u,g]of Object.entries(y.slots))C.has(u)||C.set(u,g);let A={low:.15,medium:.5,high:.85};if(e.initialPersona)w=b({},e.initialPersona);else{let u=document.documentElement.dataset;u.sentientPersona?w={persona:u.sentientPersona,confidence:(z=A[(ue=u.sentientConfidence)!=null?ue:"low"])!=null?z:.15}:y&&(w={persona:y.persona,confidence:(re=A[y.band])!=null?re:.15})}if(e.initialAssignments)for(let[u,g]of Object.entries(e.initialAssignments))p.set(u,I,{variantId:g,assignedAt:Date.now(),segment:I,confidence:1});let M=Promise.resolve(),W=a.getSessionId();if(W){let u=we((Q=document.referrer)!=null?Q:""),{utmParams:g,clickIds:v}=wn(),T=b(b(b({sessionId:W,deviceClass:E,trafficSource:O,referrerDomain:u,utmParams:g,clickIds:v,timeOfDay:xe(new Date),dayOfWeek:["sun","mon","tue","wed","thu","fri","sat"][new Date().getDay()],ephemeral:a.isEphemeral(),automation:typeof navigator!="undefined"&&navigator.webdriver===!0||he((Xe=navigator.userAgent)!=null?Xe:"")},e.userId?{userId:e.userId}:{}),e.persona?{persona:e.persona}:{}),e.country?{country:e.country}:{}),L=async()=>{for(let h=0;;h++){try{let _=await fetch(`${r}/sessions`,{method:"POST",keepalive:!0,body:JSON.stringify(T),headers:o});if(_.status===402){console.warn("[SentientUI] Session limit exceeded for this project. The bandit will stop learning until the limit resets. Upgrade at sentient-ui.com/pricing");return}if(_.ok||ie(_)==="dropped")return}catch(_){}if(h>=hn){console.warn("[SentientUI] Could not register the session after retries. Conversions in this visit may not be recorded.");return}await new Promise(_=>setTimeout(_,X(h+1)))}};try{M=L()}catch(h){}}e.debug&&(console.log("[sentient] initialized",{context:e.context}),window.__sentient={client:null,queue:s});let q=Sn(),K=null,ne=!1,f={goal(u,g={},v=1,T=0){var pe,ge,oe,k,H,Ee,B;let L=a.getSessionId();if(!L)return;let h=yn(g)?g:{metadata:g},_=[u,(pe=h.externalId)!=null?pe:"",(ge=h.stepIndex)!=null?ge:T,(oe=h.weight)!=null?oe:v].join("\0"),U=h.value!==void 0?`${h.value}\0${(k=h.currency)!=null?k:""}`:null;if(q.firedBefore(_,U)){e.debug&&console.log(`[sentient] goal("${u}") already recorded for this action \u2014 not sent twice`);return}let D=ze(),J={sessionId:L,name:u,metadata:(H=h.metadata)!=null?H:{},weight:(Ee=h.weight)!=null?Ee:v,stepIndex:(B=h.stepIndex)!=null?B:T,goalId:D,value:h.value,currency:h.currency,externalId:h.externalId};e.debug&&console.log("[sentient] goal",J);let ee={id:D,body:JSON.stringify(J)};M.then(()=>S.send(ee))},componentGoal(u,g,v){var D,J,ee;let T=a.getSessionId();if(!T)return;let L=p.get(u,I),h=L?null:(D=C.get(u))!=null?D:null;if(!L&&h===null){e.debug&&console.warn(`[sentient] componentGoal("${u}"): no assignment or slot decision yet \u2014 render its <Adaptive>/adaptive hook or call assign()/decide() before recording a goal.`);return}let _=L?L.variantId:Le(h),U={id:ze(),sessionId:T,projectId:e.apiKey,componentId:u,variantId:_,eventType:"goal_achieved",goalType:g,payload:b({reward:(J=v==null?void 0:v.reward)!=null?J:1,goalValue:v==null?void 0:v.value,currency:v==null?void 0:v.currency},(ee=v==null?void 0:v.metadata)!=null?ee:{}),timestamp:Date.now(),timeInSession:Date.now()-c,path:Ne()};e.debug&&console.log("[sentient] componentGoal",U),M.then(()=>s.push(U))},identify(u){let g=a.getSessionId();g&&M.then(()=>{fetch(`${r}/sessions`,{method:"POST",keepalive:!0,body:JSON.stringify({sessionId:g,userId:u,ephemeral:a.isEphemeral()}),headers:o}).catch(()=>{})})},track(u){let g=a.getSessionId();if(!g)return;let v=se(b({path:Ne()},u),{id:ze(),sessionId:g,timestamp:Date.now(),timeInSession:Date.now()-c});e.debug&&console.log("[sentient] track",v),M.then(()=>s.push(v))},getAssignment(u,g){return p.get(u,g)},async assign(u,g,v,T){let L=a.getSessionId();if(!L)return null;let h=p.get(u,I);if(h&&(g!=null&&g.length||h.content!==void 0)){let _=h.ttlMs&&h.ttlMs>0?Math.max(0,h.assignedAt+h.ttlMs-Date.now()):0;return{variantId:h.variantId,assignmentTtlMs:_,content:h.content}}return Je(G,u,async()=>{await M;try{let _={sessionId:L,componentId:u,variantIds:g};T!==void 0?_.agentDataByVariant=T:v!==void 0&&(_.agentData=v);let U=await fetch(`${r}/assign`,{method:"POST",body:JSON.stringify(_),headers:o});if(!U.ok)return null;let D=await U.json();return p.set(u,I,b({variantId:D.variantId,assignedAt:Date.now(),segment:I,confidence:1,content:D.content},D.assignmentTtlMs&&D.assignmentTtlMs>0?{ttlMs:D.assignmentTtlMs}:{})),D}catch(_){return null}})},async decide(u){var h,_;let g=a.getSessionId();if(!g)return null;let v=(h=u.slots)!=null?h:[],T={sessionId:g};u.sections&&u.sections.length>0&&(T.sections=u.sections.map(U=>({id:U}))),T.components=(_=u.components)!=null?_:[],v.length>0&&(T.slots=v.map(ke)),u.slotsFrom==="registry"&&(T.slotsFrom="registry"),u.v&&(T.v=u.v),e.persona&&(T.persona=e.persona);let L=JSON.stringify(T);return Je($,L,async()=>{var U,D,J,ee,pe,ge;await M;try{let oe=await fetch(`${r}/decide`,{method:"POST",body:L,headers:o});if(!oe.ok)return P(v),null;let k=await oe.json(),H={};for(let B of v){let V=(U=k.slots)==null?void 0:U[B.id];if(V!==void 0){H[B.id]=V,C.set(B.id,V);continue}let Ye=C.get(B.id);if(Ye!==void 0)H[B.id]=Ye;else{let qe=le(B);H[B.id]=qe,C.set(B.id,qe)}}if(k.slots)for(let[B,V]of Object.entries(k.slots))B in H||(H[B]=V,C.set(B,V));let Ee=w!=null&&w.persona!=="unknown";k.persona&&!(k.persona==="unknown"&&Ee)?w={persona:k.persona,confidence:(D=k.confidence)!=null?D:0}:w||(w={persona:"unknown",confidence:0});for(let[B,V]of Object.entries((J=k.assignments)!=null?J:{}))p.set(B,I,{variantId:V,assignedAt:Date.now(),segment:I,confidence:1});return de(e.apiKey,b(b({v:1,persona:w.persona,band:(0,Qe.confidenceBand)(w.confidence),slots:Object.fromEntries(C),layoutOrder:(ee=k.layoutOrder)!=null?ee:null,savedAt:Date.now()},k.slotConfig?{slotConfig:k.slotConfig}:{}),k.palette?{palette:k.palette}:{})),b(b(b(b({layoutOrder:(pe=k.layoutOrder)!=null?pe:null,assignments:(ge=k.assignments)!=null?ge:{},slots:H,persona:w.persona,confidence:w.confidence},k.slotConfig?{slotConfig:k.slotConfig}:{}),k.goals?{goals:k.goals}:{}),k.sectionMap?{sectionMap:k.sectionMap}:{}),k.palette?{palette:k.palette}:{})}catch(oe){return P(v),null}})},getSlotResult(u){var g;return(g=C.get(u))!=null?g:null},getPersona(){return w?{persona:w.persona,confidence:w.confidence,band:(0,Qe.confidenceBand)(w.confidence)}:null},async fetchWeights(){var u;try{let g=await fetch(`${r}/weights`,{headers:o});return g.ok?(u=(await g.json()).components)!=null?u:[]:[]}catch(g){return[]}},getGraph(){return{pageNodes:[],capturedAt:0}},dispose(){var u;K==null||K(),ne=!0,s.destroy(),S.destroy(),((u=N.get(e.apiKey))==null?void 0:u.dispose)===f.dispose&&N.delete(e.apiKey),e.debug&&console.log("[sentient] disposed")},destroy(){var u;K==null||K(),ne=!0,s.destroy(),S.destroy(),a.destroy(),p.clear(),((u=N.get(e.apiKey))==null?void 0:u.dispose)===f.dispose&&N.delete(e.apiKey);try{localStorage.removeItem(te+e.apiKey),localStorage.removeItem($e(e.apiKey)),localStorage.removeItem(Fe(e.apiKey))}catch(g){}e.debug&&console.log("[sentient] destroyed")}};if(N.set(e.apiKey,{config:e,upgrade:null,dispose:f.dispose}),K=vn(f,e.apiKey,u=>{M.then(()=>{ne||vt.add(u)})}),e.debug){let u=window;u.__sentient&&(u.__sentient.client=f)}return f}0&&(module.exports={AGENT_INTENTS,BLOCK_ALIGNS,BLOCK_EMPHASES,BLOCK_FITS,BLOCK_GAPS,BLOCK_GRID_COLUMNS,BLOCK_HEADING_LEVELS,BLOCK_JUSTIFIES,BLOCK_RATIOS,BLOCK_SIZES,BLOCK_TEXT_ALIGNS,BLOCK_TONES,BLOCK_WEIGHTS,CLICK_ID_KEYS,LEGACY_SESSION_COOKIE_NAME,LOCAL_MODE_BANNER,MAX_BLOCK_ARMS,MAX_BLOCK_CHILDREN,MAX_BLOCK_DEPTH,MAX_BLOCK_NODES,MAX_BLOCK_TEXT_LEN,PROD_KEYLESS_ERROR,SNAPSHOT_STORAGE_KEY_PREFIX,_registerConsentUpgradeInit,agentIntent,agentUaList,armOfResult,attachMicroSignalDetectors,baselineResultFor,baselineSlots,classifiedAgents,deriveSessionSegment,detectDeviceClass,detectTimeOfDay,detectTrafficSource,extractTrackedParams,grantConsent,init,isDoNotTrackEnabled,matchedAgentToken,readSnapshot,referrerDomainFromReferer,renderPrePaintScript,sessionCookieName,toWireSlot,uaTokenMatch,writeSnapshot});
|
|
1
|
+
"use strict";var Rt=Object.create;var fe=Object.defineProperty,Tt=Object.defineProperties,Ot=Object.getOwnPropertyDescriptor,Pt=Object.getOwnPropertyDescriptors,Bt=Object.getOwnPropertyNames,st=Object.getOwnPropertySymbols,Lt=Object.getPrototypeOf,at=Object.prototype.hasOwnProperty,Dt=Object.prototype.propertyIsEnumerable;var it=(e,t,n)=>t in e?fe(e,t,{enumerable:!0,configurable:!0,writable:!0,value:n}):e[t]=n,k=(e,t)=>{for(var n in t||(t={}))at.call(t,n)&&it(e,n,t[n]);if(st)for(var n of st(t))Dt.call(t,n)&&it(e,n,t[n]);return e},se=(e,t)=>Tt(e,Pt(t));var Mt=(e,t)=>{for(var n in t)fe(e,n,{get:t[n],enumerable:!0})},lt=(e,t,n,d)=>{if(t&&typeof t=="object"||typeof t=="function")for(let p of Bt(t))!at.call(e,p)&&p!==n&&fe(e,p,{get:()=>t[p],enumerable:!(d=Ot(t,p))||d.enumerable});return e};var Nt=(e,t,n)=>(n=e!=null?Rt(Lt(e)):{},lt(t||!e||!e.__esModule?fe(n,"default",{value:e,enumerable:!0}):n,e)),Kt=e=>lt(fe({},"__esModule",{value:!0}),e);var Ln={};Mt(Ln,{AGENT_INTENTS:()=>Pe,BLOCK_ALIGNS:()=>sn,BLOCK_EMPHASES:()=>un,BLOCK_FITS:()=>fn,BLOCK_GAPS:()=>on,BLOCK_GRID_COLUMNS:()=>mn,BLOCK_HEADING_LEVELS:()=>yn,BLOCK_JUSTIFIES:()=>an,BLOCK_RATIOS:()=>gn,BLOCK_SIZES:()=>ln,BLOCK_TEXT_ALIGNS:()=>pn,BLOCK_TONES:()=>dn,BLOCK_WEIGHTS:()=>cn,CLICK_ID_KEYS:()=>We,FORM_FIELD_KINDS:()=>Sn,FORM_INPUT_TYPES:()=>hn,LEGACY_SESSION_COOKIE_NAME:()=>Ge,LOCAL_MODE_BANNER:()=>He,MAX_BLOCK_ARMS:()=>In,MAX_BLOCK_CHILDREN:()=>xn,MAX_BLOCK_DEPTH:()=>kn,MAX_BLOCK_NODES:()=>bn,MAX_BLOCK_TEXT_LEN:()=>En,MAX_FORM_FIELDS:()=>vn,MAX_FORM_SELECT_OPTIONS:()=>wn,PROD_KEYLESS_ERROR:()=>Ne,SNAPSHOT_STORAGE_KEY_PREFIX:()=>ne,_registerConsentUpgradeInit:()=>Cn,agentIntent:()=>mt,agentUaList:()=>ye,armOfResult:()=>Le,attachMicroSignalDetectors:()=>xt,baselineResultFor:()=>le,baselineSlots:()=>ht,classifiedAgents:()=>yt,containsFormBlock:()=>kt,deriveSessionSegment:()=>St,detectDeviceClass:()=>he,detectTimeOfDay:()=>be,detectTrafficSource:()=>ve,extractTrackedParams:()=>Be,grantConsent:()=>Pn,init:()=>At,isDoNotTrackEnabled:()=>Xe,matchedAgentToken:()=>je,readSnapshot:()=>ce,referrerDomainFromReferer:()=>we,renderPrePaintScript:()=>vt,sessionCookieName:()=>Ce,toWireSlot:()=>ke,uaTokenMatch:()=>Se,writeSnapshot:()=>de});module.exports=Kt(Ln);function Ee(){try{if(typeof crypto!="undefined"&&typeof crypto.randomUUID=="function")return crypto.randomUUID()}catch(e){}try{if(typeof crypto!="undefined"&&typeof crypto.getRandomValues=="function"){let e=new Uint8Array(16);crypto.getRandomValues(e),e[6]=e[6]&15|64,e[8]=e[8]&63|128;let t="";for(let n=0;n<16;n++)t+=e[n].toString(16).padStart(2,"0"),(n===3||n===5||n===7||n===9)&&(t+="-");return t}}catch(e){}return"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,e=>{let t=Math.random()*16|0;return(e==="x"?t:t&3|8).toString(16)})}function me(e){return e?`_${e.slice(0,12)}`:""}var Ge="_snt_uid";function Ce(e){return`${Ge}${me(e)}`}var Gt="_snt_uid",Ut=365,_e="_snt_uid";function Ft(){return Ee()}function ct(e){try{let t=document.cookie.match(new RegExp(`(?:^|; )${e}=([^;]*)`));return t?decodeURIComponent(t[1]):null}catch(t){return null}}function $t(e,t,n){try{document.cookie=`${e}=${encodeURIComponent(t)}; max-age=${n}; SameSite=strict; path=/`}catch(d){}}function Ue(e){try{return localStorage.getItem(e)}catch(t){return null}}function dt(e,t){try{return localStorage.setItem(e,t),!0}catch(n){return!1}}function ut(e){try{return sessionStorage.getItem(e)}catch(t){return null}}function jt(e,t){try{return sessionStorage.setItem(e,t),!0}catch(n){return!1}}function Wt(e){try{sessionStorage.removeItem(e)}catch(t){}}function Ht(e){try{return document.cookie=`${e}_probe=1; max-age=1; SameSite=strict; path=/`,document.cookie.indexOf(`${e}_probe=1`)!==-1}catch(t){return!1}}function zt(e){try{localStorage.removeItem(e)}catch(t){}}function Qt(e){try{document.cookie=`${e}=; max-age=0; SameSite=strict; path=/`}catch(t){}}var Jt={getSessionId:()=>null,isEphemeral:()=>!1,destroy:()=>{}};function Ae(e){var x,P,B,I,K,$,E;if(typeof window=="undefined")return Jt;let t=me(e==null?void 0:e.apiKey),n=(x=e==null?void 0:e.cookieName)!=null?x:Ce(e==null?void 0:e.apiKey),d=`${_e}${t}`,p=`${_e}_tomb${t}`,c=((P=e==null?void 0:e.cookieTTLDays)!=null?P:Ut)*24*60*60,a=A=>A&&A.length>0?A:null,g=()=>{var A,C;return t&&!(e!=null&&e.cookieName)&&Ue(p)===null?(C=(A=a(ct(Gt)))!=null?A:a(Ue(_e)))!=null?C:a(ut(_e)):null},s=(E=($=(K=(I=(B=a(ct(n)))!=null?B:a(Ue(d)))!=null?I:a(ut(d)))!=null?K:g())!=null?$:a(e==null?void 0:e.ssrSessionId))!=null?E:Ft();$t(n,s,c);let r=dt(d,s),o=Ht(n),y=r?!1:jt(d,s),h=!r&&!o&&!y;return{getSessionId:()=>s,isEphemeral:()=>h,destroy:()=>{s=null,Qt(n),zt(d),Wt(d),t&&!(e!=null&&e.cookieName)&&dt(p,"1")}}}function Z(e){return Math.min(6e4,1e3*2**Math.min(e,6))}function ie(e){if(e.ok===!0)return"delivered";let{status:t}=e;return typeof t!="number"?"retry":t>=200&&t<300?"delivered":t>=400&&t<500&&t!==429?"dropped":"retry"}function Re(e,t){try{let n=localStorage.getItem(e);if(!n)return[];let d=JSON.parse(n);return Array.isArray(d)?(localStorage.removeItem(e),d.slice(-t)):[]}catch(n){return[]}}function ee(e,t,n){try{let d=(()=>{try{let i=localStorage.getItem(n);if(!i)return[];let c=JSON.parse(i);return Array.isArray(c)?c:[]}catch(i){return[]}})(),p=new Map;for(let i of d)p.set(i.id,i);for(let i of e)p.set(i.id,i);localStorage.setItem(n,JSON.stringify([...p.values()].slice(-t)))}catch(d){}}function Te(e,t){try{let n=localStorage.getItem(t);if(!n)return;let d=JSON.parse(n);if(!Array.isArray(d))return;let p=new Set(e),i=d.filter(c=>!p.has(c.id));if(i.length===d.length)return;i.length===0?localStorage.removeItem(t):localStorage.setItem(t,JSON.stringify(i))}catch(n){}}var Xt=500,Vt=56*1024;function Fe(e){return`_snt_retry_${e.slice(0,12)}`}var Yt={push:()=>{},flush:()=>{},destroy:()=>{}};function pt(e){var J,M,ue;if(typeof window=="undefined")return Yt;let t=(J=e.flushIntervalMs)!=null?J:5e3,n=(M=e.maxBatchSize)!=null?M:20,d=(ue=e.maxRetrySize)!=null?ue:100,p=e.ingestUrl,i=e.apiKey,c=Fe(i),a=[],g=new Set,s=[],r=m=>{for(let w of m)o.delete(w),!g.has(w)&&(g.add(w),s.push(w));for(;s.length>Xt;){let w=s.shift();w&&g.delete(w)}Te(m,c)},o=new Set,y=()=>{for(;a.length>d;){let m=a.shift();m&&o.delete(m.id)}},h=m=>{g.has(m.id)||o.has(m.id)||(o.add(m.id),a.push(m),y())},x=m=>{for(let w of m)g.has(w.id)||(o.add(w.id),a.push(w));y()},P=Re(c,d);for(let m of P)h(m);let B=0,I=0,K=(m,w=!0)=>{if(m.length===0)return;let X=JSON.stringify(m),G=m.map(V=>V.id),F;try{F=fetch(p,{method:"POST",keepalive:!0,body:X,headers:{"Content-Type":"application/json",Authorization:`Bearer ${i}`}})}catch(V){ee(m,d,c),x(m),I++,B=Date.now()+Z(I);return}let z=V=>{if(ie(V)!=="retry"){if(!V.ok&&w){let re=m.filter(Y=>Y.eventType!=="pageview");if(re.length>0&&re.length<m.length){r(m.filter(Y=>Y.eventType==="pageview").map(Y=>Y.id)),K(re,!1);return}}r(G),I=0,B=0;return}ee(m,d,c),x(m),I++,B=Date.now()+Z(I)};F instanceof Promise?F.then(z).catch(()=>{ee(m,d,c),x(m),I++,B=Date.now()+Z(I)}):z(F)},$=typeof TextEncoder!="undefined"?new TextEncoder:null,E=m=>$?$.encode(m).length:m.length,A=m=>{let w=[],X=2;for(let G of m){let F=E(JSON.stringify(G))+1;if(w.length>0&&X+F>Vt||w.length>=n)break;w.push(G),X+=F}return w},C=()=>{try{if(Date.now()<B)return;for(;a.length>0&&!(Date.now()<B);){let m=a.filter(X=>!g.has(X.id));if(a.length=0,m.length===0)break;let w=A(m);if(w.length===0)break;w.length<m.length&&a.push(...m.slice(w.length)),K(w)}}catch(m){}},f=!0,R=null;R=setInterval(()=>{f&&C()},t);let j=()=>{document.visibilityState==="hidden"&&C()},W=()=>{C()};return document.addEventListener("visibilitychange",j),window.addEventListener("pagehide",W),{push(m){h(m),a.length>=n&&C()},flush:C,destroy(){f=!1,R!==null&&(clearInterval(R),R=null),document.removeEventListener("visibilitychange",j),window.removeEventListener("pagehide",W),C()}}}var qt=200;function $e(e){return`_snt_goal_retry_${e.slice(0,12)}`}var Zt={send:()=>{},flush:()=>{},destroy:()=>{}};function gt(e){var E,A,C;if(typeof window=="undefined")return Zt;let t=(E=e.flushIntervalMs)!=null?E:5e3,n=(A=e.maxRetrySize)!=null?A:100,d=(C=e.maxPerFlush)!=null?C:5,p=$e(e.apiKey),i=[],c=new Set,a=new Set,g=[],s=0,r=0,o=!1,y=f=>{for(c.delete(f.id),a.has(f.id)||(a.add(f.id),g.push(f.id));g.length>qt;){let R=g.shift();R&&a.delete(R)}Te([f.id],p)},h=f=>{ee([f],n,p),!a.has(f.id)&&!c.has(f.id)&&(c.add(f.id),i.push(f)),Date.now()>=s&&(r++,s=Date.now()+Z(r))},x=f=>{let R;try{R=fetch(e.url,{method:"POST",keepalive:!0,body:f.body,headers:e.headers})}catch(W){h(f);return}let j=W=>{var M;let J=ie(W);if(J==="retry"){h(f);return}J==="dropped"&&((M=e.onDrop)==null||M.call(e,f,W.status)),y(f),r=0,s=0};R instanceof Promise?R.then(j).catch(()=>h(f)):j(R)},P=()=>{try{if(o||Date.now()<s)return;let f=0;for(;i.length>0&&f<d&&!(Date.now()<s);){let R=i.shift();c.delete(R.id),!a.has(R.id)&&(f++,x(R))}}catch(f){}},B=Re(p,n);for(let f of B)c.has(f.id)||(c.add(f.id),i.push(f));B.length>0&&ee(B,n,p);let I=setInterval(P,t),K=()=>{document.visibilityState==="hidden"&&P()},$=()=>P();return document.addEventListener("visibilitychange",K),window.addEventListener("pagehide",$),{send(f){if(!o&&!(a.has(f.id)||c.has(f.id))){if(Date.now()<s){ee([f],n,p),c.add(f.id),i.push(f);return}x(f)}},flush:P,destroy(){clearInterval(I),document.removeEventListener("visibilitychange",K),window.removeEventListener("pagehide",$),P(),o=!0}}}var en=1800*1e3;function Oe(e,t){return`${encodeURIComponent(e)}:${encodeURIComponent(t)}`}function ft(e=en,t){let n=new Map,d=`_snt_asgn${me(t)}_`,p=(s,r)=>`${d}${encodeURIComponent(s)}:${encodeURIComponent(r)}`,i=s=>{let r=s.slice(d.length),o=r.indexOf(":");if(o<0)return null;try{return{componentId:decodeURIComponent(r.slice(0,o)),segment:decodeURIComponent(r.slice(o+1))}}catch(y){return null}},c=()=>{try{let s=[];for(let r=0;r<localStorage.length;r++){let o=localStorage.key(r);o!=null&&o.startsWith(d)&&s.push(o)}return s}catch(s){return[]}},a=s=>s.assignedAt+(s.ttlMs&&s.ttlMs>0?s.ttlMs:e)<Date.now();return typeof window!="undefined"&&(()=>{for(let s of c())try{let r=localStorage.getItem(s);if(!r)continue;let o=JSON.parse(r);if(a(o)){localStorage.removeItem(s);continue}let y=i(s);if(!y)continue;n.set(Oe(y.componentId,y.segment),o)}catch(r){}})(),{get(s,r){let o=n.get(Oe(s,r));return o?a(o)?(n.delete(Oe(s,r)),null):o:null},set(s,r,o){let y=Oe(s,r);n.set(y,o);try{localStorage.setItem(p(s,r),JSON.stringify(o))}catch(h){}},clear(){n.clear();for(let s of c())try{localStorage.removeItem(s)}catch(r){}}}}var ye=["GPTBot","ChatGPT-User","OAI-SearchBot","ClaudeBot","Claude-User","Claude-SearchBot","PerplexityBot","Perplexity-User","Google-Extended","Applebot-Extended","Meta-ExternalAgent","Bytespider","CCBot","Amazonbot","cohere-ai","Diffbot"];function Se(e){return je(e)!==null}function je(e){var n;if(!e)return null;let t=e.toLowerCase();return(n=ye.find(d=>t.includes(d.toLowerCase())))!=null?n:null}var Pe={GPTBot:"training","ChatGPT-User":"user","OAI-SearchBot":"search",ClaudeBot:"training","Claude-User":"user","Claude-SearchBot":"search",PerplexityBot:"search","Perplexity-User":"user","Google-Extended":"training","Applebot-Extended":"training","Meta-ExternalAgent":"training",Bytespider:"training",CCBot:"training",Amazonbot:"other","cohere-ai":"training",Diffbot:"other"};function mt(e){if(!e)return"other";let t=ye.find(n=>n.toLowerCase()===e.toLowerCase());return t&&Pe[t]||"other"}function yt(){var t;let e={user:[],search:[],training:[],other:[]};for(let n of ye)e[(t=Pe[n])!=null?t:"other"].push(n);return e}function he(e){let t=e.toLowerCase();return/ipad|tablet|playbook|kindle|silk/.test(t)?"tablet":/mobi|iphone|ipod|android.*mobile|phone/.test(t)?"mobile":"desktop"}function ve(e,t){if(!e)return"direct";try{let n=new URL(e);if(t)try{if(new URL(t).host===n.host)return"direct"}catch(p){}let d=n.hostname.toLowerCase();return/(^|\.)(google|bing|duckduckgo|yahoo)\./.test(d)?"search":/(^|\.)(twitter\.com|x\.com|facebook\.com|linkedin\.com|reddit\.com|t\.co)$/.test(d)?"social":"referral"}catch(n){return"direct"}}function we(e){if(!e)return null;try{return new URL(e).hostname}catch(t){return null}}var We=["gclid","gbraid","wbraid","fbclid","ttclid","msclkid","twclid","li_fat_id"];function Be(e){let t={},n={};try{let d=typeof e=="string"||e instanceof URLSearchParams?new URLSearchParams(e):Object.entries(e).flatMap(([p,i])=>{let c=Array.isArray(i)?i[0]:i;return c===void 0?[]:[[p,c]]});for(let[p,i]of d)p.startsWith("utm_")?p in t||(t[p]=i):We.includes(p)&&(p in n||(n[p]=i))}catch(d){}return{utmParams:t,clickIds:n}}function be(e){let t=e.getHours();return t<6?"night":t<12?"morning":t<18?"afternoon":"evening"}function St(e){let t=tn("__segment__",e);return`${t.deviceClass}:${t.trafficSource}`}function tn(e,t){var i,c,a,g,s,r,o,y;let n=(c=(i=t==null?void 0:t.userAgent)==null?void 0:i.trim())!=null?c:"",d=(g=(a=t==null?void 0:t.referer)==null?void 0:a.trim())!=null?g:"",p=(s=t==null?void 0:t.now)!=null?s:new Date;return{sessionId:e,ephemeral:!1,utmParams:(r=t==null?void 0:t.utmParams)!=null?r:{},clickIds:(o=t==null?void 0:t.clickIds)!=null?o:{},deviceClass:n?he(n):"desktop",trafficSource:d?ve(d,t==null?void 0:t.appOrigin):"direct",referrerDomain:we(d),timeOfDay:be(p),dayOfWeek:(y=["sun","mon","tue","wed","thu","fri","sat"][p.getDay()])!=null?y:"sun",automation:(t==null?void 0:t.webdriver)===!0||Se(n)}}var ae=require("@sentientui/policy");function ke(e){return k(k(k({id:e.id},e.arms?{arms:[...e.arms]}:{}),e.dims?{dims:Object.fromEntries(Object.entries(e.dims).map(([t,n])=>[t,[...n]]))}:{}),e.baseline!==void 0?{baseline:e.baseline}:{})}function le(e){let t=ke(e);return(0,ae.slotResultFor)(t,(0,ae.slotBaselineArm)(t))}function ht(e){let t={};for(let n of e)t[n.id]=le(n);return t}function Le(e){return typeof e=="string"?e:(0,ae.canonicalArm)(e)}var ne="_snt_snap:",nn=["low","medium","high"];function ce(e){try{let t=localStorage.getItem(ne+e);if(!t)return null;let n=JSON.parse(t);return!n||typeof n!="object"||n.v!==1||typeof n.persona!="string"||typeof n.band!="string"||!nn.includes(n.band)||typeof n.slots!="object"||n.slots===null||Array.isArray(n.slots)||!(n.layoutOrder===null||Array.isArray(n.layoutOrder))||typeof n.savedAt!="number"||!(n.slotConfig===void 0||typeof n.slotConfig=="object"&&n.slotConfig!==null&&!Array.isArray(n.slotConfig))?null:n}catch(t){return null}}function de(e,t){try{localStorage.setItem(ne+e,JSON.stringify(t))}catch(n){}}function vt(e){return"(function(){try{var r=localStorage.getItem("+JSON.stringify(ne+e).replace(/</g,"\\u003c")+');if(!r)return;var s=JSON.parse(r);if(!s||s.v!==1||typeof s.persona!=="string"||typeof s.band!=="string")return;var d=document.documentElement;if(d.hasAttribute("data-sentient-persona"))return;d.setAttribute("data-sentient-persona",s.persona);d.setAttribute("data-sentient-confidence",s.band);}catch(e){}})();'}var Qe=require("@sentientui/policy");var Me=require("@sentientui/policy");var Ne="[sentient] No API key configured \u2014 nothing is being learned. Set NEXT_PUBLIC_SENTIENT_API_KEY or pass localMode: true for local development.",He="[sentient] Local mode \u2014 decisions are simulated on-device and nothing is sent over the network. Add an API key to learn from real traffic.",wt=!1,De=!1;function rn(){var e;try{return(e=new URLSearchParams(window.location.search).get("sentient_persona"))!=null?e:void 0}catch(t){return}}function bt(e){var s;let t=Ae({ssrSessionId:e.ssrSessionId,apiKey:e.apiKey}),n=(s=t.getSessionId())!=null?s:"local",d=rn(),p=import("@sentientui/core/local").then(r=>{let o=r;return o.LOCAL_ENGINE_AVAILABLE?(wt||(wt=!0,console.info(He)),o):(De||(De=!0,console.error(Ne)),null)}).catch(()=>(De||(De=!0,console.error(Ne)),null)),i=null,c={low:.15,medium:.5,high:.85},a=(()=>{var o;if(e.initialPersona)return k({},e.initialPersona);let r=ce(e.apiKey||"local");return r?{persona:r.persona,confidence:(o=c[r.band])!=null?o:.15}:null})();function g(r){let o=document.documentElement;o.dataset.sentientPersona===void 0&&(o.dataset.sentientPersona=r.persona,o.dataset.sentientConfidence=(0,Me.confidenceBand)(r.confidence))}return{isLocal:!0,async decide(r){var h,x,P;let o=await p;if(!o)return null;let y=o.createLocalEngine({sessionId:n,forcedPersona:d}).decide(r);return i=se(k({},y),{layoutOrder:(x=(h=y.layoutOrder)!=null?h:i==null?void 0:i.layoutOrder)!=null?x:null,slots:k(k({},(P=i==null?void 0:i.slots)!=null?P:{}),y.slots)}),de(e.apiKey||"local",{v:1,persona:i.persona,band:(0,Me.confidenceBand)(i.confidence),slots:i.slots,layoutOrder:i.layoutOrder,savedAt:Date.now()}),g(y),y},getSlotResult(r){var o,y,h;return(h=(y=i==null?void 0:i.slots[r])!=null?y:(o=e.initialSlots)==null?void 0:o[r])!=null?h:null},getSlotConfig(r){var o,y;return(y=(o=e.initialSlotConfig)==null?void 0:o[r])!=null?y:null},getSitePalette(){var r;return(r=e.initialPalette)!=null?r:null},reportSlots(){},getPersona(){let r=i!=null?i:a;return r?{persona:r.persona,confidence:r.confidence,band:(0,Me.confidenceBand)(r.confidence)}:null},async assign(r,o){var x;let y=await p;return!y||!o||o.length===0?o!=null&&o[0]?{variantId:o[0],assignmentTtlMs:0}:null:{variantId:(x=y.createLocalEngine({sessionId:n,forcedPersona:d}).decide({components:[{id:r,variantIds:o}]}).assignments[r])!=null?x:o[0],assignmentTtlMs:0}},track:()=>{},goal:()=>{},componentGoal:()=>{},identify:()=>{},getAssignment:()=>null,fetchWeights:()=>Promise.resolve([]),getGraph:()=>({pageNodes:[],capturedAt:0}),dispose:()=>{},destroy:()=>t.destroy()}}var on=["none","sm","md","lg"],sn=["start","center","end","stretch"],an=["start","center","end","between"],ln=["sm","md","lg"],cn=["normal","medium","bold"],dn=["default","muted","accent"],un=["primary","secondary","ghost"],pn=["left","center","right"],gn=["auto","square","landscape","wide"],fn=["cover","contain"],mn=[2,3,4],yn=[2,3,4],Sn=["input","textarea","select"],hn=["text","email","number","tel"],vn=5,wn=8,bn=64,kn=5,xn=12,In=4,En=500;function kt(e){if(e==null||typeof e!="object"||Array.isArray(e))return!1;let t=e;return t.type==="form"?!0:Array.isArray(t.children)?t.children.some(kt):!1}function xt(e,t,n,d){let p=[];{let a=!1,g=[],s=()=>{if(a)return;let r=Date.now();for(g.push(r);g.length>0&&r-g[0]>500;)g.shift();g.length>=3&&(a=!0,e("rage_click"))};t.addEventListener("click",s),p.push(()=>t.removeEventListener("click",s))}{let i=!1,c=a=>{if(i||!(a.target instanceof Node)||!t.contains(a.target)&&t!==a.target)return;i=!0;let g=typeof window!="undefined"?window.getSelection():null,s=g?g.toString().length:0;e("text_copy",{selectionLength:s})};document.addEventListener("copy",c),p.push(()=>document.removeEventListener("copy",c))}{let i=!1,c=!1,a=null,g=()=>{a!==null&&(clearTimeout(a),a=null)},s=()=>{i||!c||(g(),a=setTimeout(()=>{!i&&c&&(i=!0,e("scroll_hesitation"))},3e3))},r=()=>{g(),s()},o=h=>{for(let x of h)c=x.intersectionRatio>.3,c?s():g()},y=new IntersectionObserver(o,{threshold:[.3]});y.observe(t),window.addEventListener("scroll",r,{passive:!0}),p.push(()=>{y.disconnect(),window.removeEventListener("scroll",r),g()})}if((d==null?void 0:d.tabLoss)!==!1){let i=!1,c=n!=null?n:Date.now(),a=()=>{if(i||document.visibilityState!=="hidden")return;let g=Date.now()-c;g<15e3&&(i=!0,e("tab_loss",{timeOnPage:g}))};document.addEventListener("visibilitychange",a),p.push(()=>document.removeEventListener("visibilitychange",a))}return()=>{for(let i of p)i()}}var It="https://api.sentient-ui.com/v1/events",N=new Map,Et=null;function Cn(e,t){let n=N.get(e);n&&n.upgrade&&(n.reinit=t)}function _n(e){return"value"in e||"currency"in e||"externalId"in e||"metadata"in e||"weight"in e||"stepIndex"in e}var An=3;function ze(){return Ee()}function Ke(){var e;if(typeof window!="undefined")return((e=window.location)==null?void 0:e.pathname)||void 0}function Rn(){let e=new WeakMap,t=new Map,n=!1,d=(a,g,s)=>{let r=a.get(g);return s===null?r?!0:(a.set(g,new Set),!1):r?r.has(s)?!0:(r.add(s),!1):(a.set(g,new Set([s])),!1)},p=typeof window!="undefined"&&"event"in window,i=()=>{if(!p)return;let a=window.event;return typeof Event=="function"&&a instanceof Event?a:void 0},c=()=>{if(p){Promise.resolve().then(()=>{n=!1,t.clear()});return}let a=!1,g=()=>{a||(a=!0,n=!1,t.clear())};if(setTimeout(g,0),typeof MessageChannel=="function"){let s=new MessageChannel;s.port1.onmessage=()=>{s.port1.close(),s.port2.close(),g()},s.port2.postMessage(0)}};return{firedBefore(a,g){let s=i();if(s){let o=e.get(s);return o||(o=new Map,e.set(s,o)),d(o,a,g)}let r=d(t,a,g);return!r&&!n&&(n=!0,c()),r}}}var Ct=new Set;function Tn(e,t,n){let d=typeof window=="undefined"?null:window.history;if(!d)return()=>{};let p=!1,i,c=()=>{if(p)return;let s=Ke();!s||s===i||(i=s,e.track({projectId:t,componentId:"__page__",eventType:"pageview",payload:{}}),n(`${t}:${s}`))},a=[];for(let s of["pushState","replaceState"]){let r=d[s],o=function(...y){let h=r.apply(this,y);return c(),h};d[s]=o,a.push([s,r,o])}window.addEventListener("popstate",c);let g=Ke();return g&&Ct.has(`${t}:${g}`)?i=g:c(),()=>{if(!p){p=!0,window.removeEventListener("popstate",c);for(let[s,r,o]of a)d[s]===o&&(d[s]=r)}}}var xe={track:()=>{},goal:()=>{},componentGoal:()=>{},identify:()=>{},getAssignment:()=>null,assign:()=>Promise.resolve(null),decide:()=>Promise.resolve(null),getSlotResult:()=>null,getSlotConfig:()=>null,getSitePalette:()=>null,reportSlots:()=>{},getPersona:()=>null,fetchWeights:()=>Promise.resolve([]),getGraph:()=>({pageNodes:[],capturedAt:0}),dispose:()=>{},destroy:()=>{}};function On(){try{return Be(window.location.search)}catch(e){return{utmParams:{},clickIds:{}}}}function _t(e){return e.replace(/\/events\/?$/,"")}function Xe(){return typeof navigator!="undefined"&&navigator.globalPrivacyControl===!0?!0:[typeof navigator!="undefined"?navigator.doNotTrack:void 0,typeof window!="undefined"?window.doNotTrack:void 0,typeof navigator!="undefined"?navigator.msDoNotTrack:void 0].some(t=>t==="1"||t==="yes")}function Pn(e){var g;if(typeof window=="undefined")return;let t=e!=null?e:Et;if(!t){console.warn("[sentient] grantConsent() called before init()");return}let n=N.get(t);if(!n){console.warn("[sentient] grantConsent() called before init()");return}let{config:d,upgrade:p,reinit:i}=n;if(!p){n.upgradeBlockedReason&&console.warn(n.upgradeBlockedReason);return}if(d.respectDoNotTrack!==!1&&Xe())return;let c=(i!=null?i:At)(se(k({},d),{consent:!0}));p(c);let a=(g=N.get(t))==null?void 0:g.dispose;N.set(t,{config:se(k({},d),{consent:!0}),upgrade:null,dispose:a})}function Bn(e){var s;let t=e.preConsentBehavior==="statistical_winner",n=_t((s=e.ingestUrl)!=null?s:It),d={"Content-Type":"application/json",Authorization:`Bearer ${e.apiKey}`},p=new Map,i=new Map,c={track:()=>{},goal:()=>{},componentGoal:()=>{},identify:()=>{},getAssignment:()=>null,fetchWeights:()=>Promise.resolve([]),assign(r,o,y){if(!t)return Promise.resolve(null);let h=p.get(r);return h?Promise.resolve(h):Je(i,r,async()=>{try{let x=new URLSearchParams({componentId:r});for(let K of o!=null?o:[])x.append("variantIds[]",K);let P=await fetch(`${n}/winner?${x.toString()}`,{headers:d});if(!P.ok)return o!=null&&o[0]?{variantId:o[0],assignmentTtlMs:0}:null;let I={variantId:(await P.json()).variantId,assignmentTtlMs:0};return p.set(r,I),I}catch(x){return o!=null&&o[0]?{variantId:o[0],assignmentTtlMs:0}:null}})},decide:()=>Promise.resolve(null),getSlotResult:()=>null,getSlotConfig:()=>null,getSitePalette:()=>null,reportSlots:()=>{},getPersona:()=>null,getGraph:()=>({pageNodes:[],capturedAt:0}),dispose:()=>{},destroy:()=>{}},a={track:r=>c.track(r),goal:((r,o,y,h)=>c.goal(r,o,y,h)),componentGoal:(r,o,y)=>c.componentGoal(r,o,y),identify:r=>c.identify(r),getAssignment:(r,o)=>c.getAssignment(r,o),assign:(r,o,y,h)=>c.assign(r,o,y,h),decide:r=>c.decide(r),getSlotResult:r=>c.getSlotResult(r),getSlotConfig:r=>c.getSlotConfig(r),getSitePalette:()=>c.getSitePalette(),reportSlots:r=>c.reportSlots(r),getPersona:()=>c.getPersona(),fetchWeights:()=>c.fetchWeights(),getGraph:()=>c.getGraph(),dispose:()=>c.dispose(),destroy:()=>c.destroy()};function g(r){c=r}return{proxy:a,setInner:g}}function Je(e,t,n){let d=e.get(t);if(d)return d;let p=(async()=>{try{return await n()}finally{e.delete(t)}})();return e.set(t,p),p}function At(e){var V,re,Y,Ve,Ye,qe,Ze,et,tt,nt;if(typeof window=="undefined")return xe;Et=e.apiKey||"local";let t=N.get(e.apiKey||"local");if(t!=null&&t.dispose)try{t.dispose()}catch(l){}let n=e.respectDoNotTrack!==!1&&Xe(),d=e.consent===!1||n,p=typeof e.apiKey=="string"&&e.apiKey.startsWith("pk_");if(e.localMode===!0||!p&&e.localMode!==!1)return N.set(e.apiKey||"local",{config:e,upgrade:null,upgradeBlockedReason:"[sentient] grantConsent(): this client is keyless/local \u2014 there is no hosted client to upgrade to. Configure a pk_ API key to enable tracking."}),d?xe:bt(e);if(d){if(!e.apiKey||!e.apiKey.startsWith("pk_"))return e.preConsentBehavior==="statistical_winner"&&console.warn("[sentient] init() called with an invalid apiKey \u2014 expected a pk_ public key. SDK disabled."),N.set(e.apiKey||"local",{config:e,upgrade:null,upgradeBlockedReason:"[sentient] grantConsent(): the client was initialized with an invalid apiKey (expected a pk_ public key) \u2014 consent cannot enable tracking."}),xe;let{proxy:l,setInner:u}=Bn(e);return N.set(e.apiKey,{config:e,upgrade:n?null:u}),l}if(!e.apiKey||!e.apiKey.startsWith("pk_"))return console.warn("[sentient] init() called with an invalid apiKey \u2014 expected a pk_ public key. SDK disabled."),xe;if(e.ingestUrl==="")return console.warn("[sentient] init() called with an empty ingestUrl. SDK disabled."),xe;let i=(V=e.ingestUrl)!=null?V:It,c=Date.now(),a=Ae({ssrSessionId:e.ssrSessionId,apiKey:e.apiKey}),g=ft(void 0,e.apiKey),s=pt({ingestUrl:i,apiKey:e.apiKey}),r=_t(i),o={"Content-Type":"application/json",Authorization:`Bearer ${e.apiKey}`},y=new Set,h=gt({url:`${r}/goals`,apiKey:e.apiKey,headers:o,onDrop:(l,u)=>{if(!e.debug){if(y.has(u))return;y.add(u)}console.warn(`[sentient] goal dropped (HTTP ${u}) \u2014 this will not be retried. `+(u===400?"The session was not found: call init() and let the session upsert complete before firing goals.":u===401||u===403?"Check the API key and that this origin is on the project allowlist.":"See the response status for the cause."),l)}}),x=he((re=navigator.userAgent)!=null?re:""),P=window.location.origin,B=ve((Y=document.referrer)!=null?Y:"",P),I=(Ve=e.sessionSegment)!=null?Ve:`${x}:${B}`,K=new Map,$=new Map,E=new Map,A=new Map,C=null,f=null,R=new Set,j=new Set,W=null,J=l=>{for(let u of l)E.has(u.id)||E.set(u.id,le(u))};if(e.initialSlots)for(let[l,u]of Object.entries(e.initialSlots))E.set(l,u);if(e.initialSlotConfig)for(let[l,u]of Object.entries(e.initialSlotConfig))A.set(l,u);e.initialPalette&&(C=e.initialPalette);let M=ce(e.apiKey);if(M){for(let[l,u]of Object.entries(M.slots))E.has(l)||E.set(l,u);for(let[l,u]of Object.entries((Ye=M.slotConfig)!=null?Ye:{}))A.has(l)||A.set(l,u);!C&&M.palette&&(C=M.palette)}let ue={low:.15,medium:.5,high:.85};if(e.initialPersona)f=k({},e.initialPersona);else{let l=document.documentElement.dataset;l.sentientPersona?f={persona:l.sentientPersona,confidence:(Ze=ue[(qe=l.sentientConfidence)!=null?qe:"low"])!=null?Ze:.15}:M&&(f={persona:M.persona,confidence:(et=ue[M.band])!=null?et:.15})}if(e.initialAssignments)for(let[l,u]of Object.entries(e.initialAssignments))g.set(l,I,{variantId:u,assignedAt:Date.now(),segment:I,confidence:1});let m=Promise.resolve(),w=a.getSessionId();if(w){let l=we((tt=document.referrer)!=null?tt:""),{utmParams:u,clickIds:v}=On(),T=k(k(k(k({sessionId:w,deviceClass:x,trafficSource:B,referrerDomain:l,utmParams:u,clickIds:v,timeOfDay:be(new Date),dayOfWeek:["sun","mon","tue","wed","thu","fri","sat"][new Date().getDay()],ephemeral:a.isEphemeral(),automation:typeof navigator!="undefined"&&navigator.webdriver===!0||Se((nt=navigator.userAgent)!=null?nt:"")},e.userId?{userId:e.userId}:{}),e.persona?{persona:e.persona}:{}),e.country?{country:e.country}:{}),e.sdk?{sdk:e.sdk.name,sdkVersion:e.sdk.version}:{}),L=async()=>{for(let S=0;;S++){try{let _=await fetch(`${r}/sessions`,{method:"POST",keepalive:!0,body:JSON.stringify(T),headers:o});if(_.status===402){console.warn("[SentientUI] Session limit exceeded for this project. The bandit will stop learning until the limit resets. Upgrade at sentient-ui.com/pricing");return}if(_.ok||ie(_)==="dropped")return}catch(_){}if(S>=An){console.warn("[SentientUI] Could not register the session after retries. Conversions in this visit may not be recorded.");return}await new Promise(_=>setTimeout(_,Z(S+1)))}};try{m=L()}catch(S){}}e.debug&&(console.log("[sentient] initialized",{context:e.context}),window.__sentient={client:null,queue:s});let X=Rn(),G=null,F=!1,z={goal(l,u={},v=1,T=0){var pe,ge,oe,b,Q,Ie,O;let L=a.getSessionId();if(!L)return;let S=_n(u)?u:{metadata:u},_=[l,(pe=S.externalId)!=null?pe:"",(ge=S.stepIndex)!=null?ge:T,(oe=S.weight)!=null?oe:v].join("\0"),U=S.value!==void 0?`${S.value}\0${(b=S.currency)!=null?b:""}`:null;if(X.firedBefore(_,U)){e.debug&&console.log(`[sentient] goal("${l}") already recorded for this action \u2014 not sent twice`);return}let D=ze(),q={sessionId:L,name:l,metadata:(Q=S.metadata)!=null?Q:{},weight:(Ie=S.weight)!=null?Ie:v,stepIndex:(O=S.stepIndex)!=null?O:T,goalId:D,value:S.value,currency:S.currency,externalId:S.externalId};e.debug&&console.log("[sentient] goal",q);let te={id:D,body:JSON.stringify(q)};m.then(()=>h.send(te))},componentGoal(l,u,v){var D,q,te;let T=a.getSessionId();if(!T)return;let L=g.get(l,I),S=L?null:(D=E.get(l))!=null?D:null;if(!L&&S===null){e.debug&&console.warn(`[sentient] componentGoal("${l}"): no assignment or slot decision yet \u2014 render its <Adaptive>/adaptive hook or call assign()/decide() before recording a goal.`);return}let _=L?L.variantId:Le(S),U={id:ze(),sessionId:T,projectId:e.apiKey,componentId:l,variantId:_,eventType:"goal_achieved",goalType:u,payload:k({reward:(q=v==null?void 0:v.reward)!=null?q:1,goalValue:v==null?void 0:v.value,currency:v==null?void 0:v.currency},(te=v==null?void 0:v.metadata)!=null?te:{}),timestamp:Date.now(),timeInSession:Date.now()-c,path:Ke()};e.debug&&console.log("[sentient] componentGoal",U),m.then(()=>s.push(U))},identify(l){let u=a.getSessionId();u&&m.then(()=>{fetch(`${r}/sessions`,{method:"POST",keepalive:!0,body:JSON.stringify({sessionId:u,userId:l,ephemeral:a.isEphemeral()}),headers:o}).catch(()=>{})})},track(l){let u=a.getSessionId();if(!u)return;let v=se(k({path:Ke()},l),{id:ze(),sessionId:u,timestamp:Date.now(),timeInSession:Date.now()-c});e.debug&&console.log("[sentient] track",v),m.then(()=>s.push(v))},getAssignment(l,u){return g.get(l,u)},async assign(l,u,v,T){let L=a.getSessionId();if(!L)return null;let S=g.get(l,I);if(S&&(u!=null&&u.length||S.content!==void 0)){let _=S.ttlMs&&S.ttlMs>0?Math.max(0,S.assignedAt+S.ttlMs-Date.now()):0;return{variantId:S.variantId,assignmentTtlMs:_,content:S.content}}return Je(K,l,async()=>{await m;try{let _={sessionId:L,componentId:l,variantIds:u};T!==void 0?_.agentDataByVariant=T:v!==void 0&&(_.agentData=v);let U=await fetch(`${r}/assign`,{method:"POST",body:JSON.stringify(_),headers:o});if(!U.ok)return null;let D=await U.json();return g.set(l,I,k({variantId:D.variantId,assignedAt:Date.now(),segment:I,confidence:1,content:D.content},D.assignmentTtlMs&&D.assignmentTtlMs>0?{ttlMs:D.assignmentTtlMs}:{})),D}catch(_){return null}})},async decide(l){var S,_;let u=a.getSessionId();if(!u)return null;let v=(S=l.slots)!=null?S:[],T={sessionId:u};l.sections&&l.sections.length>0&&(T.sections=l.sections.map(U=>({id:U}))),T.components=(_=l.components)!=null?_:[],v.length>0&&(T.slots=v.map(ke)),l.slotsFrom==="registry"&&(T.slotsFrom="registry"),l.v&&(T.v=l.v),typeof l.pp=="number"&&(T.pp=l.pp),e.persona&&(T.persona=e.persona);let L=JSON.stringify(T);return Je($,L,async()=>{var U,D,q,te,pe,ge;await m;try{let oe=await fetch(`${r}/decide`,{method:"POST",body:L,headers:o});if(!oe.ok)return J(v),null;let b=await oe.json(),Q={};for(let O of v){let H=(U=b.slots)==null?void 0:U[O.id];if(H!==void 0){Q[O.id]=H,E.set(O.id,H);continue}let rt=E.get(O.id);if(rt!==void 0)Q[O.id]=rt;else{let ot=le(O);Q[O.id]=ot,E.set(O.id,ot)}}if(b.slots)for(let[O,H]of Object.entries(b.slots))O in Q||(Q[O]=H,E.set(O,H));let Ie=f!=null&&f.persona!=="unknown";b.persona&&!(b.persona==="unknown"&&Ie)?f={persona:b.persona,confidence:(D=b.confidence)!=null?D:0}:f||(f={persona:"unknown",confidence:0});for(let[O,H]of Object.entries((q=b.assignments)!=null?q:{}))g.set(O,I,{variantId:H,assignedAt:Date.now(),segment:I,confidence:1});if(b.slotConfig)for(let[O,H]of Object.entries(b.slotConfig))A.set(O,H);return b.palette&&(C=b.palette),de(e.apiKey,k(k({v:1,persona:f.persona,band:(0,Qe.confidenceBand)(f.confidence),slots:Object.fromEntries(E),layoutOrder:(te=b.layoutOrder)!=null?te:null,savedAt:Date.now()},A.size>0?{slotConfig:Object.fromEntries(A)}:{}),C?{palette:C}:{})),k(k(k(k({layoutOrder:(pe=b.layoutOrder)!=null?pe:null,assignments:(ge=b.assignments)!=null?ge:{},slots:Q,persona:f.persona,confidence:f.confidence},b.slotConfig?{slotConfig:b.slotConfig}:{}),b.goals?{goals:b.goals}:{}),b.sectionMap?{sectionMap:b.sectionMap}:{}),b.palette?{palette:b.palette}:{})}catch(oe){return J(v),null}})},getSlotResult(l){var u;return(u=E.get(l))!=null?u:null},getSlotConfig(l){var u;return(u=A.get(l))!=null?u:null},reportSlots(l){for(let u of l)typeof u=="string"&&u.length>0&&!R.has(u)&&(R.add(u),j.add(u));j.size===0||W!=null||(W=setTimeout(()=>{W=null;let u=[...j].slice(0,20);j.clear(),u.length!==0&&fetch(`${r}/slots/observed`,{method:"POST",headers:o,body:JSON.stringify({slotIds:u})}).catch(()=>{})},1e3))},getSitePalette(){return C},getPersona(){return f?{persona:f.persona,confidence:f.confidence,band:(0,Qe.confidenceBand)(f.confidence)}:null},async fetchWeights(){var l;try{let u=await fetch(`${r}/weights`,{headers:o});return u.ok?(l=(await u.json()).components)!=null?l:[]:[]}catch(u){return[]}},getGraph(){return{pageNodes:[],capturedAt:0}},dispose(){var l;G==null||G(),F=!0,s.destroy(),h.destroy(),((l=N.get(e.apiKey))==null?void 0:l.dispose)===z.dispose&&N.delete(e.apiKey),e.debug&&console.log("[sentient] disposed")},destroy(){var l;G==null||G(),F=!0,s.destroy(),h.destroy(),a.destroy(),g.clear(),((l=N.get(e.apiKey))==null?void 0:l.dispose)===z.dispose&&N.delete(e.apiKey);try{localStorage.removeItem(ne+e.apiKey),localStorage.removeItem(Fe(e.apiKey)),localStorage.removeItem($e(e.apiKey))}catch(u){}e.debug&&console.log("[sentient] destroyed")}};if(N.set(e.apiKey,{config:e,upgrade:null,dispose:z.dispose}),G=Tn(z,e.apiKey,l=>{m.then(()=>{F||Ct.add(l)})}),e.debug){let l=window;l.__sentient&&(l.__sentient.client=z)}return z}0&&(module.exports={AGENT_INTENTS,BLOCK_ALIGNS,BLOCK_EMPHASES,BLOCK_FITS,BLOCK_GAPS,BLOCK_GRID_COLUMNS,BLOCK_HEADING_LEVELS,BLOCK_JUSTIFIES,BLOCK_RATIOS,BLOCK_SIZES,BLOCK_TEXT_ALIGNS,BLOCK_TONES,BLOCK_WEIGHTS,CLICK_ID_KEYS,FORM_FIELD_KINDS,FORM_INPUT_TYPES,LEGACY_SESSION_COOKIE_NAME,LOCAL_MODE_BANNER,MAX_BLOCK_ARMS,MAX_BLOCK_CHILDREN,MAX_BLOCK_DEPTH,MAX_BLOCK_NODES,MAX_BLOCK_TEXT_LEN,MAX_FORM_FIELDS,MAX_FORM_SELECT_OPTIONS,PROD_KEYLESS_ERROR,SNAPSHOT_STORAGE_KEY_PREFIX,_registerConsentUpgradeInit,agentIntent,agentUaList,armOfResult,attachMicroSignalDetectors,baselineResultFor,baselineSlots,classifiedAgents,containsFormBlock,deriveSessionSegment,detectDeviceClass,detectTimeOfDay,detectTrafficSource,extractTrackedParams,grantConsent,init,isDoNotTrackEnabled,matchedAgentToken,readSnapshot,referrerDomainFromReferer,renderPrePaintScript,sessionCookieName,toWireSlot,uaTokenMatch,writeSnapshot});
|
|
2
2
|
//# sourceMappingURL=index.js.map
|