@sentientui/core 0.16.5 → 0.16.7

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.
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index-graph.ts","../src/session.ts","../src/queue.ts","../src/cache.ts","../src/session-meta.ts","../src/slots.ts","../src/snapshot.ts","../src/index.ts","../src/local-mode.ts","../src/engagement/classify.ts","../src/scanner.ts","../src/graph.ts"],"sourcesContent":["/**\n * Graph-capable entry point for @sentientui/core.\n *\n * Import from `@sentientui/core/graph` when you need DOM graph scanning and\n * page-structure sync. This entry pulls in `scanner.ts` and `graph.ts` at\n * build time, giving bundlers a real tree-shaking boundary. Standard A/B tests\n * should use `@sentientui/core` (the lean entry) instead.\n */\n\n// Re-export everything from the lean entry except `init`, which we override below.\nexport {\n detectDeviceClass,\n detectTrafficSource,\n detectTimeOfDay,\n deriveSessionSegment,\n referrerDomainFromReferer,\n} from './index.js';\n// SSR preload helpers moved to `@sentientui/core/server` in 0.6.0.\nexport type {\n SentientConfig,\n AssignResult,\n SentientClient,\n} from './index.js';\nexport type {\n SessionConfig,\n SessionManager,\n EventType,\n SentientEvent,\n QueueConfig,\n Assignment,\n} from './index.js';\n// EventQueue/AssignmentCache are no longer on the lean barrel — source them from\n// their own modules so the `/graph` subpath keeps exposing them unchanged.\nexport type { EventQueue } from './queue.js';\nexport type { AssignmentCache } from './cache.js';\n// Scanner + graph types (live in this entry only).\nexport type {\n ScannedNode,\n ScanResult,\n ContentAddedEvent,\n DOMScanner,\n} from './scanner.js';\nexport type {\n PageNode,\n GraphSnapshot,\n GraphConfig,\n GraphClient,\n} from './graph.js';\n\nimport { init as initLean, isDoNotTrackEnabled, type SentientConfig, type SentientClient } from './index.js';\n\nconst DEFAULT_INGEST_URL = 'https://api.sentient-ui.com/v1/events';\nimport { createDOMScanner } from './scanner.js';\nimport { createGraphClient, type PageNode } from './graph.js';\n\nexport type GraphSentientConfig = SentientConfig & {\n /**\n * Enable DOM graph scanning and page-structure sync. Wires a MutationObserver\n * and a localStorage-backed graph. When `false` (default) this entry behaves\n * identically to `@sentientui/core`.\n */\n graph?: boolean;\n};\n\nfunction readSntUid(): string | undefined {\n try {\n const m = document.cookie.match(/(?:^|; )_snt_uid=([^;]*)/);\n return m ? decodeURIComponent(m[1]) : undefined;\n } catch {\n return undefined;\n }\n}\n\n/**\n * Graph-capable variant of `init()`. Identical to the lean `init` when\n * `config.graph` is `false` (or omitted). When `config.graph: true`, mounts\n * a DOM scanner + graph client and enables `client.getGraph()`.\n *\n * Import from `@sentientui/core/graph` — do not call the lean `init` alongside\n * this function as that would initialise two clients.\n */\nexport function init(config: GraphSentientConfig): SentientClient {\n const client = initLean(config);\n\n // Mirror initLean's opt-out gate: a DNT/GPC visitor (or consent:false) must\n // not get the DOM scanner + graph sync, which POST page-structure beacons and\n // read `_snt_uid` — tracking that bypasses the lean client's own gating, even\n // under `preConsentBehavior: 'statistical_winner'` (audit P1).\n const dntBlocked = config.respectDoNotTrack !== false && isDoNotTrackEnabled();\n const gated = config.consent === false || dntBlocked;\n\n // Keyless zero-network contract: with no api key there is nothing to feed —\n // the scanner must never mount and /v1/graph/sync must never fire.\n if (!config.graph || !config.apiKey || gated || typeof window === 'undefined') return client;\n\n const domScanner = createDOMScanner();\n const resolvedIngestUrl = config.ingestUrl ?? DEFAULT_INGEST_URL;\n const graphClient = createGraphClient({\n syncUrl: resolvedIngestUrl.replace(/\\/events\\/?$/, '/graph/sync'),\n apiKey: config.apiKey,\n projectId: config.apiKey,\n sessionId: readSntUid(),\n });\n\n // Restore persisted page-node state from a previous page load.\n try {\n const nodesRaw = localStorage.getItem('_snt_graph_nodes');\n if (nodesRaw) {\n graphClient.restore(\n JSON.stringify({ pageNodes: JSON.parse(nodesRaw) as PageNode[] }),\n );\n }\n } catch {\n /* best-effort */\n }\n\n void domScanner.scan().then((result) => {\n for (const node of result.nodes) {\n graphClient.addPageNode({\n id: node.componentId,\n componentId: node.componentId,\n semanticType: node.semanticType,\n answers: node.headingText ? [node.headingText] : [],\n prominenceScore: node.prominenceScore,\n depth: node.depth,\n });\n }\n for (const edge of result.edges) {\n graphClient.addStructuralEdge(edge);\n }\n graphClient.syncOnce();\n });\n\n let syncDebounceTimer: ReturnType<typeof setTimeout> | null = null;\n const debouncedSync = (): void => {\n if (syncDebounceTimer !== null) clearTimeout(syncDebounceTimer);\n syncDebounceTimer = setTimeout(() => {\n syncDebounceTimer = null;\n graphClient.syncOnce();\n }, 500);\n };\n\n domScanner.observe((event) => {\n for (const node of event.nodes) {\n graphClient.addPageNode({\n id: node.componentId,\n componentId: node.componentId,\n semanticType: node.semanticType,\n answers: node.headingText ? [node.headingText] : [],\n prominenceScore: node.prominenceScore,\n depth: node.depth,\n });\n }\n for (const edge of event.edges) {\n graphClient.addStructuralEdge(edge);\n }\n debouncedSync();\n });\n\n return {\n ...client,\n getGraph: () => graphClient.snapshot(),\n dispose: () => {\n if (syncDebounceTimer !== null) clearTimeout(syncDebounceTimer);\n domScanner.destroy();\n graphClient.destroy();\n client.dispose();\n },\n destroy: () => {\n if (syncDebounceTimer !== null) clearTimeout(syncDebounceTimer);\n domScanner.destroy();\n graphClient.destroy();\n client.destroy();\n },\n };\n}\n","/** Manages anonymous session identity with cookie + localStorage layers. */\n\nexport type SessionConfig = {\n cookieName?: string;\n cookieTTLDays?: number;\n /**\n * Session ID generated during SSR (e.g. from `loadAdaptiveAssignments`).\n * Used as the fallback when no existing cookie or localStorage entry is found,\n * so the client adopts the same session the server used for variant assignment\n * on first visit rather than generating a new, orphaned ID.\n */\n ssrSessionId?: string;\n};\n\nexport type SessionManager = {\n getSessionId(): string | null;\n /** True when neither cookie nor localStorage could be written — id is in-memory only. */\n isEphemeral(): boolean;\n destroy(): void;\n};\n\nconst DEFAULT_COOKIE_NAME = '_snt_uid';\nconst DEFAULT_COOKIE_TTL_DAYS = 365;\nconst STORAGE_KEY = '_snt_uid';\n\n/**\n * Generates a unique session identifier.\n */\nfunction generateSessionId(): string {\n try {\n if (typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function') {\n return crypto.randomUUID();\n }\n } catch {\n /* fall through */\n }\n const hex = () =>\n Math.floor(Math.random() * 0xffffffff)\n .toString(16)\n .padStart(8, '0');\n return `${hex()}-${hex()}-${hex()}-${hex()}`;\n}\n\nfunction readCookie(name: string): string | null {\n try {\n const match = document.cookie.match(new RegExp(`(?:^|; )${name}=([^;]*)`));\n return match ? decodeURIComponent(match[1]) : null;\n } catch {\n return null;\n }\n}\n\nfunction writeCookie(name: string, value: string, maxAgeSeconds: number): void {\n try {\n document.cookie = `${name}=${encodeURIComponent(value)}; max-age=${maxAgeSeconds}; SameSite=strict; path=/`;\n } catch {\n /* ignore */\n }\n}\n\nfunction readLocalStorage(key: string): string | null {\n try {\n return localStorage.getItem(key);\n } catch {\n return null;\n }\n}\n\nfunction writeLocalStorage(key: string, value: string): boolean {\n try {\n localStorage.setItem(key, value);\n return true;\n } catch {\n return false;\n }\n}\n\nfunction readSessionStorage(key: string): string | null {\n try {\n return sessionStorage.getItem(key);\n } catch {\n return null;\n }\n}\n\nfunction writeSessionStorage(key: string, value: string): boolean {\n try {\n sessionStorage.setItem(key, value);\n return true;\n } catch {\n return false;\n }\n}\n\nfunction removeSessionStorage(key: string): void {\n try {\n sessionStorage.removeItem(key);\n } catch {\n /* ignore */\n }\n}\n\nfunction probeCookieWritable(name: string): boolean {\n try {\n document.cookie = `${name}_probe=1; max-age=1; SameSite=strict; path=/`;\n return /(?:^|; )_snt_uid_probe=1/.test(document.cookie) ||\n document.cookie.indexOf(`${name}_probe=1`) !== -1;\n } catch {\n return false;\n }\n}\n\nfunction removeLocalStorage(key: string): void {\n try {\n localStorage.removeItem(key);\n } catch {\n /* ignore */\n }\n}\n\nfunction clearCookie(name: string): void {\n try {\n document.cookie = `${name}=; max-age=0; SameSite=strict; path=/`;\n } catch {\n /* ignore */\n }\n}\n\nconst SSR_MANAGER: SessionManager = {\n getSessionId: () => null,\n isEphemeral: () => false,\n destroy: () => undefined,\n};\n\n/**\n * Initializes session management. Returns a no-op manager in SSR environments.\n */\nexport function initSession(config?: SessionConfig): SessionManager {\n if (typeof window === 'undefined') {\n return SSR_MANAGER;\n }\n\n const cookieName = config?.cookieName ?? DEFAULT_COOKIE_NAME;\n const cookieTTLDays = config?.cookieTTLDays ?? DEFAULT_COOKIE_TTL_DAYS;\n const maxAgeSeconds = cookieTTLDays * 24 * 60 * 60;\n\n let sessionId: string | null =\n readCookie(cookieName) ?? readLocalStorage(STORAGE_KEY) ?? readSessionStorage(STORAGE_KEY) ?? config?.ssrSessionId ?? generateSessionId();\n\n writeCookie(cookieName, sessionId, maxAgeSeconds);\n const lsOk = writeLocalStorage(STORAGE_KEY, sessionId);\n const cookieOk = probeCookieWritable(cookieName);\n // Always write sessionStorage when localStorage fails — it covers same-tab\n // navigation even in strict storage environments.\n const ssOk = !lsOk ? writeSessionStorage(STORAGE_KEY, sessionId) : false;\n const ephemeral = !lsOk && !cookieOk && !ssOk;\n\n return {\n getSessionId: () => sessionId,\n isEphemeral: () => ephemeral,\n destroy: () => {\n sessionId = null;\n clearCookie(cookieName);\n removeLocalStorage(STORAGE_KEY);\n removeSessionStorage(STORAGE_KEY);\n },\n };\n}\n","/** Batched event queue with reliable transport (fetch + keepalive, localStorage retry). */\n\nexport type EventType =\n | 'variant_assigned'\n | 'goal_achieved'\n | 'scroll_depth'\n | 'dwell'\n | 'cursor_signal'\n | 'component_visible'\n | 'component_exited'\n | 'micro_signal';\n\nexport type SentientEvent = {\n id: string;\n sessionId: string;\n projectId: string;\n componentId: string;\n variantId?: string;\n eventType: EventType;\n goalType?: string;\n payload: Record<string, unknown>;\n timestamp: number;\n timeInSession: number;\n};\n\nexport type QueueConfig = {\n ingestUrl: string;\n apiKey: string;\n flushIntervalMs?: number;\n maxBatchSize?: number;\n maxRetrySize?: number;\n};\n\nexport type EventQueue = {\n push(event: SentientEvent): void;\n flush(): void;\n destroy(): void;\n};\n\nconst MAX_SENT_IDS = 500;\n// 64 KB is the per-origin keepalive budget on every modern browser. We split unload\n// flushes into chunks below this to avoid the whole batch being dropped.\nconst KEEPALIVE_BUDGET_BYTES = 56 * 1024;\n\n/**\n * Persisted retry-bucket key, namespaced by apiKey prefix so multiple projects\n * on the same origin each get their own bucket. Exported so the client's\n * forget-me teardown can remove it.\n */\nexport function retryStorageKey(apiKey: string): string {\n return `_snt_retry_${apiKey.slice(0, 12)}`;\n}\n\nconst SSR_QUEUE: EventQueue = {\n push: () => undefined,\n flush: () => undefined,\n destroy: () => undefined,\n};\n\nfunction readRetryQueue(maxRetrySize: number, retryKey: string): SentientEvent[] {\n try {\n const raw = localStorage.getItem(retryKey);\n if (!raw) return [];\n const parsed = JSON.parse(raw) as SentientEvent[];\n if (!Array.isArray(parsed)) return [];\n localStorage.removeItem(retryKey);\n return parsed.slice(-maxRetrySize);\n } catch {\n return [];\n }\n}\n\nfunction writeRetryQueue(events: SentientEvent[], maxRetrySize: number, retryKey: string): void {\n try {\n const existing = (() => {\n try {\n const raw = localStorage.getItem(retryKey);\n if (!raw) return [];\n const parsed = JSON.parse(raw) as SentientEvent[];\n return Array.isArray(parsed) ? parsed : [];\n } catch {\n return [];\n }\n })();\n const merged = [...existing, ...events].slice(-maxRetrySize);\n localStorage.setItem(retryKey, JSON.stringify(merged));\n } catch {\n /* ignore */\n }\n}\n\n/**\n * Creates a batched event queue with periodic and lifecycle-triggered flushes.\n */\nexport function createEventQueue(config: QueueConfig): EventQueue {\n if (typeof window === 'undefined') {\n return SSR_QUEUE;\n }\n\n const flushIntervalMs = config.flushIntervalMs ?? 5000;\n const maxBatchSize = config.maxBatchSize ?? 20;\n const maxRetrySize = config.maxRetrySize ?? 100;\n const ingestUrl = config.ingestUrl;\n const apiKey = config.apiKey;\n const RETRY_KEY = retryStorageKey(apiKey);\n\n const queue: SentientEvent[] = [];\n const sentIds = new Set<string>();\n const sentIdOrder: string[] = [];\n\n const markSent = (ids: string[]): void => {\n for (const id of ids) {\n queuedIds.delete(id); // free the in-flight slot\n if (sentIds.has(id)) continue;\n sentIds.add(id);\n sentIdOrder.push(id);\n }\n while (sentIdOrder.length > MAX_SENT_IDS) {\n const oldest = sentIdOrder.shift();\n if (oldest) sentIds.delete(oldest);\n }\n };\n\n // Tracks IDs currently in `queue` or in flight (handed to transportBatch\n // but not yet confirmed). Entries are removed on markSent or when an event\n // is dropped, so the set stays bounded by in-flight + queued size.\n const queuedIds = new Set<string>();\n\n const enqueue = (event: SentientEvent): void => {\n if (sentIds.has(event.id) || queuedIds.has(event.id)) return;\n queuedIds.add(event.id);\n queue.push(event);\n };\n\n // A retryable failure hands the batch back to the in-memory queue so the\n // backoff-gated interval flush retries it in-session (localStorage is only the\n // cross-reload backstop). The ids are still tracked in queuedIds — they were\n // pulled from `queue` on flush but never markSent — so we re-add them WITHOUT\n // going through enqueue (which would skip them as already-queued) and without\n // deleting/re-adding queuedIds. Already-sent events are never re-queued, and a\n // single event stays a single copy: `queue` is fully drained each flush.\n const requeueFailed = (batch: SentientEvent[]): void => {\n for (const event of batch) {\n if (sentIds.has(event.id)) continue;\n queuedIds.add(event.id); // idempotent — keeps id-tracking consistent\n queue.push(event);\n }\n };\n\n const retryEvents = readRetryQueue(maxRetrySize, RETRY_KEY);\n for (const event of retryEvents) {\n enqueue(event);\n }\n\n // Pause flushing until backoff expires.\n let backoffUntil = 0;\n let consecutiveFailures = 0;\n\n // Authenticated transport. keepalive: true gives unload-survival; we still await\n // the response so 5xx/429 actually surface and we can retry.\n const transportBatch = (batch: SentientEvent[]): void => {\n if (batch.length === 0) return;\n const body = JSON.stringify(batch);\n const ids = batch.map((e) => e.id);\n\n let pending: Promise<Response> | Response;\n try {\n pending = fetch(ingestUrl, {\n method: 'POST',\n keepalive: true,\n body,\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${apiKey}`,\n },\n });\n } catch {\n // Synchronous throw (typically jsdom in tests, or extreme browser failure).\n writeRetryQueue(batch, maxRetrySize, RETRY_KEY);\n requeueFailed(batch);\n consecutiveFailures++;\n backoffUntil = Date.now() + Math.min(60_000, 1000 * 2 ** Math.min(consecutiveFailures, 6));\n return;\n }\n\n // fetch can sometimes return a value directly (tests using stubGlobal). Handle both.\n const handleResponse = (res: Response): void => {\n if (res.ok || (res.status >= 400 && res.status < 500 && res.status !== 429)) {\n // 2xx = success. 4xx (except 429) will never succeed — drop them rather than loop.\n markSent(ids);\n consecutiveFailures = 0;\n backoffUntil = 0;\n return;\n }\n // 5xx / 429 → retry with backoff.\n writeRetryQueue(batch, maxRetrySize, RETRY_KEY);\n requeueFailed(batch);\n consecutiveFailures++;\n backoffUntil = Date.now() + Math.min(60_000, 1000 * 2 ** Math.min(consecutiveFailures, 6));\n };\n\n if (pending instanceof Promise) {\n pending.then(handleResponse).catch(() => {\n writeRetryQueue(batch, maxRetrySize, RETRY_KEY);\n requeueFailed(batch);\n consecutiveFailures++;\n backoffUntil = Date.now() + Math.min(60_000, 1000 * 2 ** Math.min(consecutiveFailures, 6));\n });\n } else {\n handleResponse(pending);\n }\n };\n\n // Pack events into a batch that stays under the keepalive byte budget AND the\n // configured event-count cap. Returns the prefix of `pool` that fits.\n // Uses UTF-8 byte length (not char count) — emoji/CJK payloads can be 3–4×\n // larger as bytes and would otherwise blow the keepalive budget.\n const encoder = typeof TextEncoder !== 'undefined' ? new TextEncoder() : null;\n const byteLength = (s: string): number =>\n encoder ? encoder.encode(s).length : s.length;\n const packBatch = (pool: SentientEvent[]): SentientEvent[] => {\n const out: SentientEvent[] = [];\n let bytes = 2; // for the surrounding []\n for (const e of pool) {\n const size = byteLength(JSON.stringify(e)) + 1; // +1 for comma\n if (out.length > 0 && bytes + size > KEEPALIVE_BUDGET_BYTES) break;\n if (out.length >= maxBatchSize) break;\n out.push(e);\n bytes += size;\n }\n return out;\n };\n\n const flush = (): void => {\n try {\n if (Date.now() < backoffUntil) return;\n while (queue.length > 0) {\n // Re-check inside the loop: transportBatch can set backoffUntil\n // synchronously (a sync throw or a same-tick Response) and re-enqueue the\n // failed batch, so a mid-drain backoff must stop further chunks this tick\n // (otherwise the just-re-enqueued batch would immediately retry-loop).\n if (Date.now() < backoffUntil) break;\n const pending = queue.filter((e) => !sentIds.has(e.id));\n queue.length = 0;\n if (pending.length === 0) break;\n const batch = packBatch(pending);\n if (batch.length === 0) break;\n // Put anything we didn't pack back on the queue for the next tick.\n if (batch.length < pending.length) {\n queue.push(...pending.slice(batch.length));\n }\n transportBatch(batch);\n }\n } catch {\n /* never throw */\n }\n };\n\n let flushTimerActive = true;\n let intervalId: ReturnType<typeof setInterval> | null = null;\n\n intervalId = setInterval(() => {\n if (!flushTimerActive) return;\n flush();\n }, flushIntervalMs);\n\n const onVisibilityChange = (): void => {\n if (document.visibilityState === 'hidden') {\n flush();\n }\n };\n\n // `pagehide` (not `beforeunload`): a `beforeunload` listener can disqualify a\n // page from the back/forward cache. `pagehide` fires on real unloads AND on\n // bfcache eviction, and `visibilitychange:hidden` already covers most leaves,\n // so flush-on-leave stays covered without inhibiting bfcache.\n const onPageHide = (): void => {\n flush();\n };\n\n document.addEventListener('visibilitychange', onVisibilityChange);\n window.addEventListener('pagehide', onPageHide);\n\n return {\n push(event: SentientEvent): void {\n enqueue(event);\n if (queue.length >= maxBatchSize) {\n flush();\n }\n },\n flush,\n destroy(): void {\n flushTimerActive = false;\n if (intervalId !== null) {\n clearInterval(intervalId);\n intervalId = null;\n }\n document.removeEventListener('visibilitychange', onVisibilityChange);\n window.removeEventListener('pagehide', onPageHide);\n flush();\n },\n };\n}\n","/** Synchronous variant assignment cache (memory + localStorage). */\n\nexport type Assignment = {\n variantId: string;\n assignedAt: number;\n segment: string;\n confidence: number;\n content?: string;\n /**\n * Per-entry expiry (ms from `assignedAt`). When present it overrides the\n * cache-wide default TTL — lets the server-provided `assignmentTtlMs` govern\n * how long this specific assignment stays valid. Persists across reloads.\n */\n ttlMs?: number;\n};\n\nexport type AssignmentCache = {\n get(componentId: string, segment: string): Assignment | null;\n set(componentId: string, segment: string, assignment: Assignment): void;\n invalidate(componentId: string): void;\n clear(): void;\n};\n\nconst KEY_PREFIX = '_snt_asgn_';\nconst DEFAULT_TTL_MS = 30 * 60 * 1000;\n\nfunction cacheKey(componentId: string, segment: string): string {\n // URI-encode each part (same scheme as storageKey) and join with a literal\n // ':'. Since encodeURIComponent escapes ':', the separator is unambiguous —\n // otherwise a segment like `device:source` could collide two distinct\n // (componentId, segment) pairs onto the same raw `${id}:${segment}` string.\n return `${encodeURIComponent(componentId)}:${encodeURIComponent(segment)}`;\n}\n\nfunction storageKey(componentId: string, segment: string): string {\n // Both parts are URI-encoded and joined with a literal ':' separator. Since\n // encodeURIComponent escapes ':' (and the parts can otherwise contain '_' or\n // ':'), the separator is unambiguous and the key round-trips exactly.\n return `${KEY_PREFIX}${encodeURIComponent(componentId)}:${encodeURIComponent(segment)}`;\n}\n\nfunction parseStorageKey(key: string): { componentId: string; segment: string } | null {\n const suffix = key.slice(KEY_PREFIX.length);\n const sep = suffix.indexOf(':');\n if (sep < 0) return null;\n try {\n return {\n componentId: decodeURIComponent(suffix.slice(0, sep)),\n segment: decodeURIComponent(suffix.slice(sep + 1)),\n };\n } catch {\n return null;\n }\n}\n\nfunction listStorageKeys(): string[] {\n try {\n const keys: string[] = [];\n for (let i = 0; i < localStorage.length; i++) {\n const key = localStorage.key(i);\n if (key?.startsWith(KEY_PREFIX)) {\n keys.push(key);\n }\n }\n return keys;\n } catch {\n return [];\n }\n}\n\n/**\n * Creates an assignment cache with optional TTL (default 30 minutes).\n */\nexport function createAssignmentCache(ttlMs: number = DEFAULT_TTL_MS): AssignmentCache {\n const memory = new Map<string, Assignment>();\n\n // Honor a per-entry TTL (server-provided assignmentTtlMs) when present; fall\n // back to the cache-wide default otherwise.\n const isExpired = (assignment: Assignment): boolean =>\n assignment.assignedAt + (assignment.ttlMs && assignment.ttlMs > 0 ? assignment.ttlMs : ttlMs) < Date.now();\n\n const restoreFromStorage = (): void => {\n for (const key of listStorageKeys()) {\n try {\n const raw = localStorage.getItem(key);\n if (!raw) continue;\n const assignment = JSON.parse(raw) as Assignment;\n if (isExpired(assignment)) {\n localStorage.removeItem(key);\n continue;\n }\n const parsed = parseStorageKey(key);\n if (!parsed) continue;\n memory.set(cacheKey(parsed.componentId, parsed.segment), assignment);\n } catch {\n /* ignore corrupt entries */\n }\n }\n };\n\n if (typeof window !== 'undefined') {\n restoreFromStorage();\n }\n\n return {\n get(componentId: string, segment: string): Assignment | null {\n const entry = memory.get(cacheKey(componentId, segment));\n if (!entry) return null;\n if (isExpired(entry)) {\n memory.delete(cacheKey(componentId, segment));\n return null;\n }\n return entry;\n },\n\n set(componentId: string, segment: string, assignment: Assignment): void {\n const key = cacheKey(componentId, segment);\n memory.set(key, assignment);\n try {\n localStorage.setItem(storageKey(componentId, segment), JSON.stringify(assignment));\n } catch {\n /* ignore */\n }\n },\n\n invalidate(componentId: string): void {\n // Memory keys are encodeURIComponent(componentId) + ':' + encoded segment,\n // so match on the encoded-id prefix (the ':' after it can't appear inside\n // the encoded id).\n const prefix = `${encodeURIComponent(componentId)}:`;\n for (const key of [...memory.keys()]) {\n if (key.startsWith(prefix)) {\n memory.delete(key);\n }\n }\n for (const storageK of listStorageKeys()) {\n const parsed = parseStorageKey(storageK);\n if (parsed?.componentId === componentId) {\n try {\n localStorage.removeItem(storageK);\n } catch {\n /* ignore */\n }\n }\n }\n },\n\n clear(): void {\n memory.clear();\n for (const storageK of listStorageKeys()) {\n try {\n localStorage.removeItem(storageK);\n } catch {\n /* ignore */\n }\n }\n },\n };\n}\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\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\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 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 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 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 * 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 * Decision snapshot: the SPA / return-visit pre-paint source. Written after\n * every successful decide; read by the inline pre-paint script (before any\n * framework code runs) and by init() to seed slot/persona state.\n */\nimport type { SlotResult } from './slots.js';\n\nexport const SNAPSHOT_STORAGE_KEY_PREFIX = '_snt_snap:';\n\n/** Versioned compound locator: resolve id → dataAttr → selector, then verify\n * against fingerprint. Lets a slot survive DOM/markup drift. */\nexport type CompoundLocator = {\n v?: number;\n id?: string;\n dataAttr?: { name: string; value: string };\n selector?: string;\n urlMatch?: string;\n fingerprint?: { tag?: string; text?: string };\n semanticId?: string;\n};\n\n/** Bounded, declarative operations a registry arm may apply to its element.\n * The style set is a fixed whitelist (validated server-side); no arbitrary CSS,\n * HTML, or JS ever. `text` is applied via textContent; https-only URLs.\n * moveBefore/moveAfter (exactly one) reposition the element relative to a\n * uniquely-resolving sibling anchor — post-decide only, never pre-paint. */\nexport type SlotOps = {\n text?: string;\n style?: Record<string, string>;\n hidden?: boolean;\n href?: string;\n imageSrc?: string;\n imageAlt?: string;\n moveBefore?: CompoundLocator;\n moveAfter?: CompoundLocator;\n};\n\n/** Registry-mode apply info per slot: where to apply and what to set. Stored so\n * a returning visitor's pre-paint can reapply it. `target` is the Phase-2 bare\n * selector; `locator` (Phase 3) is the compound locator, preferred when present. */\nexport type SlotConfigEntry = {\n kind: 'tokens' | 'arms';\n target?: string;\n locator?: CompoundLocator;\n content?: string;\n ops?: SlotOps;\n};\n\nexport type DecisionSnapshot = {\n v: 1;\n persona: string;\n band: 'low' | 'medium' | 'high';\n slots: Record<string, SlotResult>;\n layoutOrder: string[] | null;\n savedAt: number;\n // Additive (kept at v:1 so existing snapshots stay valid — bumping the version\n // would flush every returning visitor's snapshot and flash the baseline once).\n // Present only for registry-mode (no-code) installs.\n slotConfig?: Record<string, SlotConfigEntry>;\n};\n\nconst BANDS = ['low', 'medium', 'high'];\n\n/** Returns null on missing, corrupt, or wrong-version data — never throws. */\nexport function readSnapshot(apiKey: string): DecisionSnapshot | null {\n try {\n const raw = localStorage.getItem(SNAPSHOT_STORAGE_KEY_PREFIX + apiKey);\n if (!raw) return null;\n const p = JSON.parse(raw) as Partial<DecisionSnapshot> | null;\n if (\n !p ||\n typeof p !== 'object' ||\n p.v !== 1 ||\n typeof p.persona !== 'string' ||\n typeof p.band !== 'string' ||\n !BANDS.includes(p.band) ||\n typeof p.slots !== 'object' ||\n p.slots === null ||\n Array.isArray(p.slots) ||\n !(p.layoutOrder === null || Array.isArray(p.layoutOrder)) ||\n typeof p.savedAt !== 'number' ||\n // slotConfig is optional; when present it must be a plain object.\n !(p.slotConfig === undefined || (typeof p.slotConfig === 'object' && p.slotConfig !== null && !Array.isArray(p.slotConfig)))\n ) {\n return null;\n }\n return p as DecisionSnapshot;\n } catch {\n return null;\n }\n}\n\n/** Best-effort persist — storage failures are swallowed. */\nexport function writeSnapshot(apiKey: string, snap: DecisionSnapshot): void {\n try {\n localStorage.setItem(SNAPSHOT_STORAGE_KEY_PREFIX + apiKey, JSON.stringify(snap));\n } catch {\n /* private mode / quota — the snapshot is an optimization, never a requirement */\n }\n}\n\n/**\n * Inline pre-paint script (Rung 1a): reads the snapshot and sets\n * `data-sentient-persona` / `data-sentient-confidence` on <html> before\n * first paint. Single-writer: it never overwrites attributes already set.\n *\n * Safety properties (pinned by tests):\n * - apiKey goes through JSON.stringify, then '<' is escaped to <, so a\n * hostile key can neither break the JS string nor terminate the <script>.\n * - Built by string concatenation and contains no backticks, so the output\n * survives being embedded in template-literal-based renderers.\n */\nexport function renderPrePaintScript(apiKey: string): string {\n const key = JSON.stringify(SNAPSHOT_STORAGE_KEY_PREFIX + apiKey).replace(/</g, '\\\\u003c');\n return (\n '(function(){try{' +\n 'var r=localStorage.getItem(' + key + ');if(!r)return;' +\n 'var s=JSON.parse(r);' +\n 'if(!s||s.v!==1||typeof s.persona!==\"string\"||typeof s.band!==\"string\")return;' +\n 'var d=document.documentElement;' +\n 'if(d.hasAttribute(\"data-sentient-persona\"))return;' +\n 'd.setAttribute(\"data-sentient-persona\",s.persona);' +\n 'd.setAttribute(\"data-sentient-confidence\",s.band);' +\n '}catch(e){}})();'\n );\n}\n","import { initSession, type SessionConfig, type SessionManager } from './session';\nimport {\n createEventQueue,\n retryStorageKey,\n type EventQueue,\n type EventType,\n type QueueConfig,\n type SentientEvent,\n} from './queue';\nimport {\n createAssignmentCache,\n type Assignment,\n} from './cache';\nimport type {\n GraphConfig,\n GraphSnapshot,\n} from './graph';\nimport {\n detectDeviceClass,\n detectTrafficSource,\n detectTimeOfDay,\n referrerDomainFromReferer,\n uaTokenMatch,\n} from './session-meta.js';\nimport {\n toWireSlot,\n baselineResultFor,\n armOfResult,\n type SlotDeclInput,\n type SlotResult,\n} from './slots.js';\nimport { readSnapshot, writeSnapshot, SNAPSHOT_STORAGE_KEY_PREFIX, type SlotConfigEntry, type CompoundLocator } from './snapshot.js';\nimport { confidenceBand } from '@sentientui/policy';\nimport { createLocalModeClient } from './local-mode.js';\n\nexport { PROD_KEYLESS_ERROR, LOCAL_MODE_BANNER } from './local-mode.js';\n\nexport {\n detectDeviceClass,\n detectTrafficSource,\n detectTimeOfDay,\n deriveSessionSegment,\n referrerDomainFromReferer,\n uaTokenMatch,\n matchedAgentToken,\n agentUaList,\n} from './session-meta.js';\n\nconst DEFAULT_INGEST_URL = 'https://api.sentient-ui.com/v1/events';\n\n// Keyed by apiKey so multiple init() calls (HMR, multi-project) don't collide.\nconst _clients = new Map<string, {\n config: SentientConfig;\n upgrade: ((c: SentientClient) => void) | null;\n // Teardown for the live client bound to this key (stops its queue interval +\n // unload listeners). Present only for the full tracking client; the no-op /\n // pre-consent / local entries have nothing to tear down. Called before a\n // re-init for the same key replaces it, so timers/listeners can't leak.\n dispose?: () => void;\n}>();\nlet _lastApiKey: string | null = null;\n\nexport type SentientConfig = {\n apiKey: string;\n context: 'landing' | 'ecommerce' | 'saas' | 'marketplace';\n /** @internal — not exposed to users; defaults to the hosted SentientUI API. */\n ingestUrl?: string;\n debug?: boolean;\n /**\n * Pre-seeded assignments from `preloadAssignments()` (SSR).\n * Seeds the local cache so `assign()` returns without a network call for\n * listed code variants, guaranteeing server and client render the same\n * variant on first paint. Managed-text components (assign with no\n * variantIds) still fetch once when the seed carries no content.\n */\n initialAssignments?: Record<string, string>;\n /**\n * Segment used for SSR preload (`device:source`). When set with `initialAssignments`,\n * seeds the assignment cache under this key so hydration matches the server bandit row.\n */\n sessionSegment?: string;\n /**\n * Consent gate. When `false`, returns a no-op client and performs no tracking.\n * Defaults to `true`. Re-call `init()` (via `AdaptiveProvider` consent prop) when\n * the user grants or revokes consent mid-session.\n */\n consent?: boolean;\n /**\n * Behavior before consent is granted. `'statistical_winner'` fetches the\n * best-performing variant via `GET /v1/winner` — no session or tracking data\n * is stored. `'control'` (default) shows `variantIds[0]` with no API call.\n * Applies when tracking is gated off — either `consent: false` or an active\n * Do Not Track signal.\n */\n preConsentBehavior?: 'statistical_winner' | 'control';\n /**\n * Whether to honor the browser's Do Not Track (DNT) signal. Defaults to `true`.\n * When `true` and the visitor has DNT enabled, the SDK sets no cookies and\n * sends no tracking data — behaving exactly as `consent: false` (still serving\n * the read-only `preConsentBehavior` winner if configured), and `grantConsent()`\n * will not upgrade it. Set `false` to make your own consent gate authoritative.\n */\n respectDoNotTrack?: boolean;\n userId?: string;\n /**\n * Session ID generated server-side (from `loadAdaptiveAssignments` / `loadAdaptiveDecision`).\n * When provided, the client adopts this ID on first visit instead of generating a new one,\n * ensuring events and goals are attributed to the same session the server used for assignment.\n */\n ssrSessionId?: string;\n /**\n * ISO 3166-1 alpha-2 country code for the visitor. When provided (e.g. from\n * the `CF-IPCountry` header in a Next.js server component), it is included in\n * the session upsert so country-based segmentation works without client-side\n * geo lookup.\n */\n country?: string;\n /**\n * Pre-seeded slot results from `preloadDecisions()` / `loadAdaptiveDecision()`\n * (SSR). Seeds the local slot state so `getSlotResult()` agrees with the\n * server-rendered markup on first paint.\n */\n initialSlots?: Record<string, SlotResult>;\n /**\n * Persona decided during SSR. Takes priority over the html-attribute\n * adoption and the local snapshot.\n */\n initialPersona?: { persona: string; confidence: number };\n /**\n * Keyless local mode. 'auto' (default) simulates decisions on-device when no\n * valid API key is configured — but only in development builds (the\n * `development` export condition); production bundles physically exclude the\n * engine. `true` forces the local engine regardless of key (escape hatch);\n * `false` restores the silent keyless no-op.\n */\n localMode?: 'auto' | boolean;\n};\n\nexport type AssignResult = { variantId: string; assignmentTtlMs: number; content?: string };\n\nexport type { SlotDeclInput, SlotResult };\nexport { armOfResult, baselineResultFor, baselineSlots, toWireSlot } from './slots.js';\n\nexport {\n SNAPSHOT_STORAGE_KEY_PREFIX,\n readSnapshot,\n writeSnapshot,\n renderPrePaintScript,\n} from './snapshot.js';\nexport type { DecisionSnapshot, SlotConfigEntry, SlotOps, CompoundLocator } from './snapshot.js';\n\n/** An editor-defined goal delivered with a registry-mode decision, for the\n * snippet to install delegated listeners from. */\nexport type GoalDefinition = {\n goalId: string;\n event: 'click' | 'form_submit' | 'url_reached';\n locator?: CompoundLocator;\n urlPattern?: string;\n slotId?: string;\n};\n\n/** One served section-classification row (registry mode): where it is on the\n * page (url match + compound locator) and its semantic type. The snippet\n * resolves the locator to build capture's `typeOf` hook. */\nexport type SectionMapEntry = {\n urlMatch: string;\n locator: CompoundLocator;\n type: string;\n};\n\nexport type DecideOutcome = {\n layoutOrder: string[] | null;\n assignments: Record<string, string>;\n slots: Record<string, SlotResult>;\n persona: string;\n confidence: number;\n // Registry mode only: where/what to apply for server-defined slots.\n slotConfig?: Record<string, SlotConfigEntry>;\n // Registry mode only: editor-defined goals to wire up.\n goals?: GoalDefinition[];\n // Registry mode only: served section-classification map the snippet turns\n // into capture's `typeOf` hook.\n sectionMap?: SectionMapEntry[];\n};\n\nexport type DecideInput = {\n sections?: string[];\n components?: Array<{ id: string; variantIds?: string[] }>;\n slots?: SlotDeclInput[];\n // 'registry' → serve the project's published slot_definitions in addition to\n // any declared slots (registry wins on id collision). Default 'request'.\n slotsFrom?: 'request' | 'registry';\n /**\n * Caller's build version (e.g. the snippet's `__SNIPPET_VERSION__`), sent\n * as `v` on the wire. Additive/best-effort: the server persists it for\n * version-skew reporting (see apps/api decide route) and ignores it\n * entirely on older deployments. Omit if the caller has no version to report.\n */\n v?: string;\n};\n\nexport type WeightEntry = { variantId: string; pulls: number; avgReward: number | null };\nexport type ComponentWeightEntry = { componentId: string; updatedAt: number; variants: WeightEntry[] };\n\nexport type ComponentGoalOptions = {\n /** Reward credited to the served variant (0–1). Defaults to 1. */\n reward?: number;\n /** Extra fields merged into the event payload. */\n metadata?: Record<string, unknown>;\n};\n\nexport type SentientClient = {\n track(\n event: Omit<SentientEvent, 'id' | 'sessionId' | 'timestamp' | 'timeInSession'>,\n ): void;\n goal(name: string, metadata?: Record<string, unknown>, weight?: number, stepIndex?: number): void;\n /**\n * Records a conversion attributed to the variant currently served for\n * `componentId`, so it feeds the per-variant CVR funnel. Resolves the served\n * variant from the local assignment cache — no need to pass variantId or\n * projectId. No-ops if the component has not been assigned yet (render its\n * `<Adaptive>`/call `assign()` first). Prefer this over bare `goal()` for\n * variant experiments; `goal()` is session-level only (no component attribution).\n */\n componentGoal(componentId: string, goalType: string, opts?: ComponentGoalOptions): void;\n identify(userId: string): void;\n getAssignment(componentId: string, segment: string): Assignment | null;\n /** Server-side variant assignment. Caches the result locally per (component, segment). */\n assign(componentId: string, variantIds?: string[], agentData?: unknown, agentDataByVariant?: Record<string, unknown>): Promise<AssignResult | null>;\n /**\n * Single-roundtrip decision for layout sections, component variants, and\n * adaptive slots. Awaits the session upsert (like `assign`) so the server\n * never decides for a session row that doesn't exist yet. A response\n * without a `slots` field means the server predates slots — every declared\n * slot resolves to its baseline and no retry is made.\n */\n decide(input: DecideInput): Promise<DecideOutcome | null>;\n /** Slot result served this session (decide result, SSR seed, snapshot, or failure baseline). Null when unknown. */\n getSlotResult(slotId: string): SlotResult | null;\n /** Current persona estimate. Band is always `confidenceBand(confidence)`. Null when nothing is known yet. */\n getPersona(): { persona: string; confidence: number; band: 'low' | 'medium' | 'high' } | null;\n /** Fetches current bandit weights for all components in this project. Used by the provider to keep live-weight polling fresh. */\n fetchWeights(): Promise<ComponentWeightEntry[]>;\n getGraph(): GraphSnapshot;\n /**\n * Routine teardown: stops timers/listeners and flushes pending events, but\n * KEEPS the visitor identity, decision snapshot, and retry bucket. Use for\n * component unmount / re-init (framework providers call this on cleanup).\n */\n dispose(): void;\n /**\n * Consent-revocation / forget-me teardown: everything `dispose()` does,\n * plus deletion of the visitor identity (`_snt_uid`), the decision\n * snapshot, and the persisted retry bucket. The next visit starts as a\n * brand-new visitor.\n */\n destroy(): void;\n /** True when this client is the keyless local-mode client (dev only). */\n readonly isLocal?: boolean;\n};\n\n// SSR preload helpers moved to the `@sentientui/core/server` entry in 0.6.0 so\n// ~200 lines of Node-only fetch logic stop shipping in the browser bundle.\n\nexport { attachMicroSignalDetectors } from './micro-signals.js';\nexport type { MicroSignalEmitter, MicroSignalType } from './micro-signals.js';\n\nexport type {\n SessionConfig,\n SessionManager,\n EventType,\n SentientEvent,\n QueueConfig,\n Assignment,\n GraphSnapshot,\n GraphConfig,\n};\n\nfunction generateEventId(): string {\n try {\n if (typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function') {\n return crypto.randomUUID();\n }\n } catch {\n /* fall through */\n }\n // Fallback: RFC 4122 v4 UUID built from Math.random() — avoids invalid UUID\n // syntax that would cause `invalid input syntax for type uuid` on INSERT.\n return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {\n const r = (Math.random() * 16) | 0;\n return (c === 'x' ? r : (r & 0x3) | 0x8).toString(16);\n });\n}\n\nconst SSR_CLIENT: SentientClient = {\n track: () => undefined,\n goal: () => undefined,\n componentGoal: () => undefined,\n identify: () => undefined,\n getAssignment: () => null,\n assign: () => Promise.resolve(null),\n decide: () => Promise.resolve(null),\n getSlotResult: () => null,\n getPersona: () => null,\n fetchWeights: () => Promise.resolve([]),\n getGraph: () => ({ pageNodes: [], capturedAt: 0 }),\n dispose: () => undefined,\n destroy: () => undefined,\n};\n\nfunction readUtmParams(): Record<string, string> {\n try {\n const out: Record<string, string> = {};\n const sp = new URLSearchParams(window.location.search);\n for (const [k, v] of sp) {\n if (k.startsWith('utm_')) out[k] = v;\n }\n return out;\n } catch {\n return {};\n }\n}\n\nfunction deriveBaseUrl(ingestUrl: string): string {\n return ingestUrl.replace(/\\/events\\/?$/, '');\n}\n\n/**\n * Detects whether the visitor has signalled a tracking opt-out. Honors Global\n * Privacy Control (`navigator.globalPrivacyControl`) — the legally-enforceable\n * CCPA/CPRA signal — as well as Do Not Track (`navigator.doNotTrack`, the legacy\n * `window.doNotTrack` on older Firefox, and `navigator.msDoNotTrack` on old\n * IE/Edge). GPC is a boolean; DNT is opt-out only when explicitly `'1'`/`'yes'`.\n */\nexport function isDoNotTrackEnabled(): boolean {\n // GPC is a boolean flag, checked separately from the DNT string signals.\n if (\n typeof navigator !== 'undefined' &&\n (navigator as unknown as { globalPrivacyControl?: boolean }).globalPrivacyControl === true\n ) {\n return true;\n }\n const signals = [\n typeof navigator !== 'undefined' ? navigator.doNotTrack : undefined,\n typeof window !== 'undefined'\n ? (window as unknown as { doNotTrack?: string | null }).doNotTrack\n : undefined,\n typeof navigator !== 'undefined'\n ? (navigator as unknown as { msDoNotTrack?: string | null }).msDoNotTrack\n : undefined,\n ];\n return signals.some((v) => v === '1' || v === 'yes');\n}\n\n/**\n * Upgrades a pre-consent client (created with `consent: false, preConsentBehavior: 'statistical_winner'`)\n * to a fully-tracking client. Call this from your consent management platform callback.\n * For React apps, prefer updating the `consent` prop on `<AdaptiveProvider>`.\n * Pass `apiKey` to target a specific project; omit to upgrade the most-recently-initialized client.\n */\nexport function grantConsent(apiKey?: string): void {\n if (typeof window === 'undefined') return;\n\n const key = apiKey ?? _lastApiKey;\n if (!key) {\n console.warn('[sentient] grantConsent() called before init()');\n return;\n }\n\n const entry = _clients.get(key);\n if (!entry) {\n console.warn('[sentient] grantConsent() called before init()');\n return;\n }\n\n const { config, upgrade } = entry;\n if (!upgrade) return;\n\n // Honor an active Do Not Track signal — consent cannot override a global opt-out.\n if (config.respectDoNotTrack !== false && isDoNotTrackEnabled()) return;\n\n const fullClient = init({ ...config, consent: true });\n upgrade(fullClient);\n // init() just registered the full client (with its dispose) under `key`.\n // Preserve that dispose so a later re-init/teardown can still tear it down —\n // we only need to clear the upgrade hook now that consent is granted.\n const disposed = _clients.get(key)?.dispose;\n _clients.set(key, { config: { ...config, consent: true }, upgrade: null, dispose: disposed });\n}\n\nfunction createPreConsentProxy(config: SentientConfig): { proxy: SentientClient; setInner: (c: SentientClient) => void } {\n const baseUrl = deriveBaseUrl(config.ingestUrl ?? DEFAULT_INGEST_URL);\n const authHeaders = {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${config.apiKey}`,\n } as const;\n\n let inner: SentientClient = {\n track: () => undefined,\n goal: () => undefined,\n componentGoal: () => undefined,\n identify: () => undefined,\n getAssignment: () => null,\n fetchWeights: () => Promise.resolve([]),\n async assign(componentId, variantIds, _agentData?) {\n try {\n const params = new URLSearchParams({ componentId });\n for (const v of variantIds ?? []) params.append('variantIds[]', v);\n const res = await fetch(`${baseUrl}/winner?${params.toString()}`, {\n headers: authHeaders,\n });\n if (!res.ok) return variantIds?.[0] ? { variantId: variantIds[0], assignmentTtlMs: 0 } : null;\n const body = (await res.json()) as { variantId: string };\n return { variantId: body.variantId, assignmentTtlMs: 0 };\n } catch {\n return variantIds?.[0] ? { variantId: variantIds[0], assignmentTtlMs: 0 } : null;\n }\n },\n decide: () => Promise.resolve(null),\n getSlotResult: () => null,\n getPersona: () => null,\n getGraph: () => ({ pageNodes: [], capturedAt: 0 }),\n dispose: () => undefined,\n destroy: () => undefined,\n };\n\n const proxy: SentientClient = {\n track: (e) => inner.track(e),\n goal: (n, m, w, s) => inner.goal(n, m, w, s),\n componentGoal: (c, g, o) => inner.componentGoal(c, g, o),\n identify: (u) => inner.identify(u),\n getAssignment: (c, s) => inner.getAssignment(c, s),\n assign: (c, v, a, av) => inner.assign(c, v, a, av),\n decide: (i) => inner.decide(i),\n getSlotResult: (s) => inner.getSlotResult(s),\n getPersona: () => inner.getPersona(),\n fetchWeights: () => inner.fetchWeights(),\n getGraph: () => inner.getGraph(),\n dispose: () => inner.dispose(),\n destroy: () => inner.destroy(),\n };\n\n function setInner(fullClient: SentientClient) {\n inner = fullClient;\n }\n\n return { proxy, setInner };\n}\n\n/**\n * Initializes the Sentient client. Returns a no-op client during SSR.\n */\nexport function init(config: SentientConfig): SentientClient {\n if (typeof window === 'undefined') {\n return SSR_CLIENT;\n }\n\n _lastApiKey = config.apiKey;\n\n // A re-init for the same key (HMR, consent toggle, provider remount) supersedes\n // the prior client. Dispose it first so its queue's setInterval and\n // visibilitychange/pagehide listeners don't leak — the map only ever held its\n // config, so without this the old client kept flushing forever.\n const prevEntry = _clients.get(config.apiKey || 'local');\n if (prevEntry?.dispose) {\n try {\n prevEntry.dispose();\n } catch {\n /* teardown must never throw on re-init */\n }\n }\n\n // DNT/GPC (a global opt-out) or an explicit `consent: false` must be evaluated\n // BEFORE the local-mode branch: createLocalModeClient() calls initSession()\n // unconditionally, so a gated visitor would otherwise be issued the 365-day\n // `_snt_uid` identity cookie in keyless/local mode (audit P2). DNT/GPC also\n // gates tracking off even when the site passes `consent: true`, and blocks\n // `grantConsent()` from upgrading.\n const dntBlocked = config.respectDoNotTrack !== false && isDoNotTrackEnabled();\n const gated = config.consent === false || dntBlocked;\n\n // Keyless local mode. `localMode: true` forces the local engine (documented\n // escape hatch); 'auto' (default) engages it only when no valid key is\n // present. In production builds `@sentientui/core/local` resolves to a stub\n // and this degrades to a no-op client + one console.error per page load\n // (createLocalModeClient handles that), so no NODE_ENV check is needed here.\n const keyValid = typeof config.apiKey === 'string' && config.apiKey.startsWith('pk_');\n if (config.localMode === true || (!keyValid && config.localMode !== false)) {\n // A gated visitor must never get the identity cookie. Local mode has no\n // server to serve a statistical winner from, so return a plain no-op.\n if (gated) {\n _clients.set(config.apiKey || 'local', { config, upgrade: null });\n return SSR_CLIENT;\n }\n _clients.set(config.apiKey || 'local', { config, upgrade: null });\n return createLocalModeClient(config);\n }\n\n if (gated) {\n if (config.preConsentBehavior === 'statistical_winner') {\n if (!config.apiKey || !config.apiKey.startsWith('pk_')) {\n console.warn('[sentient] init() called with an invalid apiKey — expected a pk_ public key. SDK disabled.');\n return SSR_CLIENT;\n }\n const { proxy, setInner } = createPreConsentProxy(config);\n // Under DNT the read-only winner still serves, but consent can never\n // upgrade it to tracking — so drop the upgrade hook.\n _clients.set(config.apiKey, { config, upgrade: dntBlocked ? null : setInner });\n return proxy;\n }\n _clients.set(config.apiKey, { config, upgrade: null });\n return SSR_CLIENT;\n }\n\n if (!config.apiKey || !config.apiKey.startsWith('pk_')) {\n console.warn('[sentient] init() called with an invalid apiKey — expected a pk_ public key. SDK disabled.');\n return SSR_CLIENT;\n }\n\n if (config.ingestUrl === '') {\n console.warn('[sentient] init() called with an empty ingestUrl. SDK disabled.');\n return SSR_CLIENT;\n }\n\n const resolvedIngestUrl = config.ingestUrl ?? DEFAULT_INGEST_URL;\n\n const sessionStart = Date.now();\n const session = initSession({ ssrSessionId: config.ssrSessionId });\n const assignmentCache = createAssignmentCache();\n const eventQueue = createEventQueue({ ingestUrl: resolvedIngestUrl, apiKey: config.apiKey });\n const baseUrl = deriveBaseUrl(resolvedIngestUrl);\n\n const authHeaders = {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${config.apiKey}`,\n } as const;\n\n const deviceClass = detectDeviceClass(navigator.userAgent ?? '');\n const appOrigin = typeof window !== 'undefined' ? window.location.origin : undefined;\n const trafficSource = detectTrafficSource(document.referrer ?? '', appOrigin);\n const sessionSegment =\n config.sessionSegment ?? `${deviceClass}:${trafficSource}`;\n const inflightAssigns = new Map<string, Promise<AssignResult | null>>();\n\n // --- Adaptive-slot state (decide) ---\n // Results served for this session, keyed by slot id. Written by decide();\n // read by getSlotResult() (Task 3.3) and componentGoal's slot fallback.\n const slotStore = new Map<string, SlotResult>();\n let personaState: { persona: string; confidence: number } | null = null;\n\n // On decide failure every declared slot must still resolve — to its baseline.\n // Never overwrite a previously served result.\n const seedSlotBaselines = (decls: SlotDeclInput[]): void => {\n for (const d of decls) {\n if (!slotStore.has(d.id)) slotStore.set(d.id, baselineResultFor(d));\n }\n };\n\n // Seed slot/persona state. Priority: explicit SSR seeds → snapshot.\n if (config.initialSlots) {\n for (const [slotId, result] of Object.entries(config.initialSlots)) {\n slotStore.set(slotId, result);\n }\n }\n const seedSnapshot = readSnapshot(config.apiKey);\n if (seedSnapshot) {\n for (const [slotId, result] of Object.entries(seedSnapshot.slots)) {\n if (!slotStore.has(slotId)) slotStore.set(slotId, result);\n }\n }\n\n // Band-only persona sources (html attrs, snapshot) become a band-consistent\n // numeric confidence so confidenceBand(confidence) always equals the band.\n const BAND_CONFIDENCE: Record<string, number> = { low: 0.15, medium: 0.5, high: 0.85 };\n if (config.initialPersona) {\n personaState = { ...config.initialPersona };\n } else {\n // Single-writer rule: the inline pre-paint script owns the <html>\n // attributes. The client ADOPTS them as truth and never rewrites them\n // mid-session (next visit's script picks up the new snapshot instead).\n const ds = document.documentElement.dataset;\n if (ds.sentientPersona) {\n personaState = {\n persona: ds.sentientPersona,\n confidence: BAND_CONFIDENCE[ds.sentientConfidence ?? 'low'] ?? 0.15,\n };\n } else if (seedSnapshot) {\n personaState = {\n persona: seedSnapshot.persona,\n confidence: BAND_CONFIDENCE[seedSnapshot.band] ?? 0.15,\n };\n }\n }\n\n // Seed SSR-preloaded assignments into the local cache so assign() finds a\n // cache hit immediately — no network call, no variant flash on hydration.\n if (config.initialAssignments) {\n for (const [componentId, variantId] of Object.entries(config.initialAssignments)) {\n assignmentCache.set(componentId, sessionSegment, {\n variantId,\n assignedAt: Date.now(),\n segment: sessionSegment,\n confidence: 1,\n });\n }\n }\n\n // Upsert session metadata once on init. assign() awaits this promise so\n // the server isn't asked to assign for a session row that doesn't exist yet.\n let sessionReady: Promise<void> = Promise.resolve();\n\n const sessionId = session.getSessionId();\n if (sessionId) {\n const referrerDomain = referrerDomainFromReferer(document.referrer ?? '');\n const sessionBody = {\n sessionId,\n deviceClass,\n trafficSource,\n referrerDomain,\n utmParams: readUtmParams(),\n timeOfDay: detectTimeOfDay(new Date()),\n dayOfWeek: ['sun', 'mon', 'tue', 'wed', 'thu', 'fri', 'sat'][new Date().getDay()],\n ephemeral: session.isEphemeral(),\n // Likely-automation hint: navigator.webdriver (set under automation\n // control) or a known agent token in the UA. Probabilistic — used for\n // metrics + bandit exclusion server-side, never to change what's served.\n automation:\n (typeof navigator !== 'undefined' && navigator.webdriver === true) ||\n uaTokenMatch(navigator.userAgent ?? ''),\n ...(config.userId ? { userId: config.userId } : {}),\n ...(config.country ? { country: config.country } : {}),\n };\n try {\n sessionReady = fetch(`${baseUrl}/sessions`, {\n method: 'POST',\n keepalive: true,\n body: JSON.stringify(sessionBody),\n headers: authHeaders,\n })\n .then((res) => {\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 }\n return undefined;\n })\n .catch(() => undefined);\n } catch {\n /* never throw on init */\n }\n }\n\n if (config.debug) {\n console.log('[sentient] initialized', { context: config.context });\n (\n window as unknown as {\n __sentient?: {\n client: SentientClient;\n queue: EventQueue;\n };\n }\n ).__sentient = {\n client: null as unknown as SentientClient,\n queue: eventQueue,\n };\n }\n\n const client: SentientClient = {\n goal(name, metadata = {}, weight = 1.0, stepIndex = 0) {\n const sid = session.getSessionId();\n if (!sid) return;\n const goalId = generateEventId();\n sessionReady.then(() => {\n fetch(`${baseUrl}/goals`, {\n method: 'POST',\n keepalive: true,\n body: JSON.stringify({ sessionId: sid, name, metadata, weight, stepIndex, goalId }),\n headers: authHeaders,\n }).catch(() => undefined);\n });\n },\n\n componentGoal(componentId, goalType, opts) {\n const sid = session.getSessionId();\n if (!sid) return;\n // Variant experiments resolve from the assignment cache; adaptive slots\n // (useAdaptiveTokens / AdaptiveGroup) resolve from the slot state, using\n // the canonical arm string as the attributed variantId.\n const assignment = assignmentCache.get(componentId, sessionSegment);\n const slotResult = assignment ? null : slotStore.get(componentId) ?? null;\n if (!assignment && slotResult === null) {\n if (config.debug) {\n console.warn(\n `[sentient] componentGoal(\"${componentId}\"): no assignment or slot decision yet — render its <Adaptive>/adaptive hook or call assign()/decide() before recording a goal.`,\n );\n }\n return;\n }\n const attributedVariantId = assignment ? assignment.variantId : armOfResult(slotResult!);\n const fullEvent: SentientEvent = {\n id: generateEventId(),\n sessionId: sid,\n projectId: config.apiKey,\n componentId,\n variantId: attributedVariantId,\n eventType: 'goal_achieved',\n goalType,\n payload: { reward: opts?.reward ?? 1, ...(opts?.metadata ?? {}) },\n timestamp: Date.now(),\n timeInSession: Date.now() - sessionStart,\n };\n if (config.debug) {\n console.log('[sentient] componentGoal', fullEvent);\n }\n sessionReady.then(() => eventQueue.push(fullEvent));\n },\n\n identify(userId) {\n const sid = session.getSessionId();\n if (!sid) return;\n sessionReady.then(() => {\n fetch(`${baseUrl}/sessions`, {\n method: 'POST',\n keepalive: true,\n body: JSON.stringify({ sessionId: sid, userId, ephemeral: session.isEphemeral() }),\n headers: authHeaders,\n }).catch(() => undefined);\n });\n },\n\n track(event) {\n const sessionId = session.getSessionId();\n if (!sessionId) return;\n\n const fullEvent: SentientEvent = {\n ...event,\n id: generateEventId(),\n sessionId,\n timestamp: Date.now(),\n timeInSession: Date.now() - sessionStart,\n };\n\n if (config.debug) {\n console.log('[sentient] track', fullEvent);\n }\n\n sessionReady.then(() => eventQueue.push(fullEvent));\n },\n\n getAssignment(componentId, segment) {\n return assignmentCache.get(componentId, segment);\n },\n\n async assign(componentId, variantIds, agentData?, agentDataByVariant?) {\n const sid = session.getSessionId();\n if (!sid) return null;\n\n const cached = assignmentCache.get(componentId, sessionSegment);\n // When variantIds are provided (A/B code variant), a cache hit is always final.\n // When variantIds are absent (managed text component), only hit the cache if content\n // is present — a seed from initialAssignments has no content and must still fetch.\n if (cached && (variantIds?.length || cached.content !== undefined)) {\n // Surface the entry's remaining TTL (server-provided when set) instead of\n // a hardcoded 0, so callers can reason about when a re-assign is due.\n const remainingTtlMs =\n cached.ttlMs && cached.ttlMs > 0\n ? Math.max(0, cached.assignedAt + cached.ttlMs - Date.now())\n : 0;\n return { variantId: cached.variantId, assignmentTtlMs: remainingTtlMs, content: cached.content };\n }\n\n // Coalesce concurrent assigns for the same component (e.g. several\n // mounted slots sharing one id) into a single network request.\n const inflight = inflightAssigns.get(componentId);\n if (inflight) return inflight;\n\n const request = (async (): Promise<AssignResult | null> => {\n await sessionReady;\n try {\n const body: Record<string, unknown> = { sessionId: sid, componentId, variantIds };\n if (agentDataByVariant !== undefined) body.agentDataByVariant = agentDataByVariant;\n else if (agentData !== undefined) body.agentData = agentData;\n const res = await fetch(`${baseUrl}/assign`, {\n method: 'POST',\n body: JSON.stringify(body),\n headers: authHeaders,\n });\n if (!res.ok) return null;\n const result = (await res.json()) as AssignResult;\n assignmentCache.set(componentId, sessionSegment, {\n variantId: result.variantId,\n assignedAt: Date.now(),\n segment: sessionSegment,\n confidence: 1,\n content: result.content,\n // Honor the server's TTL as this entry's expiry; omit when absent/0\n // so the cache falls back to its default (DEFAULT_TTL_MS).\n ...(result.assignmentTtlMs && result.assignmentTtlMs > 0\n ? { ttlMs: result.assignmentTtlMs }\n : {}),\n });\n return result;\n } catch {\n return null;\n } finally {\n inflightAssigns.delete(componentId);\n }\n })();\n inflightAssigns.set(componentId, request);\n return request;\n },\n\n async decide(input) {\n const sid = session.getSessionId();\n if (!sid) return null;\n const declared = input.slots ?? [];\n await sessionReady;\n try {\n const body: Record<string, unknown> = { sessionId: sid };\n if (input.sections && input.sections.length > 0) {\n body.sections = input.sections.map((id) => ({ id }));\n }\n body.components = input.components ?? [];\n if (declared.length > 0) body.slots = declared.map(toWireSlot);\n if (input.slotsFrom === 'registry') body.slotsFrom = 'registry';\n if (input.v) body.v = input.v;\n\n const res = await fetch(`${baseUrl}/decide`, {\n method: 'POST',\n body: JSON.stringify(body),\n headers: authHeaders,\n });\n if (!res.ok) {\n seedSlotBaselines(declared);\n return null;\n }\n const data = (await res.json()) as {\n layoutOrder?: string[] | null;\n assignments?: Record<string, string>;\n slots?: Record<string, SlotResult>;\n slotConfig?: Record<string, SlotConfigEntry>;\n goals?: GoalDefinition[];\n sectionMap?: SectionMapEntry[];\n persona?: string;\n confidence?: number;\n };\n\n const slots: Record<string, SlotResult> = {};\n for (const d of declared) {\n // `data.slots === undefined` means the server predates the slots\n // contract: serve the declared baseline everywhere, do NOT retry.\n // (Distinct from `slots: {}`, which also falls back per-slot.)\n slots[d.id] = data.slots?.[d.id] ?? baselineResultFor(d);\n }\n // Registry mode: the server returns slots the request never declared.\n // Take them verbatim (classic mode returns only declared slots, so this\n // union is a no-op there — back-compatible).\n if (data.slots) {\n for (const [slotId, result] of Object.entries(data.slots)) {\n if (!(slotId in slots)) slots[slotId] = result;\n }\n }\n for (const [slotId, result] of Object.entries(slots)) slotStore.set(slotId, result);\n // Only overwrite persona when the response actually carries one, and\n // never downgrade a known persona to 'unknown' — a decide that omits\n // persona (or returns 'unknown') must not clobber a good SSR/snapshot/\n // initialPersona value, and must not persist that regression below.\n const known = personaState != null && personaState.persona !== 'unknown';\n if (data.persona && !(data.persona === 'unknown' && known)) {\n personaState = { persona: data.persona, confidence: data.confidence ?? 0 };\n } else if (!personaState) {\n personaState = { persona: 'unknown', confidence: 0 };\n }\n\n // Seed component assignments so <Adaptive>/assign() agree with this\n // decide (same shape as the initialAssignments seed above).\n for (const [componentId, variantId] of Object.entries(data.assignments ?? {})) {\n assignmentCache.set(componentId, sessionSegment, {\n variantId,\n assignedAt: Date.now(),\n segment: sessionSegment,\n confidence: 1,\n });\n }\n\n // Persist for the next visit's pre-paint (SPA cache-first pattern).\n writeSnapshot(config.apiKey, {\n v: 1,\n persona: personaState.persona,\n band: confidenceBand(personaState.confidence),\n slots: Object.fromEntries(slotStore),\n layoutOrder: data.layoutOrder ?? null,\n savedAt: Date.now(),\n ...(data.slotConfig ? { slotConfig: data.slotConfig } : {}),\n });\n\n return {\n layoutOrder: data.layoutOrder ?? null,\n assignments: data.assignments ?? {},\n slots,\n persona: personaState.persona,\n confidence: personaState.confidence,\n ...(data.slotConfig ? { slotConfig: data.slotConfig } : {}),\n ...(data.goals ? { goals: data.goals } : {}),\n ...(data.sectionMap ? { sectionMap: data.sectionMap } : {}),\n };\n } catch {\n seedSlotBaselines(declared);\n return null;\n }\n },\n\n getSlotResult(slotId) {\n return slotStore.get(slotId) ?? null;\n },\n\n getPersona() {\n if (!personaState) return null;\n return {\n persona: personaState.persona,\n confidence: personaState.confidence,\n band: confidenceBand(personaState.confidence),\n };\n },\n\n async fetchWeights() {\n try {\n const res = await fetch(`${baseUrl}/weights`, { headers: authHeaders });\n if (!res.ok) return [];\n const data = (await res.json()) as { components: ComponentWeightEntry[] };\n return data.components ?? [];\n } catch {\n return [];\n }\n },\n\n getGraph() {\n return { pageNodes: [], capturedAt: 0 };\n },\n\n dispose() {\n // Stops the flush timer and unload listeners (with a final flush) but\n // leaves identity, snapshot, and retry bucket for the next client.\n eventQueue.destroy();\n // Drop the registry entry so a later re-init doesn't try to dispose an\n // already-torn-down client (and so the map doesn't pin this closure).\n if (_clients.get(config.apiKey)?.dispose === client.dispose) {\n _clients.delete(config.apiKey);\n }\n if (config.debug) {\n console.log('[sentient] disposed');\n }\n },\n\n destroy() {\n eventQueue.destroy();\n session.destroy();\n if (_clients.get(config.apiKey)?.dispose === client.dispose) {\n _clients.delete(config.apiKey);\n }\n // Forget-me must be total: a surviving decision snapshot would\n // re-personalize the next visit via the pre-paint script, and a\n // persisted retry bucket would re-send events for the deleted identity.\n try {\n localStorage.removeItem(SNAPSHOT_STORAGE_KEY_PREFIX + config.apiKey);\n localStorage.removeItem(retryStorageKey(config.apiKey));\n } catch {\n /* storage unavailable — nothing persisted to remove */\n }\n if (config.debug) {\n console.log('[sentient] destroyed');\n }\n },\n };\n\n _clients.set(config.apiKey, { config, upgrade: null, dispose: client.dispose });\n\n if (config.debug) {\n const win = window as unknown as { __sentient?: { client: SentientClient } };\n if (win.__sentient) {\n win.__sentient.client = client;\n }\n }\n\n return client;\n}\n","/**\n * Keyless local mode — client factory.\n *\n * This module ships in the MAIN core bundle and contains no engine code: the\n * engine arrives through a dynamic import of the bare specifier\n * `@sentientui/core/local`, which the consumer's bundler resolves through the\n * development/production export conditions. In production builds that\n * specifier is the stub, and this client degrades to a no-op plus one\n * console.error per page load.\n */\nimport { initSession } from './session.js';\nimport { writeSnapshot } from './snapshot.js';\nimport { confidenceBand } from '@sentientui/policy';\nimport type {\n DecideOutcome,\n SentientClient,\n SentientConfig,\n SlotDeclInput,\n} from './index.js';\n\ntype LocalEngineModule = {\n LOCAL_ENGINE_AVAILABLE: boolean;\n createLocalEngine(opts: { sessionId: string; forcedPersona?: string }): {\n decide(input: {\n sections?: string[];\n components?: Array<{ id: string; variantIds?: string[] }>;\n slots?: SlotDeclInput[];\n }): DecideOutcome;\n };\n};\n\nexport const PROD_KEYLESS_ERROR =\n '[sentient] No API key configured — nothing is being learned. Set NEXT_PUBLIC_SENTIENT_API_KEY or pass localMode: true for local development.';\n\nexport const LOCAL_MODE_BANNER =\n '[sentient] Local mode — decisions are simulated on-device and nothing is sent over the network. Add an API key to learn from real traffic.';\n\n// Once-per-page-load guards (module state resets on reload).\nlet bannerShown = false;\nlet prodErrorShown = false;\n\n/** @internal test hook */\nexport function __resetLocalModeLogGuards(): void {\n bannerShown = false;\n prodErrorShown = false;\n}\n\n/** Mirrors the ?sentient_variant= override pattern; validated by the engine. */\nfunction readPersonaOverrideFromUrl(): string | undefined {\n try {\n return new URLSearchParams(window.location.search).get('sentient_persona') ?? undefined;\n } catch {\n return undefined;\n }\n}\n\nexport function createLocalModeClient(config: SentientConfig): SentientClient {\n const session = initSession({ ssrSessionId: config.ssrSessionId });\n const sessionId = session.getSessionId() ?? 'local';\n const forcedPersona = readPersonaOverrideFromUrl();\n\n // The specifier MUST stay a bare package subpath (never a relative path) so\n // the consumer's bundler applies the development/production export\n // conditions. tsup keeps it external (see tsup.config.ts).\n const modPromise: Promise<LocalEngineModule | null> = import('@sentientui/core/local')\n .then((mod) => {\n const m = mod as unknown as LocalEngineModule;\n if (!m.LOCAL_ENGINE_AVAILABLE) {\n if (!prodErrorShown) {\n prodErrorShown = true;\n console.error(PROD_KEYLESS_ERROR);\n }\n return null;\n }\n if (!bannerShown) {\n bannerShown = true;\n console.info(LOCAL_MODE_BANNER);\n }\n return m;\n })\n .catch(() => {\n if (!prodErrorShown) {\n prodErrorShown = true;\n console.error(PROD_KEYLESS_ERROR);\n }\n return null;\n });\n\n let lastOutcome: DecideOutcome | null = null;\n\n function applyPersonaAttributes(outcome: DecideOutcome): void {\n // Single-writer rule: adopt attributes already written (e.g. by the\n // AdaptiveRoot inline script); only write when nothing has yet.\n const el = document.documentElement;\n if (el.dataset.sentientPersona === undefined) {\n el.dataset.sentientPersona = outcome.persona;\n el.dataset.sentientConfidence = confidenceBand(outcome.confidence);\n }\n }\n\n return {\n isLocal: true,\n\n async decide(input) {\n const mod = await modPromise;\n if (!mod) return null;\n const outcome = mod.createLocalEngine({ sessionId, forcedPersona }).decide(input);\n // Accumulate across calls: slots decide lazily one at a time (per-slot\n // decide from useSlotResult), so a later decide must not evict results\n // an earlier one served. Same (sessionId, persona) → merging is safe.\n lastOutcome = {\n ...outcome,\n layoutOrder: outcome.layoutOrder ?? lastOutcome?.layoutOrder ?? null,\n slots: { ...(lastOutcome?.slots ?? {}), ...outcome.slots },\n };\n writeSnapshot(config.apiKey || 'local', {\n v: 1,\n persona: lastOutcome.persona,\n band: confidenceBand(lastOutcome.confidence),\n slots: lastOutcome.slots,\n layoutOrder: lastOutcome.layoutOrder,\n savedAt: Date.now(),\n });\n applyPersonaAttributes(outcome);\n return outcome;\n },\n\n getSlotResult(slotId) {\n return lastOutcome?.slots[slotId] ?? config.initialSlots?.[slotId] ?? null;\n },\n\n getPersona() {\n if (!lastOutcome) return null;\n return {\n persona: lastOutcome.persona,\n confidence: lastOutcome.confidence,\n band: confidenceBand(lastOutcome.confidence),\n };\n },\n\n async assign(componentId, variantIds) {\n const mod = await modPromise;\n if (!mod || !variantIds || variantIds.length === 0) {\n return variantIds?.[0] ? { variantId: variantIds[0], assignmentTtlMs: 0 } : null;\n }\n const outcome = mod\n .createLocalEngine({ sessionId, forcedPersona })\n .decide({ components: [{ id: componentId, variantIds }] });\n return { variantId: outcome.assignments[componentId] ?? variantIds[0], assignmentTtlMs: 0 };\n },\n\n // Local mode never talks to the network: the tracking surface no-ops.\n track: () => undefined,\n goal: () => undefined,\n componentGoal: () => undefined,\n identify: () => undefined,\n getAssignment: () => null,\n fetchWeights: () => Promise.resolve([]),\n getGraph: () => ({ pageNodes: [], capturedAt: 0 }),\n dispose: () => undefined, // no timers/network in local mode; keep the session\n destroy: () => session.destroy(),\n };\n}\n","// Semantic section classification for no-code section capture (Phase 3 §2.4).\n// Pure heuristic: element → one of the graph_nodes semantic_type enum. Mirrors\n// the SDK graph scanner's vocabulary so the persona × section matrix consumes\n// snippet-captured sections unchanged.\n//\n// The classifier is split into a pure feature-based core (classifyFeatures —\n// also used server-side on crawled HTML by the site-audit classification job)\n// and a DOM wrapper (classifySection). Content-based patterns detect\n// pricing/social-proof/trust/comparison from BODY TEXT, so div-soup pages with\n// uninformative ids/classes still classify (persona-coverage spec 2026-07-23).\n\nexport type SemanticType =\n | 'pricing' | 'hero' | 'social_proof' | 'cta' | 'features'\n | 'faq' | 'comparison' | 'trust' | 'navigation' | 'generic';\n\n/** Canonical vocabulary — mirrors the graph_nodes semantic_type CHECK enum. */\nexport const SEMANTIC_TYPES: readonly SemanticType[] = [\n 'pricing', 'hero', 'social_proof', 'cta', 'features',\n 'faq', 'comparison', 'trust', 'navigation', 'generic',\n] as const;\n\n/** Environment-agnostic section features — buildable from a browser Element or\n * a server-parsed node (node-html-parser). */\nexport type SectionFeatures = {\n tag: string; // lowercase tag name\n idClass: string; // `${id} ${className}`\n headingText: string;\n bodyText: string; // normalized text content, first 2000 chars\n actionCount: number;\n textLength: number;\n};\n\n// Ordered most-specific first — the first keyword hit wins.\nconst KEYWORDS: Array<[SemanticType, RegExp]> = [\n ['pricing', /\\b(pricing|price|plans?|subscriptions?|per month|\\/mo|tier)\\b/i],\n ['faq', /\\b(faq|frequently asked|common questions?)\\b/i],\n ['comparison', /\\b(compare|comparison|versus|vs\\.)\\b/i],\n ['social_proof', /\\b(testimonial|reviews?|trusted by|loved by|customers?|logos|rated|case stud)\\b/i],\n ['trust', /\\b(security|guarantee|privacy|compliance|secure|gdpr|soc ?2|encrypt)\\b/i],\n ['features', /\\b(features?|how it works|benefits?|capabilit|what you get)\\b/i],\n];\n\n// Content-evidence patterns — run against bodyText. Ordered most-specific first.\n// Deliberately conservative: pricing needs per-period/plan context next to money\n// so an article mentioning \"$5 million\" stays generic.\nconst CONTENT_PATTERNS: Array<[SemanticType, RegExp]> = [\n ['pricing', /(?:[$€£]\\s?\\d[\\d,.]*\\s*(?:\\/|per\\s)\\s*(?:mo|month|yr|year|seat|user))|(?:\\b(?:starter|basic|pro|growth|premium|enterprise)\\b[^.]{0,60}[$€£]\\s?\\d)/i],\n ['social_proof', /(?:★{2,})|(?:\\b\\d(?:\\.\\d)?\\s*(?:out of|\\/)\\s*5\\b)|(?:\\brated\\b)|(?:[\"“][^\"”]{20,160}[\"”]\\s*[—–-]\\s*[A-Z][a-z]+)/],\n ['trust', /\\b(?:money[- ]back guarantee|refund(?:s)? within|encrypted|soc ?2|iso ?27001|gdpr[- ]compliant|cancel anytime)\\b/i],\n ['comparison', /\\b(?:vs|versus)\\b\\.?[^.!?]{0,80}\\b(?:compare|comparison|plans?|features?|alternative)\\b|\\bhow (?:we|it) compares?\\b/i],\n];\n\nfunction headingText(el: Element): string {\n const h = el.querySelector('h1, h2, h3');\n return (h?.textContent ?? '').slice(0, 160);\n}\n\n/**\n * Pure classification over extracted features. `strong` = keyword or content\n * evidence (trustable enough to auto-apply); `weak` = structural fallback\n * (cta/hero/navigation/generic — capture-worthy but not persona evidence).\n */\nexport function classifyFeatures(f: SectionFeatures): { type: SemanticType; strength: 'strong' | 'weak' } {\n if (f.tag === 'nav' || f.tag === 'footer') return { type: 'navigation', strength: 'weak' };\n const hay = `${f.idClass} ${f.headingText}`.toLowerCase();\n for (const [type, re] of KEYWORDS) {\n if (re.test(hay)) return { type, strength: 'strong' };\n }\n for (const [type, re] of CONTENT_PATTERNS) {\n if (re.test(f.bodyText)) return { type, strength: 'strong' };\n }\n if (f.actionCount >= 1 && f.textLength > 0 && f.textLength < 200) return { type: 'cta', strength: 'weak' };\n if (f.tag === 'header') return { type: 'hero', strength: 'weak' };\n if (/\\b(hero|headline|banner)\\b/i.test(hay)) return { type: 'hero', strength: 'weak' };\n return { type: 'generic', strength: 'weak' };\n}\n\n/** Feature extraction from a live DOM element (browser paths). */\nexport function featuresFromElement(el: Element): SectionFeatures {\n const text = (el.textContent ?? '').replace(/\\s+/g, ' ').trim();\n return {\n tag: el.tagName.toLowerCase(),\n idClass: `${el.id} ${String(el.className ?? '')}`,\n headingText: headingText(el),\n bodyText: text.slice(0, 2000),\n actionCount: el.querySelectorAll('a, button, [role=\"button\"]').length,\n textLength: text.length,\n };\n}\n\n/** Classify a page section into a semantic type (never null — falls back to\n * 'generic' so the caller can still capture attention on it). */\nexport function classifySection(el: Element): SemanticType {\n return classifyFeatures(featuresFromElement(el)).type;\n}\n","/** Reads the rendered DOM to build the page-side context graph. */\n\nimport { classifySection, SEMANTIC_TYPES, type SemanticType } from './engagement/classify';\n\nexport type ScannedNode = {\n componentId: string;\n semanticType: string;\n ariaLabel?: string;\n headingText?: string;\n isAboveFold: boolean;\n prominenceScore: number;\n depth: number;\n reactComponentName?: string;\n dataAttributes: Record<string, string>;\n};\n\nexport type StructuralEdge = {\n fromComponentId: string;\n toComponentId: string;\n /** 0.6 for direct parent → child, 0.3 for sibling (both directions emitted). */\n weight: number;\n};\n\nexport type ScanResult = {\n nodes: ScannedNode[];\n edges: StructuralEdge[];\n scannedAt: number;\n};\n\nexport type ContentAddedEvent = {\n nodes: ScannedNode[];\n edges: StructuralEdge[];\n addedAt: number;\n};\n\nexport type DOMScanner = {\n scan(): Promise<ScanResult>;\n observe(onContentAdded: (event: ContentAddedEvent) => void): void;\n getProminenceScore(element: Element): number;\n destroy(): void;\n};\n\nconst OBSERVE_TAGS = new Set(['SECTION', 'ARTICLE', 'MAIN', 'DIV']);\nconst HEADING_SELECTOR = 'h1, h2, h3';\n\nconst SSR_SCANNER: DOMScanner = {\n scan: async () => ({ nodes: [], edges: [], scannedAt: 0 }),\n observe: () => undefined,\n getProminenceScore: () => 0,\n destroy: () => undefined,\n};\n\nfunction normalize(value: number, min: number, max: number): number {\n if (max <= min) return 0;\n return Math.max(0, Math.min(1, (value - min) / (max - min)));\n}\n\nfunction readReactComponentName(element: Element): string | undefined {\n try {\n const record = element as unknown as Record<string, unknown>;\n for (const key of Object.keys(record)) {\n if (!key.startsWith('__reactFiber') && !key.startsWith('__reactInternalInstance')) {\n continue;\n }\n const fiber = record[key] as { type?: { name?: string; displayName?: string } };\n const name = fiber?.type?.displayName ?? fiber?.type?.name;\n if (name && name.length > 1) {\n return name;\n }\n }\n } catch {\n /* ignore */\n }\n return undefined;\n}\n\nfunction extractDataAttributes(element: Element): Record<string, string> {\n const attrs: Record<string, string> = {};\n for (const attr of Array.from(element.attributes)) {\n if (attr.name.startsWith('data-')) {\n attrs[attr.name] = attr.value;\n }\n }\n return attrs;\n}\n\nconst SEMANTIC_SET: ReadonlySet<string> = new Set(SEMANTIC_TYPES);\n\n// Explicit tag wins → role → heuristic → generic. Every output is normalized to\n// the graph_nodes enum: an out-of-vocabulary data-sentient-type/role falls\n// through to the heuristic instead of being emitted raw (raw values violated\n// the graph_nodes CHECK constraint on insert).\nfunction inferSemanticType(element: Element): SemanticType {\n const explicit = element.getAttribute('data-sentient-type');\n if (explicit && SEMANTIC_SET.has(explicit)) return explicit as SemanticType;\n const role = element.getAttribute('role');\n if (role && SEMANTIC_SET.has(role)) return role as SemanticType;\n return classifySection(element);\n}\n\nfunction componentIdFor(element: Element): string {\n return (\n element.getAttribute('data-sentient-id') ??\n element.getAttribute('id') ??\n element.tagName.toLowerCase()\n );\n}\n\nfunction depthOf(element: Element): number {\n let depth = 0;\n let current: Element | null = element.parentElement;\n while (current) {\n depth++;\n current = current.parentElement;\n }\n return depth;\n}\n\nfunction scanElement(\n element: Element,\n getProminenceScore: (el: Element) => number,\n): ScannedNode {\n const heading = element.querySelector(HEADING_SELECTOR);\n return {\n componentId: componentIdFor(element),\n semanticType: inferSemanticType(element),\n ariaLabel: element.getAttribute('aria-label') ?? undefined,\n headingText: heading?.textContent?.trim() ?? undefined,\n isAboveFold: element.getBoundingClientRect().top < window.innerHeight,\n prominenceScore: getProminenceScore(element),\n depth: depthOf(element),\n reactComponentName: readReactComponentName(element),\n dataAttributes: extractDataAttributes(element),\n };\n}\n\n/**\n * Detects structural relationships between scanned component elements:\n * - parent → child (direct ancestor link, weight 0.6)\n * - sibling ↔ sibling (share nearest component ancestor, weight 0.3, bidirectional)\n * A pair appears at most once per direction.\n */\nfunction detectStructuralEdges(elementToId: Map<Element, string>): StructuralEdge[] {\n const edges: StructuralEdge[] = [];\n const seen = new Set<string>();\n const ROOT = '__root__';\n\n // Parent → child: nearest component ancestor only.\n const groups = new Map<string, Element[]>();\n for (const [el, _id] of elementToId) {\n let ancestor: Element | null = el.parentElement;\n let groupKey = ROOT;\n while (ancestor) {\n if (elementToId.has(ancestor)) {\n groupKey = elementToId.get(ancestor)!;\n const childId = elementToId.get(el)!;\n const key = `${groupKey}->${childId}`;\n if (!seen.has(key) && groupKey !== childId) {\n seen.add(key);\n edges.push({ fromComponentId: groupKey, toComponentId: childId, weight: 0.6 });\n }\n break;\n }\n ancestor = ancestor.parentElement;\n }\n const siblings = groups.get(groupKey) ?? [];\n siblings.push(el);\n groups.set(groupKey, siblings);\n }\n\n // Sibling ↔ sibling: same ancestor group.\n for (const sibs of groups.values()) {\n if (sibs.length < 2) continue;\n for (let i = 0; i < sibs.length; i++) {\n for (let j = i + 1; j < sibs.length; j++) {\n const aId = elementToId.get(sibs[i]!)!;\n const bId = elementToId.get(sibs[j]!)!;\n if (aId === bId) continue;\n const fwd = `${aId}->${bId}::sib`;\n const rev = `${bId}->${aId}::sib`;\n if (!seen.has(fwd)) {\n seen.add(fwd);\n edges.push({ fromComponentId: aId, toComponentId: bId, weight: 0.3 });\n }\n if (!seen.has(rev)) {\n seen.add(rev);\n edges.push({ fromComponentId: bId, toComponentId: aId, weight: 0.3 });\n }\n }\n }\n }\n\n return edges;\n}\n\nfunction collectNodesAndEdges(\n getProminenceScore: (el: Element) => number,\n): { nodes: ScannedNode[]; edges: StructuralEdge[] } {\n const nodes: ScannedNode[] = [];\n const seen = new Set<Element>();\n const elementToId = new Map<Element, string>();\n\n const registered = document.querySelectorAll('[data-sentient-id]');\n registered.forEach((el) => {\n if (el instanceof Element && !seen.has(el)) {\n seen.add(el);\n const node = scanElement(el, getProminenceScore);\n nodes.push(node);\n elementToId.set(el, node.componentId);\n }\n });\n\n const structural = document.querySelectorAll('section, article, main, aside');\n structural.forEach((el) => {\n if (!(el instanceof Element) || seen.has(el)) return;\n const hasAria = el.hasAttribute('aria-label');\n const hasSentientId = el.hasAttribute('data-sentient-id');\n if (!hasAria && !hasSentientId) return;\n seen.add(el);\n const node = scanElement(el, getProminenceScore);\n nodes.push(node);\n elementToId.set(el, node.componentId);\n });\n\n return { nodes, edges: detectStructuralEdges(elementToId) };\n}\n\n/**\n * Creates a DOM scanner that uses idle callbacks and mutation observation.\n */\nexport function createDOMScanner(): DOMScanner {\n if (typeof window === 'undefined') {\n return SSR_SCANNER;\n }\n\n let observer: MutationObserver | null = null;\n let idleCallbackId = 0;\n let contentCallback: ((event: ContentAddedEvent) => void) | null = null;\n\n const getProminenceScore = (element: Element): number => {\n try {\n const styles = window.getComputedStyle(element);\n const fontSize = parseFloat(styles.fontSize) || 12;\n const zIndex = parseFloat(styles.zIndex) || 0;\n const rect = element.getBoundingClientRect();\n const distanceFromTop = Math.max(rect.top, 0);\n const viewportHeight = window.innerHeight || 1;\n const inverseDistance = 1 / (distanceFromTop / viewportHeight + 1);\n\n const score =\n normalize(fontSize, 12, 48) * 0.4 +\n normalize(inverseDistance, 0, 1) * 0.4 +\n normalize(zIndex, 0, 100) * 0.2;\n\n return Math.max(0, Math.min(1, score));\n } catch {\n return 0.5;\n }\n };\n\n const scan = (): Promise<ScanResult> =>\n new Promise((resolve) => {\n const run = (): void => {\n const { nodes, edges } = collectNodesAndEdges(getProminenceScore);\n resolve({ nodes, edges, scannedAt: Date.now() });\n };\n\n try {\n if (typeof requestIdleCallback === 'function') {\n idleCallbackId = requestIdleCallback(run, { timeout: 100 });\n } else {\n run();\n }\n } catch {\n run();\n }\n });\n\n const observe = (onContentAdded: (event: ContentAddedEvent) => void): void => {\n contentCallback = onContentAdded;\n try {\n observer = new MutationObserver((mutations) => {\n const added: ScannedNode[] = [];\n const addedElementToId = new Map<Element, string>();\n for (const mutation of mutations) {\n if (mutation.type !== 'childList') continue;\n mutation.addedNodes.forEach((node) => {\n if (!(node instanceof Element)) return;\n if (!OBSERVE_TAGS.has(node.tagName)) return;\n const hasId = node.hasAttribute('data-sentient-id');\n const hasAria = node.hasAttribute('aria-label');\n if (!hasId && !hasAria) return;\n const scanned = scanElement(node, getProminenceScore);\n added.push(scanned);\n addedElementToId.set(node, scanned.componentId);\n });\n }\n if (added.length > 0 && contentCallback) {\n contentCallback({\n nodes: added,\n edges: detectStructuralEdges(addedElementToId),\n addedAt: Date.now(),\n });\n }\n });\n observer.observe(document.body, { childList: true, subtree: true });\n } catch {\n /* ignore */\n }\n };\n\n const destroy = (): void => {\n if (observer) {\n observer.disconnect();\n observer = null;\n }\n if (idleCallbackId && typeof cancelIdleCallback === 'function') {\n try {\n cancelIdleCallback(idleCallbackId);\n } catch {\n /* ignore */\n }\n }\n idleCallbackId = 0;\n contentCallback = null;\n };\n\n return {\n scan,\n observe,\n getProminenceScore,\n destroy,\n };\n}\n","/** In-memory context graph with persistence and backend sync. */\n\nexport type PageNode = {\n id: string;\n componentId: string;\n semanticType: string;\n answers: string[];\n prominenceScore: number;\n depth: number;\n};\n\nexport type GraphSnapshot = {\n pageNodes: PageNode[];\n capturedAt: number;\n};\n\nexport type GraphConfig = {\n syncUrl?: string;\n apiKey?: string;\n projectId?: string;\n sessionId?: string;\n};\n\nexport type StructuralEdge = {\n fromComponentId: string;\n toComponentId: string;\n weight: number;\n};\n\nexport type GraphClient = {\n addPageNode(node: PageNode): void;\n /** Record a DOM-derived parent/child or sibling relationship between two components. */\n addStructuralEdge(edge: StructuralEdge): void;\n /** One-shot batch sync of all current page nodes to the backend. */\n syncOnce(): void;\n snapshot(): GraphSnapshot;\n serialize(): string;\n restore(data: string): void;\n destroy(): void;\n};\n\nconst NODES_KEY = '_snt_graph_nodes';\n// _snt_graph_edges was written by earlier builds but never synced to the backend.\n// We still read and clear any leftover key on init/destroy so old clients don't\n// accumulate stale data, but we no longer write it.\nconst STALE_EDGES_KEY = '_snt_graph_edges';\n\nconst SEMANTIC_NEIGHBOURS: Record<string, string[]> = {\n pricing: ['features', 'faq'],\n features: ['pricing'],\n faq: ['pricing'],\n social_proof: ['cta'],\n cta: ['social_proof', 'hero', 'trust'],\n hero: ['cta'],\n comparison: ['pricing'],\n trust: ['cta'],\n};\n\nfunction neighboursFor(semanticType: string): string[] {\n return SEMANTIC_NEIGHBOURS[semanticType] ?? [];\n}\n\nfunction readStorage<T>(key: string, fallback: T): T {\n try {\n const raw = localStorage.getItem(key);\n if (!raw) return fallback;\n return JSON.parse(raw) as T;\n } catch {\n return fallback;\n }\n}\n\nfunction writeStorage(key: string, value: unknown): void {\n try {\n localStorage.setItem(key, JSON.stringify(value));\n } catch {\n /* ignore */\n }\n}\n\nconst VALID_SEMANTIC_TYPES = new Set([\n 'pricing', 'hero', 'social_proof', 'cta', 'features',\n 'faq', 'comparison', 'trust', 'navigation', 'generic',\n]);\n\nfunction toValidSemanticType(type: string): string {\n return VALID_SEMANTIC_TYPES.has(type) ? type : 'generic';\n}\n\nfunction contentHashOf(componentId: string, semanticType: string, answers: string[]): string {\n const input = `${componentId}:${semanticType}:${answers.join(',')}`;\n let h = 5381;\n for (let i = 0; i < input.length; i++) {\n h = ((h << 5) + h + input.charCodeAt(i)) & 0xffffffff;\n }\n return (h >>> 0).toString(16).padStart(8, '0');\n}\n\n/**\n * Creates an in-memory graph client with optional persistence and sync.\n */\nexport function createGraphClient(config?: GraphConfig): GraphClient {\n const pageNodes = new Map<string, PageNode>();\n const structuralEdges = new Map<string, StructuralEdge>();\n\n const persist = (): void => {\n if (typeof window === 'undefined') return;\n writeStorage(NODES_KEY, [...pageNodes.values()]);\n };\n\n const restore = (data: string): void => {\n try {\n const parsed = JSON.parse(data) as { pageNodes?: PageNode[] };\n pageNodes.clear();\n for (const node of parsed.pageNodes ?? []) {\n pageNodes.set(node.componentId, node);\n }\n } catch {\n /* ignore corrupt state */\n }\n };\n\n if (typeof window !== 'undefined') {\n const storedNodes = readStorage<PageNode[]>(NODES_KEY, []);\n for (const node of storedNodes) {\n pageNodes.set(node.componentId, node);\n }\n // Clear any stale edge data written by older SDK versions.\n try { localStorage.removeItem(STALE_EDGES_KEY); } catch { /* ignore */ }\n }\n\n return {\n addPageNode(node: PageNode): void {\n pageNodes.set(node.componentId, node);\n persist();\n },\n\n addStructuralEdge(edge: StructuralEdge): void {\n const key = `${edge.fromComponentId}->${edge.toComponentId}`;\n structuralEdges.set(key, edge);\n },\n\n syncOnce(): void {\n if (!config?.syncUrl || typeof window === 'undefined') return;\n const nodes = [...pageNodes.values()];\n if (nodes.length === 0) return;\n try {\n // Build semantic edges from the SEMANTIC_NEIGHBOURS map. Each node emits\n // an edge to every present sibling that matches one of its neighbour types.\n // Weight matches propagate()'s 0.4 attention factor; confidence is high\n // because the mapping is curated, not inferred.\n const nodesByType = new Map<string, PageNode[]>();\n for (const n of nodes) {\n const list = nodesByType.get(n.semanticType) ?? [];\n list.push(n);\n nodesByType.set(n.semanticType, list);\n }\n const edges: Array<{\n fromComponentId: string;\n toComponentId: string;\n type: 'semantic' | 'structural';\n weight: number;\n confidence: number;\n }> = [];\n const seen = new Set<string>();\n for (const source of nodes) {\n for (const neighbourType of neighboursFor(source.semanticType)) {\n const targets = nodesByType.get(neighbourType) ?? [];\n for (const target of targets) {\n if (target.componentId === source.componentId) continue;\n const key = `semantic:${source.componentId}->${target.componentId}`;\n if (seen.has(key)) continue;\n seen.add(key);\n edges.push({\n fromComponentId: source.componentId,\n toComponentId: target.componentId,\n type: 'semantic',\n weight: 0.4,\n confidence: 0.9,\n });\n }\n }\n }\n\n // Structural edges from DOM relationships, recorded during scan.\n // Only emit when both endpoints are present in this page's node set —\n // a structural edge to/from a node that no longer exists would dangle.\n const componentIds = new Set(nodes.map((n) => n.componentId));\n for (const edge of structuralEdges.values()) {\n if (!componentIds.has(edge.fromComponentId) || !componentIds.has(edge.toComponentId)) continue;\n const key = `structural:${edge.fromComponentId}->${edge.toComponentId}`;\n if (seen.has(key)) continue;\n seen.add(key);\n edges.push({\n fromComponentId: edge.fromComponentId,\n toComponentId: edge.toComponentId,\n type: 'structural',\n weight: edge.weight,\n confidence: 1.0,\n });\n }\n\n const payload = {\n pageUrl: window.location.href,\n nodes: nodes.map((n) => {\n const semanticType = toValidSemanticType(n.semanticType);\n return {\n componentId: n.componentId,\n semanticType,\n answers: n.answers,\n contentHash: contentHashOf(n.componentId, semanticType, n.answers),\n prominenceScore: n.prominenceScore,\n depthInPage: n.depth,\n };\n }),\n edges,\n };\n fetch(config.syncUrl, {\n method: 'POST',\n keepalive: true,\n headers: {\n 'Content-Type': 'application/json',\n ...(config.apiKey ? { Authorization: `Bearer ${config.apiKey}` } : {}),\n },\n body: JSON.stringify(payload),\n }).catch(() => undefined);\n } catch {\n /* ignore */\n }\n },\n\n snapshot(): GraphSnapshot {\n return {\n pageNodes: [...pageNodes.values()],\n capturedAt: Date.now(),\n };\n },\n\n serialize(): string {\n return JSON.stringify({ pageNodes: [...pageNodes.values()] });\n },\n\n restore,\n\n destroy(): void {\n if (typeof window === 'undefined') return;\n try {\n localStorage.removeItem(NODES_KEY);\n localStorage.removeItem(STALE_EDGES_KEY);\n } catch {\n /* ignore */\n }\n },\n };\n}\n"],"mappings":"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","names":["index_graph_exports","__export","deriveSessionSegment","detectDeviceClass","detectTimeOfDay","detectTrafficSource","init","referrerDomainFromReferer","__toCommonJS","DEFAULT_COOKIE_NAME","STORAGE_KEY","generateSessionId","e","hex","readCookie","name","match","writeCookie","value","maxAgeSeconds","readLocalStorage","key","writeLocalStorage","readSessionStorage","writeSessionStorage","removeSessionStorage","probeCookieWritable","removeLocalStorage","clearCookie","SSR_MANAGER","initSession","config","_a","_b","_c","_d","_e","_f","cookieName","DEFAULT_COOKIE_NAME","sessionId","lsOk","cookieOk","ssOk","ephemeral","retryStorageKey","apiKey","SSR_QUEUE","readRetryQueue","maxRetrySize","retryKey","raw","parsed","e","writeRetryQueue","events","merged","createEventQueue","config","_a","_b","_c","flushIntervalMs","maxBatchSize","ingestUrl","RETRY_KEY","queue","sentIds","sentIdOrder","markSent","ids","id","queuedIds","oldest","enqueue","event","requeueFailed","batch","retryEvents","backoffUntil","consecutiveFailures","transportBatch","body","pending","handleResponse","res","encoder","byteLength","s","packBatch","pool","out","bytes","size","flush","flushTimerActive","intervalId","onVisibilityChange","onPageHide","KEY_PREFIX","cacheKey","componentId","segment","storageKey","KEY_PREFIX","parseStorageKey","key","suffix","sep","e","listStorageKeys","keys","i","createAssignmentCache","ttlMs","memory","isExpired","assignment","raw","parsed","entry","prefix","storageK","agentUaList","uaTokenMatch","userAgent","matchedAgentToken","_a","s","token","detectDeviceClass","detectTrafficSource","referrer","appOrigin","refUrl","e","host","referrerDomainFromReferer","detectTimeOfDay","d","h","deriveSessionSegment","opts","body","buildSessionUpsertPayload","sessionId","_b","_c","_d","_e","_f","_g","ua","referer","now","import_policy","toWireSlot","d","__spreadValues","dim","values","baselineResultFor","decl","armOfResult","result","SNAPSHOT_STORAGE_KEY_PREFIX","BANDS","readSnapshot","apiKey","raw","p","e","writeSnapshot","snap","import_policy","import_policy","PROD_KEYLESS_ERROR","LOCAL_MODE_BANNER","bannerShown","prodErrorShown","readPersonaOverrideFromUrl","_a","e","createLocalModeClient","config","session","initSession","sessionId","forcedPersona","modPromise","mod","m","bannerShown","LOCAL_MODE_BANNER","prodErrorShown","PROD_KEYLESS_ERROR","lastOutcome","applyPersonaAttributes","outcome","el","input","_b","_c","__spreadProps","__spreadValues","writeSnapshot","slotId","componentId","variantIds","DEFAULT_INGEST_URL","_clients","_lastApiKey","generateEventId","c","r","SSR_CLIENT","readUtmParams","out","sp","k","v","deriveBaseUrl","ingestUrl","isDoNotTrackEnabled","createPreConsentProxy","config","_a","baseUrl","deriveBaseUrl","DEFAULT_INGEST_URL","authHeaders","inner","componentId","variantIds","_agentData","params","v","res","e","proxy","n","m","w","s","c","g","o","u","a","av","i","setInner","fullClient","init","_b","_c","_d","_e","_f","_g","_h","_i","SSR_CLIENT","_lastApiKey","prevEntry","_clients","dntBlocked","isDoNotTrackEnabled","gated","keyValid","createLocalModeClient","resolvedIngestUrl","sessionStart","session","initSession","assignmentCache","createAssignmentCache","eventQueue","createEventQueue","deviceClass","detectDeviceClass","appOrigin","trafficSource","detectTrafficSource","sessionSegment","inflightAssigns","slotStore","personaState","seedSlotBaselines","decls","d","baselineResultFor","slotId","result","seedSnapshot","readSnapshot","BAND_CONFIDENCE","__spreadValues","ds","variantId","sessionReady","sessionId","referrerDomain","referrerDomainFromReferer","sessionBody","readUtmParams","detectTimeOfDay","uaTokenMatch","client","name","metadata","weight","stepIndex","sid","goalId","generateEventId","goalType","opts","assignment","slotResult","attributedVariantId","armOfResult","fullEvent","userId","event","__spreadProps","segment","agentData","agentDataByVariant","cached","remainingTtlMs","inflight","request","body","input","declared","id","toWireSlot","data","slots","known","writeSnapshot","SNAPSHOT_STORAGE_KEY_PREFIX","retryStorageKey","win","SEMANTIC_TYPES","KEYWORDS","CONTENT_PATTERNS","headingText","el","_a","h","classifyFeatures","f","hay","type","re","featuresFromElement","_b","text","classifySection","OBSERVE_TAGS","HEADING_SELECTOR","SSR_SCANNER","normalize","value","min","max","readReactComponentName","element","_a","_b","_c","record","key","fiber","name","e","extractDataAttributes","attrs","attr","SEMANTIC_SET","SEMANTIC_TYPES","inferSemanticType","explicit","role","classifySection","componentIdFor","depthOf","depth","current","scanElement","getProminenceScore","heading","detectStructuralEdges","elementToId","edges","seen","ROOT","groups","el","_id","ancestor","groupKey","childId","siblings","sibs","i","j","aId","bId","fwd","rev","collectNodesAndEdges","nodes","node","hasAria","hasSentientId","createDOMScanner","observer","idleCallbackId","contentCallback","styles","fontSize","zIndex","rect","distanceFromTop","viewportHeight","inverseDistance","score","resolve","run","onContentAdded","mutations","added","addedElementToId","mutation","hasId","scanned","NODES_KEY","STALE_EDGES_KEY","SEMANTIC_NEIGHBOURS","neighboursFor","semanticType","_a","readStorage","key","fallback","raw","e","writeStorage","value","VALID_SEMANTIC_TYPES","toValidSemanticType","type","contentHashOf","componentId","answers","input","h","createGraphClient","config","pageNodes","structuralEdges","persist","restore","data","parsed","node","storedNodes","edge","_b","nodes","nodesByType","n","list","edges","seen","source","neighbourType","targets","target","componentIds","payload","__spreadValues","DEFAULT_INGEST_URL","readSntUid","m","init","config","_a","client","dntBlocked","isDoNotTrackEnabled","gated","domScanner","createDOMScanner","resolvedIngestUrl","graphClient","createGraphClient","nodesRaw","e","result","node","edge","syncDebounceTimer","debouncedSync","event","__spreadProps","__spreadValues"]}
1
+ {"version":3,"sources":["../src/index-graph.ts","../src/uuid.ts","../src/session.ts","../src/queue.ts","../src/cache.ts","../src/session-meta.ts","../src/slots.ts","../src/snapshot.ts","../src/index.ts","../src/local-mode.ts","../src/engagement/classify.ts","../src/scanner.ts","../src/graph.ts"],"sourcesContent":["/**\n * Graph-capable entry point for @sentientui/core.\n *\n * Import from `@sentientui/core/graph` when you need DOM graph scanning and\n * page-structure sync. This entry pulls in `scanner.ts` and `graph.ts` at\n * build time, giving bundlers a real tree-shaking boundary. Standard A/B tests\n * should use `@sentientui/core` (the lean entry) instead.\n */\n\n// Re-export everything from the lean entry except `init`, which we override below.\nexport {\n detectDeviceClass,\n detectTrafficSource,\n detectTimeOfDay,\n deriveSessionSegment,\n referrerDomainFromReferer,\n} from './index.js';\n// SSR preload helpers moved to `@sentientui/core/server` in 0.6.0.\nexport type {\n SentientConfig,\n AssignResult,\n SentientClient,\n} from './index.js';\nexport type {\n SessionConfig,\n SessionManager,\n EventType,\n SentientEvent,\n QueueConfig,\n Assignment,\n} from './index.js';\n// EventQueue/AssignmentCache are no longer on the lean barrel — source them from\n// their own modules so the `/graph` subpath keeps exposing them unchanged.\nexport type { EventQueue } from './queue.js';\nexport type { AssignmentCache } from './cache.js';\n// Scanner + graph types (live in this entry only).\nexport type {\n ScannedNode,\n ScanResult,\n ContentAddedEvent,\n DOMScanner,\n} from './scanner.js';\nexport type {\n PageNode,\n GraphSnapshot,\n GraphConfig,\n GraphClient,\n} from './graph.js';\n\nimport { init as initLean, isDoNotTrackEnabled, type SentientConfig, type SentientClient } from './index.js';\n\nconst DEFAULT_INGEST_URL = 'https://api.sentient-ui.com/v1/events';\nimport { createDOMScanner } from './scanner.js';\nimport { createGraphClient } from './graph.js';\n\nexport type GraphSentientConfig = SentientConfig & {\n /**\n * Enable DOM graph scanning and page-structure sync. Wires a MutationObserver\n * and a localStorage-backed graph. When `false` (default) this entry behaves\n * identically to `@sentientui/core`.\n */\n graph?: boolean;\n};\n\nfunction readSntUid(): string | undefined {\n try {\n const m = document.cookie.match(/(?:^|; )_snt_uid=([^;]*)/);\n return m ? decodeURIComponent(m[1]) : undefined;\n } catch {\n return undefined;\n }\n}\n\n/**\n * Graph-capable variant of `init()`. Identical to the lean `init` when\n * `config.graph` is `false` (or omitted). When `config.graph: true`, mounts\n * a DOM scanner + graph client and enables `client.getGraph()`.\n *\n * Import from `@sentientui/core/graph` — do not call the lean `init` alongside\n * this function as that would initialise two clients.\n */\n// Teardown for the graph resources (scanner + MutationObserver + debounce\n// timer + graph client) bound to each apiKey. initLean already disposes the\n// lean client's own timers/listeners on re-init, but it knows nothing about\n// these graph resources, so this entry tracks and tears them down itself —\n// otherwise an HMR / consent-toggle / provider-remount re-init would leak a\n// live MutationObserver and a pending sync timer per mount.\nconst _graphTeardowns = new Map<string, () => void>();\n\nexport function init(config: GraphSentientConfig): SentientClient {\n const client = initLean(config);\n\n // Mirror initLean's opt-out gate: a DNT/GPC visitor (or consent:false) must\n // not get the DOM scanner + graph sync, which POST page-structure beacons and\n // read `_snt_uid` — tracking that bypasses the lean client's own gating, even\n // under `preConsentBehavior: 'statistical_winner'` (audit P1).\n const dntBlocked = config.respectDoNotTrack !== false && isDoNotTrackEnabled();\n const gated = config.consent === false || dntBlocked;\n\n // Keyless zero-network contract: with no api key there is nothing to feed —\n // the scanner must never mount and /v1/graph/sync must never fire.\n if (!config.graph || !config.apiKey || gated || typeof window === 'undefined') return client;\n\n // A prior graph mount for this key is now superseded — tear it down first so\n // its observer/timer don't leak alongside the new mount's.\n const prevTeardown = _graphTeardowns.get(config.apiKey);\n if (prevTeardown) {\n try {\n prevTeardown();\n } catch {\n /* teardown must never throw on re-init */\n }\n }\n\n const domScanner = createDOMScanner();\n const resolvedIngestUrl = config.ingestUrl ?? DEFAULT_INGEST_URL;\n const graphClient = createGraphClient({\n syncUrl: resolvedIngestUrl.replace(/\\/events\\/?$/, '/graph/sync'),\n apiKey: config.apiKey,\n projectId: config.apiKey,\n sessionId: readSntUid(),\n });\n\n // Persisted page-node state from a previous page load is restored by the graph\n // client's own constructor (it reads `_snt_graph_nodes` on creation). We no\n // longer re-read the key and call restore() here — that was a redundant second\n // load path that cleared and reloaded identical data.\n\n void domScanner.scan().then((result) => {\n for (const node of result.nodes) {\n graphClient.addPageNode({\n id: node.componentId,\n componentId: node.componentId,\n semanticType: node.semanticType,\n answers: node.headingText ? [node.headingText] : [],\n prominenceScore: node.prominenceScore,\n depth: node.depth,\n });\n }\n for (const edge of result.edges) {\n graphClient.addStructuralEdge(edge);\n }\n graphClient.syncOnce();\n });\n\n let syncDebounceTimer: ReturnType<typeof setTimeout> | null = null;\n const debouncedSync = (): void => {\n if (syncDebounceTimer !== null) clearTimeout(syncDebounceTimer);\n syncDebounceTimer = setTimeout(() => {\n syncDebounceTimer = null;\n graphClient.syncOnce();\n }, 500);\n };\n\n domScanner.observe((event) => {\n for (const node of event.nodes) {\n graphClient.addPageNode({\n id: node.componentId,\n componentId: node.componentId,\n semanticType: node.semanticType,\n answers: node.headingText ? [node.headingText] : [],\n prominenceScore: node.prominenceScore,\n depth: node.depth,\n });\n }\n for (const edge of event.edges) {\n graphClient.addStructuralEdge(edge);\n }\n debouncedSync();\n });\n\n // Tears down only the graph resources; the caller pairs it with the lean\n // client's own dispose/destroy. Idempotent, and de-registers itself so a\n // later re-init or teardown can't run it twice on already-freed resources.\n const teardownGraph = (): void => {\n if (syncDebounceTimer !== null) {\n clearTimeout(syncDebounceTimer);\n syncDebounceTimer = null;\n }\n domScanner.destroy();\n graphClient.destroy();\n if (_graphTeardowns.get(config.apiKey) === teardownGraph) {\n _graphTeardowns.delete(config.apiKey);\n }\n };\n _graphTeardowns.set(config.apiKey, teardownGraph);\n\n return {\n ...client,\n getGraph: () => graphClient.snapshot(),\n dispose: () => {\n teardownGraph();\n client.dispose();\n },\n destroy: () => {\n teardownGraph();\n client.destroy();\n },\n };\n}\n","/** Shared RFC 4122 v4 UUID generator. */\n\n/**\n * Returns a random RFC 4122 v4 UUID.\n *\n * Resolution order, each falling through only on absence/throw:\n * 1. `crypto.randomUUID()` — requires a **secure context** (HTTPS/localhost),\n * so it is absent on plain `http://` (non-localhost) pages.\n * 2. `crypto.getRandomValues()` — a CSPRNG that, unlike `randomUUID`, *is*\n * available in insecure contexts, so ids stay cryptographically random\n * exactly where step 1 is unavailable.\n * 3. `Math.random()` — last resort only when no Web Crypto exists at all.\n * These ids are anonymous analytics session/event keys (not secrets or\n * authenticators), so a well-formed value the Postgres `uuid` column accepts\n * matters more than PRNG quality here — and the generator must never throw\n * (a missing/malformed id would break session creation and the host page).\n */\nexport function randomUuidV4(): string {\n try {\n if (typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function') {\n return crypto.randomUUID();\n }\n } catch {\n /* fall through to getRandomValues */\n }\n try {\n if (typeof crypto !== 'undefined' && typeof crypto.getRandomValues === 'function') {\n const buf = new Uint8Array(16);\n crypto.getRandomValues(buf);\n buf[6] = (buf[6]! & 0x0f) | 0x40; // version 4\n buf[8] = (buf[8]! & 0x3f) | 0x80; // variant 10\n let out = '';\n for (let i = 0; i < 16; i++) {\n out += buf[i]!.toString(16).padStart(2, '0');\n if (i === 3 || i === 5 || i === 7 || i === 9) out += '-';\n }\n return out;\n }\n } catch {\n /* fall through to the Math.random() builder */\n }\n return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {\n const r = (Math.random() * 16) | 0;\n return (c === 'x' ? r : (r & 0x3) | 0x8).toString(16);\n });\n}\n","/** Manages anonymous session identity with cookie + localStorage layers. */\n\nimport { randomUuidV4 } from './uuid.js';\n\nexport type SessionConfig = {\n cookieName?: string;\n cookieTTLDays?: number;\n /**\n * Session ID generated during SSR (e.g. from `loadAdaptiveAssignments`).\n * Used as the fallback when no existing cookie or localStorage entry is found,\n * so the client adopts the same session the server used for variant assignment\n * on first visit rather than generating a new, orphaned ID.\n */\n ssrSessionId?: string;\n};\n\nexport type SessionManager = {\n getSessionId(): string | null;\n /** True when neither cookie nor localStorage could be written — id is in-memory only. */\n isEphemeral(): boolean;\n destroy(): void;\n};\n\nconst DEFAULT_COOKIE_NAME = '_snt_uid';\nconst DEFAULT_COOKIE_TTL_DAYS = 365;\nconst STORAGE_KEY = '_snt_uid';\n\n/**\n * Generates a unique session identifier. Always an RFC 4122 v4 UUID — the id is\n * stored server-side in a Postgres `uuid` column, so the insecure-context\n * fallback must not emit a malformed value (see `randomUuidV4`).\n */\nfunction generateSessionId(): string {\n return randomUuidV4();\n}\n\nfunction readCookie(name: string): string | null {\n try {\n const match = document.cookie.match(new RegExp(`(?:^|; )${name}=([^;]*)`));\n return match ? decodeURIComponent(match[1]) : null;\n } catch {\n return null;\n }\n}\n\nfunction writeCookie(name: string, value: string, maxAgeSeconds: number): void {\n try {\n document.cookie = `${name}=${encodeURIComponent(value)}; max-age=${maxAgeSeconds}; SameSite=strict; path=/`;\n } catch {\n /* ignore */\n }\n}\n\nfunction readLocalStorage(key: string): string | null {\n try {\n return localStorage.getItem(key);\n } catch {\n return null;\n }\n}\n\nfunction writeLocalStorage(key: string, value: string): boolean {\n try {\n localStorage.setItem(key, value);\n return true;\n } catch {\n return false;\n }\n}\n\nfunction readSessionStorage(key: string): string | null {\n try {\n return sessionStorage.getItem(key);\n } catch {\n return null;\n }\n}\n\nfunction writeSessionStorage(key: string, value: string): boolean {\n try {\n sessionStorage.setItem(key, value);\n return true;\n } catch {\n return false;\n }\n}\n\nfunction removeSessionStorage(key: string): void {\n try {\n sessionStorage.removeItem(key);\n } catch {\n /* ignore */\n }\n}\n\nfunction probeCookieWritable(name: string): boolean {\n try {\n document.cookie = `${name}_probe=1; max-age=1; SameSite=strict; path=/`;\n return document.cookie.indexOf(`${name}_probe=1`) !== -1;\n } catch {\n return false;\n }\n}\n\nfunction removeLocalStorage(key: string): void {\n try {\n localStorage.removeItem(key);\n } catch {\n /* ignore */\n }\n}\n\nfunction clearCookie(name: string): void {\n try {\n document.cookie = `${name}=; max-age=0; SameSite=strict; path=/`;\n } catch {\n /* ignore */\n }\n}\n\nconst SSR_MANAGER: SessionManager = {\n getSessionId: () => null,\n isEphemeral: () => false,\n destroy: () => undefined,\n};\n\n/**\n * Initializes session management. Returns a no-op manager in SSR environments.\n */\nexport function initSession(config?: SessionConfig): SessionManager {\n if (typeof window === 'undefined') {\n return SSR_MANAGER;\n }\n\n const cookieName = config?.cookieName ?? DEFAULT_COOKIE_NAME;\n const cookieTTLDays = config?.cookieTTLDays ?? DEFAULT_COOKIE_TTL_DAYS;\n const maxAgeSeconds = cookieTTLDays * 24 * 60 * 60;\n\n // Treat an empty string from any layer as absent. A `_snt_uid=` cookie with no\n // value (or an empty localStorage/sessionStorage entry) decodes to '', which is\n // not nullish — a plain `??` chain would keep it and persist it for a year,\n // leaving getSessionId() falsy so every track/goal/upsert bails out and the\n // visitor is permanently muted with no way to regenerate. Coercing '' to null\n // lets the chain fall through to a real id (or a freshly generated one).\n const nonEmpty = (value: string | null | undefined): string | null =>\n value && value.length > 0 ? value : null;\n\n let sessionId: string | null =\n nonEmpty(readCookie(cookieName)) ??\n nonEmpty(readLocalStorage(STORAGE_KEY)) ??\n nonEmpty(readSessionStorage(STORAGE_KEY)) ??\n nonEmpty(config?.ssrSessionId) ??\n generateSessionId();\n\n writeCookie(cookieName, sessionId, maxAgeSeconds);\n const lsOk = writeLocalStorage(STORAGE_KEY, sessionId);\n const cookieOk = probeCookieWritable(cookieName);\n // Always write sessionStorage when localStorage fails — it covers same-tab\n // navigation even in strict storage environments.\n const ssOk = !lsOk ? writeSessionStorage(STORAGE_KEY, sessionId) : false;\n const ephemeral = !lsOk && !cookieOk && !ssOk;\n\n return {\n getSessionId: () => sessionId,\n isEphemeral: () => ephemeral,\n destroy: () => {\n sessionId = null;\n clearCookie(cookieName);\n removeLocalStorage(STORAGE_KEY);\n removeSessionStorage(STORAGE_KEY);\n },\n };\n}\n","/** Batched event queue with reliable transport (fetch + keepalive, localStorage retry). */\n\nexport type EventType =\n | 'variant_assigned'\n | 'goal_achieved'\n | 'scroll_depth'\n | 'dwell'\n | 'cursor_signal'\n | 'component_visible'\n | 'component_exited'\n | 'micro_signal';\n\nexport type SentientEvent = {\n id: string;\n sessionId: string;\n projectId: string;\n componentId: string;\n variantId?: string;\n eventType: EventType;\n goalType?: string;\n payload: Record<string, unknown>;\n timestamp: number;\n timeInSession: number;\n};\n\nexport type QueueConfig = {\n ingestUrl: string;\n apiKey: string;\n flushIntervalMs?: number;\n maxBatchSize?: number;\n maxRetrySize?: number;\n};\n\nexport type EventQueue = {\n push(event: SentientEvent): void;\n flush(): void;\n destroy(): void;\n};\n\nconst MAX_SENT_IDS = 500;\n// 64 KB is the per-origin keepalive budget on every modern browser. We split unload\n// flushes into chunks below this to avoid the whole batch being dropped.\nconst KEEPALIVE_BUDGET_BYTES = 56 * 1024;\n\n/**\n * Persisted retry-bucket key, namespaced by apiKey prefix so multiple projects\n * on the same origin each get their own bucket. Exported so the client's\n * forget-me teardown can remove it.\n */\nexport function retryStorageKey(apiKey: string): string {\n return `_snt_retry_${apiKey.slice(0, 12)}`;\n}\n\nconst SSR_QUEUE: EventQueue = {\n push: () => undefined,\n flush: () => undefined,\n destroy: () => undefined,\n};\n\nfunction readRetryQueue(maxRetrySize: number, retryKey: string): SentientEvent[] {\n try {\n const raw = localStorage.getItem(retryKey);\n if (!raw) return [];\n const parsed = JSON.parse(raw) as SentientEvent[];\n if (!Array.isArray(parsed)) return [];\n localStorage.removeItem(retryKey);\n return parsed.slice(-maxRetrySize);\n } catch {\n return [];\n }\n}\n\nfunction writeRetryQueue(events: SentientEvent[], maxRetrySize: number, retryKey: string): void {\n try {\n const existing = (() => {\n try {\n const raw = localStorage.getItem(retryKey);\n if (!raw) return [];\n const parsed = JSON.parse(raw) as SentientEvent[];\n return Array.isArray(parsed) ? parsed : [];\n } catch {\n return [];\n }\n })();\n // De-dupe by event.id (last write wins) before the size cap. A batch that\n // 5xx's repeatedly in-session hands the same ids to writeRetryQueue on every\n // retry; without this, each retry appends another copy and slice(-maxRetrySize)\n // evicts other distinct failed events to make room for the duplicates.\n const byId = new Map<string, SentientEvent>();\n for (const e of existing) byId.set(e.id, e);\n for (const e of events) byId.set(e.id, e);\n const merged = [...byId.values()].slice(-maxRetrySize);\n localStorage.setItem(retryKey, JSON.stringify(merged));\n } catch {\n /* ignore */\n }\n}\n\n/**\n * Removes the given event ids from the persisted retry bucket. Called once a\n * batch is acknowledged so a transient 5xx that was written to localStorage\n * isn't replayed on the next page load after the in-session retry succeeds.\n */\nfunction purgeFromRetryQueue(ids: string[], retryKey: string): void {\n try {\n const raw = localStorage.getItem(retryKey);\n if (!raw) return;\n const parsed = JSON.parse(raw) as SentientEvent[];\n if (!Array.isArray(parsed)) return;\n const drop = new Set(ids);\n const remaining = parsed.filter((e) => !drop.has(e.id));\n if (remaining.length === parsed.length) return; // nothing to remove\n if (remaining.length === 0) localStorage.removeItem(retryKey);\n else localStorage.setItem(retryKey, JSON.stringify(remaining));\n } catch {\n /* ignore */\n }\n}\n\n/**\n * Creates a batched event queue with periodic and lifecycle-triggered flushes.\n */\nexport function createEventQueue(config: QueueConfig): EventQueue {\n if (typeof window === 'undefined') {\n return SSR_QUEUE;\n }\n\n const flushIntervalMs = config.flushIntervalMs ?? 5000;\n const maxBatchSize = config.maxBatchSize ?? 20;\n const maxRetrySize = config.maxRetrySize ?? 100;\n const ingestUrl = config.ingestUrl;\n const apiKey = config.apiKey;\n const RETRY_KEY = retryStorageKey(apiKey);\n\n const queue: SentientEvent[] = [];\n const sentIds = new Set<string>();\n const sentIdOrder: string[] = [];\n\n const markSent = (ids: string[]): void => {\n for (const id of ids) {\n queuedIds.delete(id); // free the in-flight slot\n if (sentIds.has(id)) continue;\n sentIds.add(id);\n sentIdOrder.push(id);\n }\n while (sentIdOrder.length > MAX_SENT_IDS) {\n const oldest = sentIdOrder.shift();\n if (oldest) sentIds.delete(oldest);\n }\n // A batch that previously 5xx'd was persisted to the cross-reload retry\n // backstop. Now that it's acknowledged (delivered, or 4xx-dropped as\n // unretryable), drop those ids from localStorage too — otherwise the next\n // page load would replay an already-handled event. `sentIds` is in-memory\n // only, so it can't suppress that cross-reload duplicate on its own.\n purgeFromRetryQueue(ids, RETRY_KEY);\n };\n\n // Tracks IDs currently in `queue` or in flight (handed to transportBatch\n // but not yet confirmed). Entries are removed on markSent or when an event\n // is dropped, so the set stays bounded by in-flight + queued size.\n const queuedIds = new Set<string>();\n\n const enqueue = (event: SentientEvent): void => {\n if (sentIds.has(event.id) || queuedIds.has(event.id)) return;\n queuedIds.add(event.id);\n queue.push(event);\n };\n\n // A retryable failure hands the batch back to the in-memory queue so the\n // backoff-gated interval flush retries it in-session (localStorage is only the\n // cross-reload backstop). The ids are still tracked in queuedIds — they were\n // pulled from `queue` on flush but never markSent — so we re-add them WITHOUT\n // going through enqueue (which would skip them as already-queued) and without\n // deleting/re-adding queuedIds. Already-sent events are never re-queued, and a\n // single event stays a single copy: `queue` is fully drained each flush.\n const requeueFailed = (batch: SentientEvent[]): void => {\n for (const event of batch) {\n if (sentIds.has(event.id)) continue;\n queuedIds.add(event.id); // idempotent — keeps id-tracking consistent\n queue.push(event);\n }\n };\n\n const retryEvents = readRetryQueue(maxRetrySize, RETRY_KEY);\n for (const event of retryEvents) {\n enqueue(event);\n }\n\n // Pause flushing until backoff expires.\n let backoffUntil = 0;\n let consecutiveFailures = 0;\n\n // Authenticated transport. keepalive: true gives unload-survival; we still await\n // the response so 5xx/429 actually surface and we can retry.\n const transportBatch = (batch: SentientEvent[]): void => {\n if (batch.length === 0) return;\n const body = JSON.stringify(batch);\n const ids = batch.map((e) => e.id);\n\n let pending: Promise<Response> | Response;\n try {\n pending = fetch(ingestUrl, {\n method: 'POST',\n keepalive: true,\n body,\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${apiKey}`,\n },\n });\n } catch {\n // Synchronous throw (typically jsdom in tests, or extreme browser failure).\n writeRetryQueue(batch, maxRetrySize, RETRY_KEY);\n requeueFailed(batch);\n consecutiveFailures++;\n backoffUntil = Date.now() + Math.min(60_000, 1000 * 2 ** Math.min(consecutiveFailures, 6));\n return;\n }\n\n // fetch can sometimes return a value directly (tests using stubGlobal). Handle both.\n const handleResponse = (res: Response): void => {\n if (res.ok || (res.status >= 400 && res.status < 500 && res.status !== 429)) {\n // 2xx = success. 4xx (except 429) will never succeed — drop them rather than loop.\n markSent(ids);\n consecutiveFailures = 0;\n backoffUntil = 0;\n return;\n }\n // 5xx / 429 → retry with backoff.\n writeRetryQueue(batch, maxRetrySize, RETRY_KEY);\n requeueFailed(batch);\n consecutiveFailures++;\n backoffUntil = Date.now() + Math.min(60_000, 1000 * 2 ** Math.min(consecutiveFailures, 6));\n };\n\n if (pending instanceof Promise) {\n pending.then(handleResponse).catch(() => {\n writeRetryQueue(batch, maxRetrySize, RETRY_KEY);\n requeueFailed(batch);\n consecutiveFailures++;\n backoffUntil = Date.now() + Math.min(60_000, 1000 * 2 ** Math.min(consecutiveFailures, 6));\n });\n } else {\n handleResponse(pending);\n }\n };\n\n // Pack events into a batch that stays under the keepalive byte budget AND the\n // configured event-count cap. Returns the prefix of `pool` that fits.\n // Uses UTF-8 byte length (not char count) — emoji/CJK payloads can be 3–4×\n // larger as bytes and would otherwise blow the keepalive budget.\n const encoder = typeof TextEncoder !== 'undefined' ? new TextEncoder() : null;\n const byteLength = (s: string): number =>\n encoder ? encoder.encode(s).length : s.length;\n const packBatch = (pool: SentientEvent[]): SentientEvent[] => {\n const out: SentientEvent[] = [];\n let bytes = 2; // for the surrounding []\n for (const e of pool) {\n const size = byteLength(JSON.stringify(e)) + 1; // +1 for comma\n if (out.length > 0 && bytes + size > KEEPALIVE_BUDGET_BYTES) break;\n if (out.length >= maxBatchSize) break;\n out.push(e);\n bytes += size;\n }\n return out;\n };\n\n const flush = (): void => {\n try {\n if (Date.now() < backoffUntil) return;\n while (queue.length > 0) {\n // Re-check inside the loop: transportBatch can set backoffUntil\n // synchronously (a sync throw or a same-tick Response) and re-enqueue the\n // failed batch, so a mid-drain backoff must stop further chunks this tick\n // (otherwise the just-re-enqueued batch would immediately retry-loop).\n if (Date.now() < backoffUntil) break;\n const pending = queue.filter((e) => !sentIds.has(e.id));\n queue.length = 0;\n if (pending.length === 0) break;\n const batch = packBatch(pending);\n if (batch.length === 0) break;\n // Put anything we didn't pack back on the queue for the next tick.\n if (batch.length < pending.length) {\n queue.push(...pending.slice(batch.length));\n }\n transportBatch(batch);\n }\n } catch {\n /* never throw */\n }\n };\n\n let flushTimerActive = true;\n let intervalId: ReturnType<typeof setInterval> | null = null;\n\n intervalId = setInterval(() => {\n if (!flushTimerActive) return;\n flush();\n }, flushIntervalMs);\n\n const onVisibilityChange = (): void => {\n if (document.visibilityState === 'hidden') {\n flush();\n }\n };\n\n // `pagehide` (not `beforeunload`): a `beforeunload` listener can disqualify a\n // page from the back/forward cache. `pagehide` fires on real unloads AND on\n // bfcache eviction, and `visibilitychange:hidden` already covers most leaves,\n // so flush-on-leave stays covered without inhibiting bfcache.\n const onPageHide = (): void => {\n flush();\n };\n\n document.addEventListener('visibilitychange', onVisibilityChange);\n window.addEventListener('pagehide', onPageHide);\n\n return {\n push(event: SentientEvent): void {\n enqueue(event);\n if (queue.length >= maxBatchSize) {\n flush();\n }\n },\n flush,\n destroy(): void {\n flushTimerActive = false;\n if (intervalId !== null) {\n clearInterval(intervalId);\n intervalId = null;\n }\n document.removeEventListener('visibilitychange', onVisibilityChange);\n window.removeEventListener('pagehide', onPageHide);\n flush();\n },\n };\n}\n","/** Synchronous variant assignment cache (memory + localStorage). */\n\nexport type Assignment = {\n variantId: string;\n assignedAt: number;\n segment: string;\n confidence: number;\n content?: string;\n /**\n * Per-entry expiry (ms from `assignedAt`). When present it overrides the\n * cache-wide default TTL — lets the server-provided `assignmentTtlMs` govern\n * how long this specific assignment stays valid. Persists across reloads.\n */\n ttlMs?: number;\n};\n\nexport type AssignmentCache = {\n get(componentId: string, segment: string): Assignment | null;\n set(componentId: string, segment: string, assignment: Assignment): void;\n invalidate(componentId: string): void;\n clear(): void;\n};\n\nconst KEY_PREFIX = '_snt_asgn_';\nconst DEFAULT_TTL_MS = 30 * 60 * 1000;\n\nfunction cacheKey(componentId: string, segment: string): string {\n // URI-encode each part (same scheme as storageKey) and join with a literal\n // ':'. Since encodeURIComponent escapes ':', the separator is unambiguous —\n // otherwise a segment like `device:source` could collide two distinct\n // (componentId, segment) pairs onto the same raw `${id}:${segment}` string.\n return `${encodeURIComponent(componentId)}:${encodeURIComponent(segment)}`;\n}\n\nfunction storageKey(componentId: string, segment: string): string {\n // Both parts are URI-encoded and joined with a literal ':' separator. Since\n // encodeURIComponent escapes ':' (and the parts can otherwise contain '_' or\n // ':'), the separator is unambiguous and the key round-trips exactly.\n return `${KEY_PREFIX}${encodeURIComponent(componentId)}:${encodeURIComponent(segment)}`;\n}\n\nfunction parseStorageKey(key: string): { componentId: string; segment: string } | null {\n const suffix = key.slice(KEY_PREFIX.length);\n const sep = suffix.indexOf(':');\n if (sep < 0) return null;\n try {\n return {\n componentId: decodeURIComponent(suffix.slice(0, sep)),\n segment: decodeURIComponent(suffix.slice(sep + 1)),\n };\n } catch {\n return null;\n }\n}\n\nfunction listStorageKeys(): string[] {\n try {\n const keys: string[] = [];\n for (let i = 0; i < localStorage.length; i++) {\n const key = localStorage.key(i);\n if (key?.startsWith(KEY_PREFIX)) {\n keys.push(key);\n }\n }\n return keys;\n } catch {\n return [];\n }\n}\n\n/**\n * Creates an assignment cache with optional TTL (default 30 minutes).\n */\nexport function createAssignmentCache(ttlMs: number = DEFAULT_TTL_MS): AssignmentCache {\n const memory = new Map<string, Assignment>();\n\n // Honor a per-entry TTL (server-provided assignmentTtlMs) when present; fall\n // back to the cache-wide default otherwise.\n const isExpired = (assignment: Assignment): boolean =>\n assignment.assignedAt + (assignment.ttlMs && assignment.ttlMs > 0 ? assignment.ttlMs : ttlMs) < Date.now();\n\n const restoreFromStorage = (): void => {\n for (const key of listStorageKeys()) {\n try {\n const raw = localStorage.getItem(key);\n if (!raw) continue;\n const assignment = JSON.parse(raw) as Assignment;\n if (isExpired(assignment)) {\n localStorage.removeItem(key);\n continue;\n }\n const parsed = parseStorageKey(key);\n if (!parsed) continue;\n memory.set(cacheKey(parsed.componentId, parsed.segment), assignment);\n } catch {\n /* ignore corrupt entries */\n }\n }\n };\n\n if (typeof window !== 'undefined') {\n restoreFromStorage();\n }\n\n return {\n get(componentId: string, segment: string): Assignment | null {\n const entry = memory.get(cacheKey(componentId, segment));\n if (!entry) return null;\n if (isExpired(entry)) {\n memory.delete(cacheKey(componentId, segment));\n return null;\n }\n return entry;\n },\n\n set(componentId: string, segment: string, assignment: Assignment): void {\n const key = cacheKey(componentId, segment);\n memory.set(key, assignment);\n try {\n localStorage.setItem(storageKey(componentId, segment), JSON.stringify(assignment));\n } catch {\n /* ignore */\n }\n },\n\n invalidate(componentId: string): void {\n // Memory keys are encodeURIComponent(componentId) + ':' + encoded segment,\n // so match on the encoded-id prefix (the ':' after it can't appear inside\n // the encoded id).\n const prefix = `${encodeURIComponent(componentId)}:`;\n for (const key of [...memory.keys()]) {\n if (key.startsWith(prefix)) {\n memory.delete(key);\n }\n }\n for (const storageK of listStorageKeys()) {\n const parsed = parseStorageKey(storageK);\n if (parsed?.componentId === componentId) {\n try {\n localStorage.removeItem(storageK);\n } catch {\n /* ignore */\n }\n }\n }\n },\n\n clear(): void {\n memory.clear();\n for (const storageK of listStorageKeys()) {\n try {\n localStorage.removeItem(storageK);\n } catch {\n /* ignore */\n }\n }\n },\n };\n}\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\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\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 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 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 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 * 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 * Decision snapshot: the SPA / return-visit pre-paint source. Written after\n * every successful decide; read by the inline pre-paint script (before any\n * framework code runs) and by init() to seed slot/persona state.\n */\nimport type { SlotResult } from './slots.js';\n\nexport const SNAPSHOT_STORAGE_KEY_PREFIX = '_snt_snap:';\n\n/** Versioned compound locator: resolve id → dataAttr → selector, then verify\n * against fingerprint. Lets a slot survive DOM/markup drift. */\nexport type CompoundLocator = {\n v?: number;\n id?: string;\n dataAttr?: { name: string; value: string };\n selector?: string;\n urlMatch?: string;\n fingerprint?: { tag?: string; text?: string };\n semanticId?: string;\n};\n\n/** Bounded, declarative operations a registry arm may apply to its element.\n * The style set is a fixed whitelist (validated server-side); no arbitrary CSS,\n * HTML, or JS ever. `text` is applied via textContent; https-only URLs.\n * moveBefore/moveAfter (exactly one) reposition the element relative to a\n * uniquely-resolving sibling anchor — post-decide only, never pre-paint. */\nexport type SlotOps = {\n text?: string;\n style?: Record<string, string>;\n hidden?: boolean;\n href?: string;\n imageSrc?: string;\n imageAlt?: string;\n moveBefore?: CompoundLocator;\n moveAfter?: CompoundLocator;\n};\n\n/** Registry-mode apply info per slot: where to apply and what to set. Stored so\n * a returning visitor's pre-paint can reapply it. `target` is the Phase-2 bare\n * selector; `locator` (Phase 3) is the compound locator, preferred when present. */\nexport type SlotConfigEntry = {\n kind: 'tokens' | 'arms';\n target?: string;\n locator?: CompoundLocator;\n content?: string;\n ops?: SlotOps;\n};\n\nexport type DecisionSnapshot = {\n v: 1;\n persona: string;\n band: 'low' | 'medium' | 'high';\n slots: Record<string, SlotResult>;\n layoutOrder: string[] | null;\n savedAt: number;\n // Additive (kept at v:1 so existing snapshots stay valid — bumping the version\n // would flush every returning visitor's snapshot and flash the baseline once).\n // Present only for registry-mode (no-code) installs.\n slotConfig?: Record<string, SlotConfigEntry>;\n};\n\nconst BANDS = ['low', 'medium', 'high'];\n\n/** Returns null on missing, corrupt, or wrong-version data — never throws. */\nexport function readSnapshot(apiKey: string): DecisionSnapshot | null {\n try {\n const raw = localStorage.getItem(SNAPSHOT_STORAGE_KEY_PREFIX + apiKey);\n if (!raw) return null;\n const p = JSON.parse(raw) as Partial<DecisionSnapshot> | null;\n if (\n !p ||\n typeof p !== 'object' ||\n p.v !== 1 ||\n typeof p.persona !== 'string' ||\n typeof p.band !== 'string' ||\n !BANDS.includes(p.band) ||\n typeof p.slots !== 'object' ||\n p.slots === null ||\n Array.isArray(p.slots) ||\n !(p.layoutOrder === null || Array.isArray(p.layoutOrder)) ||\n typeof p.savedAt !== 'number' ||\n // slotConfig is optional; when present it must be a plain object.\n !(p.slotConfig === undefined || (typeof p.slotConfig === 'object' && p.slotConfig !== null && !Array.isArray(p.slotConfig)))\n ) {\n return null;\n }\n return p as DecisionSnapshot;\n } catch {\n return null;\n }\n}\n\n/** Best-effort persist — storage failures are swallowed. */\nexport function writeSnapshot(apiKey: string, snap: DecisionSnapshot): void {\n try {\n localStorage.setItem(SNAPSHOT_STORAGE_KEY_PREFIX + apiKey, JSON.stringify(snap));\n } catch {\n /* private mode / quota — the snapshot is an optimization, never a requirement */\n }\n}\n\n/**\n * Inline pre-paint script (Rung 1a): reads the snapshot and sets\n * `data-sentient-persona` / `data-sentient-confidence` on <html> before\n * first paint. Single-writer: it never overwrites attributes already set.\n *\n * Safety properties (pinned by tests):\n * - apiKey goes through JSON.stringify, then '<' is escaped to <, so a\n * hostile key can neither break the JS string nor terminate the <script>.\n * - Built by string concatenation and contains no backticks, so the output\n * survives being embedded in template-literal-based renderers.\n */\nexport function renderPrePaintScript(apiKey: string): string {\n const key = JSON.stringify(SNAPSHOT_STORAGE_KEY_PREFIX + apiKey).replace(/</g, '\\\\u003c');\n return (\n '(function(){try{' +\n 'var r=localStorage.getItem(' + key + ');if(!r)return;' +\n 'var s=JSON.parse(r);' +\n 'if(!s||s.v!==1||typeof s.persona!==\"string\"||typeof s.band!==\"string\")return;' +\n 'var d=document.documentElement;' +\n 'if(d.hasAttribute(\"data-sentient-persona\"))return;' +\n 'd.setAttribute(\"data-sentient-persona\",s.persona);' +\n 'd.setAttribute(\"data-sentient-confidence\",s.band);' +\n '}catch(e){}})();'\n );\n}\n","import { initSession, type SessionConfig, type SessionManager } from './session';\nimport {\n createEventQueue,\n retryStorageKey,\n type EventQueue,\n type EventType,\n type QueueConfig,\n type SentientEvent,\n} from './queue';\nimport {\n createAssignmentCache,\n type Assignment,\n} from './cache';\nimport type {\n GraphConfig,\n GraphSnapshot,\n} from './graph';\nimport {\n detectDeviceClass,\n detectTrafficSource,\n detectTimeOfDay,\n referrerDomainFromReferer,\n uaTokenMatch,\n} from './session-meta.js';\nimport {\n toWireSlot,\n baselineResultFor,\n armOfResult,\n type SlotDeclInput,\n type SlotResult,\n} from './slots.js';\nimport { readSnapshot, writeSnapshot, SNAPSHOT_STORAGE_KEY_PREFIX, type SlotConfigEntry, type CompoundLocator } from './snapshot.js';\nimport { confidenceBand } from '@sentientui/policy';\nimport { createLocalModeClient } from './local-mode.js';\nimport { randomUuidV4 } from './uuid.js';\n\nexport { PROD_KEYLESS_ERROR, LOCAL_MODE_BANNER } from './local-mode.js';\n\nexport {\n detectDeviceClass,\n detectTrafficSource,\n detectTimeOfDay,\n deriveSessionSegment,\n referrerDomainFromReferer,\n uaTokenMatch,\n matchedAgentToken,\n agentUaList,\n} from './session-meta.js';\n\nconst DEFAULT_INGEST_URL = 'https://api.sentient-ui.com/v1/events';\n\n// Keyed by apiKey so multiple init() calls (HMR, multi-project) don't collide.\nconst _clients = new Map<string, {\n config: SentientConfig;\n upgrade: ((c: SentientClient) => void) | null;\n // Teardown for the live client bound to this key (stops its queue interval +\n // unload listeners). Present only for the full tracking client; the no-op /\n // pre-consent / local entries have nothing to tear down. Called before a\n // re-init for the same key replaces it, so timers/listeners can't leak.\n dispose?: () => void;\n}>();\nlet _lastApiKey: string | null = null;\n\nexport type SentientConfig = {\n apiKey: string;\n context: 'landing' | 'ecommerce' | 'saas' | 'marketplace';\n /** @internal — not exposed to users; defaults to the hosted SentientUI API. */\n ingestUrl?: string;\n debug?: boolean;\n /**\n * Pre-seeded assignments from `preloadAssignments()` (SSR).\n * Seeds the local cache so `assign()` returns without a network call for\n * listed code variants, guaranteeing server and client render the same\n * variant on first paint. Managed-text components (assign with no\n * variantIds) still fetch once when the seed carries no content.\n */\n initialAssignments?: Record<string, string>;\n /**\n * Segment used for SSR preload (`device:source`). When set with `initialAssignments`,\n * seeds the assignment cache under this key so hydration matches the server bandit row.\n */\n sessionSegment?: string;\n /**\n * Consent gate. When `false`, returns a no-op client and performs no tracking.\n * Defaults to `true`. Re-call `init()` (via `AdaptiveProvider` consent prop) when\n * the user grants or revokes consent mid-session.\n */\n consent?: boolean;\n /**\n * Behavior before consent is granted. `'statistical_winner'` fetches the\n * best-performing variant via `GET /v1/winner` — no session or tracking data\n * is stored. `'control'` (default) shows `variantIds[0]` with no API call.\n * Applies when tracking is gated off — either `consent: false` or an active\n * Do Not Track signal.\n */\n preConsentBehavior?: 'statistical_winner' | 'control';\n /**\n * Whether to honor the browser's Do Not Track (DNT) signal. Defaults to `true`.\n * When `true` and the visitor has DNT enabled, the SDK sets no cookies and\n * sends no tracking data — behaving exactly as `consent: false` (still serving\n * the read-only `preConsentBehavior` winner if configured), and `grantConsent()`\n * will not upgrade it. Set `false` to make your own consent gate authoritative.\n */\n respectDoNotTrack?: boolean;\n userId?: string;\n /**\n * Session ID generated server-side (from `loadAdaptiveAssignments` / `loadAdaptiveDecision`).\n * When provided, the client adopts this ID on first visit instead of generating a new one,\n * ensuring events and goals are attributed to the same session the server used for assignment.\n */\n ssrSessionId?: string;\n /**\n * ISO 3166-1 alpha-2 country code for the visitor. When provided (e.g. from\n * the `CF-IPCountry` header in a Next.js server component), it is included in\n * the session upsert so country-based segmentation works without client-side\n * geo lookup.\n */\n country?: string;\n /**\n * Pre-seeded slot results from `preloadDecisions()` / `loadAdaptiveDecision()`\n * (SSR). Seeds the local slot state so `getSlotResult()` agrees with the\n * server-rendered markup on first paint.\n */\n initialSlots?: Record<string, SlotResult>;\n /**\n * Persona decided during SSR. Takes priority over the html-attribute\n * adoption and the local snapshot.\n */\n initialPersona?: { persona: string; confidence: number };\n /**\n * Keyless local mode. 'auto' (default) simulates decisions on-device when no\n * valid API key is configured — but only in development builds (the\n * `development` export condition); production bundles physically exclude the\n * engine. `true` forces the local engine regardless of key (escape hatch);\n * `false` restores the silent keyless no-op.\n */\n localMode?: 'auto' | boolean;\n};\n\nexport type AssignResult = { variantId: string; assignmentTtlMs: number; content?: string };\n\nexport type { SlotDeclInput, SlotResult };\nexport { armOfResult, baselineResultFor, baselineSlots, toWireSlot } from './slots.js';\n\nexport {\n SNAPSHOT_STORAGE_KEY_PREFIX,\n readSnapshot,\n writeSnapshot,\n renderPrePaintScript,\n} from './snapshot.js';\nexport type { DecisionSnapshot, SlotConfigEntry, SlotOps, CompoundLocator } from './snapshot.js';\n\n/** An editor-defined goal delivered with a registry-mode decision, for the\n * snippet to install delegated listeners from. */\nexport type GoalDefinition = {\n goalId: string;\n event: 'click' | 'form_submit' | 'url_reached';\n locator?: CompoundLocator;\n urlPattern?: string;\n slotId?: string;\n};\n\n/** One served section-classification row (registry mode): where it is on the\n * page (url match + compound locator) and its semantic type. The snippet\n * resolves the locator to build capture's `typeOf` hook. */\nexport type SectionMapEntry = {\n urlMatch: string;\n locator: CompoundLocator;\n type: string;\n};\n\nexport type DecideOutcome = {\n layoutOrder: string[] | null;\n assignments: Record<string, string>;\n slots: Record<string, SlotResult>;\n persona: string;\n confidence: number;\n // Registry mode only: where/what to apply for server-defined slots.\n slotConfig?: Record<string, SlotConfigEntry>;\n // Registry mode only: editor-defined goals to wire up.\n goals?: GoalDefinition[];\n // Registry mode only: served section-classification map the snippet turns\n // into capture's `typeOf` hook.\n sectionMap?: SectionMapEntry[];\n};\n\nexport type DecideInput = {\n sections?: string[];\n components?: Array<{ id: string; variantIds?: string[] }>;\n slots?: SlotDeclInput[];\n // 'registry' → serve the project's published slot_definitions in addition to\n // any declared slots (registry wins on id collision). Default 'request'.\n slotsFrom?: 'request' | 'registry';\n /**\n * Caller's build version (e.g. the snippet's `__SNIPPET_VERSION__`), sent\n * as `v` on the wire. Additive/best-effort: the server persists it for\n * version-skew reporting (see apps/api decide route) and ignores it\n * entirely on older deployments. Omit if the caller has no version to report.\n */\n v?: string;\n};\n\nexport type WeightEntry = { variantId: string; pulls: number; avgReward: number | null };\nexport type ComponentWeightEntry = { componentId: string; updatedAt: number; variants: WeightEntry[] };\n\nexport type ComponentGoalOptions = {\n /** Reward credited to the served variant (0–1). Defaults to 1. */\n reward?: number;\n /** Extra fields merged into the event payload. */\n metadata?: Record<string, unknown>;\n};\n\nexport type SentientClient = {\n track(\n event: Omit<SentientEvent, 'id' | 'sessionId' | 'timestamp' | 'timeInSession'>,\n ): void;\n goal(name: string, metadata?: Record<string, unknown>, weight?: number, stepIndex?: number): void;\n /**\n * Records a conversion attributed to the variant currently served for\n * `componentId`, so it feeds the per-variant CVR funnel. Resolves the served\n * variant from the local assignment cache — no need to pass variantId or\n * projectId. No-ops if the component has not been assigned yet (render its\n * `<Adaptive>`/call `assign()` first). Prefer this over bare `goal()` for\n * variant experiments; `goal()` is session-level only (no component attribution).\n */\n componentGoal(componentId: string, goalType: string, opts?: ComponentGoalOptions): void;\n identify(userId: string): void;\n getAssignment(componentId: string, segment: string): Assignment | null;\n /** Server-side variant assignment. Caches the result locally per (component, segment). */\n assign(componentId: string, variantIds?: string[], agentData?: unknown, agentDataByVariant?: Record<string, unknown>): Promise<AssignResult | null>;\n /**\n * Single-roundtrip decision for layout sections, component variants, and\n * adaptive slots. Awaits the session upsert (like `assign`) so the server\n * never decides for a session row that doesn't exist yet. A response\n * without a `slots` field means the server predates slots — every declared\n * slot resolves to its baseline and no retry is made.\n */\n decide(input: DecideInput): Promise<DecideOutcome | null>;\n /** Slot result served this session (decide result, SSR seed, snapshot, or failure baseline). Null when unknown. */\n getSlotResult(slotId: string): SlotResult | null;\n /** Current persona estimate. Band is always `confidenceBand(confidence)`. Null when nothing is known yet. */\n getPersona(): { persona: string; confidence: number; band: 'low' | 'medium' | 'high' } | null;\n /** Fetches current bandit weights for all components in this project. Used by the provider to keep live-weight polling fresh. */\n fetchWeights(): Promise<ComponentWeightEntry[]>;\n getGraph(): GraphSnapshot;\n /**\n * Routine teardown: stops timers/listeners and flushes pending events, but\n * KEEPS the visitor identity, decision snapshot, and retry bucket. Use for\n * component unmount / re-init (framework providers call this on cleanup).\n */\n dispose(): void;\n /**\n * Consent-revocation / forget-me teardown: everything `dispose()` does,\n * plus deletion of the visitor identity (`_snt_uid`), the decision\n * snapshot, and the persisted retry bucket. The next visit starts as a\n * brand-new visitor.\n */\n destroy(): void;\n /** True when this client is the keyless local-mode client (dev only). */\n readonly isLocal?: boolean;\n};\n\n// SSR preload helpers moved to the `@sentientui/core/server` entry in 0.6.0 so\n// ~200 lines of Node-only fetch logic stop shipping in the browser bundle.\n\nexport { attachMicroSignalDetectors } from './micro-signals.js';\nexport type { MicroSignalEmitter, MicroSignalType } from './micro-signals.js';\n\nexport type {\n SessionConfig,\n SessionManager,\n EventType,\n SentientEvent,\n QueueConfig,\n Assignment,\n GraphSnapshot,\n GraphConfig,\n};\n\nfunction generateEventId(): string {\n return randomUuidV4();\n}\n\nconst SSR_CLIENT: SentientClient = {\n track: () => undefined,\n goal: () => undefined,\n componentGoal: () => undefined,\n identify: () => undefined,\n getAssignment: () => null,\n assign: () => Promise.resolve(null),\n decide: () => Promise.resolve(null),\n getSlotResult: () => null,\n getPersona: () => null,\n fetchWeights: () => Promise.resolve([]),\n getGraph: () => ({ pageNodes: [], capturedAt: 0 }),\n dispose: () => undefined,\n destroy: () => undefined,\n};\n\nfunction readUtmParams(): Record<string, string> {\n try {\n const out: Record<string, string> = {};\n const sp = new URLSearchParams(window.location.search);\n for (const [k, v] of sp) {\n if (k.startsWith('utm_')) out[k] = v;\n }\n return out;\n } catch {\n return {};\n }\n}\n\nfunction deriveBaseUrl(ingestUrl: string): string {\n return ingestUrl.replace(/\\/events\\/?$/, '');\n}\n\n/**\n * Detects whether the visitor has signalled a tracking opt-out. Honors Global\n * Privacy Control (`navigator.globalPrivacyControl`) — the legally-enforceable\n * CCPA/CPRA signal — as well as Do Not Track (`navigator.doNotTrack`, the legacy\n * `window.doNotTrack` on older Firefox, and `navigator.msDoNotTrack` on old\n * IE/Edge). GPC is a boolean; DNT is opt-out only when explicitly `'1'`/`'yes'`.\n */\nexport function isDoNotTrackEnabled(): boolean {\n // GPC is a boolean flag, checked separately from the DNT string signals.\n if (\n typeof navigator !== 'undefined' &&\n (navigator as unknown as { globalPrivacyControl?: boolean }).globalPrivacyControl === true\n ) {\n return true;\n }\n const signals = [\n typeof navigator !== 'undefined' ? navigator.doNotTrack : undefined,\n typeof window !== 'undefined'\n ? (window as unknown as { doNotTrack?: string | null }).doNotTrack\n : undefined,\n typeof navigator !== 'undefined'\n ? (navigator as unknown as { msDoNotTrack?: string | null }).msDoNotTrack\n : undefined,\n ];\n return signals.some((v) => v === '1' || v === 'yes');\n}\n\n/**\n * Upgrades a pre-consent client (created with `consent: false, preConsentBehavior: 'statistical_winner'`)\n * to a fully-tracking client. Call this from your consent management platform callback.\n * For React apps, prefer updating the `consent` prop on `<AdaptiveProvider>`.\n * Pass `apiKey` to target a specific project; omit to upgrade the most-recently-initialized client.\n */\nexport function grantConsent(apiKey?: string): void {\n if (typeof window === 'undefined') return;\n\n const key = apiKey ?? _lastApiKey;\n if (!key) {\n console.warn('[sentient] grantConsent() called before init()');\n return;\n }\n\n const entry = _clients.get(key);\n if (!entry) {\n console.warn('[sentient] grantConsent() called before init()');\n return;\n }\n\n const { config, upgrade } = entry;\n if (!upgrade) return;\n\n // Honor an active Do Not Track signal — consent cannot override a global opt-out.\n if (config.respectDoNotTrack !== false && isDoNotTrackEnabled()) return;\n\n const fullClient = init({ ...config, consent: true });\n upgrade(fullClient);\n // init() just registered the full client (with its dispose) under `key`.\n // Preserve that dispose so a later re-init/teardown can still tear it down —\n // we only need to clear the upgrade hook now that consent is granted.\n const disposed = _clients.get(key)?.dispose;\n _clients.set(key, { config: { ...config, consent: true }, upgrade: null, dispose: disposed });\n}\n\nfunction createPreConsentProxy(config: SentientConfig): { proxy: SentientClient; setInner: (c: SentientClient) => void } {\n const baseUrl = deriveBaseUrl(config.ingestUrl ?? DEFAULT_INGEST_URL);\n const authHeaders = {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${config.apiKey}`,\n } as const;\n\n let inner: SentientClient = {\n track: () => undefined,\n goal: () => undefined,\n componentGoal: () => undefined,\n identify: () => undefined,\n getAssignment: () => null,\n fetchWeights: () => Promise.resolve([]),\n async assign(componentId, variantIds, _agentData?) {\n try {\n const params = new URLSearchParams({ componentId });\n for (const v of variantIds ?? []) params.append('variantIds[]', v);\n const res = await fetch(`${baseUrl}/winner?${params.toString()}`, {\n headers: authHeaders,\n });\n if (!res.ok) return variantIds?.[0] ? { variantId: variantIds[0], assignmentTtlMs: 0 } : null;\n const body = (await res.json()) as { variantId: string };\n return { variantId: body.variantId, assignmentTtlMs: 0 };\n } catch {\n return variantIds?.[0] ? { variantId: variantIds[0], assignmentTtlMs: 0 } : null;\n }\n },\n decide: () => Promise.resolve(null),\n getSlotResult: () => null,\n getPersona: () => null,\n getGraph: () => ({ pageNodes: [], capturedAt: 0 }),\n dispose: () => undefined,\n destroy: () => undefined,\n };\n\n const proxy: SentientClient = {\n track: (e) => inner.track(e),\n goal: (n, m, w, s) => inner.goal(n, m, w, s),\n componentGoal: (c, g, o) => inner.componentGoal(c, g, o),\n identify: (u) => inner.identify(u),\n getAssignment: (c, s) => inner.getAssignment(c, s),\n assign: (c, v, a, av) => inner.assign(c, v, a, av),\n decide: (i) => inner.decide(i),\n getSlotResult: (s) => inner.getSlotResult(s),\n getPersona: () => inner.getPersona(),\n fetchWeights: () => inner.fetchWeights(),\n getGraph: () => inner.getGraph(),\n dispose: () => inner.dispose(),\n destroy: () => inner.destroy(),\n };\n\n function setInner(fullClient: SentientClient) {\n inner = fullClient;\n }\n\n return { proxy, setInner };\n}\n\n/**\n * Initializes the Sentient client. Returns a no-op client during SSR.\n */\nexport function init(config: SentientConfig): SentientClient {\n if (typeof window === 'undefined') {\n return SSR_CLIENT;\n }\n\n _lastApiKey = config.apiKey;\n\n // A re-init for the same key (HMR, consent toggle, provider remount) supersedes\n // the prior client. Dispose it first so its queue's setInterval and\n // visibilitychange/pagehide listeners don't leak — the map only ever held its\n // config, so without this the old client kept flushing forever.\n const prevEntry = _clients.get(config.apiKey || 'local');\n if (prevEntry?.dispose) {\n try {\n prevEntry.dispose();\n } catch {\n /* teardown must never throw on re-init */\n }\n }\n\n // DNT/GPC (a global opt-out) or an explicit `consent: false` must be evaluated\n // BEFORE the local-mode branch: createLocalModeClient() calls initSession()\n // unconditionally, so a gated visitor would otherwise be issued the 365-day\n // `_snt_uid` identity cookie in keyless/local mode (audit P2). DNT/GPC also\n // gates tracking off even when the site passes `consent: true`, and blocks\n // `grantConsent()` from upgrading.\n const dntBlocked = config.respectDoNotTrack !== false && isDoNotTrackEnabled();\n const gated = config.consent === false || dntBlocked;\n\n // Keyless local mode. `localMode: true` forces the local engine (documented\n // escape hatch); 'auto' (default) engages it only when no valid key is\n // present. In production builds `@sentientui/core/local` resolves to a stub\n // and this degrades to a no-op client + one console.error per page load\n // (createLocalModeClient handles that), so no NODE_ENV check is needed here.\n const keyValid = typeof config.apiKey === 'string' && config.apiKey.startsWith('pk_');\n if (config.localMode === true || (!keyValid && config.localMode !== false)) {\n // A gated visitor must never get the identity cookie. Local mode has no\n // server to serve a statistical winner from, so return a plain no-op.\n if (gated) {\n _clients.set(config.apiKey || 'local', { config, upgrade: null });\n return SSR_CLIENT;\n }\n _clients.set(config.apiKey || 'local', { config, upgrade: null });\n return createLocalModeClient(config);\n }\n\n if (gated) {\n if (config.preConsentBehavior === 'statistical_winner') {\n if (!config.apiKey || !config.apiKey.startsWith('pk_')) {\n console.warn('[sentient] init() called with an invalid apiKey — expected a pk_ public key. SDK disabled.');\n return SSR_CLIENT;\n }\n const { proxy, setInner } = createPreConsentProxy(config);\n // Under DNT the read-only winner still serves, but consent can never\n // upgrade it to tracking — so drop the upgrade hook.\n _clients.set(config.apiKey, { config, upgrade: dntBlocked ? null : setInner });\n return proxy;\n }\n _clients.set(config.apiKey, { config, upgrade: null });\n return SSR_CLIENT;\n }\n\n if (!config.apiKey || !config.apiKey.startsWith('pk_')) {\n console.warn('[sentient] init() called with an invalid apiKey — expected a pk_ public key. SDK disabled.');\n return SSR_CLIENT;\n }\n\n if (config.ingestUrl === '') {\n console.warn('[sentient] init() called with an empty ingestUrl. SDK disabled.');\n return SSR_CLIENT;\n }\n\n const resolvedIngestUrl = config.ingestUrl ?? DEFAULT_INGEST_URL;\n\n const sessionStart = Date.now();\n const session = initSession({ ssrSessionId: config.ssrSessionId });\n const assignmentCache = createAssignmentCache();\n const eventQueue = createEventQueue({ ingestUrl: resolvedIngestUrl, apiKey: config.apiKey });\n const baseUrl = deriveBaseUrl(resolvedIngestUrl);\n\n const authHeaders = {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${config.apiKey}`,\n } as const;\n\n const deviceClass = detectDeviceClass(navigator.userAgent ?? '');\n const appOrigin = typeof window !== 'undefined' ? window.location.origin : undefined;\n const trafficSource = detectTrafficSource(document.referrer ?? '', appOrigin);\n const sessionSegment =\n config.sessionSegment ?? `${deviceClass}:${trafficSource}`;\n const inflightAssigns = new Map<string, Promise<AssignResult | null>>();\n\n // --- Adaptive-slot state (decide) ---\n // Results served for this session, keyed by slot id. Written by decide();\n // read by getSlotResult() (Task 3.3) and componentGoal's slot fallback.\n const slotStore = new Map<string, SlotResult>();\n let personaState: { persona: string; confidence: number } | null = null;\n\n // On decide failure every declared slot must still resolve — to its baseline.\n // Never overwrite a previously served result.\n const seedSlotBaselines = (decls: SlotDeclInput[]): void => {\n for (const d of decls) {\n if (!slotStore.has(d.id)) slotStore.set(d.id, baselineResultFor(d));\n }\n };\n\n // Seed slot/persona state. Priority: explicit SSR seeds → snapshot.\n if (config.initialSlots) {\n for (const [slotId, result] of Object.entries(config.initialSlots)) {\n slotStore.set(slotId, result);\n }\n }\n const seedSnapshot = readSnapshot(config.apiKey);\n if (seedSnapshot) {\n for (const [slotId, result] of Object.entries(seedSnapshot.slots)) {\n if (!slotStore.has(slotId)) slotStore.set(slotId, result);\n }\n }\n\n // Band-only persona sources (html attrs, snapshot) become a band-consistent\n // numeric confidence so confidenceBand(confidence) always equals the band.\n const BAND_CONFIDENCE: Record<string, number> = { low: 0.15, medium: 0.5, high: 0.85 };\n if (config.initialPersona) {\n personaState = { ...config.initialPersona };\n } else {\n // Single-writer rule: the inline pre-paint script owns the <html>\n // attributes. The client ADOPTS them as truth and never rewrites them\n // mid-session (next visit's script picks up the new snapshot instead).\n const ds = document.documentElement.dataset;\n if (ds.sentientPersona) {\n personaState = {\n persona: ds.sentientPersona,\n confidence: BAND_CONFIDENCE[ds.sentientConfidence ?? 'low'] ?? 0.15,\n };\n } else if (seedSnapshot) {\n personaState = {\n persona: seedSnapshot.persona,\n confidence: BAND_CONFIDENCE[seedSnapshot.band] ?? 0.15,\n };\n }\n }\n\n // Seed SSR-preloaded assignments into the local cache so assign() finds a\n // cache hit immediately — no network call, no variant flash on hydration.\n if (config.initialAssignments) {\n for (const [componentId, variantId] of Object.entries(config.initialAssignments)) {\n assignmentCache.set(componentId, sessionSegment, {\n variantId,\n assignedAt: Date.now(),\n segment: sessionSegment,\n confidence: 1,\n });\n }\n }\n\n // Upsert session metadata once on init. assign() awaits this promise so\n // the server isn't asked to assign for a session row that doesn't exist yet.\n let sessionReady: Promise<void> = Promise.resolve();\n\n const sessionId = session.getSessionId();\n if (sessionId) {\n const referrerDomain = referrerDomainFromReferer(document.referrer ?? '');\n const sessionBody = {\n sessionId,\n deviceClass,\n trafficSource,\n referrerDomain,\n utmParams: readUtmParams(),\n timeOfDay: detectTimeOfDay(new Date()),\n dayOfWeek: ['sun', 'mon', 'tue', 'wed', 'thu', 'fri', 'sat'][new Date().getDay()],\n ephemeral: session.isEphemeral(),\n // Likely-automation hint: navigator.webdriver (set under automation\n // control) or a known agent token in the UA. Probabilistic — used for\n // metrics + bandit exclusion server-side, never to change what's served.\n automation:\n (typeof navigator !== 'undefined' && navigator.webdriver === true) ||\n uaTokenMatch(navigator.userAgent ?? ''),\n ...(config.userId ? { userId: config.userId } : {}),\n ...(config.country ? { country: config.country } : {}),\n };\n try {\n sessionReady = fetch(`${baseUrl}/sessions`, {\n method: 'POST',\n keepalive: true,\n body: JSON.stringify(sessionBody),\n headers: authHeaders,\n })\n .then((res) => {\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 }\n return undefined;\n })\n .catch(() => undefined);\n } catch {\n /* never throw on init */\n }\n }\n\n if (config.debug) {\n console.log('[sentient] initialized', { context: config.context });\n (\n window as unknown as {\n __sentient?: {\n client: SentientClient;\n queue: EventQueue;\n };\n }\n ).__sentient = {\n client: null as unknown as SentientClient,\n queue: eventQueue,\n };\n }\n\n const client: SentientClient = {\n goal(name, metadata = {}, weight = 1.0, stepIndex = 0) {\n const sid = session.getSessionId();\n if (!sid) return;\n const goalId = generateEventId();\n sessionReady.then(() => {\n fetch(`${baseUrl}/goals`, {\n method: 'POST',\n keepalive: true,\n body: JSON.stringify({ sessionId: sid, name, metadata, weight, stepIndex, goalId }),\n headers: authHeaders,\n }).catch(() => undefined);\n });\n },\n\n componentGoal(componentId, goalType, opts) {\n const sid = session.getSessionId();\n if (!sid) return;\n // Variant experiments resolve from the assignment cache; adaptive slots\n // (useAdaptiveTokens / AdaptiveGroup) resolve from the slot state, using\n // the canonical arm string as the attributed variantId.\n const assignment = assignmentCache.get(componentId, sessionSegment);\n const slotResult = assignment ? null : slotStore.get(componentId) ?? null;\n if (!assignment && slotResult === null) {\n if (config.debug) {\n console.warn(\n `[sentient] componentGoal(\"${componentId}\"): no assignment or slot decision yet — render its <Adaptive>/adaptive hook or call assign()/decide() before recording a goal.`,\n );\n }\n return;\n }\n const attributedVariantId = assignment ? assignment.variantId : armOfResult(slotResult!);\n const fullEvent: SentientEvent = {\n id: generateEventId(),\n sessionId: sid,\n projectId: config.apiKey,\n componentId,\n variantId: attributedVariantId,\n eventType: 'goal_achieved',\n goalType,\n payload: { reward: opts?.reward ?? 1, ...(opts?.metadata ?? {}) },\n timestamp: Date.now(),\n timeInSession: Date.now() - sessionStart,\n };\n if (config.debug) {\n console.log('[sentient] componentGoal', fullEvent);\n }\n sessionReady.then(() => eventQueue.push(fullEvent));\n },\n\n identify(userId) {\n const sid = session.getSessionId();\n if (!sid) return;\n sessionReady.then(() => {\n fetch(`${baseUrl}/sessions`, {\n method: 'POST',\n keepalive: true,\n body: JSON.stringify({ sessionId: sid, userId, ephemeral: session.isEphemeral() }),\n headers: authHeaders,\n }).catch(() => undefined);\n });\n },\n\n track(event) {\n const sessionId = session.getSessionId();\n if (!sessionId) return;\n\n const fullEvent: SentientEvent = {\n ...event,\n id: generateEventId(),\n sessionId,\n timestamp: Date.now(),\n timeInSession: Date.now() - sessionStart,\n };\n\n if (config.debug) {\n console.log('[sentient] track', fullEvent);\n }\n\n sessionReady.then(() => eventQueue.push(fullEvent));\n },\n\n getAssignment(componentId, segment) {\n return assignmentCache.get(componentId, segment);\n },\n\n async assign(componentId, variantIds, agentData?, agentDataByVariant?) {\n const sid = session.getSessionId();\n if (!sid) return null;\n\n const cached = assignmentCache.get(componentId, sessionSegment);\n // When variantIds are provided (A/B code variant), a cache hit is always final.\n // When variantIds are absent (managed text component), only hit the cache if content\n // is present — a seed from initialAssignments has no content and must still fetch.\n if (cached && (variantIds?.length || cached.content !== undefined)) {\n // Surface the entry's remaining TTL (server-provided when set) instead of\n // a hardcoded 0, so callers can reason about when a re-assign is due.\n const remainingTtlMs =\n cached.ttlMs && cached.ttlMs > 0\n ? Math.max(0, cached.assignedAt + cached.ttlMs - Date.now())\n : 0;\n return { variantId: cached.variantId, assignmentTtlMs: remainingTtlMs, content: cached.content };\n }\n\n // Coalesce concurrent assigns for the same component (e.g. several\n // mounted slots sharing one id) into a single network request.\n const inflight = inflightAssigns.get(componentId);\n if (inflight) return inflight;\n\n const request = (async (): Promise<AssignResult | null> => {\n await sessionReady;\n try {\n const body: Record<string, unknown> = { sessionId: sid, componentId, variantIds };\n if (agentDataByVariant !== undefined) body.agentDataByVariant = agentDataByVariant;\n else if (agentData !== undefined) body.agentData = agentData;\n const res = await fetch(`${baseUrl}/assign`, {\n method: 'POST',\n body: JSON.stringify(body),\n headers: authHeaders,\n });\n if (!res.ok) return null;\n const result = (await res.json()) as AssignResult;\n assignmentCache.set(componentId, sessionSegment, {\n variantId: result.variantId,\n assignedAt: Date.now(),\n segment: sessionSegment,\n confidence: 1,\n content: result.content,\n // Honor the server's TTL as this entry's expiry; omit when absent/0\n // so the cache falls back to its default (DEFAULT_TTL_MS).\n ...(result.assignmentTtlMs && result.assignmentTtlMs > 0\n ? { ttlMs: result.assignmentTtlMs }\n : {}),\n });\n return result;\n } catch {\n return null;\n } finally {\n inflightAssigns.delete(componentId);\n }\n })();\n inflightAssigns.set(componentId, request);\n return request;\n },\n\n async decide(input) {\n const sid = session.getSessionId();\n if (!sid) return null;\n const declared = input.slots ?? [];\n await sessionReady;\n try {\n const body: Record<string, unknown> = { sessionId: sid };\n if (input.sections && input.sections.length > 0) {\n body.sections = input.sections.map((id) => ({ id }));\n }\n body.components = input.components ?? [];\n if (declared.length > 0) body.slots = declared.map(toWireSlot);\n if (input.slotsFrom === 'registry') body.slotsFrom = 'registry';\n if (input.v) body.v = input.v;\n\n const res = await fetch(`${baseUrl}/decide`, {\n method: 'POST',\n body: JSON.stringify(body),\n headers: authHeaders,\n });\n if (!res.ok) {\n seedSlotBaselines(declared);\n return null;\n }\n const data = (await res.json()) as {\n layoutOrder?: string[] | null;\n assignments?: Record<string, string>;\n slots?: Record<string, SlotResult>;\n slotConfig?: Record<string, SlotConfigEntry>;\n goals?: GoalDefinition[];\n sectionMap?: SectionMapEntry[];\n persona?: string;\n confidence?: number;\n };\n\n const slots: Record<string, SlotResult> = {};\n for (const d of declared) {\n // `data.slots === undefined` means the server predates the slots\n // contract: serve the declared baseline everywhere, do NOT retry.\n // (Distinct from `slots: {}`, which also falls back per-slot.)\n slots[d.id] = data.slots?.[d.id] ?? baselineResultFor(d);\n }\n // Registry mode: the server returns slots the request never declared.\n // Take them verbatim (classic mode returns only declared slots, so this\n // union is a no-op there — back-compatible).\n if (data.slots) {\n for (const [slotId, result] of Object.entries(data.slots)) {\n if (!(slotId in slots)) slots[slotId] = result;\n }\n }\n for (const [slotId, result] of Object.entries(slots)) slotStore.set(slotId, result);\n // Only overwrite persona when the response actually carries one, and\n // never downgrade a known persona to 'unknown' — a decide that omits\n // persona (or returns 'unknown') must not clobber a good SSR/snapshot/\n // initialPersona value, and must not persist that regression below.\n const known = personaState != null && personaState.persona !== 'unknown';\n if (data.persona && !(data.persona === 'unknown' && known)) {\n personaState = { persona: data.persona, confidence: data.confidence ?? 0 };\n } else if (!personaState) {\n personaState = { persona: 'unknown', confidence: 0 };\n }\n\n // Seed component assignments so <Adaptive>/assign() agree with this\n // decide (same shape as the initialAssignments seed above).\n for (const [componentId, variantId] of Object.entries(data.assignments ?? {})) {\n assignmentCache.set(componentId, sessionSegment, {\n variantId,\n assignedAt: Date.now(),\n segment: sessionSegment,\n confidence: 1,\n });\n }\n\n // Persist for the next visit's pre-paint (SPA cache-first pattern).\n writeSnapshot(config.apiKey, {\n v: 1,\n persona: personaState.persona,\n band: confidenceBand(personaState.confidence),\n slots: Object.fromEntries(slotStore),\n layoutOrder: data.layoutOrder ?? null,\n savedAt: Date.now(),\n ...(data.slotConfig ? { slotConfig: data.slotConfig } : {}),\n });\n\n return {\n layoutOrder: data.layoutOrder ?? null,\n assignments: data.assignments ?? {},\n slots,\n persona: personaState.persona,\n confidence: personaState.confidence,\n ...(data.slotConfig ? { slotConfig: data.slotConfig } : {}),\n ...(data.goals ? { goals: data.goals } : {}),\n ...(data.sectionMap ? { sectionMap: data.sectionMap } : {}),\n };\n } catch {\n seedSlotBaselines(declared);\n return null;\n }\n },\n\n getSlotResult(slotId) {\n return slotStore.get(slotId) ?? null;\n },\n\n getPersona() {\n if (!personaState) return null;\n return {\n persona: personaState.persona,\n confidence: personaState.confidence,\n band: confidenceBand(personaState.confidence),\n };\n },\n\n async fetchWeights() {\n try {\n const res = await fetch(`${baseUrl}/weights`, { headers: authHeaders });\n if (!res.ok) return [];\n const data = (await res.json()) as { components: ComponentWeightEntry[] };\n return data.components ?? [];\n } catch {\n return [];\n }\n },\n\n getGraph() {\n return { pageNodes: [], capturedAt: 0 };\n },\n\n dispose() {\n // Stops the flush timer and unload listeners (with a final flush) but\n // leaves identity, snapshot, and retry bucket for the next client.\n eventQueue.destroy();\n // Drop the registry entry so a later re-init doesn't try to dispose an\n // already-torn-down client (and so the map doesn't pin this closure).\n if (_clients.get(config.apiKey)?.dispose === client.dispose) {\n _clients.delete(config.apiKey);\n }\n if (config.debug) {\n console.log('[sentient] disposed');\n }\n },\n\n destroy() {\n eventQueue.destroy();\n session.destroy();\n if (_clients.get(config.apiKey)?.dispose === client.dispose) {\n _clients.delete(config.apiKey);\n }\n // Forget-me must be total: a surviving decision snapshot would\n // re-personalize the next visit via the pre-paint script, and a\n // persisted retry bucket would re-send events for the deleted identity.\n try {\n localStorage.removeItem(SNAPSHOT_STORAGE_KEY_PREFIX + config.apiKey);\n localStorage.removeItem(retryStorageKey(config.apiKey));\n } catch {\n /* storage unavailable — nothing persisted to remove */\n }\n if (config.debug) {\n console.log('[sentient] destroyed');\n }\n },\n };\n\n _clients.set(config.apiKey, { config, upgrade: null, dispose: client.dispose });\n\n if (config.debug) {\n const win = window as unknown as { __sentient?: { client: SentientClient } };\n if (win.__sentient) {\n win.__sentient.client = client;\n }\n }\n\n return client;\n}\n","/**\n * Keyless local mode — client factory.\n *\n * This module ships in the MAIN core bundle and contains no engine code: the\n * engine arrives through a dynamic import of the bare specifier\n * `@sentientui/core/local`, which the consumer's bundler resolves through the\n * development/production export conditions. In production builds that\n * specifier is the stub, and this client degrades to a no-op plus one\n * console.error per page load.\n */\nimport { initSession } from './session.js';\nimport { writeSnapshot } from './snapshot.js';\nimport { confidenceBand } from '@sentientui/policy';\nimport type {\n DecideOutcome,\n SentientClient,\n SentientConfig,\n SlotDeclInput,\n} from './index.js';\n\ntype LocalEngineModule = {\n LOCAL_ENGINE_AVAILABLE: boolean;\n createLocalEngine(opts: { sessionId: string; forcedPersona?: string }): {\n decide(input: {\n sections?: string[];\n components?: Array<{ id: string; variantIds?: string[] }>;\n slots?: SlotDeclInput[];\n }): DecideOutcome;\n };\n};\n\nexport const PROD_KEYLESS_ERROR =\n '[sentient] No API key configured — nothing is being learned. Set NEXT_PUBLIC_SENTIENT_API_KEY or pass localMode: true for local development.';\n\nexport const LOCAL_MODE_BANNER =\n '[sentient] Local mode — decisions are simulated on-device and nothing is sent over the network. Add an API key to learn from real traffic.';\n\n// Once-per-page-load guards (module state resets on reload).\nlet bannerShown = false;\nlet prodErrorShown = false;\n\n/** @internal test hook */\nexport function __resetLocalModeLogGuards(): void {\n bannerShown = false;\n prodErrorShown = false;\n}\n\n/** Mirrors the ?sentient_variant= override pattern; validated by the engine. */\nfunction readPersonaOverrideFromUrl(): string | undefined {\n try {\n return new URLSearchParams(window.location.search).get('sentient_persona') ?? undefined;\n } catch {\n return undefined;\n }\n}\n\nexport function createLocalModeClient(config: SentientConfig): SentientClient {\n const session = initSession({ ssrSessionId: config.ssrSessionId });\n const sessionId = session.getSessionId() ?? 'local';\n const forcedPersona = readPersonaOverrideFromUrl();\n\n // The specifier MUST stay a bare package subpath (never a relative path) so\n // the consumer's bundler applies the development/production export\n // conditions. tsup keeps it external (see tsup.config.ts).\n const modPromise: Promise<LocalEngineModule | null> = import('@sentientui/core/local')\n .then((mod) => {\n const m = mod as unknown as LocalEngineModule;\n if (!m.LOCAL_ENGINE_AVAILABLE) {\n if (!prodErrorShown) {\n prodErrorShown = true;\n console.error(PROD_KEYLESS_ERROR);\n }\n return null;\n }\n if (!bannerShown) {\n bannerShown = true;\n console.info(LOCAL_MODE_BANNER);\n }\n return m;\n })\n .catch(() => {\n if (!prodErrorShown) {\n prodErrorShown = true;\n console.error(PROD_KEYLESS_ERROR);\n }\n return null;\n });\n\n let lastOutcome: DecideOutcome | null = null;\n\n function applyPersonaAttributes(outcome: DecideOutcome): void {\n // Single-writer rule: adopt attributes already written (e.g. by the\n // AdaptiveRoot inline script); only write when nothing has yet.\n const el = document.documentElement;\n if (el.dataset.sentientPersona === undefined) {\n el.dataset.sentientPersona = outcome.persona;\n el.dataset.sentientConfidence = confidenceBand(outcome.confidence);\n }\n }\n\n return {\n isLocal: true,\n\n async decide(input) {\n const mod = await modPromise;\n if (!mod) return null;\n const outcome = mod.createLocalEngine({ sessionId, forcedPersona }).decide(input);\n // Accumulate across calls: slots decide lazily one at a time (per-slot\n // decide from useSlotResult), so a later decide must not evict results\n // an earlier one served. Same (sessionId, persona) → merging is safe.\n lastOutcome = {\n ...outcome,\n layoutOrder: outcome.layoutOrder ?? lastOutcome?.layoutOrder ?? null,\n slots: { ...(lastOutcome?.slots ?? {}), ...outcome.slots },\n };\n writeSnapshot(config.apiKey || 'local', {\n v: 1,\n persona: lastOutcome.persona,\n band: confidenceBand(lastOutcome.confidence),\n slots: lastOutcome.slots,\n layoutOrder: lastOutcome.layoutOrder,\n savedAt: Date.now(),\n });\n applyPersonaAttributes(outcome);\n return outcome;\n },\n\n getSlotResult(slotId) {\n return lastOutcome?.slots[slotId] ?? config.initialSlots?.[slotId] ?? null;\n },\n\n getPersona() {\n if (!lastOutcome) return null;\n return {\n persona: lastOutcome.persona,\n confidence: lastOutcome.confidence,\n band: confidenceBand(lastOutcome.confidence),\n };\n },\n\n async assign(componentId, variantIds) {\n const mod = await modPromise;\n if (!mod || !variantIds || variantIds.length === 0) {\n return variantIds?.[0] ? { variantId: variantIds[0], assignmentTtlMs: 0 } : null;\n }\n const outcome = mod\n .createLocalEngine({ sessionId, forcedPersona })\n .decide({ components: [{ id: componentId, variantIds }] });\n return { variantId: outcome.assignments[componentId] ?? variantIds[0], assignmentTtlMs: 0 };\n },\n\n // Local mode never talks to the network: the tracking surface no-ops.\n track: () => undefined,\n goal: () => undefined,\n componentGoal: () => undefined,\n identify: () => undefined,\n getAssignment: () => null,\n fetchWeights: () => Promise.resolve([]),\n getGraph: () => ({ pageNodes: [], capturedAt: 0 }),\n dispose: () => undefined, // no timers/network in local mode; keep the session\n destroy: () => session.destroy(),\n };\n}\n","// Semantic section classification for no-code section capture (Phase 3 §2.4).\n// Pure heuristic: element → one of the graph_nodes semantic_type enum. Mirrors\n// the SDK graph scanner's vocabulary so the persona × section matrix consumes\n// snippet-captured sections unchanged.\n//\n// The classifier is split into a pure feature-based core (classifyFeatures —\n// also used server-side on crawled HTML by the site-audit classification job)\n// and a DOM wrapper (classifySection). Content-based patterns detect\n// pricing/social-proof/trust/comparison from BODY TEXT, so div-soup pages with\n// uninformative ids/classes still classify (persona-coverage spec 2026-07-23).\n\nexport type SemanticType =\n | 'pricing' | 'hero' | 'social_proof' | 'cta' | 'features'\n | 'faq' | 'comparison' | 'trust' | 'navigation' | 'generic';\n\n/** Canonical vocabulary — mirrors the graph_nodes semantic_type CHECK enum. */\nexport const SEMANTIC_TYPES: readonly SemanticType[] = [\n 'pricing', 'hero', 'social_proof', 'cta', 'features',\n 'faq', 'comparison', 'trust', 'navigation', 'generic',\n] as const;\n\n/** Environment-agnostic section features — buildable from a browser Element or\n * a server-parsed node (node-html-parser). */\nexport type SectionFeatures = {\n tag: string; // lowercase tag name\n idClass: string; // `${id} ${className}`\n headingText: string;\n bodyText: string; // normalized text content, first 2000 chars\n actionCount: number;\n textLength: number;\n};\n\n// Ordered most-specific first — the first keyword hit wins.\nconst KEYWORDS: Array<[SemanticType, RegExp]> = [\n ['pricing', /\\b(pricing|price|plans?|subscriptions?|per month|\\/mo|tier)\\b/i],\n ['faq', /\\b(faq|frequently asked|common questions?)\\b/i],\n ['comparison', /\\b(compare|comparison|versus|vs\\.)\\b/i],\n ['social_proof', /\\b(testimonial|reviews?|trusted by|loved by|customers?|logos|rated|case stud)\\b/i],\n ['trust', /\\b(security|guarantee|privacy|compliance|secure|gdpr|soc ?2|encrypt)\\b/i],\n ['features', /\\b(features?|how it works|benefits?|capabilit|what you get)\\b/i],\n];\n\n// Content-evidence patterns — run against bodyText. Ordered most-specific first.\n// Deliberately conservative: pricing needs per-period/plan context next to money\n// so an article mentioning \"$5 million\" stays generic.\nconst CONTENT_PATTERNS: Array<[SemanticType, RegExp]> = [\n ['pricing', /(?:[$€£]\\s?\\d[\\d,.]*\\s*(?:\\/|per\\s)\\s*(?:mo|month|yr|year|seat|user))|(?:\\b(?:starter|basic|pro|growth|premium|enterprise)\\b[^.]{0,60}[$€£]\\s?\\d)/i],\n ['social_proof', /(?:★{2,})|(?:\\b\\d(?:\\.\\d)?\\s*(?:out of|\\/)\\s*5\\b)|(?:\\brated\\b)|(?:[\"“][^\"”]{20,160}[\"”]\\s*[—–-]\\s*[A-Z][a-z]+)/],\n ['trust', /\\b(?:money[- ]back guarantee|refund(?:s)? within|encrypted|soc ?2|iso ?27001|gdpr[- ]compliant|cancel anytime)\\b/i],\n ['comparison', /\\b(?:vs|versus)\\b\\.?[^.!?]{0,80}\\b(?:compare|comparison|plans?|features?|alternative)\\b|\\bhow (?:we|it) compares?\\b/i],\n];\n\nfunction headingText(el: Element): string {\n const h = el.querySelector('h1, h2, h3');\n return (h?.textContent ?? '').slice(0, 160);\n}\n\n/**\n * Pure classification over extracted features. `strong` = keyword or content\n * evidence (trustable enough to auto-apply); `weak` = structural fallback\n * (cta/hero/navigation/generic — capture-worthy but not persona evidence).\n */\nexport function classifyFeatures(f: SectionFeatures): { type: SemanticType; strength: 'strong' | 'weak' } {\n if (f.tag === 'nav' || f.tag === 'footer') return { type: 'navigation', strength: 'weak' };\n const hay = `${f.idClass} ${f.headingText}`.toLowerCase();\n for (const [type, re] of KEYWORDS) {\n if (re.test(hay)) return { type, strength: 'strong' };\n }\n for (const [type, re] of CONTENT_PATTERNS) {\n if (re.test(f.bodyText)) return { type, strength: 'strong' };\n }\n if (f.actionCount >= 1 && f.textLength > 0 && f.textLength < 200) return { type: 'cta', strength: 'weak' };\n if (f.tag === 'header') return { type: 'hero', strength: 'weak' };\n if (/\\b(hero|headline|banner)\\b/i.test(hay)) return { type: 'hero', strength: 'weak' };\n return { type: 'generic', strength: 'weak' };\n}\n\n/** Feature extraction from a live DOM element (browser paths). */\nexport function featuresFromElement(el: Element): SectionFeatures {\n const text = (el.textContent ?? '').replace(/\\s+/g, ' ').trim();\n return {\n tag: el.tagName.toLowerCase(),\n idClass: `${el.id} ${String(el.className ?? '')}`,\n headingText: headingText(el),\n bodyText: text.slice(0, 2000),\n actionCount: el.querySelectorAll('a, button, [role=\"button\"]').length,\n textLength: text.length,\n };\n}\n\n/** Classify a page section into a semantic type (never null — falls back to\n * 'generic' so the caller can still capture attention on it). */\nexport function classifySection(el: Element): SemanticType {\n return classifyFeatures(featuresFromElement(el)).type;\n}\n","/** Reads the rendered DOM to build the page-side context graph. */\n\nimport { classifySection, SEMANTIC_TYPES, type SemanticType } from './engagement/classify';\n\nexport type ScannedNode = {\n componentId: string;\n semanticType: string;\n ariaLabel?: string;\n headingText?: string;\n isAboveFold: boolean;\n prominenceScore: number;\n depth: number;\n reactComponentName?: string;\n dataAttributes: Record<string, string>;\n};\n\nexport type StructuralEdge = {\n fromComponentId: string;\n toComponentId: string;\n /** 0.6 for direct parent → child, 0.3 for sibling (both directions emitted). */\n weight: number;\n};\n\nexport type ScanResult = {\n nodes: ScannedNode[];\n edges: StructuralEdge[];\n scannedAt: number;\n};\n\nexport type ContentAddedEvent = {\n nodes: ScannedNode[];\n edges: StructuralEdge[];\n addedAt: number;\n};\n\nexport type DOMScanner = {\n scan(): Promise<ScanResult>;\n observe(onContentAdded: (event: ContentAddedEvent) => void): void;\n getProminenceScore(element: Element): number;\n destroy(): void;\n};\n\nconst OBSERVE_TAGS = new Set(['SECTION', 'ARTICLE', 'MAIN', 'DIV']);\nconst HEADING_SELECTOR = 'h1, h2, h3';\n\n// Sibling detection is O(n²) per group and emits two edges per pair, so a page\n// with hundreds of co-located components (e.g. a 200-cell product grid) would\n// otherwise emit tens of thousands of low-signal sibling edges. We cap the\n// sibling fan-out per group and the total structural edges per detection pass.\n// The total stays well under the server's 2000-edge-per-sync limit, leaving room\n// for semantic edges. Parent→child edges (higher signal) are emitted first, so\n// the cap sheds sibling edges before it ever touches them.\nconst MAX_SIBLINGS_PER_GROUP = 30;\nconst MAX_STRUCTURAL_EDGES = 1500;\n\nconst SSR_SCANNER: DOMScanner = {\n scan: async () => ({ nodes: [], edges: [], scannedAt: 0 }),\n observe: () => undefined,\n getProminenceScore: () => 0,\n destroy: () => undefined,\n};\n\nfunction normalize(value: number, min: number, max: number): number {\n if (max <= min) return 0;\n return Math.max(0, Math.min(1, (value - min) / (max - min)));\n}\n\nfunction readReactComponentName(element: Element): string | undefined {\n try {\n const record = element as unknown as Record<string, unknown>;\n for (const key of Object.keys(record)) {\n if (!key.startsWith('__reactFiber') && !key.startsWith('__reactInternalInstance')) {\n continue;\n }\n const fiber = record[key] as { type?: { name?: string; displayName?: string } };\n const name = fiber?.type?.displayName ?? fiber?.type?.name;\n if (name && name.length > 1) {\n return name;\n }\n }\n } catch {\n /* ignore */\n }\n return undefined;\n}\n\nfunction extractDataAttributes(element: Element): Record<string, string> {\n const attrs: Record<string, string> = {};\n for (const attr of Array.from(element.attributes)) {\n if (attr.name.startsWith('data-')) {\n attrs[attr.name] = attr.value;\n }\n }\n return attrs;\n}\n\nconst SEMANTIC_SET: ReadonlySet<string> = new Set(SEMANTIC_TYPES);\n\n// Explicit tag wins → role → heuristic → generic. Every output is normalized to\n// the graph_nodes enum: an out-of-vocabulary data-sentient-type/role falls\n// through to the heuristic instead of being emitted raw (raw values violated\n// the graph_nodes CHECK constraint on insert).\nfunction inferSemanticType(element: Element): SemanticType {\n const explicit = element.getAttribute('data-sentient-type');\n if (explicit && SEMANTIC_SET.has(explicit)) return explicit as SemanticType;\n const role = element.getAttribute('role');\n if (role && SEMANTIC_SET.has(role)) return role as SemanticType;\n return classifySection(element);\n}\n\n// djb2 over a string → 8-hex-char digest. Stable and collision-resistant enough\n// to disambiguate co-located components that declare no id of their own.\nfunction shortHash(input: string): string {\n let h = 5381;\n for (let i = 0; i < input.length; i++) {\n h = ((h << 5) + h + input.charCodeAt(i)) & 0xffffffff;\n }\n return (h >>> 0).toString(16).padStart(8, '0');\n}\n\n// A structural path (tag + sibling index at each level up to the root) uniquely\n// and stably identifies an element's DOM position, so two id-less sections don't\n// hash to the same value the way a bare tagName would.\nfunction domPathSignature(element: Element): string {\n const parts: string[] = [];\n let current: Element | null = element;\n while (current) {\n const parent: Element | null = current.parentElement;\n if (!parent) {\n parts.push(current.tagName.toLowerCase());\n break;\n }\n const index = Array.prototype.indexOf.call(parent.children, current);\n parts.push(`${current.tagName.toLowerCase()}[${index}]`);\n current = parent;\n }\n return parts.reverse().join('/');\n}\n\n// Prefer an author-declared id. When none exists, synthesize a stable, unique id\n// from the element's DOM path — the old `tagName.toLowerCase()` fallback gave\n// every id-less <section> the same \"section\" id, so a second such node silently\n// overwrote the first in the componentId-keyed graph Map (node loss).\nfunction componentIdFor(element: Element): string {\n const declared =\n element.getAttribute('data-sentient-id') ?? element.getAttribute('id');\n if (declared) return declared;\n return `${element.tagName.toLowerCase()}-${shortHash(domPathSignature(element))}`;\n}\n\nfunction depthOf(element: Element): number {\n let depth = 0;\n let current: Element | null = element.parentElement;\n while (current) {\n depth++;\n current = current.parentElement;\n }\n return depth;\n}\n\nfunction scanElement(\n element: Element,\n getProminenceScore: (el: Element) => number,\n): ScannedNode {\n const heading = element.querySelector(HEADING_SELECTOR);\n return {\n componentId: componentIdFor(element),\n semanticType: inferSemanticType(element),\n ariaLabel: element.getAttribute('aria-label') ?? undefined,\n headingText: heading?.textContent?.trim() ?? undefined,\n isAboveFold: element.getBoundingClientRect().top < window.innerHeight,\n prominenceScore: getProminenceScore(element),\n depth: depthOf(element),\n reactComponentName: readReactComponentName(element),\n dataAttributes: extractDataAttributes(element),\n };\n}\n\n/**\n * Detects structural relationships between scanned component elements:\n * - parent → child (direct ancestor link, weight 0.6)\n * - sibling ↔ sibling (share nearest component ancestor, weight 0.3, bidirectional)\n * A pair appears at most once per direction.\n */\nfunction detectStructuralEdges(elementToId: Map<Element, string>): StructuralEdge[] {\n const edges: StructuralEdge[] = [];\n const seen = new Set<string>();\n const ROOT = '__root__';\n\n // Parent → child: nearest component ancestor only.\n const groups = new Map<string, Element[]>();\n for (const [el, _id] of elementToId) {\n let ancestor: Element | null = el.parentElement;\n let groupKey = ROOT;\n while (ancestor) {\n if (elementToId.has(ancestor)) {\n groupKey = elementToId.get(ancestor)!;\n const childId = elementToId.get(el)!;\n const key = `${groupKey}->${childId}`;\n if (!seen.has(key) && groupKey !== childId) {\n seen.add(key);\n edges.push({ fromComponentId: groupKey, toComponentId: childId, weight: 0.6 });\n }\n break;\n }\n ancestor = ancestor.parentElement;\n }\n const siblings = groups.get(groupKey) ?? [];\n siblings.push(el);\n groups.set(groupKey, siblings);\n }\n\n // Sibling ↔ sibling: same ancestor group.\n for (const sibs of groups.values()) {\n if (sibs.length < 2) continue;\n // Cap the fan-out of any single group before the O(n²) pairing.\n const capped = sibs.length > MAX_SIBLINGS_PER_GROUP ? sibs.slice(0, MAX_SIBLINGS_PER_GROUP) : sibs;\n for (let i = 0; i < capped.length; i++) {\n for (let j = i + 1; j < capped.length; j++) {\n if (edges.length >= MAX_STRUCTURAL_EDGES) return edges;\n const aId = elementToId.get(capped[i]!)!;\n const bId = elementToId.get(capped[j]!)!;\n if (aId === bId) continue;\n const fwd = `${aId}->${bId}::sib`;\n const rev = `${bId}->${aId}::sib`;\n if (!seen.has(fwd)) {\n seen.add(fwd);\n edges.push({ fromComponentId: aId, toComponentId: bId, weight: 0.3 });\n }\n if (!seen.has(rev)) {\n seen.add(rev);\n edges.push({ fromComponentId: bId, toComponentId: aId, weight: 0.3 });\n }\n }\n }\n }\n\n return edges;\n}\n\nfunction collectNodesAndEdges(\n getProminenceScore: (el: Element) => number,\n): { nodes: ScannedNode[]; edges: StructuralEdge[]; elementToId: Map<Element, string> } {\n const nodes: ScannedNode[] = [];\n const seen = new Set<Element>();\n const elementToId = new Map<Element, string>();\n\n const registered = document.querySelectorAll('[data-sentient-id]');\n registered.forEach((el) => {\n if (el instanceof Element && !seen.has(el)) {\n seen.add(el);\n const node = scanElement(el, getProminenceScore);\n nodes.push(node);\n elementToId.set(el, node.componentId);\n }\n });\n\n const structural = document.querySelectorAll('section, article, main, aside');\n structural.forEach((el) => {\n if (!(el instanceof Element) || seen.has(el)) return;\n const hasAria = el.hasAttribute('aria-label');\n const hasSentientId = el.hasAttribute('data-sentient-id');\n if (!hasAria && !hasSentientId) return;\n seen.add(el);\n const node = scanElement(el, getProminenceScore);\n nodes.push(node);\n elementToId.set(el, node.componentId);\n });\n\n return { nodes, edges: detectStructuralEdges(elementToId), elementToId };\n}\n\n/**\n * Creates a DOM scanner that uses idle callbacks and mutation observation.\n */\nexport function createDOMScanner(): DOMScanner {\n if (typeof window === 'undefined') {\n return SSR_SCANNER;\n }\n\n let observer: MutationObserver | null = null;\n let idleCallbackId = 0;\n let contentCallback: ((event: ContentAddedEvent) => void) | null = null;\n // Element→componentId for every node registered so far (initial scan + prior\n // mutations). The MutationObserver detects edges against this whole set — not\n // just the nodes in the current mutation — so a child inserted under an\n // already-scanned parent still gets its parent→child edge.\n const knownElementToId = new Map<Element, string>();\n\n const getProminenceScore = (element: Element): number => {\n try {\n const styles = window.getComputedStyle(element);\n const fontSize = parseFloat(styles.fontSize) || 12;\n const zIndex = parseFloat(styles.zIndex) || 0;\n const rect = element.getBoundingClientRect();\n const distanceFromTop = Math.max(rect.top, 0);\n const viewportHeight = window.innerHeight || 1;\n const inverseDistance = 1 / (distanceFromTop / viewportHeight + 1);\n\n // inverseDistance is already in (0, 1] by construction, so it needs no\n // further normalization — only fontSize and zIndex are rescaled.\n const score =\n normalize(fontSize, 12, 48) * 0.4 +\n inverseDistance * 0.4 +\n normalize(zIndex, 0, 100) * 0.2;\n\n return Math.max(0, Math.min(1, score));\n } catch {\n return 0.5;\n }\n };\n\n const scan = (): Promise<ScanResult> =>\n new Promise((resolve) => {\n const run = (): void => {\n const { nodes, edges, elementToId } = collectNodesAndEdges(getProminenceScore);\n // Seed the known-element registry so the observer can resolve parents that\n // were registered in this scan when later children are inserted.\n knownElementToId.clear();\n for (const [el, id] of elementToId) knownElementToId.set(el, id);\n resolve({ nodes, edges, scannedAt: Date.now() });\n };\n\n try {\n if (typeof requestIdleCallback === 'function') {\n idleCallbackId = requestIdleCallback(run, { timeout: 100 });\n } else {\n run();\n }\n } catch {\n run();\n }\n });\n\n const observe = (onContentAdded: (event: ContentAddedEvent) => void): void => {\n contentCallback = onContentAdded;\n try {\n observer = new MutationObserver((mutations) => {\n const added: ScannedNode[] = [];\n const addedIds = new Set<string>();\n for (const mutation of mutations) {\n if (mutation.type !== 'childList') continue;\n mutation.addedNodes.forEach((node) => {\n if (!(node instanceof Element)) return;\n if (!OBSERVE_TAGS.has(node.tagName)) return;\n const hasId = node.hasAttribute('data-sentient-id');\n const hasAria = node.hasAttribute('aria-label');\n if (!hasId && !hasAria) return;\n const scanned = scanElement(node, getProminenceScore);\n added.push(scanned);\n addedIds.add(scanned.componentId);\n knownElementToId.set(node, scanned.componentId);\n });\n }\n if (added.length === 0 || !contentCallback) return;\n // Drop elements no longer in the document so removed nodes don't dangle\n // and the map stays bounded.\n for (const el of [...knownElementToId.keys()]) {\n if (!el.isConnected) knownElementToId.delete(el);\n }\n // Detect over the FULL known set so a child inserted under an already-\n // scanned parent still gets its parent→child edge, then surface only the\n // edges that touch a newly-added node (existing edges were already emitted).\n const edges = detectStructuralEdges(knownElementToId).filter(\n (e) => addedIds.has(e.fromComponentId) || addedIds.has(e.toComponentId),\n );\n contentCallback({ nodes: added, edges, addedAt: Date.now() });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n } catch {\n /* ignore */\n }\n };\n\n const destroy = (): void => {\n if (observer) {\n observer.disconnect();\n observer = null;\n }\n if (idleCallbackId && typeof cancelIdleCallback === 'function') {\n try {\n cancelIdleCallback(idleCallbackId);\n } catch {\n /* ignore */\n }\n }\n idleCallbackId = 0;\n contentCallback = null;\n knownElementToId.clear();\n };\n\n return {\n scan,\n observe,\n getProminenceScore,\n destroy,\n };\n}\n","/** In-memory context graph with persistence and backend sync. */\n\nexport type PageNode = {\n id: string;\n componentId: string;\n semanticType: string;\n answers: string[];\n prominenceScore: number;\n depth: number;\n};\n\nexport type GraphSnapshot = {\n pageNodes: PageNode[];\n capturedAt: number;\n};\n\nexport type GraphConfig = {\n syncUrl?: string;\n apiKey?: string;\n projectId?: string;\n sessionId?: string;\n};\n\nexport type StructuralEdge = {\n fromComponentId: string;\n toComponentId: string;\n weight: number;\n};\n\nexport type GraphClient = {\n addPageNode(node: PageNode): void;\n /** Record a DOM-derived parent/child or sibling relationship between two components. */\n addStructuralEdge(edge: StructuralEdge): void;\n /** One-shot batch sync of all current page nodes to the backend. */\n syncOnce(): void;\n snapshot(): GraphSnapshot;\n serialize(): string;\n restore(data: string): void;\n destroy(): void;\n};\n\nconst NODES_KEY = '_snt_graph_nodes';\n// _snt_graph_edges was written by earlier builds but never synced to the backend.\n// We still read and clear any leftover key on init/destroy so old clients don't\n// accumulate stale data, but we no longer write it.\nconst STALE_EDGES_KEY = '_snt_graph_edges';\n\nconst SEMANTIC_NEIGHBOURS: Record<string, string[]> = {\n pricing: ['features', 'faq'],\n features: ['pricing'],\n faq: ['pricing'],\n social_proof: ['cta'],\n cta: ['social_proof', 'hero', 'trust'],\n hero: ['cta'],\n comparison: ['pricing'],\n trust: ['cta'],\n};\n\nfunction neighboursFor(semanticType: string): string[] {\n return SEMANTIC_NEIGHBOURS[semanticType] ?? [];\n}\n\nfunction readStorage<T>(key: string, fallback: T): T {\n try {\n const raw = localStorage.getItem(key);\n if (!raw) return fallback;\n return JSON.parse(raw) as T;\n } catch {\n return fallback;\n }\n}\n\nfunction writeStorage(key: string, value: unknown): void {\n try {\n localStorage.setItem(key, JSON.stringify(value));\n } catch {\n /* ignore */\n }\n}\n\nconst VALID_SEMANTIC_TYPES = new Set([\n 'pricing', 'hero', 'social_proof', 'cta', 'features',\n 'faq', 'comparison', 'trust', 'navigation', 'generic',\n]);\n\nfunction toValidSemanticType(type: string): string {\n return VALID_SEMANTIC_TYPES.has(type) ? type : 'generic';\n}\n\nfunction contentHashOf(componentId: string, semanticType: string, answers: string[]): string {\n const input = `${componentId}:${semanticType}:${answers.join(',')}`;\n let h = 5381;\n for (let i = 0; i < input.length; i++) {\n h = ((h << 5) + h + input.charCodeAt(i)) & 0xffffffff;\n }\n return (h >>> 0).toString(16).padStart(8, '0');\n}\n\n/**\n * Creates an in-memory graph client with optional persistence and sync.\n */\nexport function createGraphClient(config?: GraphConfig): GraphClient {\n const pageNodes = new Map<string, PageNode>();\n const structuralEdges = new Map<string, StructuralEdge>();\n\n const persist = (): void => {\n if (typeof window === 'undefined') return;\n writeStorage(NODES_KEY, [...pageNodes.values()]);\n };\n\n const restore = (data: string): void => {\n try {\n const parsed = JSON.parse(data) as { pageNodes?: PageNode[] };\n pageNodes.clear();\n for (const node of parsed.pageNodes ?? []) {\n pageNodes.set(node.componentId, node);\n }\n } catch {\n /* ignore corrupt state */\n }\n };\n\n if (typeof window !== 'undefined') {\n const storedNodes = readStorage<PageNode[]>(NODES_KEY, []);\n for (const node of storedNodes) {\n pageNodes.set(node.componentId, node);\n }\n // Clear any stale edge data written by older SDK versions.\n try { localStorage.removeItem(STALE_EDGES_KEY); } catch { /* ignore */ }\n }\n\n return {\n addPageNode(node: PageNode): void {\n pageNodes.set(node.componentId, node);\n persist();\n },\n\n addStructuralEdge(edge: StructuralEdge): void {\n const key = `${edge.fromComponentId}->${edge.toComponentId}`;\n structuralEdges.set(key, edge);\n },\n\n syncOnce(): void {\n if (!config?.syncUrl || typeof window === 'undefined') return;\n const nodes = [...pageNodes.values()];\n if (nodes.length === 0) return;\n try {\n // Build semantic edges from the SEMANTIC_NEIGHBOURS map. Each node emits\n // an edge to every present sibling that matches one of its neighbour types.\n // Weight matches propagate()'s 0.4 attention factor; confidence is high\n // because the mapping is curated, not inferred.\n const nodesByType = new Map<string, PageNode[]>();\n for (const n of nodes) {\n const list = nodesByType.get(n.semanticType) ?? [];\n list.push(n);\n nodesByType.set(n.semanticType, list);\n }\n const edges: Array<{\n fromComponentId: string;\n toComponentId: string;\n type: 'semantic' | 'structural';\n weight: number;\n confidence: number;\n }> = [];\n const seen = new Set<string>();\n for (const source of nodes) {\n for (const neighbourType of neighboursFor(source.semanticType)) {\n const targets = nodesByType.get(neighbourType) ?? [];\n for (const target of targets) {\n if (target.componentId === source.componentId) continue;\n const key = `semantic:${source.componentId}->${target.componentId}`;\n if (seen.has(key)) continue;\n seen.add(key);\n edges.push({\n fromComponentId: source.componentId,\n toComponentId: target.componentId,\n type: 'semantic',\n weight: 0.4,\n confidence: 0.9,\n });\n }\n }\n }\n\n // Structural edges from DOM relationships, recorded during scan.\n // Only emit when both endpoints are present in this page's node set —\n // a structural edge to/from a node that no longer exists would dangle.\n const componentIds = new Set(nodes.map((n) => n.componentId));\n for (const edge of structuralEdges.values()) {\n if (!componentIds.has(edge.fromComponentId) || !componentIds.has(edge.toComponentId)) continue;\n const key = `structural:${edge.fromComponentId}->${edge.toComponentId}`;\n if (seen.has(key)) continue;\n seen.add(key);\n edges.push({\n fromComponentId: edge.fromComponentId,\n toComponentId: edge.toComponentId,\n type: 'structural',\n weight: edge.weight,\n confidence: 1.0,\n });\n }\n\n const payload = {\n pageUrl: window.location.href,\n // Visitor/project attribution. The /graph/sync handler ignores unknown\n // top-level fields (Fastify additionalProperties + Zod strips extras),\n // so these ride along harmlessly and let beacons carry attribution.\n ...(config.sessionId ? { sessionId: config.sessionId } : {}),\n ...(config.projectId ? { projectId: config.projectId } : {}),\n nodes: nodes.map((n) => {\n const semanticType = toValidSemanticType(n.semanticType);\n return {\n componentId: n.componentId,\n semanticType,\n answers: n.answers,\n contentHash: contentHashOf(n.componentId, semanticType, n.answers),\n prominenceScore: n.prominenceScore,\n depthInPage: n.depth,\n };\n }),\n edges,\n };\n fetch(config.syncUrl, {\n method: 'POST',\n keepalive: true,\n headers: {\n 'Content-Type': 'application/json',\n ...(config.apiKey ? { Authorization: `Bearer ${config.apiKey}` } : {}),\n },\n body: JSON.stringify(payload),\n }).catch(() => undefined);\n } catch {\n /* ignore */\n }\n },\n\n snapshot(): GraphSnapshot {\n return {\n pageNodes: [...pageNodes.values()],\n capturedAt: Date.now(),\n };\n },\n\n serialize(): string {\n return JSON.stringify({ pageNodes: [...pageNodes.values()] });\n },\n\n restore,\n\n destroy(): void {\n if (typeof window === 'undefined') return;\n try {\n localStorage.removeItem(NODES_KEY);\n localStorage.removeItem(STALE_EDGES_KEY);\n } catch {\n /* ignore */\n }\n },\n };\n}\n"],"mappings":"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","names":["index_graph_exports","__export","deriveSessionSegment","detectDeviceClass","detectTimeOfDay","detectTrafficSource","init","referrerDomainFromReferer","__toCommonJS","randomUuidV4","buf","out","i","c","r","DEFAULT_COOKIE_NAME","DEFAULT_COOKIE_TTL_DAYS","STORAGE_KEY","generateSessionId","randomUuidV4","readCookie","name","match","e","writeCookie","value","maxAgeSeconds","readLocalStorage","key","writeLocalStorage","readSessionStorage","writeSessionStorage","removeSessionStorage","probeCookieWritable","removeLocalStorage","clearCookie","SSR_MANAGER","initSession","config","_a","_b","_c","_d","_e","_f","cookieName","nonEmpty","sessionId","lsOk","cookieOk","ssOk","ephemeral","retryStorageKey","apiKey","SSR_QUEUE","readRetryQueue","maxRetrySize","retryKey","raw","parsed","e","writeRetryQueue","events","existing","byId","merged","purgeFromRetryQueue","ids","drop","remaining","createEventQueue","config","_a","_b","_c","flushIntervalMs","maxBatchSize","ingestUrl","RETRY_KEY","queue","sentIds","sentIdOrder","markSent","id","queuedIds","oldest","enqueue","event","requeueFailed","batch","retryEvents","backoffUntil","consecutiveFailures","transportBatch","body","pending","handleResponse","res","encoder","byteLength","s","packBatch","pool","out","bytes","size","flush","flushTimerActive","intervalId","onVisibilityChange","onPageHide","KEY_PREFIX","cacheKey","componentId","segment","storageKey","KEY_PREFIX","parseStorageKey","key","suffix","sep","e","listStorageKeys","keys","i","createAssignmentCache","ttlMs","memory","isExpired","assignment","raw","parsed","entry","prefix","storageK","agentUaList","uaTokenMatch","userAgent","matchedAgentToken","_a","s","token","detectDeviceClass","detectTrafficSource","referrer","appOrigin","refUrl","e","host","referrerDomainFromReferer","detectTimeOfDay","d","h","deriveSessionSegment","opts","body","buildSessionUpsertPayload","sessionId","_b","_c","_d","_e","_f","_g","ua","referer","now","import_policy","toWireSlot","d","__spreadValues","dim","values","baselineResultFor","decl","armOfResult","result","SNAPSHOT_STORAGE_KEY_PREFIX","BANDS","readSnapshot","apiKey","raw","p","e","writeSnapshot","snap","import_policy","import_policy","PROD_KEYLESS_ERROR","LOCAL_MODE_BANNER","bannerShown","prodErrorShown","readPersonaOverrideFromUrl","_a","e","createLocalModeClient","config","session","initSession","sessionId","forcedPersona","modPromise","mod","m","bannerShown","LOCAL_MODE_BANNER","prodErrorShown","PROD_KEYLESS_ERROR","lastOutcome","applyPersonaAttributes","outcome","el","input","_b","_c","__spreadProps","__spreadValues","writeSnapshot","slotId","componentId","variantIds","DEFAULT_INGEST_URL","_clients","_lastApiKey","generateEventId","randomUuidV4","SSR_CLIENT","readUtmParams","out","sp","k","v","deriveBaseUrl","ingestUrl","isDoNotTrackEnabled","createPreConsentProxy","config","_a","baseUrl","deriveBaseUrl","DEFAULT_INGEST_URL","authHeaders","inner","componentId","variantIds","_agentData","params","v","res","e","proxy","n","m","w","s","c","g","o","u","a","av","setInner","fullClient","init","_b","_c","_d","_e","_f","_g","_h","_i","SSR_CLIENT","_lastApiKey","prevEntry","_clients","dntBlocked","isDoNotTrackEnabled","gated","keyValid","createLocalModeClient","resolvedIngestUrl","sessionStart","session","initSession","assignmentCache","createAssignmentCache","eventQueue","createEventQueue","deviceClass","detectDeviceClass","appOrigin","trafficSource","detectTrafficSource","sessionSegment","inflightAssigns","slotStore","personaState","seedSlotBaselines","decls","d","baselineResultFor","slotId","result","seedSnapshot","readSnapshot","BAND_CONFIDENCE","__spreadValues","ds","variantId","sessionReady","sessionId","referrerDomain","referrerDomainFromReferer","sessionBody","readUtmParams","detectTimeOfDay","uaTokenMatch","client","name","metadata","weight","stepIndex","sid","goalId","generateEventId","goalType","opts","assignment","slotResult","attributedVariantId","armOfResult","fullEvent","userId","event","__spreadProps","segment","agentData","agentDataByVariant","cached","remainingTtlMs","inflight","request","body","input","declared","id","toWireSlot","data","slots","known","writeSnapshot","SNAPSHOT_STORAGE_KEY_PREFIX","retryStorageKey","win","SEMANTIC_TYPES","KEYWORDS","CONTENT_PATTERNS","headingText","el","_a","h","classifyFeatures","f","hay","type","re","featuresFromElement","_b","text","classifySection","OBSERVE_TAGS","HEADING_SELECTOR","MAX_SIBLINGS_PER_GROUP","MAX_STRUCTURAL_EDGES","SSR_SCANNER","normalize","value","min","max","readReactComponentName","element","_a","_b","_c","record","key","fiber","name","e","extractDataAttributes","attrs","attr","SEMANTIC_SET","SEMANTIC_TYPES","inferSemanticType","explicit","role","classifySection","shortHash","input","h","i","domPathSignature","parts","current","parent","index","componentIdFor","declared","depthOf","depth","scanElement","getProminenceScore","heading","detectStructuralEdges","elementToId","edges","seen","ROOT","groups","el","_id","ancestor","groupKey","childId","siblings","sibs","capped","j","aId","bId","fwd","rev","collectNodesAndEdges","nodes","node","hasAria","hasSentientId","createDOMScanner","observer","idleCallbackId","contentCallback","knownElementToId","styles","fontSize","zIndex","rect","distanceFromTop","viewportHeight","inverseDistance","score","resolve","run","id","onContentAdded","mutations","added","addedIds","mutation","hasId","scanned","NODES_KEY","STALE_EDGES_KEY","SEMANTIC_NEIGHBOURS","neighboursFor","semanticType","_a","readStorage","key","fallback","raw","e","writeStorage","value","VALID_SEMANTIC_TYPES","toValidSemanticType","type","contentHashOf","componentId","answers","input","h","i","createGraphClient","config","pageNodes","structuralEdges","persist","restore","data","parsed","node","storedNodes","edge","_b","nodes","nodesByType","n","list","edges","seen","source","neighbourType","targets","target","componentIds","payload","__spreadProps","__spreadValues","DEFAULT_INGEST_URL","readSntUid","m","_graphTeardowns","init","config","_a","client","dntBlocked","isDoNotTrackEnabled","gated","prevTeardown","e","domScanner","createDOMScanner","resolvedIngestUrl","graphClient","createGraphClient","result","node","edge","syncDebounceTimer","debouncedSync","event","teardownGraph","__spreadProps","__spreadValues"]}