@kywi-software/js 0.15.0 → 0.15.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/audience/behavioral-collector.d.ts +2 -1
- package/dist/audience/behavioral-collector.d.ts.map +1 -1
- package/dist/audience/evaluate.d.ts +9 -1
- package/dist/audience/evaluate.d.ts.map +1 -1
- package/dist/audience/query-collector.d.ts +15 -0
- package/dist/audience/query-collector.d.ts.map +1 -0
- package/dist/audience/selfid-api.d.ts +57 -0
- package/dist/audience/selfid-api.d.ts.map +1 -0
- package/dist/audience/selfid-collector.d.ts +2 -0
- package/dist/audience/selfid-collector.d.ts.map +1 -1
- package/dist/consent.d.ts.map +1 -1
- package/dist/index.d.ts +15 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/kywi.esm.js +244 -128
- package/dist/kywi.esm.js.map +4 -4
- package/dist/kywi.js +244 -128
- package/dist/kywi.js.map +4 -4
- package/package.json +2 -2
package/dist/kywi.esm.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
|
-
"sources": ["../src/context.ts", "../src/entity.ts", "../src/feed.ts", "../src/render-feed.ts", "../src/module-system.ts", "../src/form-hooks.ts", "../src/nav/menu.ts", "../src/experiments/index.ts", "../../core/src/audiences/types.ts", "../../core/src/audiences/evaluator/glob.ts", "../../core/src/audiences/evaluator/instant-match.ts", "../../core/src/audiences/evaluator/score.ts", "../../core/src/audiences/evaluator/condition-registry.ts", "../../core/src/audiences/evaluator/resolver.ts", "../src/audience/
|
|
4
|
-
"sourcesContent": ["// packages/kywi-js/src/context.ts\nimport type { KywiContext } from './types.js'\n\nexport function readContext(): KywiContext {\n const meta = (name: string): string => {\n if (typeof document === 'undefined') return ''\n const el = document.querySelector(`meta[name=\"kywi:${name}\"]`)\n return el?.getAttribute('content') ?? ''\n }\n return {\n siteId: meta('site-id') || document?.documentElement?.getAttribute('data-kywi-site') || '',\n locale: document?.documentElement?.lang || 'en',\n themePath: meta('theme-path') || '',\n basePath: meta('base-path') || '/',\n csrfToken: meta('csrf-token') || '',\n isEditMode: document?.body?.classList?.contains('kywi-edit-mode') ?? false,\n }\n}\n", "// packages/kywi-js/src/entity.ts\nimport type { KywiEntityObject } from './types.js'\n\nfunction createEntity(type: string, basePath: string, csrfToken: string, data: Record<string, unknown>): KywiEntityObject {\n const fields = { ...data }\n const dirty = new Map<string, unknown>()\n const errors: string[] = []\n const headers: Record<string, string> = { 'Content-Type': 'application/json' }\n if (csrfToken) headers['X-CSRF-Token'] = csrfToken\n\n const entity: KywiEntityObject = {\n get(field: string) { return fields[field] },\n set(field: string, value: unknown) { fields[field] = value; dirty.set(field, value); return entity },\n async save() {\n const id = fields['id'] as string | undefined\n const url = id ? `${basePath}/content/${type}/${id}` : `${basePath}/content/${type}`\n const res = await fetch(url, { method: id ? 'PUT' : 'POST', headers, body: JSON.stringify(Object.fromEntries(dirty)) })\n const json = await res.json() as { data?: Record<string, unknown>; error?: { message: string } }\n if (!res.ok || json.error) { errors.push(json.error?.message ?? 'Save failed'); throw entity }\n if (json.data) Object.assign(fields, json.data)\n dirty.clear()\n return entity\n },\n async delete() {\n const id = fields['id'] as string\n await fetch(`${basePath}/content/${type}/${id}`, { method: 'DELETE', headers })\n },\n getErrors() { return [...errors] },\n async loadBy(field: string, value: string) {\n const url = field === 'id' ? `${basePath}/content/${type}/${value}` : `${basePath}/content/${type}?${field}=${encodeURIComponent(value)}`\n const res = await fetch(url, { headers })\n const json = await res.json() as { data?: Record<string, unknown>; error?: { message: string } }\n if (!res.ok || json.error) { errors.push(json.error?.message ?? 'Load failed'); throw entity }\n if (json.data) Object.assign(fields, json.data)\n return entity\n },\n }\n return entity\n}\n\nexport function createEntityFactory(basePath: string, csrfToken: string) {\n return (type: string): KywiEntityObject => createEntity(type, basePath, csrfToken, {})\n}\n", "// packages/kywi-js/src/feed.ts\nimport type { KywiEntityObject, KywiFeedResult } from './types.js'\n\ninterface Filter { field: string; operator: string; value?: unknown; conjunction: 'and' | 'or' }\n\nexport type AggregateFunction = 'count' | 'sum' | 'min' | 'max' | 'avg'\nexport type AggregateResult = Record<string, unknown>\n\nclass PropBuilder {\n constructor(private builder: FeedBuilder, private field: string, private conj: 'and' | 'or') {}\n private add(op: string, value?: unknown) { this.builder._filters.push({ field: this.field, operator: op, value, conjunction: this.conj }); return this.builder }\n isEQ(v: unknown) { return this.add('eq', v) }\n isNEQ(v: unknown) { return this.add('neq', v) }\n isLT(v: unknown) { return this.add('lt', v) }\n isLTE(v: unknown) { return this.add('lte', v) }\n isGT(v: unknown) { return this.add('gt', v) }\n isGTE(v: unknown) { return this.add('gte', v) }\n beginsWith(v: unknown) { return this.add('beginsWith', v) }\n endsWith(v: unknown) { return this.add('endsWith', v) }\n contains(v: unknown) { return this.add('contains', v) }\n isNull() { return this.add('isNull') }\n isNotNull() { return this.add('isNotNull') }\n}\n\nclass FeedBuilder {\n _filters: Filter[] = []\n private _sort: { field: string; direction: 'asc' | 'desc' } | null = null\n private _limit: number | null = null\n private _page: number | null = null\n constructor(private contentType: string, private basePath: string, private csrfToken: string) {}\n\n where(): this { return this }\n prop(field: string) { return new PropBuilder(this, field, 'and') }\n andProp(field: string) { return new PropBuilder(this, field, 'and') }\n orProp(field: string) { return new PropBuilder(this, field, 'or') }\n sort(field: string, direction: 'asc' | 'desc' = 'asc'): this { this._sort = { field, direction }; return this }\n maxItems(n: number): this { this._limit = n; return this }\n page(n: number): this { this._page = n; return this }\n\n private buildBody(): Record<string, unknown> {\n const filterArray = this._filters.map(f => ({\n field: f.field,\n operator: f.operator,\n value: f.value,\n conjunction: f.conjunction,\n }))\n const body: Record<string, unknown> = { contentType: this.contentType }\n if (filterArray.length > 0) body['filters'] = filterArray\n if (this._sort) body['sort'] = this._sort\n if (this._limit !== null) body['limit'] = this._limit\n if (this._page !== null) body['page'] = this._page\n return body\n }\n\n private buildHeaders(): Record<string, string> {\n const headers: Record<string, string> = { 'Content-Type': 'application/json' }\n if (this.csrfToken) headers['X-CSRF-Token'] = this.csrfToken\n return headers\n }\n\n async aggregate(fn: AggregateFunction, field?: string): Promise<AggregateResult> {\n const body = this.buildBody()\n const agg: Record<string, string> = { function: fn }\n if (field) agg['field'] = field\n body['aggregate'] = agg\n\n const res = await fetch(`${this.basePath}/feeds/aggregate`, {\n method: 'POST',\n headers: this.buildHeaders(),\n body: JSON.stringify(body),\n })\n const json = await res.json() as { data?: AggregateResult }\n return json.data ?? {}\n }\n\n async getQuery(): Promise<KywiFeedResult> {\n const body = this.buildBody()\n const res = await fetch(`${this.basePath}/feeds/query`, {\n method: 'POST',\n headers: this.buildHeaders(),\n body: JSON.stringify(body),\n })\n // Two response envelopes are supported:\n // 1. The live @kywi-software/core server (POST /feeds/query -> jsonOk(executeFeedQuery))\n // returns { data: { items, total, page, pageSize, totalPages } }.\n // 2. A flatter { data: [ ...items ], meta: { total, page, totalPages } } shape.\n // Normalise both so the decoupled client works against the real API.\n const json = await res.json() as {\n data?: Array<Record<string, unknown>> | { items?: Array<Record<string, unknown>>; total?: number; page?: number; pageSize?: number; totalPages?: number }\n meta?: { total?: number; page?: number; totalPages?: number }\n }\n\n const payload = json.data\n const rawItems: Array<Record<string, unknown>> = Array.isArray(payload)\n ? payload\n : (payload?.items ?? [])\n const total = Array.isArray(payload) ? (json.meta?.total ?? rawItems.length) : (payload?.total ?? rawItems.length)\n const page = Array.isArray(payload) ? (json.meta?.page ?? 1) : (payload?.page ?? 1)\n const pages = Array.isArray(payload) ? (json.meta?.totalPages ?? 0) : (payload?.totalPages ?? 0)\n\n const items: KywiEntityObject[] = rawItems.map(d => ({\n get(field: string) { return d[field] },\n set() { throw new Error('Feed results are read-only') },\n save() { return Promise.reject(new Error('Feed results are read-only')) },\n delete() { return Promise.reject(new Error('Feed results are read-only')) },\n getErrors() { return [] },\n loadBy() { return Promise.reject(new Error('Use Kywi.getEntity() to load by field')) },\n }))\n\n return { items, total, page, pages }\n }\n}\n\nexport function createFeedFactory(basePath: string, csrfToken: string) {\n return (type: string) => new FeedBuilder(type, basePath, csrfToken)\n}\n", "// packages/kywi-js/src/render-feed.ts\nimport type { KywiEntityObject, KywiFeedResult } from './types.js'\n\ninterface RenderFeedOptions {\n feed: { getQuery(): Promise<KywiFeedResult> }\n container: HTMLElement\n template: (item: KywiEntityObject) => string | Node\n loadingText?: string; emptyText?: string\n wrapperTag?: string; wrapperClass?: string\n unsafeHtml?: boolean\n onRender?: (container: HTMLElement) => void\n}\n\nexport async function renderFeed(options: RenderFeedOptions): Promise<void> {\n const { feed, container, template, loadingText = '', emptyText = '', wrapperTag = 'div', wrapperClass, unsafeHtml = false, onRender } = options\n if (loadingText) container.textContent = loadingText\n const result = await feed.getQuery()\n container.textContent = ''\n if (result.items.length === 0) { if (emptyText) container.textContent = emptyText; return }\n const wrapper = document.createElement(wrapperTag)\n if (wrapperClass) wrapper.className = wrapperClass\n for (const item of result.items) {\n const rendered = template(item)\n if (rendered instanceof Node) {\n wrapper.appendChild(rendered)\n } else if (unsafeHtml) {\n const span = document.createElement('span')\n // intentional: caller explicitly opts in via unsafeHtml\n span['innerHTML'] = rendered\n wrapper.appendChild(span)\n } else {\n const span = document.createElement('span')\n span.textContent = rendered // SAFE: always textContent for string templates\n wrapper.appendChild(span)\n }\n }\n container.appendChild(wrapper)\n if (onRender) onRender(container)\n}\n", "// packages/kywi-js/src/module-system.ts\nimport { Kywi } from './index.js'\nimport type { KywiModuleContext } from './types.js'\n\nfunction readParams(el: HTMLElement): Record<string, string> {\n const params: Record<string, string> = {}\n for (const attr of Array.from(el.attributes)) {\n if (attr.name.startsWith('data-kywi-param-')) {\n params[attr.name.slice(16)] = attr.value\n }\n }\n return params\n}\n\nexport function initModules(): void {\n const elements = document.querySelectorAll('[data-kywi-module]')\n for (const el of Array.from(elements)) {\n const htmlEl = el as HTMLElement\n const moduleName = htmlEl.getAttribute('data-kywi-module')\n if (!moduleName) continue\n const def = Kywi.Module[moduleName]\n if (!def || !def.renderClient) continue\n const context: KywiModuleContext = { targetEl: htmlEl, ...readParams(htmlEl) }\n def.renderClient.call({ context, ...def })\n }\n}\n", "// packages/kywi-js/src/form-hooks.ts\nimport { Kywi } from './index.js'\n\nexport function initFormHooks(): void {\n const forms = document.querySelectorAll('[data-kywi-form]')\n for (const formEl of Array.from(forms)) {\n const htmlForm = formEl as HTMLFormElement\n const hooks = Kywi.DisplayObject.Form._hooks\n if (hooks['onAfterRender']) hooks['onAfterRender'].call({ context: { targetEl: htmlForm } })\n htmlForm.addEventListener('submit', (e) => {\n if (hooks['onSubmit']) {\n const result = hooks['onSubmit'].call({ context: { targetEl: htmlForm } })\n if (result === false) e.preventDefault()\n }\n })\n }\n}\n\nexport async function submitKywiForm(form: HTMLFormElement, endpoint: string): Promise<void> {\n const formData = new FormData(form)\n const data: Record<string, string> = {}\n formData.forEach((value, key) => { data[key] = value as string })\n\n const res = await fetch(endpoint, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(data),\n })\n const json = await res.json() as { success: boolean; errors?: Record<string, string>; data?: Record<string, unknown> }\n const hooks = Kywi.DisplayObject.Form._hooks\n\n if (!json.success && json.errors) {\n if (typeof hooks['onAfterErrorRender'] === 'function') {\n hooks['onAfterErrorRender']({ errors: json.errors, targetEl: form })\n }\n } else if (json.success && json.data) {\n if (typeof hooks['onAfterResponseRender'] === 'function') {\n hooks['onAfterResponseRender']({ response: json.data, targetEl: form })\n }\n }\n}\n", "// packages/kywi-js/src/nav/menu.ts\n//\n// Progressive enhancement for the server-rendered nav modules (`navMenu` /\n// `navigation`, kywi-cms#111). The markup is already usable without this file:\n// every link is server-rendered, panels open on `:hover` / `:focus-within`, and\n// below the breakpoint the whole bar collapses behind a native `<details>`\n// hamburger. What this adds is the polish CSS cannot express \u2014 hover intent\n// (a close delay so the pointer can cross the gap), a real click target with\n// `aria-expanded` in sync, Escape-to-close with focus returned, arrow-key\n// movement inside an open panel, outside-click dismissal, and flipping a panel\n// that would run off the right edge of the viewport.\n//\n// Dependency-free and defensive: it enhances whatever `[data-kywi-nav]` roots\n// exist and no-ops when a nav has no disclosure items.\n\n/**\n * Desktop breakpoint, in pixels. The single source shared with core's\n * `site/styles.css`, whose one media query is `(max-width: 859.98px)` \u2014 the\n * last step below this value. Change both together.\n */\nexport const NAV_BREAKPOINT = 860\n\n/** Grace period before a hover-opened panel closes, in ms. */\nconst CLOSE_DELAY_MS = 120\n\nconst ITEM_CLASS = 'kywi-nav-item--has-children'\nconst OPEN_CLASS = 'kywi-nav-item--open'\nconst TOGGLE_CLASS = 'kywi-nav-toggle'\nconst PANEL_CLASS = 'kywi-nav-panel'\nconst FLIP_CLASS = 'kywi-nav-panel--flip'\nconst READY_ATTRIBUTE = 'data-kywi-nav-ready'\n\ninterface NavEntry {\n item: HTMLElement\n toggle: HTMLElement\n panel: HTMLElement\n}\n\nfunction directChild(parent: HTMLElement, className: string): HTMLElement | null {\n for (const child of Array.from(parent.children)) {\n if (child instanceof HTMLElement && child.classList.contains(className)) return child\n }\n return null\n}\n\nfunction isDesktop(): boolean {\n if (typeof window === 'undefined') return false\n if (typeof window.matchMedia !== 'function') return true\n return window.matchMedia(`(min-width: ${NAV_BREAKPOINT}px)`).matches\n}\n\nfunction panelLinks(panel: HTMLElement): HTMLElement[] {\n return Array.from(panel.querySelectorAll<HTMLElement>('a[href], button:not([disabled])'))\n}\n\n/** Enhance one `[data-kywi-nav]` root. Returns a teardown for tests/SPAs. */\nexport function enhanceNav(nav: HTMLElement): () => void {\n const cleanups: Array<() => void> = []\n const entries: NavEntry[] = []\n\n for (const item of Array.from(nav.querySelectorAll<HTMLElement>(`.${ITEM_CLASS}`))) {\n const toggle = directChild(item, TOGGLE_CLASS)\n const panel = directChild(item, PANEL_CLASS)\n if (toggle && panel) entries.push({ item, toggle, panel })\n }\n if (entries.length === 0) return () => {}\n\n let closeTimer: ReturnType<typeof setTimeout> | undefined\n const cancelClose = () => {\n if (closeTimer !== undefined) { clearTimeout(closeTimer); closeTimer = undefined }\n }\n\n function isOpen(entry: NavEntry): boolean {\n return entry.item.classList.contains(OPEN_CLASS)\n }\n\n function close(entry: NavEntry): void {\n entry.item.classList.remove(OPEN_CLASS)\n entry.toggle.setAttribute('aria-expanded', 'false')\n entry.panel.classList.remove(FLIP_CLASS)\n }\n\n function closeAll(except?: NavEntry): void {\n for (const entry of entries) {\n // A nested fly-out keeps its ancestors open.\n if (except && (entry === except || entry.item.contains(except.item))) continue\n close(entry)\n }\n }\n\n /** Flip a panel that would overflow the right edge (measured while open). */\n function applyEdgeFlip(entry: NavEntry): void {\n if (typeof entry.panel.getBoundingClientRect !== 'function') return\n entry.panel.classList.remove(FLIP_CLASS)\n const rect = entry.panel.getBoundingClientRect()\n const viewport = window.innerWidth || document.documentElement.clientWidth || 0\n if (viewport > 0 && rect.width > 0 && rect.right > viewport - 4) {\n entry.panel.classList.add(FLIP_CLASS)\n }\n }\n\n function open(entry: NavEntry): void {\n cancelClose()\n closeAll(entry)\n entry.item.classList.add(OPEN_CLASS)\n entry.toggle.setAttribute('aria-expanded', 'true')\n applyEdgeFlip(entry)\n }\n\n function on<E extends Event>(\n target: EventTarget,\n type: string,\n handler: (event: E) => void,\n options?: AddEventListenerOptions,\n ): void {\n target.addEventListener(type, handler as EventListener, options)\n cleanups.push(() => target.removeEventListener(type, handler as EventListener, options))\n }\n\n for (const entry of entries) {\n on(entry.toggle, 'click', (event: MouseEvent) => {\n event.preventDefault()\n if (isOpen(entry)) { close(entry) } else { open(entry) }\n })\n\n // Hover intent \u2014 pointer only, and only where panels actually float.\n on(entry.item, 'mouseenter', () => {\n if (!isDesktop()) return\n open(entry)\n })\n on(entry.item, 'mouseleave', () => {\n if (!isDesktop()) return\n cancelClose()\n closeTimer = setTimeout(() => { close(entry); closeTimer = undefined }, CLOSE_DELAY_MS)\n })\n }\n\n // Keyboard focus moving into a panel opens it; moving out of the item closes\n // it. Focus landing on the toggle itself is left alone so a click (which\n // focuses first) still reads as a toggle rather than an open-then-close.\n on(nav, 'focusin', (event: FocusEvent) => {\n const target = event.target as Node | null\n if (!target) return\n for (const entry of entries) {\n if (entry.panel.contains(target)) { open(entry); return }\n }\n for (const entry of entries) {\n if (!entry.item.contains(target)) close(entry)\n }\n })\n\n on(nav, 'keydown', (event: KeyboardEvent) => {\n const target = event.target as HTMLElement | null\n if (!target) return\n const entry = entries.find((e) => e.item.contains(target))\n if (!entry) return\n\n if (event.key === 'Escape') {\n const openEntry = entries.filter((e) => e.item.contains(target) && isOpen(e)).pop()\n if (!openEntry) return\n event.preventDefault()\n close(openEntry)\n openEntry.toggle.focus()\n return\n }\n\n if (event.key !== 'ArrowDown' && event.key !== 'ArrowUp') return\n\n // On the trigger: ArrowDown opens and enters the panel, ArrowUp closes.\n if (target === entry.toggle) {\n if (event.key === 'ArrowDown') {\n event.preventDefault()\n if (!isOpen(entry)) open(entry)\n panelLinks(entry.panel)[0]?.focus()\n } else if (isOpen(entry)) {\n event.preventDefault()\n close(entry)\n }\n return\n }\n\n // Inside a panel: move between its links.\n const owner = entries.find((e) => e.panel.contains(target))\n if (!owner) return\n const links = panelLinks(owner.panel)\n const index = links.indexOf(target)\n if (index === -1) return\n event.preventDefault()\n const next = event.key === 'ArrowDown'\n ? links[(index + 1) % links.length]\n : links[(index - 1 + links.length) % links.length]\n next?.focus()\n })\n\n on(document, 'click', (event: MouseEvent) => {\n const target = event.target as Node | null\n if (target && nav.contains(target)) return\n cancelClose()\n closeAll()\n })\n\n // Crossing the breakpoint changes what \"open\" means; start from closed.\n if (typeof window.matchMedia === 'function') {\n const query = window.matchMedia(`(min-width: ${NAV_BREAKPOINT}px)`)\n const onChange = () => { cancelClose(); closeAll() }\n if (typeof query.addEventListener === 'function') {\n query.addEventListener('change', onChange)\n cleanups.push(() => query.removeEventListener('change', onChange))\n }\n }\n\n nav.setAttribute(READY_ATTRIBUTE, '')\n\n return () => {\n cancelClose()\n for (const undo of cleanups) undo()\n cleanups.length = 0\n closeAll()\n nav.removeAttribute(READY_ATTRIBUTE)\n }\n}\n\n/**\n * Enhance every nav under `root` that has not been enhanced yet. Called from\n * `Kywi.boot()`; safe to call again after injecting markup client-side.\n */\nexport function initNavMenus(root: ParentNode = document): () => void {\n if (typeof document === 'undefined') return () => {}\n const teardowns: Array<() => void> = []\n for (const nav of Array.from(root.querySelectorAll<HTMLElement>('[data-kywi-nav]'))) {\n if (nav.hasAttribute(READY_ATTRIBUTE)) continue\n teardowns.push(enhanceNav(nav))\n }\n return () => { for (const undo of teardowns) undo() }\n}\n", "// packages/kywi-js/src/experiments/index.ts\n//\n// Client-side A/B experiment beacons. Reads the server-rendered kywi:* meta\n// tags, confirms the exposure, and reports conversion signals (pageview,\n// form-submit, custom) back to the runtime endpoints. Fire-and-forget: every\n// call is best-effort and never throws into the host page.\n\nimport { readContext } from '../context.js'\nimport type { KywiContext } from '../types.js'\n\nexport interface ConversionSignal {\n type: 'pageview' | 'form_submit' | 'custom'\n url?: string\n formId?: string\n eventName?: string\n}\n\nexport interface ExperimentBootConfig {\n context?: KywiContext\n apiBase?: string\n visitorId?: string\n currentPath?: string\n /** Auto-emit a pageview conversion signal on boot (default true). */\n autoPageview?: boolean\n}\n\nexport interface ExperimentRuntime {\n /** Emit a custom conversion event, e.g. kywi.track('signup'). */\n track(eventName: string): void\n /** Emit a raw conversion signal. */\n trackConversion(signal: ConversionSignal): void\n}\n\nfunction apiBaseFrom(ctx: KywiContext): string {\n return ctx.basePath && ctx.basePath !== '/' ? `${ctx.basePath}/api/v1` : '/api/v1'\n}\n\nfunction metaContent(name: string): string | null {\n if (typeof document === 'undefined') return null\n return document.querySelector(`meta[name=\"kywi:${name}\"]`)?.getAttribute('content') ?? null\n}\n\n// A single document-level submit listener is installed once and routes to the\n// most recent boot's handler \u2014 booting twice must not double-count conversions.\nlet submitListenerInstalled = false\nlet currentFormConversion: ((formId: string) => void) | null = null\n\nfunction installFormListener(): void {\n if (submitListenerInstalled || typeof document === 'undefined') return\n submitListenerInstalled = true\n document.addEventListener(\n 'submit',\n (e) => {\n const form = e.target as HTMLElement | null\n if (!form || form.tagName !== 'FORM') return\n const formId = form.getAttribute('data-kywi-form-id') ?? form.getAttribute('id')\n if (formId && currentFormConversion) currentFormConversion(formId)\n },\n true,\n )\n}\n\nfunction beacon(url: string, body: unknown, csrfToken?: string): void {\n try {\n void fetch(url, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', ...(csrfToken ? { 'X-CSRF-Token': csrfToken } : {}) },\n body: JSON.stringify(body),\n keepalive: true,\n }).catch(() => {})\n } catch {\n /* never throw into the host page */\n }\n}\n\n/**\n * Boot the experiment beacons. Safe to call once per page load; when no\n * experiment meta is present it only wires the conversion listeners.\n */\nexport function bootExperiments(config: ExperimentBootConfig = {}): ExperimentRuntime {\n const ctx = config.context ?? readContext()\n const apiBase = config.apiBase ?? apiBaseFrom(ctx)\n const csrf = ctx.csrfToken || undefined\n const experimentId = metaContent('experiment-id')\n const visitorId = config.visitorId ?? metaContent('visitor-id') ?? undefined\n const currentPath = config.currentPath ?? (typeof location !== 'undefined' ? location.pathname : '/')\n\n function trackConversion(signal: ConversionSignal): void {\n beacon(`${apiBase}/kywi/experiments/conversion`, { visitorId, event: signal }, csrf)\n }\n function track(eventName: string): void {\n trackConversion({ type: 'custom', eventName })\n }\n\n // 1) Exposure \u2014 confirm the server-rendered variant was seen. The variant is\n // recomputed server-side, so we only send the experiment + visitor ids.\n if (experimentId) {\n beacon(`${apiBase}/kywi/experiments/exposure`, { experimentId, visitorId }, csrf)\n }\n\n // 2) Pageview conversion signal \u2014 lets pageview-goal experiments convert when\n // an enrolled visitor lands on the goal path.\n if (config.autoPageview !== false) {\n trackConversion({ type: 'pageview', url: currentPath })\n }\n\n // 3) Form-submit conversion \u2014 capture submits of any tagged form. The listener\n // is installed once; this boot becomes its active handler.\n currentFormConversion = (formId: string) => trackConversion({ type: 'form_submit', formId })\n installFormListener()\n\n const runtime: ExperimentRuntime = { track, trackConversion }\n\n // Expose on the data layer so analytics / manual conversions can reach it.\n const g = ((globalThis as unknown as { kywi?: Record<string, unknown> }).kywi ?? {})\n g['track'] = track\n g['trackConversion'] = trackConversion\n ;(globalThis as unknown as { kywi?: Record<string, unknown> }).kywi = g\n\n return runtime\n}\n", "// \u2500\u2500 Visitor Signals \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport interface VisitorSignals {\n utm: {\n source: string | null\n medium: string | null\n campaign: string | null\n term: string | null\n content: string | null\n }\n referrer: {\n raw: string | null\n domain: string | null\n sourceType: 'organic' | 'paid' | 'social' | 'email' | 'direct' | 'other'\n }\n session: {\n pageViewCount: number\n totalPageViews: number\n sessionDuration: number\n entryPage: string\n pagesVisited: string[]\n }\n behavioral: {\n categoryScores: Record<string, number>\n totalScore: number\n }\n selfId: Record<string, string>\n identity: {\n visitorId: string\n isKnown: boolean\n maLeadId: string | null\n }\n adapters: Record<string, unknown>\n meta: {\n isOptedOut: boolean\n isPreview: boolean\n previewAudienceId: string | null\n resolvedAt: 'server' | 'client'\n }\n}\n\n// \u2500\u2500 Evaluation Result \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport interface VisitorContext {\n request: Request\n partialSignals?: Partial<VisitorSignals>\n}\n\nexport interface AudienceResult {\n winningAudienceId: string | null\n allMatchedIds: string[]\n signals: VisitorSignals\n isOptedOut: boolean\n}\n\n// \u2500\u2500 Audience & Rule Types \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport type AudienceStatus = 'active' | 'draft' | 'archived'\n\nexport interface Audience {\n id: string\n siteId: string\n name: string\n description: string | null\n priority: number\n status: AudienceStatus\n conditionGroups: ConditionGroup[]\n scoreThreshold: number | null\n createdAt: Date\n updatedAt: Date\n}\n\nexport interface ConditionGroup {\n id: string\n audienceId: string\n sortOrder: number\n conditions: Condition[]\n}\n\nexport type Condition = InstantMatchCondition | ScoreContributionCondition\n\nexport interface InstantMatchCondition {\n type: 'instant'\n id: string\n groupId: string\n signal: InstantSignalRef\n operator: InstantOperator\n value: string | string[]\n}\n\nexport type InstantSignalRef =\n | 'utm.source' | 'utm.medium' | 'utm.campaign' | 'utm.term' | 'utm.content'\n | 'referrer.domain' | 'referrer.sourceType'\n | `selfId.${string}`\n | `adapter.${string}`\n | 'identity.isKnown'\n | 'session.entryPage'\n | 'session.pagesVisited'\n\nexport type InstantOperator =\n | 'equals' | 'not_equals' | 'contains' | 'starts_with' | 'matches_glob'\n | 'is_one_of' | 'is_true' | 'is_false'\n | 'gte' | 'lte'\n\nexport interface ScoreContributionCondition {\n type: 'score'\n id: string\n groupId: string\n trigger: ScoreTrigger\n points: number\n threshold?: number\n category?: string\n pattern?: string\n /**\n * For `trigger: 'signalMatch'` \u2014 award `points` when an arbitrary instant\n * signal matches (e.g. `utm.campaign contains 'enterprise' \u2192 +30`). Reuses the\n * instant-match operator set so any signal (UTM / referrer / self-ID / adapter)\n * can contribute to a score, not just the five behavioral triggers.\n */\n signal?: InstantSignalRef\n operator?: InstantOperator\n value?: string | string[]\n}\n\nexport type ScoreTrigger =\n | 'pageViewCount'\n | 'totalPageViews'\n | 'urlMatch'\n | 'categoryViewed'\n | 'timeOnSite'\n | 'signalMatch'\n\n// \u2500\u2500 Self-ID Types \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport interface SelfIdField {\n id: string\n siteId: string\n label: string\n picklist: Array<{ value: string; label: string }>\n required: boolean\n sortOrder: number\n}\n\nexport interface SelfIdWidgetConfig {\n id: string\n siteId: string\n displayMode: 'hello_bar_top' | 'hello_bar_bottom' | 'modal' | 'inline' | 'drawer' | 'slide-in'\n trigger: SelfIdTrigger\n headline: string\n subheadline: string | null\n fieldIds: string[]\n submitLabel: string\n skipLabel: string | null\n frequency: 'once_visitor' | 'once_session' | 'always'\n}\n\nexport type SelfIdTrigger =\n | { type: 'immediate' }\n | { type: 'scroll'; depth: number }\n | { type: 'delay'; seconds: number }\n | { type: 'manual' }\n\n// \u2500\u2500 Preview Token \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport interface PreviewTokenPayload {\n jti: string\n audienceId: string\n expiresAt: number\n}\n\n// \u2500\u2500 Default Signals \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport function createEmptySignals(): VisitorSignals {\n return {\n utm: { source: null, medium: null, campaign: null, term: null, content: null },\n referrer: { raw: null, domain: null, sourceType: 'direct' },\n session: { pageViewCount: 0, totalPageViews: 0, sessionDuration: 0, entryPage: '/', pagesVisited: [] },\n behavioral: { categoryScores: {}, totalScore: 0 },\n selfId: {},\n identity: { visitorId: '', isKnown: false, maLeadId: null },\n adapters: {},\n meta: { isOptedOut: false, isPreview: false, previewAudienceId: null, resolvedAt: 'server' },\n }\n}\n", "export function matchGlob(pattern: string, value: string): boolean {\n const parts = pattern.split('*')\n if (parts.length === 1) return pattern === value\n let pos = 0\n const first = parts[0]!\n if (first !== '') {\n if (!value.startsWith(first)) return false\n pos = first.length\n }\n for (let i = 1; i < parts.length - 1; i++) {\n const part = parts[i]!\n if (part === '') continue\n const idx = value.indexOf(part, pos)\n if (idx < 0) return false\n pos = idx + part.length\n }\n const last = parts[parts.length - 1]!\n if (last !== '') {\n if (!value.endsWith(last)) return false\n if (value.length - last.length < pos) return false\n }\n return true\n}\n", "import type { VisitorSignals, InstantMatchCondition, InstantSignalRef } from '../types.js'\nimport { matchGlob } from './glob.js'\n\nexport function resolveSignalValue(signals: VisitorSignals, ref: InstantSignalRef): string | null {\n if (ref === 'utm.source') return signals.utm.source\n if (ref === 'utm.medium') return signals.utm.medium\n if (ref === 'utm.campaign') return signals.utm.campaign\n if (ref === 'utm.term') return signals.utm.term\n if (ref === 'utm.content') return signals.utm.content\n if (ref === 'referrer.domain') return signals.referrer.domain\n if (ref === 'referrer.sourceType') return signals.referrer.sourceType\n if (ref === 'identity.isKnown') return signals.identity.isKnown ? 'true' : 'false'\n if (ref === 'session.entryPage') return signals.session.entryPage\n if (ref === 'session.pagesVisited') return signals.session.pagesVisited.join(',')\n if (ref.startsWith('selfId.')) {\n const key = ref.slice(7)\n return signals.selfId[key] ?? null\n }\n if (ref.startsWith('adapter.')) {\n const key = ref.slice(8)\n const val = signals.adapters[key]\n return val !== undefined && val !== null ? String(val) : null\n }\n return null\n}\n\nexport function evaluateInstantCondition(condition: InstantMatchCondition, signals: VisitorSignals): boolean {\n const actual = resolveSignalValue(signals, condition.signal)\n const expected = condition.value\n switch (condition.operator) {\n case 'equals': return actual === expected\n case 'not_equals': return actual !== expected\n case 'contains': return actual !== null && typeof expected === 'string' && actual.includes(expected)\n case 'starts_with': return actual !== null && typeof expected === 'string' && actual.startsWith(expected)\n case 'matches_glob': return actual !== null && typeof expected === 'string' && matchGlob(expected, actual)\n case 'is_one_of': return actual !== null && Array.isArray(condition.value) && condition.value.includes(actual)\n case 'is_true': return actual === 'true'\n case 'is_false': return actual === 'false'\n case 'gte': return actual !== null && typeof expected === 'string' && Number(actual) >= Number(expected)\n case 'lte': return actual !== null && typeof expected === 'string' && Number(actual) <= Number(expected)\n default: return false\n }\n}\n", "import type { VisitorSignals, ScoreContributionCondition } from '../types.js'\nimport { matchGlob } from './glob.js'\nimport { evaluateInstantCondition } from './instant-match.js'\n\nfunction evaluateOneScore(cond: ScoreContributionCondition, signals: VisitorSignals): number {\n const threshold = cond.threshold ?? 0\n switch (cond.trigger) {\n case 'pageViewCount': return signals.session.pageViewCount >= threshold ? cond.points : 0\n case 'totalPageViews': return signals.session.totalPageViews >= threshold ? cond.points : 0\n case 'timeOnSite': return signals.session.sessionDuration >= threshold ? cond.points : 0\n case 'categoryViewed': {\n const cat = cond.category ?? ''\n const score = signals.behavioral.categoryScores[cat]\n return score !== undefined && score >= threshold ? cond.points : 0\n }\n case 'urlMatch': {\n const pattern = cond.pattern ?? '*'\n const matched = signals.session.pagesVisited.some(url => matchGlob(pattern, url))\n return matched ? cond.points : 0\n }\n case 'signalMatch': {\n // Award points when an arbitrary instant signal matches. Reuses the\n // instant-match evaluator so every operator/signal is supported.\n if (!cond.signal || !cond.operator) return 0\n const matched = evaluateInstantCondition(\n {\n type: 'instant',\n id: cond.id,\n groupId: cond.groupId,\n signal: cond.signal,\n operator: cond.operator,\n value: cond.value ?? '',\n },\n signals,\n )\n return matched ? cond.points : 0\n }\n default: return 0\n }\n}\n\nexport function evaluateScoreConditions(conditions: ScoreContributionCondition[], signals: VisitorSignals): number {\n let total = 0\n for (const cond of conditions) { total += evaluateOneScore(cond, signals) }\n return total\n}\n", "import type { Condition, VisitorSignals } from '../types.js'\n\n// \u2500\u2500\u2500 Custom Condition Type Registry \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n//\n// Allows third-party code (plugins, adapters, custom app code) to register\n// additional condition type evaluators without forking the core evaluator.\n//\n// Usage:\n// import { registerConditionType } from '@kywi-software/core'\n//\n// registerConditionType('my_custom_type', (condition, signals) => {\n// // Return true if the condition matches the current visitor signals\n// return signals.adapters['my_adapter'] === condition.value\n// })\n\ntype ConditionEvaluator = (condition: Condition, signals: VisitorSignals) => boolean\n\nconst registry = new Map<string, ConditionEvaluator>()\n\n/**\n * Register a custom condition type evaluator.\n *\n * @param name - The condition `type` string this evaluator handles.\n * @param evaluate - Pure function that returns `true` when the condition matches.\n *\n * Calling this with an already-registered name overwrites the previous evaluator.\n */\nexport function registerConditionType(name: string, evaluate: ConditionEvaluator): void {\n registry.set(name, evaluate)\n}\n\n/**\n * Remove a previously registered condition type evaluator.\n * No-op if the name was never registered.\n */\nexport function unregisterConditionType(name: string): void {\n registry.delete(name)\n}\n\n/**\n * Evaluate a condition using a registered custom evaluator.\n * Returns `false` if no evaluator is registered for the condition's type.\n */\nexport function evaluateCustomCondition(condition: Condition, signals: VisitorSignals): boolean {\n const evaluator = registry.get(condition.type)\n if (!evaluator) return false\n return evaluator(condition, signals)\n}\n\n/**\n * Returns true if a custom evaluator is registered for the given condition type name.\n */\nexport function hasConditionType(name: string): boolean {\n return registry.has(name)\n}\n\n/**\n * Returns the names of all registered custom condition types.\n */\nexport function listConditionTypes(): string[] {\n return [...registry.keys()]\n}\n", "import type { Audience, VisitorSignals, ConditionGroup, InstantMatchCondition, ScoreContributionCondition } from '../types.js'\nimport { evaluateInstantCondition } from './instant-match.js'\nimport { evaluateScoreConditions } from './score.js'\nimport { hasConditionType, evaluateCustomCondition } from './condition-registry.js'\n\nexport interface ConditionTrace {\n conditionId: string\n type: 'instant' | 'score'\n passed: boolean\n scoreContributed?: number\n}\n\nexport interface EvaluationTrace {\n audienceId: string\n matched: boolean\n groupTraces: Array<{ groupId: string; matched: boolean; conditionTraces: ConditionTrace[] }>\n totalScore?: number\n}\n\nexport interface ResolveResult {\n winningAudienceId: string | null\n allMatchedIds: string[]\n traces: EvaluationTrace[]\n}\n\n// Evaluate one group: AND all instant + custom conditions, accumulate score conditions\nfunction evaluateGroup(group: ConditionGroup, signals: VisitorSignals) {\n const conditionTraces: ConditionTrace[] = []\n const instants: InstantMatchCondition[] = []\n const scores: ScoreContributionCondition[] = []\n const customs: typeof group.conditions = []\n\n for (const c of group.conditions) {\n // The Condition union only models 'instant'/'score', but the registry\n // dispatches custom condition kinds by their `type` string at runtime.\n // Capture it as a string so the custom branch stays reachable.\n const conditionType: string = c.type\n if (c.type === 'instant') instants.push(c)\n else if (c.type === 'score') scores.push(c)\n else if (hasConditionType(conditionType)) customs.push(c)\n // Unknown types with no registered evaluator are silently skipped\n }\n\n let allInstantsPassed = true\n\n for (const ic of instants) {\n const passed = evaluateInstantCondition(ic, signals)\n conditionTraces.push({ conditionId: ic.id, type: 'instant', passed })\n if (!passed) allInstantsPassed = false\n }\n\n // Custom conditions are ANDed with instant conditions\n for (const cc of customs) {\n const passed = evaluateCustomCondition(cc, signals)\n conditionTraces.push({ conditionId: cc.id, type: cc.type as 'instant', passed })\n if (!passed) allInstantsPassed = false\n }\n\n const totalScore = evaluateScoreConditions(scores, signals)\n for (const sc of scores) {\n const contributed = evaluateScoreConditions([sc], signals)\n conditionTraces.push({ conditionId: sc.id, type: 'score', passed: contributed > 0, scoreContributed: contributed })\n }\n\n return { matched: allInstantsPassed, conditionTraces, totalScore }\n}\n\nexport function resolveAudiences(audiences: Audience[], signals: VisitorSignals): ResolveResult {\n const allMatchedIds: string[] = []\n const traces: EvaluationTrace[] = []\n\n for (const audience of audiences) {\n const groupTraces: EvaluationTrace['groupTraces'] = []\n let audienceMatched = false\n let accumulatedScore = 0\n\n for (const group of audience.conditionGroups) {\n const { matched, conditionTraces, totalScore } = evaluateGroup(group, signals)\n accumulatedScore += totalScore\n groupTraces.push({ groupId: group.id, matched, conditionTraces })\n if (matched) audienceMatched = true\n }\n\n if (audience.scoreThreshold !== null) {\n const scoreThresholdMet = accumulatedScore >= audience.scoreThreshold\n const hasInstantConditions = audience.conditionGroups.some(g => g.conditions.some(c => c.type === 'instant'))\n if (hasInstantConditions) {\n audienceMatched = audienceMatched && scoreThresholdMet\n } else {\n audienceMatched = scoreThresholdMet\n }\n }\n\n traces.push({\n audienceId: audience.id,\n matched: audienceMatched,\n groupTraces,\n ...(accumulatedScore > 0 ? { totalScore: accumulatedScore } : {}),\n })\n if (audienceMatched) allMatchedIds.push(audience.id)\n }\n\n return { winningAudienceId: allMatchedIds[0] ?? null, allMatchedIds, traces }\n}\n", "// packages/kywi-js/src/audience/behavioral-collector.ts\nconst SESSION_KEY = 'kywi_session'\nconst TOTAL_VIEWS_KEY = 'kywi_total_views'\nconst CATEGORY_SCORES_KEY = 'kywi_category_scores'\nconst SESSION_START_KEY = 'kywi_session_start'\n\ninterface SessionData { pageViewCount: number; entryPage: string; pagesVisited: string[] }\n\nexport interface BehavioralResult {\n session: { pageViewCount: number; totalPageViews: number; sessionDuration: number; entryPage: string; pagesVisited: string[] }\n behavioral: { categoryScores: Record<string, number>; totalScore: number }\n}\n\nfunction getSession(): SessionData {\n const raw = sessionStorage.getItem(SESSION_KEY)\n if (raw) { try { return JSON.parse(raw) as SessionData } catch { /* fall through */ } }\n return { pageViewCount: 0, entryPage: '', pagesVisited: [] }\n}\n\nfunction getSessionStart(): number {\n const raw = sessionStorage.getItem(SESSION_START_KEY)\n if (raw) return Number(raw)\n const now = Date.now()\n sessionStorage.setItem(SESSION_START_KEY, String(now))\n return now\n}\n\nfunction getCategoryScores(): Record<string, number> {\n const raw = localStorage.getItem(CATEGORY_SCORES_KEY)\n if (raw) { try { return JSON.parse(raw) as Record<string, number> } catch { /* fall through */ } }\n return {}\n}\n\nexport function collectBehavioral(currentPath: string, category?: string): BehavioralResult {\n const session = getSession()\n session.pageViewCount += 1\n if (!session.entryPage) session.entryPage = currentPath\n session.pagesVisited.push(currentPath)\n sessionStorage.setItem(SESSION_KEY, JSON.stringify(session))\n\n const totalViews = (Number(localStorage.getItem(TOTAL_VIEWS_KEY)) || 0) + 1\n localStorage.setItem(TOTAL_VIEWS_KEY, String(totalViews))\n\n const sessionStart = getSessionStart()\n const categoryScores = getCategoryScores()\n if (category) {\n categoryScores[category] = (categoryScores[category] ?? 0) + 1\n localStorage.setItem(CATEGORY_SCORES_KEY, JSON.stringify(categoryScores))\n }\n\n return {\n session: { pageViewCount: session.pageViewCount, totalPageViews: totalViews, sessionDuration: Math.floor((Date.now() - sessionStart) / 1000), entryPage: session.entryPage, pagesVisited: session.pagesVisited },\n behavioral: { categoryScores, totalScore: 0 },\n }\n}\n", "// packages/kywi-js/src/audience/selfid-collector.ts\nconst SELFID_KEY = 'kywi_selfid'\n\nexport function collectSelfId(): Record<string, string> {\n const raw = localStorage.getItem(SELFID_KEY)\n if (!raw) return {}\n try { return JSON.parse(raw) as Record<string, string> } catch { return {} }\n}\n\nexport function storeSelfId(responses: Record<string, string>): void {\n localStorage.setItem(SELFID_KEY, JSON.stringify(responses))\n}\n", "// packages/kywi-js/src/audience/evaluate.ts\nimport type { Audience, VisitorSignals, AudienceResult } from '@kywi-software/core/audiences/types'\nimport { createEmptySignals } from '@kywi-software/core/audiences/types'\nimport { resolveAudiences } from '@kywi-software/core/evaluator'\nimport { collectBehavioral } from './behavioral-collector.js'\nimport { collectSelfId } from './selfid-collector.js'\n\nfunction merge(base: VisitorSignals, partial: Partial<VisitorSignals>): VisitorSignals {\n return {\n utm: partial.utm ?? base.utm, referrer: partial.referrer ?? base.referrer,\n session: partial.session ?? base.session, behavioral: partial.behavioral ?? base.behavioral,\n selfId: { ...base.selfId, ...partial.selfId }, identity: partial.identity ?? base.identity,\n adapters: { ...base.adapters, ...partial.adapters }, meta: { ...base.meta, ...partial.meta },\n }\n}\n\nexport function evaluateClientSide(\n audiences: Audience[], serverSignals: Partial<VisitorSignals>, currentPath: string, pageCategory?: string,\n): AudienceResult {\n let signals = merge(createEmptySignals(), serverSignals)\n const client = collectBehavioral(currentPath, pageCategory)\n const selfId = collectSelfId()\n signals = merge(signals, { session: client.session, behavioral: client.behavioral, selfId, meta: { ...signals.meta, resolvedAt: 'client' } })\n const { winningAudienceId, allMatchedIds } = resolveAudiences(audiences, signals)\n const isOptedOut = signals.meta.isOptedOut\n return { winningAudienceId: isOptedOut ? null : winningAudienceId, allMatchedIds, signals, isOptedOut }\n}\n", "// packages/kywi-js/src/audience/dom-patcher.ts\nexport function patchVariantContainers(winningAudienceId: string | null): void {\n const containers = document.querySelectorAll('.kywi-variant-container')\n for (const container of Array.from(containers)) {\n const el = container as HTMLElement\n const arms = Array.from(el.querySelectorAll('[data-kywi-variant]'))\n\n // Only module-level containers (VariantContainerModule in\n // packages/core/src/layout/modules-extended.tsx) render every arm into the\n // DOM for this client-side swap \u2014 each tagged `[data-kywi-variant]`, with at\n // least a \"default\" arm present even while loading. Section-level containers\n // (SectionRenderer's VariantContainerRenderer in packages/core/src/layout/\n // renderer.tsx, and components/personalization/VariantContainer.tsx) share\n // the `.kywi-variant-container` selector but are fully server-resolved: they\n // render only the winning arm's markup directly, so they never have any\n // `[data-kywi-variant]` children. That \"has arms to toggle\" structure is the\n // most robust discriminator available (unlike a class or `data-mode`\n // attribute, it can't drift out of sync with what this function actually\n // needs to do) \u2014 skip anything without arms so we never overwrite a\n // server-resolved container's `data-variant` with a client-side audience id\n // that disagrees with the content already rendered (kywi-cms#99).\n if (arms.length === 0) continue\n\n el.classList.remove('kywi-variant-loading')\n el.removeAttribute('aria-busy')\n const skeleton = el.querySelector('.kywi-variant-skeleton')\n if (skeleton) skeleton.remove()\n const requested = winningAudienceId ?? 'default'\n // The page's globally-resolved audience need not match one of THIS\n // container's own arms \u2014 it may only be bound to 'default' and some other\n // audience entirely. Hiding every arm because none matches `requested`\n // would blank out the container's own default content (kywi-cms#172), so\n // fall back to showing 'default' when nothing on this container matches.\n const hasMatch = arms.some((child) => (child as HTMLElement).getAttribute('data-kywi-variant') === requested)\n const target = hasMatch ? requested : 'default'\n el.setAttribute('data-variant', target)\n for (const child of arms) {\n const h = child as HTMLElement\n h.style.display = h.getAttribute('data-kywi-variant') === target ? '' : 'none'\n }\n }\n}\n", "// packages/kywi-js/src/cookies.ts\nexport const COOKIE_NAMES = {\n SIGNALS: 'kywi_signals', VISITOR: 'kywi_visitor', UTM: 'kywi_utm',\n AUDIENCE: 'kywi_audience', OPTOUT: 'kywi_optout', KNOWN: 'kywi_known',\n PREVIEW_INIT: 'kywi_preview_init',\n} as const\n\nexport function readCookies(): Map<string, string> {\n const map = new Map<string, string>()\n if (typeof document === 'undefined') return map\n for (const pair of document.cookie.split(';')) {\n const trimmed = pair.trim()\n const eqIdx = trimmed.indexOf('=')\n if (eqIdx < 0) continue\n map.set(trimmed.slice(0, eqIdx), decodeURIComponent(trimmed.slice(eqIdx + 1)))\n }\n return map\n}\n\nexport function setCookie(name: string, value: string, maxAge?: number): void {\n let str = `${name}=${encodeURIComponent(value)}; Path=/; SameSite=Lax`\n if (maxAge !== undefined && maxAge >= 0) str += `; Max-Age=${maxAge}`\n document.cookie = str\n}\n\nexport function deleteCookie(name: string): void {\n document.cookie = `${name}=; Path=/; Max-Age=0`\n}\n", "// packages/kywi-js/src/audience/transparency.ts\n//\n// The transparency surface: the single global control through which a visitor\n// sees THAT the page is personalized, WHICH audience it was personalized for,\n// and can switch audience, reset to automatic resolution, or turn\n// personalization off entirely (issue #105).\n//\n// Three states:\n// 1. nothing \u2014 not personalizing and no match available\n// 2. collapsed pill \u2014 small, unobtrusive, click/Enter expands\n// 3. expanded panel \u2014 audience name, switcher, reset, opt-out/opt-in\n//\n// Collapsing is NOT dismissal: there is no permanent suppression any more (a\n// promise of transparency the visitor can silence forever is not one). The\n// collapsed/expanded choice is remembered in localStorage; the legacy\n// `kywi_transparency_dismissed` flag is migrated to \"collapsed\".\n//\n// All styling lives in `@kywi-software/core`'s site/styles.css (token-driven,\n// overridable) \u2014 this module ships no stylesheet of its own.\n\nimport { COOKIE_NAMES, readCookies, setCookie, deleteCookie } from '../cookies.js'\n\nconst COLLAPSED_KEY = 'kywi_transparency_collapsed'\n/** Pre-#105 permanent-dismissal flag; read once and migrated to \"collapsed\". */\nconst LEGACY_DISMISSED_KEY = 'kywi_transparency_dismissed'\n\nconst ROOT_ID = 'kywi-transparency'\nconst PANEL_ID = 'kywi-transparency-panel'\nconst TITLE_ID = 'kywi-transparency-title'\nconst SWITCHER_LABEL_ID = 'kywi-transparency-switcher-label'\n\nconst DEFAULT_API_BASE = '/api/v1'\n\n/** An audience the visitor can be shown as, or switch to. */\nexport interface TransparencyAudience {\n id: string\n name: string\n}\n\nexport interface TransparencyState {\n /** The page is currently rendering an audience's variant content. */\n isPersonalized: boolean\n isOptedOut: boolean\n /** At least one audience matched \u2014 i.e. personalization is available here. */\n hasMatchAvailable: boolean\n /** The audience the page was personalized for (resolves the displayed name). */\n audienceId?: string | null\n /** Active audiences, by name \u2014 powers the name lookup and the switcher. */\n audiences?: TransparencyAudience[]\n /** Mounted CMS API base (e.g. `/api/v1`); the switcher posts to `{apiBase}/kywi/audience`. */\n apiBase?: string\n csrfToken?: string | null\n /**\n * `personalization.transparencyNotice.enabled`. When false the surface never\n * appears on its own; `openTransparencyPanel()` (e.g. a personalizationBadge\n * click) can still open it deliberately.\n */\n enabled?: boolean\n}\n\n// The last state boot handed us, so a programmatic open() can re-render the\n// panel with real data instead of an empty shell.\nlet lastState: TransparencyState | null = null\n// Set by openTransparencyPanel(): render even when the automatic gate says no.\nlet forcedOpen = false\n\n/* \u2500\u2500 storage (never let a blocked/full localStorage break the page) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\nfunction readStorage(key: string): string | null {\n try { return localStorage.getItem(key) } catch { return null }\n}\nfunction writeStorage(key: string, value: string): void {\n try { localStorage.setItem(key, value) } catch { /* private mode / quota */ }\n}\nfunction removeStorage(key: string): void {\n try { localStorage.removeItem(key) } catch { /* private mode / quota */ }\n}\n\n/** True when the surface should render as the pill. Defaults to collapsed. */\nexport function isCollapsed(): boolean {\n if (readStorage(LEGACY_DISMISSED_KEY) === '1') {\n // Migration: a visitor who permanently dismissed the old bar gets the pill,\n // not silence. Rewriting the flag makes the migration a one-time cost.\n removeStorage(LEGACY_DISMISSED_KEY)\n writeStorage(COLLAPSED_KEY, '1')\n return true\n }\n return readStorage(COLLAPSED_KEY) !== '0'\n}\n\nfunction setCollapsed(collapsed: boolean): void {\n writeStorage(COLLAPSED_KEY, collapsed ? '1' : '0')\n}\n\n/* \u2500\u2500 visitor preferences \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n//\n// CONSENT: `kywi_optout` is a `necessary` cookie in the map core owns\n// (`audiences/consent.ts`), so none of the writes below are gated on\n// `hasConsent()` (kywi-cms#91). It records a SUPPRESSION \u2014 \"stop personalizing\n// me\" \u2014 and refusing to remember that until the visitor first accepts cookies\n// would invert the point of the control. Deleting `kywi_audience` is likewise\n// ungated: dropping data never needs permission.\n\nexport function optOut(): void {\n setCookie(COOKIE_NAMES.OPTOUT, '1', 365 * 24 * 3600)\n deleteCookie(COOKIE_NAMES.AUDIENCE)\n}\n\nexport function optIn(): void {\n deleteCookie(COOKIE_NAMES.OPTOUT)\n}\n\n/** Clears the manual audience pin so resolution goes back to signals. */\nexport function resetToAutomatic(): void {\n deleteCookie(COOKIE_NAMES.AUDIENCE)\n}\n\nfunction hasPin(): boolean {\n return readCookies().has(COOKIE_NAMES.AUDIENCE)\n}\n\n/**\n * Pins the visitor to `audienceId` via the public runtime route \u2014 the same\n * endpoint the boot path uses, but marked `source: 'switcher'`. That flag is\n * what tells the server this is the visitor operating a control by hand rather\n * than the runtime remembering something automatically, which is why it is\n * honoured as an `essential` preference and not gated on personalization consent\n * (kywi-cms#91; the same argument as kywi_optout \u2014 see the block above).\n *\n * The server re-validates the id against the active audiences.\n *\n * Returns whether the pin will SURVIVE this page: false when the server declined\n * to store it (an older server that predates `source`, or a refusal), so the\n * caller can say so instead of reloading into an unchanged page.\n */\nexport async function pinAudience(\n audienceId: string,\n opts: { apiBase?: string | undefined; csrfToken?: string | null | undefined } = {},\n): Promise<boolean> {\n const apiBase = opts.apiBase ?? DEFAULT_API_BASE\n try {\n const res = await fetch(`${apiBase}/kywi/audience`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', ...(opts.csrfToken ? { 'X-CSRF-Token': opts.csrfToken } : {}) },\n body: JSON.stringify({ audienceId, source: 'switcher' }),\n })\n if (!res.ok) return false\n const body = (await res.json().catch(() => null)) as { data?: { persisted?: boolean } } | null\n // A server that predates `persisted` omits it; it also predates the gate, so\n // it did store the pin. Absent means stored.\n return body?.data?.persisted !== false\n } catch {\n // Offline / blocked \u2014 nothing was stored, so do not reload into a page that\n // would look unchanged for no stated reason.\n return false\n }\n}\n\n/**\n * Report a pin the server declined to store, in place of the reload that would\n * otherwise follow. Replaces any previous message so repeated attempts do not\n * stack up, and is announced politely for screen readers.\n */\nfunction showSwitcherError(group: HTMLElement): void {\n group.parentElement?.querySelector('.kywi-transparency-switcher-error')?.remove()\n const note = document.createElement('p')\n note.className = 'kywi-transparency-switcher-error'\n note.setAttribute('role', 'status')\n note.textContent = \"Couldn't save that choice. It won't be remembered on the next page.\"\n group.parentElement?.appendChild(note)\n}\n\nfunction reload(): void {\n try { window.location.reload() } catch { /* non-navigating test environments */ }\n}\n\n/* \u2500\u2500 rendering \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\nfunction removeSurface(): void {\n document.getElementById(ROOT_ID)?.remove()\n // Pre-#105 markup, in case a stale bar is still on the page.\n document.querySelector('.kywi-transparency-bar')?.remove()\n}\n\nfunction button(className: string, text: string, onClick: () => void): HTMLButtonElement {\n const btn = document.createElement('button')\n btn.type = 'button'\n btn.className = className\n btn.textContent = text\n btn.addEventListener('click', onClick)\n return btn\n}\n\nfunction shouldRenderAutomatically(state: TransparencyState): boolean {\n if (state.enabled === false) return false\n return (state.isPersonalized && !state.isOptedOut) || (state.isOptedOut && state.hasMatchAvailable)\n}\n\nfunction audienceName(state: TransparencyState): string | null {\n if (!state.audienceId) return null\n return state.audiences?.find((a) => a.id === state.audienceId)?.name ?? null\n}\n\n/**\n * Renders (or removes) the transparency surface for `state`. Safe to call on\n * every boot; it replaces whatever is currently mounted.\n */\nexport function renderTransparencyPanel(state: TransparencyState): void {\n lastState = state\n if (typeof document === 'undefined') return\n removeSurface()\n if (!forcedOpen && !shouldRenderAutomatically(state)) return\n\n const active = state.isPersonalized && !state.isOptedOut\n const collapsed = forcedOpen ? false : isCollapsed()\n\n const root = document.createElement('div')\n root.id = ROOT_ID\n root.className = `kywi-transparency ${active ? 'kywi-transparency-active' : 'kywi-transparency-opted-out'}`\n root.dataset.state = collapsed ? 'collapsed' : 'expanded'\n\n /* pill */\n const pill = document.createElement('button')\n pill.type = 'button'\n pill.className = 'kywi-transparency-pill'\n pill.setAttribute('aria-expanded', String(!collapsed))\n pill.setAttribute('aria-controls', PANEL_ID)\n const pillIcon = document.createElement('span')\n pillIcon.className = 'kywi-transparency-pill-icon'\n pillIcon.setAttribute('aria-hidden', 'true')\n pillIcon.textContent = '\u2726'\n const pillLabel = document.createElement('span')\n pillLabel.className = 'kywi-transparency-pill-label'\n pillLabel.textContent = active ? 'Personalized' : 'Personalization off'\n pill.append(pillIcon, pillLabel)\n pill.addEventListener('click', () => expandPanel())\n pill.hidden = !collapsed\n root.appendChild(pill)\n\n /* panel */\n const panel = document.createElement('section')\n panel.id = PANEL_ID\n panel.className = 'kywi-transparency-panel'\n panel.setAttribute('role', 'dialog')\n panel.setAttribute('aria-labelledby', TITLE_ID)\n panel.hidden = collapsed\n\n const header = document.createElement('div')\n header.className = 'kywi-transparency-header'\n const title = document.createElement('h2')\n title.id = TITLE_ID\n title.className = 'kywi-transparency-title'\n const name = audienceName(state)\n title.textContent = !active\n ? 'Personalization is off'\n : name\n ? 'This page is personalized for:'\n : 'This page is personalized'\n header.appendChild(title)\n const collapseBtn = button('kywi-transparency-collapse', '\u2715', () => collapsePanel())\n collapseBtn.setAttribute('aria-label', 'Collapse')\n header.appendChild(collapseBtn)\n panel.appendChild(header)\n\n if (active) {\n const audience = document.createElement('p')\n audience.className = 'kywi-transparency-audience'\n if (name) {\n const strong = document.createElement('strong')\n strong.textContent = name\n audience.appendChild(strong)\n } else {\n audience.textContent = 'Based on your browsing context.'\n }\n panel.appendChild(audience)\n }\n\n const note = document.createElement('p')\n note.className = 'kywi-transparency-note'\n note.textContent = active\n ? 'Parts of this page are chosen to match this audience. You can switch, reset, or turn personalization off.'\n : 'Personalization is turned off, so you are seeing the default version of this page.'\n panel.appendChild(note)\n\n /* audience switcher \u2014 pointless while opted out (opt-out beats a pin) */\n const options = state.audiences ?? []\n if (active && options.length > 0) {\n const switcher = document.createElement('div')\n switcher.className = 'kywi-transparency-switcher'\n const switcherLabel = document.createElement('span')\n switcherLabel.id = SWITCHER_LABEL_ID\n switcherLabel.className = 'kywi-transparency-switcher-label'\n switcherLabel.textContent = 'See this page as'\n switcher.appendChild(switcherLabel)\n\n const group = document.createElement('div')\n group.className = 'kywi-transparency-options'\n group.setAttribute('role', 'group')\n group.setAttribute('aria-labelledby', SWITCHER_LABEL_ID)\n for (const option of options) {\n const isCurrent = option.id === state.audienceId\n const optionBtn = button('kywi-transparency-option', option.name, () => {\n if (isCurrent) return\n void pinAudience(option.id, { apiBase: state.apiBase, csrfToken: state.csrfToken }).then(\n (persisted) => {\n // Reloading on a pin the server did not store would drop the visitor\n // back on the same page with no explanation \u2014 say what happened\n // instead (kywi-cms#91).\n if (persisted) reload()\n else showSwitcherError(group)\n },\n )\n })\n optionBtn.dataset.kywiAudienceId = option.id\n optionBtn.setAttribute('aria-pressed', String(isCurrent))\n group.appendChild(optionBtn)\n }\n switcher.appendChild(group)\n panel.appendChild(switcher)\n }\n\n /* actions */\n const actions = document.createElement('div')\n actions.className = 'kywi-transparency-actions'\n if (state.isOptedOut) {\n actions.appendChild(button('kywi-transparency-opt-in', 'Enable personalization', () => { optIn(); reload() }))\n } else {\n if (hasPin()) {\n actions.appendChild(button('kywi-transparency-reset', 'Reset to automatic', () => { resetToAutomatic(); reload() }))\n }\n actions.appendChild(button('kywi-transparency-opt-out', 'Turn off personalization', () => { optOut(); reload() }))\n }\n panel.appendChild(actions)\n\n panel.addEventListener('keydown', (e) => {\n if ((e as KeyboardEvent).key === 'Escape') collapsePanel()\n })\n\n root.appendChild(panel)\n document.body.appendChild(root)\n}\n\nfunction currentSurface(): { root: HTMLElement; pill: HTMLElement; panel: HTMLElement } | null {\n const root = document.getElementById(ROOT_ID)\n if (!root) return null\n const pill = root.querySelector<HTMLElement>('.kywi-transparency-pill')\n const panel = root.querySelector<HTMLElement>('.kywi-transparency-panel')\n if (!pill || !panel) return null\n return { root, pill, panel }\n}\n\nfunction setExpanded(expanded: boolean): void {\n const surface = currentSurface()\n if (!surface) return\n surface.root.dataset.state = expanded ? 'expanded' : 'collapsed'\n surface.pill.hidden = expanded\n surface.pill.setAttribute('aria-expanded', String(expanded))\n surface.panel.hidden = !expanded\n const focusTarget = expanded\n ? surface.panel.querySelector<HTMLElement>('.kywi-transparency-collapse')\n : surface.pill\n focusTarget?.focus()\n}\n\n/** Expands the panel and remembers the choice. */\nexport function expandPanel(): void {\n setCollapsed(false)\n setExpanded(true)\n}\n\n/** Collapses back to the pill and remembers the choice \u2014 never full suppression. */\nexport function collapsePanel(): void {\n forcedOpen = false\n setCollapsed(true)\n setExpanded(false)\n}\n\n/**\n * Opens the transparency panel from anywhere \u2014 the programmatic entry point the\n * personalizationBadge module's status pill calls (#107), so opt-out and\n * audience switching live in exactly one surface. Renders the panel even when\n * the automatic gate (or `transparencyNotice.enabled: false`) would hide it,\n * because opening it is an explicit visitor action.\n */\nexport function openTransparencyPanel(): void {\n if (typeof document === 'undefined') return\n forcedOpen = true\n const state: TransparencyState = lastState ?? {\n isPersonalized: false,\n isOptedOut: readCookies().has(COOKIE_NAMES.OPTOUT),\n hasMatchAvailable: false,\n }\n renderTransparencyPanel(state)\n currentSurface()?.panel.querySelector<HTMLElement>('.kywi-transparency-collapse')?.focus()\n}\n\n/** Test seam: forget the cached boot state and any forced-open flag. */\nexport function resetTransparencyState(): void {\n lastState = null\n forcedOpen = false\n}\n", "// packages/kywi-js/src/audience/preview.ts\nimport { COOKIE_NAMES, readCookies, deleteCookie } from '../cookies.js'\n\nconst PREVIEW_KEY = 'kywi_preview_audience'\n\nexport function getPreviewAudienceId(): string | null { return sessionStorage.getItem(PREVIEW_KEY) }\nexport function setPreviewAudienceId(id: string): void { sessionStorage.setItem(PREVIEW_KEY, id) }\nexport function clearPreview(): void { sessionStorage.removeItem(PREVIEW_KEY) }\n\nexport function checkPreviewInit(): void {\n const cookies = readCookies()\n const initId = cookies.get(COOKIE_NAMES.PREVIEW_INIT)\n if (initId) { setPreviewAudienceId(initId); deleteCookie(COOKIE_NAMES.PREVIEW_INIT) }\n}\n\nexport function renderPreviewBanner(audienceName: string): void {\n document.querySelector('.kywi-preview-banner')?.remove()\n const banner = document.createElement('div')\n banner.className = 'kywi-preview-banner'\n banner.setAttribute('role', 'status')\n const text = document.createElement('span')\n text.className = 'kywi-preview-text'\n text.textContent = 'Previewing as: '\n const strong = document.createElement('strong')\n strong.textContent = audienceName\n text.appendChild(strong)\n banner.appendChild(text)\n const exitBtn = document.createElement('button')\n exitBtn.className = 'kywi-preview-exit'\n exitBtn.type = 'button'\n exitBtn.textContent = 'Exit Preview'\n exitBtn.addEventListener('click', () => { clearPreview(); window.location.reload() })\n banner.appendChild(exitBtn)\n document.body.appendChild(banner)\n}\n", "// packages/kywi-js/src/audience/data-layer.ts\nimport type { KywiVisitor } from '../types.js'\n\nexport function populateDataLayer(visitor: KywiVisitor): void {\n const g = ((window as any).kywi ?? {}) as { visitor?: KywiVisitor }\n g.visitor = visitor\n ;(window as any).kywi = g\n}\n\nexport function fireKywiReady(): void { window.dispatchEvent(new CustomEvent('kywiReady')) }\n\nexport interface ServerMeta { audienceId: string | null; experimentId: string | null; variantId: string | null; visitorId: string }\n\nexport function readServerMeta(): ServerMeta {\n const m = (n: string) => document.querySelector(`meta[name=\"kywi:${n}\"]`)?.getAttribute('content') ?? null\n return { audienceId: m('audience-id'), experimentId: m('experiment-id'), variantId: m('variant-id'), visitorId: m('visitor-id') ?? '' }\n}\n", "// packages/kywi-js/src/consent.ts\nimport { readCookies, setCookie, deleteCookie } from './cookies.js'\n\nexport const CONSENT_COOKIE = 'kywi_consent' as const\n\n/** The cookie an audience pin lives in. */\nexport const AUDIENCE_PIN_COOKIE = 'kywi_audience' as const\n\n/**\n * The `kywi_audience` value prefix marking a pin the visitor chose BY HAND in\n * the transparency panel, rather than one the runtime remembered on its own.\n * Mirrors `SWITCHER_PIN_PREFIX` in core's `audiences/consent.ts`.\n */\nexport const SWITCHER_PIN_PREFIX = 'switcher:' as const\n\nexport type ConsentCategory = 'essential' | 'analytics' | 'marketing' | 'personalization'\n\n/**\n * Every cookie the engine writes \u2192 the consent category it requires.\n *\n * A MIRROR of `COOKIE_CONSENT_CATEGORY` in `@kywi-software/core`'s\n * `audiences/consent.ts`, which is the authority. This package cannot import\n * core (neither depends on the other), so the copy exists to let a decision made\n * here REVOKE the cookies it denies without a round trip.\n * `__tests__/consent-parity.test.ts` asserts the two maps are identical.\n */\nexport const COOKIE_CONSENT_CATEGORY: Record<string, ConsentCategory> = {\n kywi_consent: 'essential',\n kywi_optout: 'essential',\n kywi_preview_init: 'essential',\n kywi_signals: 'personalization',\n kywi_visitor: 'personalization',\n kywi_utm: 'personalization',\n kywi_audience: 'personalization',\n kywi_known: 'personalization',\n}\n\n/** Mirrors `ConsentGateOptions` in core's `audiences/consent.ts`. */\nexport interface ConsentGateOptions {\n /**\n * `theme.personalization.requireConsent`. Defaults to true. When explicitly\n * `false` the gate is off and every category reads as allowed \u2014 for a site\n * whose consent is collected by external tooling (kywi-cms#91).\n */\n requireConsent?: boolean\n}\n\nexport interface ConsentState {\n essential: boolean\n analytics: boolean\n marketing: boolean\n personalization: boolean\n}\n\n/**\n * Read the current consent state from the `kywi_consent` cookie.\n * Returns the parsed ConsentState, or null if the cookie is missing or malformed.\n *\n * The payload is NORMALIZED, not trusted: `essential` is forced true and every\n * other category is `=== true` or false. The cookie is client-writable, so it\n * can arrive partial (written by an older version), tampered with, or carrying\n * `\"yes\"` where a boolean belongs. Casting it to `ConsentState` and hoping left\n * callers reading `state.marketing` a truthy string. This mirrors\n * `parseConsentValue` in core's `audiences/consent.ts` exactly, so the client\n * and the server can never disagree about what a given cookie means\n * (`__tests__/consent-parity.test.ts` pins that).\n *\n * Unlike core's version this does NOT decodeURIComponent: `readCookies` has\n * already decoded the value, and decoding twice would corrupt a payload\n * containing a literal `%`.\n */\nexport function getConsentState(): ConsentState | null {\n const cookies = readCookies()\n const raw = cookies.get(CONSENT_COOKIE)\n if (!raw) return null\n\n let parsed: unknown\n try {\n parsed = JSON.parse(raw)\n } catch {\n return null\n }\n if (typeof parsed !== 'object' || parsed === null) return null\n const obj = parsed as Record<string, unknown>\n return {\n essential: true,\n analytics: obj.analytics === true,\n marketing: obj.marketing === true,\n personalization: obj.personalization === true,\n }\n}\n\n/**\n * Set consent preferences. Essential is always forced to true.\n * The cookie is stored with a 1-year Max-Age.\n */\nexport function setConsent(\n prefs: Partial<Omit<ConsentState, 'essential'>>,\n): void {\n const state: ConsentState = {\n essential: true,\n analytics: prefs.analytics ?? false,\n marketing: prefs.marketing ?? false,\n personalization: prefs.personalization ?? false,\n }\n // 1 year = 365 * 24 * 60 * 60 = 31536000 seconds\n setCookie(CONSENT_COOKIE, JSON.stringify(state), 31536000)\n // A decision REVOKES as well as denies \u2014 see clearDeniedCookies. Written\n // first so the consent record survives its own sweep.\n clearDeniedCookies(state)\n}\n\n/**\n * Delete every cookie whose category `state` does not grant, and return the\n * names deleted.\n *\n * Refusing to WRITE new cookies is only half of \"Reject All\": a visitor who\n * accepted last month and rejects today is still carrying a `kywi_visitor` id,\n * and a gate that only blocked writes would keep reading it forever\n * (kywi-cms#91).\n *\n * `kywi_signals` is HttpOnly and cannot be removed from script; the attempt is\n * harmless, the server stops writing it, and it expires on its own 60-second\n * horizon. Core's server-side read paths also ignore personalization cookies\n * whenever the gate denies, so a cookie script cannot clear is inert anyway.\n */\nexport function clearDeniedCookies(state: ConsentState | null): string[] {\n if (typeof document === 'undefined') return []\n const cookies = readCookies()\n const deleted: string[] = []\n for (const [name, category] of Object.entries(COOKIE_CONSENT_CATEGORY)) {\n if (category === 'essential') continue\n if (state && state[category] === true) continue\n if (isDeliberatePreference(name, cookies.get(name))) continue\n deleteCookie(name)\n deleted.push(name)\n }\n return deleted\n}\n\n/**\n * Does this cookie's CURRENT VALUE mark it as an explicit visitor preference\n * that outranks its category?\n *\n * `kywi_audience` is the one cookie whose category depends on how it was\n * written: automatic (\"remember what I resolved\") is `personalization`, but an\n * audience the visitor picked by hand in the transparency panel is `essential`,\n * on the same argument as `kywi_optout`. The category map is static and cannot\n * express that, so the sweep asks the value.\n *\n * Without this, rejecting personalization would delete a choice the visitor made\n * deliberately and which the server is under orders to keep honouring \u2014 the\n * revocation would quietly undo the exemption. Mirrors the same predicate in\n * core's `audiences/consent.ts`.\n */\nfunction isDeliberatePreference(name: string, value: string | undefined): boolean {\n if (name !== AUDIENCE_PIN_COOKIE || value === undefined) return false\n // The prefix alone is not a pin \u2014 `switcher:` with no id decodes to nothing\n // (parseAudiencePin rejects it), so exempting it would strand a cookie nobody\n // will ever honour.\n return value.startsWith(SWITCHER_PIN_PREFIX) && value.length > SWITCHER_PIN_PREFIX.length\n}\n\n/**\n * Check whether a specific consent category has been granted.\n *\n * `essential` is ALWAYS granted, decision or not \u2014 necessary cookies (the\n * consent record itself, the personalization opt-out flag) are never gated, and\n * before kywi-cms#91 that was true only by accident because nothing called this\n * function at all. Every other category needs an explicit `true`, so an\n * undecided visitor grants nothing and an unknown category is denied.\n *\n * `requireConsent: false` (from `theme.personalization.requireConsent`) turns the\n * gate off entirely for a site whose consent is collected by external tooling.\n *\n * The authoritative cookie\u2192category map lives in `@kywi-software/core`'s\n * `audiences/consent.ts` (`COOKIE_CONSENT_CATEGORY`) \u2014 this package cannot\n * import core, so `__tests__/consent-parity.test.ts` asserts the two agree.\n */\nexport function hasConsent(category: ConsentCategory, options: ConsentGateOptions = {}): boolean {\n if (options.requireConsent === false) return true\n if (category === 'essential') return true\n const state = getConsentState()\n if (!state) return false\n return state[category] === true\n}\n", "// packages/kywi-js/src/audience/adapter-runner.ts\nimport type { VisitorSignals } from '@kywi-software/core/audiences/types'\n\ninterface PullAdapter {\n id: string\n side: 'client' | 'server-proxy'\n fields: string[]\n collect: (signals: Partial<VisitorSignals>) => Promise<Record<string, unknown>>\n}\n\nconst CACHE_PREFIX = 'kywi_adapter_'\n\nfunction getCached(adapterId: string): Record<string, unknown> | null {\n const raw = sessionStorage.getItem(CACHE_PREFIX + adapterId)\n if (!raw) return null\n try { return JSON.parse(raw) as Record<string, unknown> } catch { return null }\n}\n\nfunction setCache(adapterId: string, data: Record<string, unknown>): void {\n sessionStorage.setItem(CACHE_PREFIX + adapterId, JSON.stringify(data))\n}\n\nexport async function runClientAdapters(\n adapters: PullAdapter[], currentSignals: Partial<VisitorSignals>,\n apiBase = '/api/v1',\n): Promise<Record<string, unknown>> {\n const merged: Record<string, unknown> = {}\n\n const tasks = adapters.map(async (adapter) => {\n const cached = getCached(adapter.id)\n if (cached) return cached\n\n let result: Record<string, unknown>\n if (adapter.side === 'server-proxy') {\n // The proxy lives in the CMS runtime namespace (`{apiBase}/kywi/adapters/:id`),\n // alongside the audience-assignment and identity routes \u2014 not a bare\n // `/api/kywi/...` path, which would miss a handler mounted at `/api/v1`.\n const res = await fetch(`${apiBase}/kywi/adapters/${adapter.id}`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ signals: currentSignals }),\n })\n const json = await res.json() as { data?: Record<string, unknown> }\n result = json.data ?? {}\n } else {\n result = await adapter.collect(currentSignals)\n }\n\n setCache(adapter.id, result)\n return result\n })\n\n const results = await Promise.all(tasks)\n for (const r of results) Object.assign(merged, r)\n return merged\n}\n", "// packages/kywi-js/src/audience/selfid-submit.ts\nimport { storeSelfId } from './selfid-collector.js'\nimport { evaluateClientSide } from './evaluate.js'\nimport { patchVariantContainers } from './dom-patcher.js'\nimport { populateDataLayer, fireKywiReady, readServerMeta } from './data-layer.js'\nimport { readCookies, COOKIE_NAMES } from '../cookies.js'\nimport type { Audience, VisitorSignals } from '@kywi-software/core/audiences/types'\n\nexport function handleSelfIdSubmit(\n responses: Record<string, string>,\n audiences?: Audience[],\n currentPath?: string,\n): void {\n // 1. Store responses (replaces previous)\n storeSelfId(responses)\n\n // 2. Re-evaluate if audiences provided\n if (audiences && currentPath) {\n const cookies = readCookies()\n const serverMeta = readServerMeta()\n const serverSignals: Partial<VisitorSignals> = {\n identity: { visitorId: serverMeta.visitorId || cookies.get(COOKIE_NAMES.VISITOR) || '', isKnown: cookies.has(COOKIE_NAMES.KNOWN), maLeadId: null },\n meta: { isOptedOut: cookies.has(COOKIE_NAMES.OPTOUT), isPreview: false, previewAudienceId: null, resolvedAt: 'server' },\n }\n const result = evaluateClientSide(audiences, serverSignals, currentPath)\n patchVariantContainers(result.winningAudienceId)\n populateDataLayer({\n visitorId: result.signals.identity.visitorId,\n audienceId: result.winningAudienceId,\n experimentId: serverMeta.experimentId,\n variantId: serverMeta.variantId,\n isPreview: false,\n isOptedOut: result.isOptedOut,\n })\n }\n}\n", "// packages/kywi-js/src/audience/selfid-widget.ts\nimport { storeSelfId } from './selfid-collector.js'\nimport { handleSelfIdSubmit } from './selfid-submit.js'\nimport type { Audience } from '@kywi-software/core/audiences/types'\n\nconst SHOWN_KEY = 'kywi_selfid_shown'\nconst MS_PER_DAY = 86_400_000\n\n/**\n * Display modes that render their own explicit dismiss control (#97, #106).\n * Every overlay mode is dismissible \u2014 an overlay a visitor cannot close is a\n * trap. Only `inline` is excluded: it is part of the host's page content and\n * has no viewport-covering surface to escape from.\n */\nconst DISMISSIBLE_MODES = new Set(['modal', 'slide-in', 'hello_bar_top', 'hello_bar_bottom', 'drawer'])\n\n/** Modes that float over the page and therefore close themselves on submit. */\nconst OVERLAY_MODES = DISMISSIBLE_MODES\n\n/**\n * Host mount contract for `inline` mode (#106): when the page contains an\n * element matching this selector, the widget renders INTO it instead of into\n * the runtime's own `#kywi-selfid-container` (which is appended to the end of\n * `<body>`, i.e. after the footer). Hosts opt in by putting\n * `<div data-kywi-selfid-mount></div>` wherever the card belongs.\n */\nexport const SELFID_MOUNT_SELECTOR = '[data-kywi-selfid-mount]'\n\n/** Body class + custom property published while a top hello bar is on screen. */\nexport const HELLO_BAR_TOP_CLASS = 'kywi-selfid-hello-bar-top-open'\nconst HELLO_BAR_HEIGHT_PROPERTY = '--kywi-selfid-hello-bar-height'\n\nconst MODAL_LABEL_ID = 'kywi-selfid-headline'\n\n/**\n * The host slot for `inline` mode, when one exists. Returns null for every\n * other display mode \u2014 modal/drawer/hello-bar/slide-in position themselves\n * against the viewport, so a content slot would be meaningless for them.\n */\nexport function resolveSelfIdMount(displayMode?: SelfIdWidgetConfig['displayMode']): HTMLElement | null {\n if (typeof document === 'undefined') return null\n if (displayMode && displayMode !== 'inline') return null\n return document.querySelector<HTMLElement>(SELFID_MOUNT_SELECTOR)\n}\n\n/** A picklist option: the value submitted, and the label shown to the visitor. */\nexport interface SelfIdFieldOption {\n value: string\n label: string\n}\n\nexport interface SelfIdFieldConfig {\n id: string\n label: string\n type: 'text' | 'select' | 'email'\n /**\n * Either `{value, label}` pairs (current server shape \u2014 see\n * resolveSelfIdWidget in the create-kywi-app scaffold) or bare strings (older\n * config shape, kept for backward compatibility). A bare string is treated as\n * both its own value and its own label.\n */\n options?: Array<string | SelfIdFieldOption>\n required?: boolean\n}\n\n/** Normalize a picklist option to `{value, label}`, treating a bare string as both. */\nfunction normalizeOption(opt: string | SelfIdFieldOption): SelfIdFieldOption {\n return typeof opt === 'string' ? { value: opt, label: opt } : opt\n}\n\n/** When the widget is shown, once frequency allows. Defaults to immediate. */\nexport type SelfIdWidgetTrigger =\n | { type: 'immediate' }\n | { type: 'scroll'; depth: number }\n | { type: 'delay'; seconds: number }\n | { type: 'manual' }\n\nexport interface SelfIdWidgetConfig {\n fields: SelfIdFieldConfig[]\n headline?: string\n subheadline?: string\n submitLabel?: string\n skipLabel?: string\n displayMode?: 'modal' | 'inline' | 'slide-in' | 'hello_bar_top' | 'hello_bar_bottom' | 'drawer'\n frequency?: 'once' | 'session' | 'daily' | 'always'\n trigger?: SelfIdWidgetTrigger\n /**\n * The full audience rule definitions to re-evaluate client-side after a\n * submission. These flow straight into `handleSelfIdSubmit` \u2192\n * `evaluateClientSide` \u2192 `resolveAudiences`, which iterates each audience's\n * `conditionGroups`, so full `Audience` objects are required (not id strings).\n */\n audiences?: Audience[]\n currentPath?: string\n}\n\nexport interface SelfIdWidget {\n shouldShow(): boolean\n markShown(): void\n render(container: HTMLElement): void\n handleSubmit(responses: Record<string, string>): void\n}\n\nconst FOCUSABLE_SELECTOR =\n 'button:not([disabled]), [href], input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex=\"-1\"])'\n\nfunction focusableElements(root: HTMLElement): HTMLElement[] {\n return Array.from(root.querySelectorAll<HTMLElement>(FOCUSABLE_SELECTOR)).filter((el) => !el.hidden)\n}\n\n/** Keeps Tab inside `root`, wrapping at both ends. */\nfunction trapTab(root: HTMLElement, e: KeyboardEvent): void {\n const focusable = focusableElements(root)\n if (focusable.length === 0) {\n e.preventDefault()\n root.focus()\n return\n }\n const first = focusable[0]!\n const last = focusable[focusable.length - 1]!\n const active = document.activeElement as HTMLElement | null\n const inside = active !== null && root.contains(active) && active !== root\n if (e.shiftKey) {\n if (!inside || active === first) { e.preventDefault(); last.focus() }\n } else if (!inside || active === last) {\n e.preventDefault()\n first.focus()\n }\n}\n\nexport function createSelfIdWidget(config: SelfIdWidgetConfig): SelfIdWidget {\n const frequency = config.frequency ?? 'once'\n const displayMode = config.displayMode ?? 'inline'\n const isModal = displayMode === 'modal'\n\n /** Undo callbacks for everything render() attached outside the container. */\n let cleanups: Array<() => void> = []\n\n function shouldShow(): boolean {\n if (frequency === 'always') return true\n if (frequency === 'session') return false\n\n const raw = localStorage.getItem(SHOWN_KEY)\n if (!raw) return true\n\n if (frequency === 'once') return false\n\n // 'daily' \u2014 true if more than 24h since last shown\n const ts = Number(raw)\n return !isNaN(ts) && Date.now() - ts > MS_PER_DAY\n }\n\n function markShown(): void {\n localStorage.setItem(SHOWN_KEY, String(Date.now()))\n }\n\n function handleSubmit(responses: Record<string, string>): void {\n storeSelfId(responses)\n // Attempt the immediate variant swap, but never let a personalization/resolver\n // error skip markShown() \u2014 otherwise a `frequency: 'once'` widget re-shows on\n // every subsequent page load forever (issue #80). The `finally` keeps the\n // frequency gate honored even if `handleSelfIdSubmit` throws.\n try {\n handleSelfIdSubmit(responses, config.audiences ?? [], config.currentPath ?? '/')\n } finally {\n markShown()\n }\n }\n\n /** Removes the widget and everything it attached to the document. */\n function teardown(container: HTMLElement): void {\n for (const undo of cleanups) undo()\n cleanups = []\n container.replaceChildren()\n container.className = ''\n container.removeAttribute('role')\n container.removeAttribute('aria-modal')\n container.removeAttribute('aria-label')\n container.removeAttribute('aria-labelledby')\n container.removeAttribute('tabindex')\n container.hidden = true\n }\n\n /**\n * Visitor closed the widget without answering. Dismissing honors the\n * frequency gate exactly like skipping does, so a `once` widget does not\n * reappear on the next page.\n */\n function dismiss(container: HTMLElement): void {\n markShown()\n teardown(container)\n }\n\n function render(container: HTMLElement): void {\n for (const undo of cleanups) undo()\n cleanups = []\n container.replaceChildren()\n container.hidden = false\n // Style hook uses kebab-case (kywi-selfid-hello-bar-top) even though the\n // config value is snake_case (hello_bar_top), matching the existing\n // kywi-selfid-slide-in convention.\n const displayModeClass = displayMode.replace(/_/g, '-')\n container.className = `kywi-selfid-widget kywi-selfid-${displayModeClass}`\n\n if (isModal) {\n // A real backdrop ELEMENT (the old ::before pseudo could not be clicked):\n // clicking it dismisses without submitting.\n const backdrop = document.createElement('div')\n backdrop.className = 'kywi-selfid-backdrop'\n backdrop.addEventListener('click', () => dismiss(container))\n ;(container.parentElement ?? document.body).appendChild(backdrop)\n cleanups.push(() => backdrop.remove())\n\n const previousOverflow = document.body.style.overflow\n document.body.style.overflow = 'hidden'\n cleanups.push(() => { document.body.style.overflow = previousOverflow })\n\n const onKeydown = (e: KeyboardEvent) => {\n if (e.key === 'Escape') { e.preventDefault(); dismiss(container); return }\n if (e.key === 'Tab') trapTab(container, e)\n }\n document.addEventListener('keydown', onKeydown, true)\n cleanups.push(() => document.removeEventListener('keydown', onKeydown, true))\n\n container.setAttribute('role', 'dialog')\n container.setAttribute('aria-modal', 'true')\n if (config.headline) {\n container.setAttribute('aria-labelledby', MODAL_LABEL_ID)\n } else {\n container.setAttribute('aria-label', 'Personalize your experience')\n }\n container.tabIndex = -1\n }\n\n if (displayMode === 'hello_bar_top') {\n // Offset hook (#106): hosts push their own content down with\n // `body.kywi-selfid-hello-bar-top-open` and/or the measured height in\n // `--kywi-selfid-hello-bar-height` \u2014 no hardcoded header height anywhere.\n document.body.classList.add(HELLO_BAR_TOP_CLASS)\n const syncHeight = () => {\n document.documentElement.style.setProperty(HELLO_BAR_HEIGHT_PROPERTY, `${container.offsetHeight}px`)\n }\n window.addEventListener('resize', syncHeight)\n cleanups.push(() => {\n document.body.classList.remove(HELLO_BAR_TOP_CLASS)\n document.documentElement.style.removeProperty(HELLO_BAR_HEIGHT_PROPERTY)\n window.removeEventListener('resize', syncHeight)\n })\n // Measured once the card's children exist (i.e. after render() returns).\n queueMicrotask(syncHeight)\n }\n\n if (DISMISSIBLE_MODES.has(displayMode)) {\n const dismissBtn = document.createElement('button')\n dismissBtn.type = 'button'\n dismissBtn.className = 'kywi-selfid-dismiss'\n dismissBtn.setAttribute('aria-label', 'Dismiss')\n dismissBtn.textContent = '\u00D7'\n dismissBtn.addEventListener('click', () => dismiss(container))\n container.appendChild(dismissBtn)\n }\n\n if (config.headline) {\n const h = document.createElement('h3')\n h.className = 'kywi-selfid-headline'\n h.textContent = config.headline\n if (isModal) h.id = MODAL_LABEL_ID\n container.appendChild(h)\n }\n\n if (config.subheadline) {\n const p = document.createElement('p')\n p.className = 'kywi-selfid-subheadline'\n p.textContent = config.subheadline\n container.appendChild(p)\n }\n\n const form = document.createElement('form')\n form.className = 'kywi-selfid-form'\n\n for (const field of config.fields) {\n const fieldWrap = document.createElement('div')\n fieldWrap.className = 'kywi-selfid-field'\n\n const label = document.createElement('label')\n label.htmlFor = `kywi-field-${field.id}`\n label.textContent = field.label\n fieldWrap.appendChild(label)\n\n let input: HTMLInputElement | HTMLSelectElement\n\n if (field.type === 'select') {\n const select = document.createElement('select')\n select.id = `kywi-field-${field.id}`\n select.name = field.id\n if (field.required) select.required = true\n if (field.options) {\n const placeholder = document.createElement('option')\n placeholder.value = ''\n placeholder.textContent = ''\n select.appendChild(placeholder)\n for (const opt of field.options) {\n const { value, label } = normalizeOption(opt)\n const option = document.createElement('option')\n option.value = value\n option.textContent = label\n select.appendChild(option)\n }\n }\n input = select\n } else {\n const inp = document.createElement('input')\n inp.id = `kywi-field-${field.id}`\n inp.name = field.id\n inp.type = field.type\n if (field.required) inp.required = true\n input = inp\n }\n\n fieldWrap.appendChild(input)\n form.appendChild(fieldWrap)\n }\n\n const actions = document.createElement('div')\n actions.className = 'kywi-selfid-actions'\n\n const submitBtn = document.createElement('button')\n submitBtn.type = 'submit'\n submitBtn.className = 'kywi-selfid-submit'\n submitBtn.textContent = config.submitLabel ?? 'Submit'\n actions.appendChild(submitBtn)\n\n if (config.skipLabel) {\n const skipBtn = document.createElement('button')\n skipBtn.type = 'button'\n skipBtn.className = 'kywi-selfid-skip'\n skipBtn.textContent = config.skipLabel\n skipBtn.addEventListener('click', () => dismiss(container))\n actions.appendChild(skipBtn)\n }\n\n form.appendChild(actions)\n\n form.addEventListener('submit', (e) => {\n e.preventDefault()\n const data = new FormData(form)\n const responses: Record<string, string> = {}\n data.forEach((value, key) => {\n responses[key] = String(value)\n })\n handleSubmit(responses)\n // An overlay must not stay on screen over the freshly personalized page \u2014\n // and for the modal, leaving it up would also leave the scroll lock on.\n if (OVERLAY_MODES.has(displayMode)) teardown(container)\n })\n\n container.appendChild(form)\n\n if (isModal) container.focus()\n }\n\n return { shouldShow, markShown, render, handleSubmit }\n}\n", "// packages/kywi-js/src/personalization/badge.ts\n//\n// The `personalizationBadge` layout module's runtime half (#107). It is a\n// PASSIVE status pill \u2014 \"Personalization active/off\", plus the audience name\n// when there is one \u2014 that opens the global transparency panel on click. It\n// deliberately embeds no opt-in/opt-out controls of its own: opting out and\n// switching audience live in exactly one surface (see audience/transparency.ts),\n// so a badge placed on a page can never disagree with the global panel.\n//\n// CONSENT: the badge writes no cookies at all \u2014 it only READS `kywi_optout` to\n// choose its label, and reads need no consent. The panel it opens writes only\n// `necessary`-category preference cookies, so nothing here is gated on\n// `hasConsent()` (kywi-cms#91). Issue #91 listed this file as an ungated\n// `kywi_optout` writer; that write had already moved to\n// `audience/transparency.ts` by the time the fix landed.\n\nimport { COOKIE_NAMES, readCookies } from '../cookies.js'\nimport { openTransparencyPanel } from '../audience/transparency.js'\n\nexport interface PersonalizationBadgeInfo {\n /** The audience the page was personalized for, if any. */\n audienceName?: string | null\n}\n\nfunction isOptedOut(): boolean {\n return readCookies().has(COOKIE_NAMES.OPTOUT)\n}\n\nexport function renderPersonalizationBadge(info: PersonalizationBadgeInfo = {}): void {\n const containers = document.querySelectorAll('[data-kywi-personalization-badge]')\n if (containers.length === 0) return\n\n const optedOut = isOptedOut()\n\n for (const container of Array.from(containers)) {\n // Clear existing badge to avoid duplication\n container.querySelector('.kywi-personalization-badge')?.remove()\n\n const badge = document.createElement('div')\n badge.className = 'kywi-personalization-badge'\n\n const trigger = document.createElement('button')\n trigger.type = 'button'\n trigger.className = 'kywi-badge-open'\n trigger.setAttribute('aria-label', 'Personalization details')\n\n const status = document.createElement('span')\n status.className = 'kywi-badge-status'\n status.textContent = optedOut ? 'Personalization off' : 'Personalization active'\n trigger.appendChild(status)\n\n if (!optedOut && info.audienceName) {\n const audience = document.createElement('span')\n audience.className = 'kywi-badge-audience'\n audience.textContent = info.audienceName\n trigger.appendChild(audience)\n }\n\n trigger.addEventListener('click', () => openTransparencyPanel())\n badge.appendChild(trigger)\n container.appendChild(badge)\n }\n}\n", "// packages/kywi-js/src/audience/index.ts\nimport type { Audience, VisitorSignals } from '@kywi-software/core/audiences/types'\nimport { evaluateClientSide } from './evaluate.js'\nimport { patchVariantContainers } from './dom-patcher.js'\nimport { renderTransparencyPanel } from './transparency.js'\nimport { checkPreviewInit, getPreviewAudienceId, renderPreviewBanner } from './preview.js'\nimport { populateDataLayer, fireKywiReady, readServerMeta } from './data-layer.js'\nimport { readCookies, COOKIE_NAMES } from '../cookies.js'\nimport { hasConsent, type ConsentGateOptions } from '../consent.js'\nimport { runClientAdapters } from './adapter-runner.js'\nimport { Kywi } from '../index.js'\nimport { createSelfIdWidget, resolveSelfIdMount, type SelfIdWidgetConfig } from './selfid-widget.js'\nimport { renderPersonalizationBadge } from '../personalization/badge.js'\n\nexport interface AudienceBootConfig {\n audiences: Audience[]\n currentPath: string\n pageCategory?: string\n serverSignals?: Partial<VisitorSignals>\n adapters?: Array<{ id: string; side: 'client' | 'server-proxy'; fields: string[]; collect: (s: Partial<VisitorSignals>) => Promise<Record<string, unknown>> }>\n selfIdWidget?: SelfIdWidgetConfig\n /**\n * Base path of the mounted CMS API (e.g. `/api/v1`). Runtime calls\n * (server-proxy adapters, audience assignment) are made under `{apiBase}/kywi/*`.\n * Defaults to the same `{basePath}/api/v1` derivation the entity/feed factories\n * use, so it matches the reference app's mount out of the box.\n */\n apiBase?: string\n /**\n * `personalization.transparencyNotice.enabled`. When false, the global\n * transparency surface never appears on its own \u2014 the visitor can still open\n * it deliberately through a `personalizationBadge` module. Defaults to true;\n * the host page passes the resolved config value through.\n */\n transparencyNotice?: boolean\n /**\n * `theme.personalization.requireConsent` (kywi-cms#91). Defaults to true \u2014\n * the resolved audience is only remembered for a visitor who consented to\n * personalization. `false` turns that gate off for a site whose consent is\n * collected by external tooling; the server applies the same flag, so the two\n * must agree or the request is made and then refused persistence.\n */\n requireConsent?: boolean\n}\n\n/** The mounted CMS API base \u2014 mirrors the derivation in Kywi.boot(). */\nfunction resolveApiBase(explicit?: string): string {\n if (explicit) return explicit\n const basePath = Kywi.context?.basePath\n if (!basePath || basePath === '/') return '/api/v1'\n return `${basePath}/api/v1`\n}\n\nexport async function bootAudienceEngine(config: AudienceBootConfig): Promise<void> {\n const { audiences, currentPath, pageCategory, serverSignals } = config\n const apiBase = resolveApiBase(config.apiBase)\n checkPreviewInit()\n const serverMeta = readServerMeta()\n const previewId = getPreviewAudienceId()\n\n // Audiences the visitor can be shown as \u2014 the switcher's option list and the\n // lookup that turns a resolved audience id into a name the visitor recognizes.\n const activeAudiences = audiences\n .filter((a) => a.status === 'active')\n .map((a) => ({ id: a.id, name: a.name }))\n const nameOf = (id: string | null): string | null =>\n id ? activeAudiences.find((a) => a.id === id)?.name ?? null : null\n\n if (previewId) {\n patchVariantContainers(previewId)\n // Show the audience's name, falling back to the raw id for an audience that\n // is not in this page's list (e.g. previewing a draft audience).\n renderPreviewBanner(nameOf(previewId) ?? previewId)\n renderPersonalizationBadge({ audienceName: nameOf(previewId) })\n populateDataLayer({ visitorId: serverMeta.visitorId, audienceId: previewId, experimentId: null, variantId: null, isPreview: true, isOptedOut: false })\n fireKywiReady()\n return\n }\n\n const cookies = readCookies()\n // `theme.personalization.requireConsent` for this page, resolved once and used\n // by every gate below. Built conditionally: under `exactOptionalPropertyTypes`\n // an explicit `undefined` is not the same as an absent key.\n const consentGate: ConsentGateOptions =\n config.requireConsent === undefined ? {} : { requireConsent: config.requireConsent }\n const mayPersonalize = hasConsent('personalization', consentGate)\n\n // `kywi_visitor` and `kywi_known` are `personalization` cookies, so the client\n // reads them through the same gate the server does (kywi-cms#91). Without it\n // this fallback quietly undid the server's own gating: the server hands an\n // unconsented visitor a throwaway id precisely so they stop being recognised,\n // and boot would then recognise them anyway off a cookie left over from before\n // they were ever asked. (`clearDeniedCookies` sweeps these on an explicit\n // Reject, but never for a visitor who simply has not answered.)\n //\n // `serverMeta.visitorId` is NOT gated: the server already applied the rule\n // when it chose what to stamp into the page, and that id describes THIS\n // request rather than stored tracking. `kywi_optout` is `essential` and is\n // never gated either \u2014 a suppression preference outranks the gate by design.\n const storedVisitorId = mayPersonalize ? cookies.get(COOKIE_NAMES.VISITOR) : undefined\n let merged: Partial<VisitorSignals> = {\n ...serverSignals,\n identity: { visitorId: serverMeta.visitorId || storedVisitorId || '', isKnown: mayPersonalize && cookies.has(COOKIE_NAMES.KNOWN), maLeadId: null },\n meta: { isOptedOut: cookies.has(COOKIE_NAMES.OPTOUT), isPreview: false, previewAudienceId: null, resolvedAt: 'server' },\n }\n\n // Run client-side adapters if configured\n if (config.adapters && config.adapters.length > 0) {\n const adapterResults = await runClientAdapters(config.adapters, merged, apiBase)\n merged.adapters = { ...merged.adapters, ...adapterResults }\n }\n\n const result = evaluateClientSide(audiences, merged, currentPath, pageCategory)\n\n // A manual pin (kywi_audience \u2014 written by the transparency panel's switcher,\n // or by this boot path persisting a client-resolved audience) beats signal\n // resolution, matching the server engine's precedence. Without this the client\n // would re-resolve from signals and immediately overwrite the visitor's own\n // choice on the very next page view.\n // The cookie records HOW it was pinned (`switcher:<id>` for a hand-picked\n // audience, a bare id for an automatic one) so the SERVER can apply the right\n // consent rule \u2014 see core's parseAudiencePin. Here only the id matters.\n const rawPin = cookies.get(COOKIE_NAMES.AUDIENCE) ?? null\n const pinnedId = rawPin?.startsWith('switcher:') ? rawPin.slice('switcher:'.length) || null : rawPin\n const isPinned = !result.isOptedOut && pinnedId !== null && activeAudiences.some((a) => a.id === pinnedId)\n const effectiveAudienceId = isPinned ? pinnedId : result.winningAudienceId\n\n patchVariantContainers(effectiveAudienceId)\n renderTransparencyPanel({\n isPersonalized: effectiveAudienceId !== null,\n isOptedOut: result.isOptedOut,\n hasMatchAvailable: result.allMatchedIds.length > 0,\n audienceId: effectiveAudienceId,\n audiences: activeAudiences,\n apiBase,\n csrfToken: Kywi.context?.csrfToken ?? null,\n enabled: config.transparencyNotice !== false,\n })\n // Render the status pill wherever a personalizationBadge module was placed.\n renderPersonalizationBadge({ audienceName: nameOf(effectiveAudienceId) })\n populateDataLayer({ visitorId: result.signals.identity.visitorId, audienceId: effectiveAudienceId, experimentId: serverMeta.experimentId, variantId: serverMeta.variantId, isPreview: false, isOptedOut: result.isOptedOut })\n fireKywiReady()\n\n // Remember the client-resolved audience so the NEXT server render can use it \u2014\n // but only for a visitor who consented to personalization. `kywi_audience` is\n // a `personalization` cookie in core's map, and unlike the transparency\n // panel's switcher this write is automatic, which is exactly the tracking\n // consent governs (kywi-cms#91). Without consent the swap above still happens\n // for this page; it simply is not remembered. The server enforces the same\n // rule, so this only avoids a request that would be refused persistence.\n if (!isPinned && mayPersonalize && result.winningAudienceId && result.winningAudienceId !== serverMeta.audienceId) {\n fetch(`${apiBase}/kywi/audience`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', ...(Kywi.context?.csrfToken ? { 'X-CSRF-Token': Kywi.context.csrfToken } : {}) },\n // Explicitly the AUTOMATIC write, so the server applies the\n // personalization gate to it (kywi-cms#91). The transparency panel's\n // hand-operated switcher sends source:'switcher' and is exempt.\n body: JSON.stringify({ audienceId: result.winningAudienceId, source: 'boot' }),\n }).catch(() => {})\n }\n\n // Self-ID widget \u2014 mount when frequency allows, timed by its trigger.\n if (config.selfIdWidget) {\n const widget = createSelfIdWidget(config.selfIdWidget)\n if (widget.shouldShow()) {\n const doRender = () => {\n // Inline mode renders into the host's own slot when one is declared\n // ([data-kywi-selfid-mount]); otherwise it falls back to the runtime's\n // container at the end of <body>, like the overlay modes.\n const mount = resolveSelfIdMount(config.selfIdWidget!.displayMode)\n if (mount) { widget.render(mount); return }\n const existing = document.getElementById('kywi-selfid-container')\n const container = existing ?? document.createElement('div')\n if (!container.id) {\n container.id = 'kywi-selfid-container'\n document.body.appendChild(container)\n }\n widget.render(container)\n }\n scheduleSelfIdWidget(config.selfIdWidget.trigger, doRender)\n }\n }\n}\n\n/**\n * Show the self-ID widget per its trigger: immediately, after a delay, or once\n * the visitor scrolls past a depth fraction (0-1). `manual` never auto-shows \u2014\n * the host page decides. An absent trigger behaves as immediate.\n */\nfunction scheduleSelfIdWidget(trigger: SelfIdWidgetConfig['trigger'], render: () => void): void {\n if (!trigger || trigger.type === 'immediate') { render(); return }\n if (trigger.type === 'manual') return\n if (trigger.type === 'delay') {\n window.setTimeout(render, Math.max(0, trigger.seconds) * 1000)\n return\n }\n // scroll: fire once when scrolled past `depth` (fraction of scrollable height).\n const target = Math.min(Math.max(trigger.depth, 0), 1)\n let fired = false\n const onScroll = () => {\n if (fired) return\n const scrollable = document.documentElement.scrollHeight - window.innerHeight\n const progress = scrollable > 0 ? window.scrollY / scrollable : 1\n if (progress >= target) {\n fired = true\n window.removeEventListener('scroll', onScroll)\n render()\n }\n }\n window.addEventListener('scroll', onScroll, { passive: true })\n onScroll()\n}\n", "// packages/kywi-js/src/index.ts\nimport type { KywiContext, KywiModuleDefinition } from './types.js'\nimport { readContext } from './context.js'\nimport { createEntityFactory } from './entity.js'\nimport { createFeedFactory } from './feed.js'\nimport { renderFeed } from './render-feed.js'\nimport { initModules } from './module-system.js'\nimport { initFormHooks } from './form-hooks.js'\nimport { initNavMenus } from './nav/menu.js'\nimport { bootExperiments } from './experiments/index.js'\n\ntype PreInitCallback = (kywi: typeof Kywi) => void\nconst preInitCallbacks: PreInitCallback[] = []\nlet booted = false\n\nexport const Kywi = {\n version: '0.1.0',\n context: null as unknown as KywiContext,\n Module: {} as Record<string, KywiModuleDefinition>,\n preInit(fn: PreInitCallback): void {\n if (booted) { fn(Kywi) } else { preInitCallbacks.push(fn) }\n },\n UI: {\n extend(definition: KywiModuleDefinition): KywiModuleDefinition {\n return { ...definition }\n },\n },\n DisplayObject: {\n Form: {\n _hooks: {} as Record<string, (...args: unknown[]) => unknown>,\n extend(methods: Record<string, (...args: unknown[]) => unknown>): void {\n Object.assign(Kywi.DisplayObject.Form._hooks, methods)\n },\n },\n },\n getEntity: null as unknown as (type: string) => any,\n getFeed: null as unknown as (type: string) => any,\n renderFeed: null as unknown as (options: any) => void,\n boot(): void {\n if (booted) return\n Kywi.context = readContext()\n for (const fn of preInitCallbacks) { fn(Kywi) }\n const basePath = Kywi.context.basePath === '/' ? '/api/v1' : `${Kywi.context.basePath}/api/v1`\n Kywi.getEntity = createEntityFactory(basePath, Kywi.context.csrfToken)\n Kywi.getFeed = createFeedFactory(basePath, Kywi.context.csrfToken)\n Kywi.renderFeed = renderFeed\n initModules()\n initFormHooks()\n // Nav modules render fully usable without JS; this layers hover intent,\n // Escape/arrow keys and viewport-edge flipping on top (kywi-cms#111).\n initNavMenus()\n // A/B experiment beacons: confirm exposure of the server-rendered variant\n // and wire conversion signals (pageview / form-submit / kywi.track).\n bootExperiments({ context: Kywi.context })\n booted = true\n },\n}\n\nif (typeof globalThis !== 'undefined') { (globalThis as any).Kywi = Kywi }\nif (typeof document !== 'undefined') {\n if (document.readyState === 'loading') {\n document.addEventListener('DOMContentLoaded', () => Kywi.boot())\n } else {\n Kywi.boot()\n }\n}\n\nexport { bootAudienceEngine } from './audience/index.js'\nexport {\n openTransparencyPanel,\n renderTransparencyPanel,\n type TransparencyState,\n type TransparencyAudience,\n} from './audience/transparency.js'\nexport { bootExperiments, type ExperimentRuntime, type ConversionSignal } from './experiments/index.js'\nexport { initNavMenus, enhanceNav, NAV_BREAKPOINT } from './nav/menu.js'\n// Cookie consent (kywi-cms#91). The shipped `cookieConsent` layout module drives\n// this itself; these are exported so a site with its OWN banner can record a\n// decision the engine's gated cookie writes will honour.\nexport {\n getConsentState,\n setConsent,\n hasConsent,\n clearDeniedCookies,\n COOKIE_CONSENT_CATEGORY,\n CONSENT_COOKIE,\n AUDIENCE_PIN_COOKIE,\n SWITCHER_PIN_PREFIX,\n type ConsentCategory,\n type ConsentGateOptions,\n type ConsentState,\n} from './consent.js'\n"],
|
|
5
|
-
"mappings": ";AAGO,SAAS,cAA2B;AACzC,QAAM,OAAO,CAAC,SAAyB;AACrC,QAAI,OAAO,aAAa,YAAa,QAAO;AAC5C,UAAM,KAAK,SAAS,cAAc,mBAAmB,IAAI,IAAI;AAC7D,WAAO,IAAI,aAAa,SAAS,KAAK;AAAA,EACxC;AACA,SAAO;AAAA,IACL,QAAQ,KAAK,SAAS,KAAK,UAAU,iBAAiB,aAAa,gBAAgB,KAAK;AAAA,IACxF,QAAQ,UAAU,iBAAiB,QAAQ;AAAA,IAC3C,WAAW,KAAK,YAAY,KAAK;AAAA,IACjC,UAAU,KAAK,WAAW,KAAK;AAAA,IAC/B,WAAW,KAAK,YAAY,KAAK;AAAA,IACjC,YAAY,UAAU,MAAM,WAAW,SAAS,gBAAgB,KAAK;AAAA,EACvE;AACF;;;ACdA,SAAS,aAAa,MAAc,UAAkB,WAAmB,MAAiD;AACxH,QAAM,SAAS,EAAE,GAAG,KAAK;AACzB,QAAM,QAAQ,oBAAI,IAAqB;AACvC,QAAM,SAAmB,CAAC;AAC1B,QAAM,UAAkC,EAAE,gBAAgB,mBAAmB;AAC7E,MAAI,UAAW,SAAQ,cAAc,IAAI;AAEzC,QAAM,SAA2B;AAAA,IAC/B,IAAI,OAAe;AAAE,aAAO,OAAO,KAAK;AAAA,IAAE;AAAA,IAC1C,IAAI,OAAe,OAAgB;AAAE,aAAO,KAAK,IAAI;AAAO,YAAM,IAAI,OAAO,KAAK;AAAG,aAAO;AAAA,IAAO;AAAA,IACnG,MAAM,OAAO;AACX,YAAM,KAAK,OAAO,IAAI;AACtB,YAAM,MAAM,KAAK,GAAG,QAAQ,YAAY,IAAI,IAAI,EAAE,KAAK,GAAG,QAAQ,YAAY,IAAI;AAClF,YAAM,MAAM,MAAM,MAAM,KAAK,EAAE,QAAQ,KAAK,QAAQ,QAAQ,SAAS,MAAM,KAAK,UAAU,OAAO,YAAY,KAAK,CAAC,EAAE,CAAC;AACtH,YAAM,OAAO,MAAM,IAAI,KAAK;AAC5B,UAAI,CAAC,IAAI,MAAM,KAAK,OAAO;AAAE,eAAO,KAAK,KAAK,OAAO,WAAW,aAAa;AAAG,cAAM;AAAA,MAAO;AAC7F,UAAI,KAAK,KAAM,QAAO,OAAO,QAAQ,KAAK,IAAI;AAC9C,YAAM,MAAM;AACZ,aAAO;AAAA,IACT;AAAA,IACA,MAAM,SAAS;AACb,YAAM,KAAK,OAAO,IAAI;AACtB,YAAM,MAAM,GAAG,QAAQ,YAAY,IAAI,IAAI,EAAE,IAAI,EAAE,QAAQ,UAAU,QAAQ,CAAC;AAAA,IAChF;AAAA,IACA,YAAY;AAAE,aAAO,CAAC,GAAG,MAAM;AAAA,IAAE;AAAA,IACjC,MAAM,OAAO,OAAe,OAAe;AACzC,YAAM,MAAM,UAAU,OAAO,GAAG,QAAQ,YAAY,IAAI,IAAI,KAAK,KAAK,GAAG,QAAQ,YAAY,IAAI,IAAI,KAAK,IAAI,mBAAmB,KAAK,CAAC;AACvI,YAAM,MAAM,MAAM,MAAM,KAAK,EAAE,QAAQ,CAAC;AACxC,YAAM,OAAO,MAAM,IAAI,KAAK;AAC5B,UAAI,CAAC,IAAI,MAAM,KAAK,OAAO;AAAE,eAAO,KAAK,KAAK,OAAO,WAAW,aAAa;AAAG,cAAM;AAAA,MAAO;AAC7F,UAAI,KAAK,KAAM,QAAO,OAAO,QAAQ,KAAK,IAAI;AAC9C,aAAO;AAAA,IACT;AAAA,EACF;AACA,SAAO;AACT;AAEO,SAAS,oBAAoB,UAAkB,WAAmB;AACvE,SAAO,CAAC,SAAmC,aAAa,MAAM,UAAU,WAAW,CAAC,CAAC;AACvF;;;AClCA,IAAM,cAAN,MAAkB;AAAA,EAChB,YAAoB,SAA8B,OAAuB,MAAoB;AAAzE;AAA8B;AAAuB;AAAA,EAAqB;AAAA,EACtF,IAAI,IAAY,OAAiB;AAAE,SAAK,QAAQ,SAAS,KAAK,EAAE,OAAO,KAAK,OAAO,UAAU,IAAI,OAAO,aAAa,KAAK,KAAK,CAAC;AAAG,WAAO,KAAK;AAAA,EAAQ;AAAA,EAC/J,KAAK,GAAY;AAAE,WAAO,KAAK,IAAI,MAAM,CAAC;AAAA,EAAE;AAAA,EAC5C,MAAM,GAAY;AAAE,WAAO,KAAK,IAAI,OAAO,CAAC;AAAA,EAAE;AAAA,EAC9C,KAAK,GAAY;AAAE,WAAO,KAAK,IAAI,MAAM,CAAC;AAAA,EAAE;AAAA,EAC5C,MAAM,GAAY;AAAE,WAAO,KAAK,IAAI,OAAO,CAAC;AAAA,EAAE;AAAA,EAC9C,KAAK,GAAY;AAAE,WAAO,KAAK,IAAI,MAAM,CAAC;AAAA,EAAE;AAAA,EAC5C,MAAM,GAAY;AAAE,WAAO,KAAK,IAAI,OAAO,CAAC;AAAA,EAAE;AAAA,EAC9C,WAAW,GAAY;AAAE,WAAO,KAAK,IAAI,cAAc,CAAC;AAAA,EAAE;AAAA,EAC1D,SAAS,GAAY;AAAE,WAAO,KAAK,IAAI,YAAY,CAAC;AAAA,EAAE;AAAA,EACtD,SAAS,GAAY;AAAE,WAAO,KAAK,IAAI,YAAY,CAAC;AAAA,EAAE;AAAA,EACtD,SAAS;AAAE,WAAO,KAAK,IAAI,QAAQ;AAAA,EAAE;AAAA,EACrC,YAAY;AAAE,WAAO,KAAK,IAAI,WAAW;AAAA,EAAE;AAC7C;AAEA,IAAM,cAAN,MAAkB;AAAA,EAKhB,YAAoB,aAA6B,UAA0B,WAAmB;AAA1E;AAA6B;AAA0B;AAAA,EAAoB;AAAA,EAJ/F,WAAqB,CAAC;AAAA,EACd,QAA6D;AAAA,EAC7D,SAAwB;AAAA,EACxB,QAAuB;AAAA,EAG/B,QAAc;AAAE,WAAO;AAAA,EAAK;AAAA,EAC5B,KAAK,OAAe;AAAE,WAAO,IAAI,YAAY,MAAM,OAAO,KAAK;AAAA,EAAE;AAAA,EACjE,QAAQ,OAAe;AAAE,WAAO,IAAI,YAAY,MAAM,OAAO,KAAK;AAAA,EAAE;AAAA,EACpE,OAAO,OAAe;AAAE,WAAO,IAAI,YAAY,MAAM,OAAO,IAAI;AAAA,EAAE;AAAA,EAClE,KAAK,OAAe,YAA4B,OAAa;AAAE,SAAK,QAAQ,EAAE,OAAO,UAAU;AAAG,WAAO;AAAA,EAAK;AAAA,EAC9G,SAAS,GAAiB;AAAE,SAAK,SAAS;AAAG,WAAO;AAAA,EAAK;AAAA,EACzD,KAAK,GAAiB;AAAE,SAAK,QAAQ;AAAG,WAAO;AAAA,EAAK;AAAA,EAE5C,YAAqC;AAC3C,UAAM,cAAc,KAAK,SAAS,IAAI,QAAM;AAAA,MAC1C,OAAO,EAAE;AAAA,MACT,UAAU,EAAE;AAAA,MACZ,OAAO,EAAE;AAAA,MACT,aAAa,EAAE;AAAA,IACjB,EAAE;AACF,UAAM,OAAgC,EAAE,aAAa,KAAK,YAAY;AACtE,QAAI,YAAY,SAAS,EAAG,MAAK,SAAS,IAAI;AAC9C,QAAI,KAAK,MAAO,MAAK,MAAM,IAAI,KAAK;AACpC,QAAI,KAAK,WAAW,KAAM,MAAK,OAAO,IAAI,KAAK;AAC/C,QAAI,KAAK,UAAU,KAAM,MAAK,MAAM,IAAI,KAAK;AAC7C,WAAO;AAAA,EACT;AAAA,EAEQ,eAAuC;AAC7C,UAAM,UAAkC,EAAE,gBAAgB,mBAAmB;AAC7E,QAAI,KAAK,UAAW,SAAQ,cAAc,IAAI,KAAK;AACnD,WAAO;AAAA,EACT;AAAA,EAEA,MAAM,UAAU,IAAuB,OAA0C;AAC/E,UAAM,OAAO,KAAK,UAAU;AAC5B,UAAM,MAA8B,EAAE,UAAU,GAAG;AACnD,QAAI,MAAO,KAAI,OAAO,IAAI;AAC1B,SAAK,WAAW,IAAI;AAEpB,UAAM,MAAM,MAAM,MAAM,GAAG,KAAK,QAAQ,oBAAoB;AAAA,MAC1D,QAAQ;AAAA,MACR,SAAS,KAAK,aAAa;AAAA,MAC3B,MAAM,KAAK,UAAU,IAAI;AAAA,IAC3B,CAAC;AACD,UAAM,OAAO,MAAM,IAAI,KAAK;AAC5B,WAAO,KAAK,QAAQ,CAAC;AAAA,EACvB;AAAA,EAEA,MAAM,WAAoC;AACxC,UAAM,OAAO,KAAK,UAAU;AAC5B,UAAM,MAAM,MAAM,MAAM,GAAG,KAAK,QAAQ,gBAAgB;AAAA,MACtD,QAAQ;AAAA,MACR,SAAS,KAAK,aAAa;AAAA,MAC3B,MAAM,KAAK,UAAU,IAAI;AAAA,IAC3B,CAAC;AAMD,UAAM,OAAO,MAAM,IAAI,KAAK;AAK5B,UAAM,UAAU,KAAK;AACrB,UAAM,WAA2C,MAAM,QAAQ,OAAO,IAClE,UACC,SAAS,SAAS,CAAC;AACxB,UAAM,QAAQ,MAAM,QAAQ,OAAO,IAAK,KAAK,MAAM,SAAS,SAAS,SAAW,SAAS,SAAS,SAAS;AAC3G,UAAM,OAAO,MAAM,QAAQ,OAAO,IAAK,KAAK,MAAM,QAAQ,IAAM,SAAS,QAAQ;AACjF,UAAM,QAAQ,MAAM,QAAQ,OAAO,IAAK,KAAK,MAAM,cAAc,IAAM,SAAS,cAAc;AAE9F,UAAM,QAA4B,SAAS,IAAI,QAAM;AAAA,MACnD,IAAI,OAAe;AAAE,eAAO,EAAE,KAAK;AAAA,MAAE;AAAA,MACrC,MAAM;AAAE,cAAM,IAAI,MAAM,4BAA4B;AAAA,MAAE;AAAA,MACtD,OAAO;AAAE,eAAO,QAAQ,OAAO,IAAI,MAAM,4BAA4B,CAAC;AAAA,MAAE;AAAA,MACxE,SAAS;AAAE,eAAO,QAAQ,OAAO,IAAI,MAAM,4BAA4B,CAAC;AAAA,MAAE;AAAA,MAC1E,YAAY;AAAE,eAAO,CAAC;AAAA,MAAE;AAAA,MACxB,SAAS;AAAE,eAAO,QAAQ,OAAO,IAAI,MAAM,uCAAuC,CAAC;AAAA,MAAE;AAAA,IACvF,EAAE;AAEF,WAAO,EAAE,OAAO,OAAO,MAAM,MAAM;AAAA,EACrC;AACF;AAEO,SAAS,kBAAkB,UAAkB,WAAmB;AACrE,SAAO,CAAC,SAAiB,IAAI,YAAY,MAAM,UAAU,SAAS;AACpE;;;ACtGA,eAAsB,WAAW,SAA2C;AAC1E,QAAM,EAAE,MAAM,WAAW,UAAU,cAAc,IAAI,YAAY,IAAI,aAAa,OAAO,cAAc,aAAa,OAAO,SAAS,IAAI;AACxI,MAAI,YAAa,WAAU,cAAc;AACzC,QAAM,SAAS,MAAM,KAAK,SAAS;AACnC,YAAU,cAAc;AACxB,MAAI,OAAO,MAAM,WAAW,GAAG;AAAE,QAAI,UAAW,WAAU,cAAc;AAAW;AAAA,EAAO;AAC1F,QAAM,UAAU,SAAS,cAAc,UAAU;AACjD,MAAI,aAAc,SAAQ,YAAY;AACtC,aAAW,QAAQ,OAAO,OAAO;AAC/B,UAAM,WAAW,SAAS,IAAI;AAC9B,QAAI,oBAAoB,MAAM;AAC5B,cAAQ,YAAY,QAAQ;AAAA,IAC9B,WAAW,YAAY;AACrB,YAAM,OAAO,SAAS,cAAc,MAAM;AAE1C,WAAK,WAAW,IAAI;AACpB,cAAQ,YAAY,IAAI;AAAA,IAC1B,OAAO;AACL,YAAM,OAAO,SAAS,cAAc,MAAM;AAC1C,WAAK,cAAc;AACnB,cAAQ,YAAY,IAAI;AAAA,IAC1B;AAAA,EACF;AACA,YAAU,YAAY,OAAO;AAC7B,MAAI,SAAU,UAAS,SAAS;AAClC;;;AClCA,SAAS,WAAW,IAAyC;AAC3D,QAAM,SAAiC,CAAC;AACxC,aAAW,QAAQ,MAAM,KAAK,GAAG,UAAU,GAAG;AAC5C,QAAI,KAAK,KAAK,WAAW,kBAAkB,GAAG;AAC5C,aAAO,KAAK,KAAK,MAAM,EAAE,CAAC,IAAI,KAAK;AAAA,IACrC;AAAA,EACF;AACA,SAAO;AACT;AAEO,SAAS,cAAoB;AAClC,QAAM,WAAW,SAAS,iBAAiB,oBAAoB;AAC/D,aAAW,MAAM,MAAM,KAAK,QAAQ,GAAG;AACrC,UAAM,SAAS;AACf,UAAM,aAAa,OAAO,aAAa,kBAAkB;AACzD,QAAI,CAAC,WAAY;AACjB,UAAM,MAAM,KAAK,OAAO,UAAU;AAClC,QAAI,CAAC,OAAO,CAAC,IAAI,aAAc;AAC/B,UAAM,UAA6B,EAAE,UAAU,QAAQ,GAAG,WAAW,MAAM,EAAE;AAC7E,QAAI,aAAa,KAAK,EAAE,SAAS,GAAG,IAAI,CAAC;AAAA,EAC3C;AACF;;;ACtBO,SAAS,gBAAsB;AACpC,QAAM,QAAQ,SAAS,iBAAiB,kBAAkB;AAC1D,aAAW,UAAU,MAAM,KAAK,KAAK,GAAG;AACtC,UAAM,WAAW;AACjB,UAAM,QAAQ,KAAK,cAAc,KAAK;AACtC,QAAI,MAAM,eAAe,EAAG,OAAM,eAAe,EAAE,KAAK,EAAE,SAAS,EAAE,UAAU,SAAS,EAAE,CAAC;AAC3F,aAAS,iBAAiB,UAAU,CAAC,MAAM;AACzC,UAAI,MAAM,UAAU,GAAG;AACrB,cAAM,SAAS,MAAM,UAAU,EAAE,KAAK,EAAE,SAAS,EAAE,UAAU,SAAS,EAAE,CAAC;AACzE,YAAI,WAAW,MAAO,GAAE,eAAe;AAAA,MACzC;AAAA,IACF,CAAC;AAAA,EACH;AACF;;;ACIO,IAAM,iBAAiB;AAG9B,IAAM,iBAAiB;AAEvB,IAAM,aAAa;AACnB,IAAM,aAAa;AACnB,IAAM,eAAe;AACrB,IAAM,cAAc;AACpB,IAAM,aAAa;AACnB,IAAM,kBAAkB;AAQxB,SAAS,YAAY,QAAqB,WAAuC;AAC/E,aAAW,SAAS,MAAM,KAAK,OAAO,QAAQ,GAAG;AAC/C,QAAI,iBAAiB,eAAe,MAAM,UAAU,SAAS,SAAS,EAAG,QAAO;AAAA,EAClF;AACA,SAAO;AACT;AAEA,SAAS,YAAqB;AAC5B,MAAI,OAAO,WAAW,YAAa,QAAO;AAC1C,MAAI,OAAO,OAAO,eAAe,WAAY,QAAO;AACpD,SAAO,OAAO,WAAW,eAAe,cAAc,KAAK,EAAE;AAC/D;AAEA,SAAS,WAAW,OAAmC;AACrD,SAAO,MAAM,KAAK,MAAM,iBAA8B,iCAAiC,CAAC;AAC1F;AAGO,SAAS,WAAW,KAA8B;AACvD,QAAM,WAA8B,CAAC;AACrC,QAAM,UAAsB,CAAC;AAE7B,aAAW,QAAQ,MAAM,KAAK,IAAI,iBAA8B,IAAI,UAAU,EAAE,CAAC,GAAG;AAClF,UAAM,SAAS,YAAY,MAAM,YAAY;AAC7C,UAAM,QAAQ,YAAY,MAAM,WAAW;AAC3C,QAAI,UAAU,MAAO,SAAQ,KAAK,EAAE,MAAM,QAAQ,MAAM,CAAC;AAAA,EAC3D;AACA,MAAI,QAAQ,WAAW,EAAG,QAAO,MAAM;AAAA,EAAC;AAExC,MAAI;AACJ,QAAM,cAAc,MAAM;AACxB,QAAI,eAAe,QAAW;AAAE,mBAAa,UAAU;AAAG,mBAAa;AAAA,IAAU;AAAA,EACnF;AAEA,WAAS,OAAO,OAA0B;AACxC,WAAO,MAAM,KAAK,UAAU,SAAS,UAAU;AAAA,EACjD;AAEA,WAAS,MAAM,OAAuB;AACpC,UAAM,KAAK,UAAU,OAAO,UAAU;AACtC,UAAM,OAAO,aAAa,iBAAiB,OAAO;AAClD,UAAM,MAAM,UAAU,OAAO,UAAU;AAAA,EACzC;AAEA,WAAS,SAAS,QAAyB;AACzC,eAAW,SAAS,SAAS;AAE3B,UAAI,WAAW,UAAU,UAAU,MAAM,KAAK,SAAS,OAAO,IAAI,GAAI;AACtE,YAAM,KAAK;AAAA,IACb;AAAA,EACF;AAGA,WAAS,cAAc,OAAuB;AAC5C,QAAI,OAAO,MAAM,MAAM,0BAA0B,WAAY;AAC7D,UAAM,MAAM,UAAU,OAAO,UAAU;AACvC,UAAM,OAAO,MAAM,MAAM,sBAAsB;AAC/C,UAAM,WAAW,OAAO,cAAc,SAAS,gBAAgB,eAAe;AAC9E,QAAI,WAAW,KAAK,KAAK,QAAQ,KAAK,KAAK,QAAQ,WAAW,GAAG;AAC/D,YAAM,MAAM,UAAU,IAAI,UAAU;AAAA,IACtC;AAAA,EACF;AAEA,WAAS,KAAK,OAAuB;AACnC,gBAAY;AACZ,aAAS,KAAK;AACd,UAAM,KAAK,UAAU,IAAI,UAAU;AACnC,UAAM,OAAO,aAAa,iBAAiB,MAAM;AACjD,kBAAc,KAAK;AAAA,EACrB;AAEA,WAAS,GACP,QACA,MACA,SACA,SACM;AACN,WAAO,iBAAiB,MAAM,SAA0B,OAAO;AAC/D,aAAS,KAAK,MAAM,OAAO,oBAAoB,MAAM,SAA0B,OAAO,CAAC;AAAA,EACzF;AAEA,aAAW,SAAS,SAAS;AAC3B,OAAG,MAAM,QAAQ,SAAS,CAAC,UAAsB;AAC/C,YAAM,eAAe;AACrB,UAAI,OAAO,KAAK,GAAG;AAAE,cAAM,KAAK;AAAA,MAAE,OAAO;AAAE,aAAK,KAAK;AAAA,MAAE;AAAA,IACzD,CAAC;AAGD,OAAG,MAAM,MAAM,cAAc,MAAM;AACjC,UAAI,CAAC,UAAU,EAAG;AAClB,WAAK,KAAK;AAAA,IACZ,CAAC;AACD,OAAG,MAAM,MAAM,cAAc,MAAM;AACjC,UAAI,CAAC,UAAU,EAAG;AAClB,kBAAY;AACZ,mBAAa,WAAW,MAAM;AAAE,cAAM,KAAK;AAAG,qBAAa;AAAA,MAAU,GAAG,cAAc;AAAA,IACxF,CAAC;AAAA,EACH;AAKA,KAAG,KAAK,WAAW,CAAC,UAAsB;AACxC,UAAM,SAAS,MAAM;AACrB,QAAI,CAAC,OAAQ;AACb,eAAW,SAAS,SAAS;AAC3B,UAAI,MAAM,MAAM,SAAS,MAAM,GAAG;AAAE,aAAK,KAAK;AAAG;AAAA,MAAO;AAAA,IAC1D;AACA,eAAW,SAAS,SAAS;AAC3B,UAAI,CAAC,MAAM,KAAK,SAAS,MAAM,EAAG,OAAM,KAAK;AAAA,IAC/C;AAAA,EACF,CAAC;AAED,KAAG,KAAK,WAAW,CAAC,UAAyB;AAC3C,UAAM,SAAS,MAAM;AACrB,QAAI,CAAC,OAAQ;AACb,UAAM,QAAQ,QAAQ,KAAK,CAAC,MAAM,EAAE,KAAK,SAAS,MAAM,CAAC;AACzD,QAAI,CAAC,MAAO;AAEZ,QAAI,MAAM,QAAQ,UAAU;AAC1B,YAAM,YAAY,QAAQ,OAAO,CAAC,MAAM,EAAE,KAAK,SAAS,MAAM,KAAK,OAAO,CAAC,CAAC,EAAE,IAAI;AAClF,UAAI,CAAC,UAAW;AAChB,YAAM,eAAe;AACrB,YAAM,SAAS;AACf,gBAAU,OAAO,MAAM;AACvB;AAAA,IACF;AAEA,QAAI,MAAM,QAAQ,eAAe,MAAM,QAAQ,UAAW;AAG1D,QAAI,WAAW,MAAM,QAAQ;AAC3B,UAAI,MAAM,QAAQ,aAAa;AAC7B,cAAM,eAAe;AACrB,YAAI,CAAC,OAAO,KAAK,EAAG,MAAK,KAAK;AAC9B,mBAAW,MAAM,KAAK,EAAE,CAAC,GAAG,MAAM;AAAA,MACpC,WAAW,OAAO,KAAK,GAAG;AACxB,cAAM,eAAe;AACrB,cAAM,KAAK;AAAA,MACb;AACA;AAAA,IACF;AAGA,UAAM,QAAQ,QAAQ,KAAK,CAAC,MAAM,EAAE,MAAM,SAAS,MAAM,CAAC;AAC1D,QAAI,CAAC,MAAO;AACZ,UAAM,QAAQ,WAAW,MAAM,KAAK;AACpC,UAAM,QAAQ,MAAM,QAAQ,MAAM;AAClC,QAAI,UAAU,GAAI;AAClB,UAAM,eAAe;AACrB,UAAM,OAAO,MAAM,QAAQ,cACvB,OAAO,QAAQ,KAAK,MAAM,MAAM,IAChC,OAAO,QAAQ,IAAI,MAAM,UAAU,MAAM,MAAM;AACnD,UAAM,MAAM;AAAA,EACd,CAAC;AAED,KAAG,UAAU,SAAS,CAAC,UAAsB;AAC3C,UAAM,SAAS,MAAM;AACrB,QAAI,UAAU,IAAI,SAAS,MAAM,EAAG;AACpC,gBAAY;AACZ,aAAS;AAAA,EACX,CAAC;AAGD,MAAI,OAAO,OAAO,eAAe,YAAY;AAC3C,UAAM,QAAQ,OAAO,WAAW,eAAe,cAAc,KAAK;AAClE,UAAM,WAAW,MAAM;AAAE,kBAAY;AAAG,eAAS;AAAA,IAAE;AACnD,QAAI,OAAO,MAAM,qBAAqB,YAAY;AAChD,YAAM,iBAAiB,UAAU,QAAQ;AACzC,eAAS,KAAK,MAAM,MAAM,oBAAoB,UAAU,QAAQ,CAAC;AAAA,IACnE;AAAA,EACF;AAEA,MAAI,aAAa,iBAAiB,EAAE;AAEpC,SAAO,MAAM;AACX,gBAAY;AACZ,eAAW,QAAQ,SAAU,MAAK;AAClC,aAAS,SAAS;AAClB,aAAS;AACT,QAAI,gBAAgB,eAAe;AAAA,EACrC;AACF;AAMO,SAAS,aAAa,OAAmB,UAAsB;AACpE,MAAI,OAAO,aAAa,YAAa,QAAO,MAAM;AAAA,EAAC;AACnD,QAAM,YAA+B,CAAC;AACtC,aAAW,OAAO,MAAM,KAAK,KAAK,iBAA8B,iBAAiB,CAAC,GAAG;AACnF,QAAI,IAAI,aAAa,eAAe,EAAG;AACvC,cAAU,KAAK,WAAW,GAAG,CAAC;AAAA,EAChC;AACA,SAAO,MAAM;AAAE,eAAW,QAAQ,UAAW,MAAK;AAAA,EAAE;AACtD;;;ACzMA,SAAS,YAAY,KAA0B;AAC7C,SAAO,IAAI,YAAY,IAAI,aAAa,MAAM,GAAG,IAAI,QAAQ,YAAY;AAC3E;AAEA,SAAS,YAAY,MAA6B;AAChD,MAAI,OAAO,aAAa,YAAa,QAAO;AAC5C,SAAO,SAAS,cAAc,mBAAmB,IAAI,IAAI,GAAG,aAAa,SAAS,KAAK;AACzF;AAIA,IAAI,0BAA0B;AAC9B,IAAI,wBAA2D;AAE/D,SAAS,sBAA4B;AACnC,MAAI,2BAA2B,OAAO,aAAa,YAAa;AAChE,4BAA0B;AAC1B,WAAS;AAAA,IACP;AAAA,IACA,CAAC,MAAM;AACL,YAAM,OAAO,EAAE;AACf,UAAI,CAAC,QAAQ,KAAK,YAAY,OAAQ;AACtC,YAAM,SAAS,KAAK,aAAa,mBAAmB,KAAK,KAAK,aAAa,IAAI;AAC/E,UAAI,UAAU,sBAAuB,uBAAsB,MAAM;AAAA,IACnE;AAAA,IACA;AAAA,EACF;AACF;AAEA,SAAS,OAAO,KAAa,MAAe,WAA0B;AACpE,MAAI;AACF,SAAK,MAAM,KAAK;AAAA,MACd,QAAQ;AAAA,MACR,SAAS,EAAE,gBAAgB,oBAAoB,GAAI,YAAY,EAAE,gBAAgB,UAAU,IAAI,CAAC,EAAG;AAAA,MACnG,MAAM,KAAK,UAAU,IAAI;AAAA,MACzB,WAAW;AAAA,IACb,CAAC,EAAE,MAAM,MAAM;AAAA,IAAC,CAAC;AAAA,EACnB,QAAQ;AAAA,EAER;AACF;AAMO,SAAS,gBAAgB,SAA+B,CAAC,GAAsB;AACpF,QAAM,MAAM,OAAO,WAAW,YAAY;AAC1C,QAAM,UAAU,OAAO,WAAW,YAAY,GAAG;AACjD,QAAM,OAAO,IAAI,aAAa;AAC9B,QAAM,eAAe,YAAY,eAAe;AAChD,QAAM,YAAY,OAAO,aAAa,YAAY,YAAY,KAAK;AACnE,QAAM,cAAc,OAAO,gBAAgB,OAAO,aAAa,cAAc,SAAS,WAAW;AAEjG,WAAS,gBAAgB,QAAgC;AACvD,WAAO,GAAG,OAAO,gCAAgC,EAAE,WAAW,OAAO,OAAO,GAAG,IAAI;AAAA,EACrF;AACA,WAAS,MAAM,WAAyB;AACtC,oBAAgB,EAAE,MAAM,UAAU,UAAU,CAAC;AAAA,EAC/C;AAIA,MAAI,cAAc;AAChB,WAAO,GAAG,OAAO,8BAA8B,EAAE,cAAc,UAAU,GAAG,IAAI;AAAA,EAClF;AAIA,MAAI,OAAO,iBAAiB,OAAO;AACjC,oBAAgB,EAAE,MAAM,YAAY,KAAK,YAAY,CAAC;AAAA,EACxD;AAIA,0BAAwB,CAAC,WAAmB,gBAAgB,EAAE,MAAM,eAAe,OAAO,CAAC;AAC3F,sBAAoB;AAEpB,QAAM,UAA6B,EAAE,OAAO,gBAAgB;AAG5D,QAAM,IAAM,WAA6D,QAAQ,CAAC;AAClF,IAAE,OAAO,IAAI;AACb,IAAE,iBAAiB,IAAI;AACtB,EAAC,WAA6D,OAAO;AAEtE,SAAO;AACT;;;ACoDM,SAAU,qBAAkB;AAChC,SAAO;IACL,KAAK,EAAE,QAAQ,MAAM,QAAQ,MAAM,UAAU,MAAM,MAAM,MAAM,SAAS,KAAI;IAC5E,UAAU,EAAE,KAAK,MAAM,QAAQ,MAAM,YAAY,SAAQ;IACzD,SAAS,EAAE,eAAe,GAAG,gBAAgB,GAAG,iBAAiB,GAAG,WAAW,KAAK,cAAc,CAAA,EAAE;IACpG,YAAY,EAAE,gBAAgB,CAAA,GAAI,YAAY,EAAC;IAC/C,QAAQ,CAAA;IACR,UAAU,EAAE,WAAW,IAAI,SAAS,OAAO,UAAU,KAAI;IACzD,UAAU,CAAA;IACV,MAAM,EAAE,YAAY,OAAO,WAAW,OAAO,mBAAmB,MAAM,YAAY,SAAQ;;AAE9F;;;ACvLM,SAAU,UAAU,SAAiB,OAAa;AACtD,QAAM,QAAQ,QAAQ,MAAM,GAAG;AAC/B,MAAI,MAAM,WAAW;AAAG,WAAO,YAAY;AAC3C,MAAI,MAAM;AACV,QAAM,QAAQ,MAAM,CAAC;AACrB,MAAI,UAAU,IAAI;AAChB,QAAI,CAAC,MAAM,WAAW,KAAK;AAAG,aAAO;AACrC,UAAM,MAAM;EACd;AACA,WAAS,IAAI,GAAG,IAAI,MAAM,SAAS,GAAG,KAAK;AACzC,UAAM,OAAO,MAAM,CAAC;AACpB,QAAI,SAAS;AAAI;AACjB,UAAM,MAAM,MAAM,QAAQ,MAAM,GAAG;AACnC,QAAI,MAAM;AAAG,aAAO;AACpB,UAAM,MAAM,KAAK;EACnB;AACA,QAAM,OAAO,MAAM,MAAM,SAAS,CAAC;AACnC,MAAI,SAAS,IAAI;AACf,QAAI,CAAC,MAAM,SAAS,IAAI;AAAG,aAAO;AAClC,QAAI,MAAM,SAAS,KAAK,SAAS;AAAK,aAAO;EAC/C;AACA,SAAO;AACT;;;ACnBM,SAAU,mBAAmB,SAAyB,KAAqB;AAC/E,MAAI,QAAQ;AAAc,WAAO,QAAQ,IAAI;AAC7C,MAAI,QAAQ;AAAc,WAAO,QAAQ,IAAI;AAC7C,MAAI,QAAQ;AAAgB,WAAO,QAAQ,IAAI;AAC/C,MAAI,QAAQ;AAAY,WAAO,QAAQ,IAAI;AAC3C,MAAI,QAAQ;AAAe,WAAO,QAAQ,IAAI;AAC9C,MAAI,QAAQ;AAAmB,WAAO,QAAQ,SAAS;AACvD,MAAI,QAAQ;AAAuB,WAAO,QAAQ,SAAS;AAC3D,MAAI,QAAQ;AAAoB,WAAO,QAAQ,SAAS,UAAU,SAAS;AAC3E,MAAI,QAAQ;AAAqB,WAAO,QAAQ,QAAQ;AACxD,MAAI,QAAQ;AAAwB,WAAO,QAAQ,QAAQ,aAAa,KAAK,GAAG;AAChF,MAAI,IAAI,WAAW,SAAS,GAAG;AAC7B,UAAM,MAAM,IAAI,MAAM,CAAC;AACvB,WAAO,QAAQ,OAAO,GAAG,KAAK;EAChC;AACA,MAAI,IAAI,WAAW,UAAU,GAAG;AAC9B,UAAM,MAAM,IAAI,MAAM,CAAC;AACvB,UAAM,MAAM,QAAQ,SAAS,GAAG;AAChC,WAAO,QAAQ,UAAa,QAAQ,OAAO,OAAO,GAAG,IAAI;EAC3D;AACA,SAAO;AACT;AAEM,SAAU,yBAAyB,WAAkC,SAAuB;AAChG,QAAM,SAAS,mBAAmB,SAAS,UAAU,MAAM;AAC3D,QAAM,WAAW,UAAU;AAC3B,UAAQ,UAAU,UAAU;IAC1B,KAAK;AAAU,aAAO,WAAW;IACjC,KAAK;AAAc,aAAO,WAAW;IACrC,KAAK;AAAY,aAAO,WAAW,QAAQ,OAAO,aAAa,YAAY,OAAO,SAAS,QAAQ;IACnG,KAAK;AAAe,aAAO,WAAW,QAAQ,OAAO,aAAa,YAAY,OAAO,WAAW,QAAQ;IACxG,KAAK;AAAgB,aAAO,WAAW,QAAQ,OAAO,aAAa,YAAY,UAAU,UAAU,MAAM;IACzG,KAAK;AAAa,aAAO,WAAW,QAAQ,MAAM,QAAQ,UAAU,KAAK,KAAK,UAAU,MAAM,SAAS,MAAM;IAC7G,KAAK;AAAW,aAAO,WAAW;IAClC,KAAK;AAAY,aAAO,WAAW;IACnC,KAAK;AAAO,aAAO,WAAW,QAAQ,OAAO,aAAa,YAAY,OAAO,MAAM,KAAK,OAAO,QAAQ;IACvG,KAAK;AAAO,aAAO,WAAW,QAAQ,OAAO,aAAa,YAAY,OAAO,MAAM,KAAK,OAAO,QAAQ;IACvG;AAAS,aAAO;EAClB;AACF;;;ACtCA,SAAS,iBAAiB,MAAkC,SAAuB;AACjF,QAAM,YAAY,KAAK,aAAa;AACpC,UAAQ,KAAK,SAAS;IACpB,KAAK;AAAiB,aAAO,QAAQ,QAAQ,iBAAiB,YAAY,KAAK,SAAS;IACxF,KAAK;AAAkB,aAAO,QAAQ,QAAQ,kBAAkB,YAAY,KAAK,SAAS;IAC1F,KAAK;AAAc,aAAO,QAAQ,QAAQ,mBAAmB,YAAY,KAAK,SAAS;IACvF,KAAK,kBAAkB;AACrB,YAAM,MAAM,KAAK,YAAY;AAC7B,YAAM,QAAQ,QAAQ,WAAW,eAAe,GAAG;AACnD,aAAO,UAAU,UAAa,SAAS,YAAY,KAAK,SAAS;IACnE;IACA,KAAK,YAAY;AACf,YAAM,UAAU,KAAK,WAAW;AAChC,YAAM,UAAU,QAAQ,QAAQ,aAAa,KAAK,SAAO,UAAU,SAAS,GAAG,CAAC;AAChF,aAAO,UAAU,KAAK,SAAS;IACjC;IACA,KAAK,eAAe;AAGlB,UAAI,CAAC,KAAK,UAAU,CAAC,KAAK;AAAU,eAAO;AAC3C,YAAM,UAAU,yBACd;QACE,MAAM;QACN,IAAI,KAAK;QACT,SAAS,KAAK;QACd,QAAQ,KAAK;QACb,UAAU,KAAK;QACf,OAAO,KAAK,SAAS;SAEvB,OAAO;AAET,aAAO,UAAU,KAAK,SAAS;IACjC;IACA;AAAS,aAAO;EAClB;AACF;AAEM,SAAU,wBAAwB,YAA0C,SAAuB;AACvG,MAAI,QAAQ;AACZ,aAAW,QAAQ,YAAY;AAAE,aAAS,iBAAiB,MAAM,OAAO;EAAE;AAC1E,SAAO;AACT;;;AC5BA,IAAM,WAAW,oBAAI,IAAG;AA0BlB,SAAU,wBAAwB,WAAsB,SAAuB;AACnF,QAAM,YAAY,SAAS,IAAI,UAAU,IAAI;AAC7C,MAAI,CAAC;AAAW,WAAO;AACvB,SAAO,UAAU,WAAW,OAAO;AACrC;AAKM,SAAU,iBAAiB,MAAY;AAC3C,SAAO,SAAS,IAAI,IAAI;AAC1B;;;AC5BA,SAAS,cAAc,OAAuB,SAAuB;AACnE,QAAM,kBAAoC,CAAA;AAC1C,QAAM,WAAoC,CAAA;AAC1C,QAAM,SAAuC,CAAA;AAC7C,QAAM,UAAmC,CAAA;AAEzC,aAAW,KAAK,MAAM,YAAY;AAIhC,UAAM,gBAAwB,EAAE;AAChC,QAAI,EAAE,SAAS;AAAW,eAAS,KAAK,CAAC;aAChC,EAAE,SAAS;AAAS,aAAO,KAAK,CAAC;aACjC,iBAAiB,aAAa;AAAG,cAAQ,KAAK,CAAC;EAE1D;AAEA,MAAI,oBAAoB;AAExB,aAAW,MAAM,UAAU;AACzB,UAAM,SAAS,yBAAyB,IAAI,OAAO;AACnD,oBAAgB,KAAK,EAAE,aAAa,GAAG,IAAI,MAAM,WAAW,OAAM,CAAE;AACpE,QAAI,CAAC;AAAQ,0BAAoB;EACnC;AAGA,aAAW,MAAM,SAAS;AACxB,UAAM,SAAS,wBAAwB,IAAI,OAAO;AAClD,oBAAgB,KAAK,EAAE,aAAa,GAAG,IAAI,MAAM,GAAG,MAAmB,OAAM,CAAE;AAC/E,QAAI,CAAC;AAAQ,0BAAoB;EACnC;AAEA,QAAM,aAAa,wBAAwB,QAAQ,OAAO;AAC1D,aAAW,MAAM,QAAQ;AACvB,UAAM,cAAc,wBAAwB,CAAC,EAAE,GAAG,OAAO;AACzD,oBAAgB,KAAK,EAAE,aAAa,GAAG,IAAI,MAAM,SAAS,QAAQ,cAAc,GAAG,kBAAkB,YAAW,CAAE;EACpH;AAEA,SAAO,EAAE,SAAS,mBAAmB,iBAAiB,WAAU;AAClE;AAEM,SAAU,iBAAiB,WAAuB,SAAuB;AAC7E,QAAM,gBAA0B,CAAA;AAChC,QAAM,SAA4B,CAAA;AAElC,aAAW,YAAY,WAAW;AAChC,UAAM,cAA8C,CAAA;AACpD,QAAI,kBAAkB;AACtB,QAAI,mBAAmB;AAEvB,eAAW,SAAS,SAAS,iBAAiB;AAC5C,YAAM,EAAE,SAAS,iBAAiB,WAAU,IAAK,cAAc,OAAO,OAAO;AAC7E,0BAAoB;AACpB,kBAAY,KAAK,EAAE,SAAS,MAAM,IAAI,SAAS,gBAAe,CAAE;AAChE,UAAI;AAAS,0BAAkB;IACjC;AAEA,QAAI,SAAS,mBAAmB,MAAM;AACpC,YAAM,oBAAoB,oBAAoB,SAAS;AACvD,YAAM,uBAAuB,SAAS,gBAAgB,KAAK,OAAK,EAAE,WAAW,KAAK,OAAK,EAAE,SAAS,SAAS,CAAC;AAC5G,UAAI,sBAAsB;AACxB,0BAAkB,mBAAmB;MACvC,OAAO;AACL,0BAAkB;MACpB;IACF;AAEA,WAAO,KAAK;MACV,YAAY,SAAS;MACrB,SAAS;MACT;MACA,GAAI,mBAAmB,IAAI,EAAE,YAAY,iBAAgB,IAAK,CAAA;KAC/D;AACD,QAAI;AAAiB,oBAAc,KAAK,SAAS,EAAE;EACrD;AAEA,SAAO,EAAE,mBAAmB,cAAc,CAAC,KAAK,MAAM,eAAe,OAAM;AAC7E;;;ACtGA,IAAM,cAAc;AACpB,IAAM,kBAAkB;AACxB,IAAM,sBAAsB;AAC5B,IAAM,oBAAoB;AAS1B,SAAS,aAA0B;AACjC,QAAM,MAAM,eAAe,QAAQ,WAAW;AAC9C,MAAI,KAAK;AAAE,QAAI;AAAE,aAAO,KAAK,MAAM,GAAG;AAAA,IAAiB,QAAQ;AAAA,IAAqB;AAAA,EAAE;AACtF,SAAO,EAAE,eAAe,GAAG,WAAW,IAAI,cAAc,CAAC,EAAE;AAC7D;AAEA,SAAS,kBAA0B;AACjC,QAAM,MAAM,eAAe,QAAQ,iBAAiB;AACpD,MAAI,IAAK,QAAO,OAAO,GAAG;AAC1B,QAAM,MAAM,KAAK,IAAI;AACrB,iBAAe,QAAQ,mBAAmB,OAAO,GAAG,CAAC;AACrD,SAAO;AACT;AAEA,SAAS,oBAA4C;AACnD,QAAM,MAAM,aAAa,QAAQ,mBAAmB;AACpD,MAAI,KAAK;AAAE,QAAI;AAAE,aAAO,KAAK,MAAM,GAAG;AAAA,IAA4B,QAAQ;AAAA,IAAqB;AAAA,EAAE;AACjG,SAAO,CAAC;AACV;AAEO,SAAS,kBAAkB,aAAqB,UAAqC;AAC1F,QAAM,UAAU,WAAW;AAC3B,UAAQ,iBAAiB;AACzB,MAAI,CAAC,QAAQ,UAAW,SAAQ,YAAY;AAC5C,UAAQ,aAAa,KAAK,WAAW;AACrC,iBAAe,QAAQ,aAAa,KAAK,UAAU,OAAO,CAAC;AAE3D,QAAM,cAAc,OAAO,aAAa,QAAQ,eAAe,CAAC,KAAK,KAAK;AAC1E,eAAa,QAAQ,iBAAiB,OAAO,UAAU,CAAC;AAExD,QAAM,eAAe,gBAAgB;AACrC,QAAM,iBAAiB,kBAAkB;AACzC,MAAI,UAAU;AACZ,mBAAe,QAAQ,KAAK,eAAe,QAAQ,KAAK,KAAK;AAC7D,iBAAa,QAAQ,qBAAqB,KAAK,UAAU,cAAc,CAAC;AAAA,EAC1E;AAEA,SAAO;AAAA,IACL,SAAS,EAAE,eAAe,QAAQ,eAAe,gBAAgB,YAAY,iBAAiB,KAAK,OAAO,KAAK,IAAI,IAAI,gBAAgB,GAAI,GAAG,WAAW,QAAQ,WAAW,cAAc,QAAQ,aAAa;AAAA,IAC/M,YAAY,EAAE,gBAAgB,YAAY,EAAE;AAAA,EAC9C;AACF;;;ACrDA,IAAM,aAAa;AAEZ,SAAS,gBAAwC;AACtD,QAAM,MAAM,aAAa,QAAQ,UAAU;AAC3C,MAAI,CAAC,IAAK,QAAO,CAAC;AAClB,MAAI;AAAE,WAAO,KAAK,MAAM,GAAG;AAAA,EAA4B,QAAQ;AAAE,WAAO,CAAC;AAAA,EAAE;AAC7E;AAEO,SAAS,YAAY,WAAyC;AACnE,eAAa,QAAQ,YAAY,KAAK,UAAU,SAAS,CAAC;AAC5D;;;ACJA,SAAS,MAAM,MAAsB,SAAkD;AACrF,SAAO;AAAA,IACL,KAAK,QAAQ,OAAO,KAAK;AAAA,IAAK,UAAU,QAAQ,YAAY,KAAK;AAAA,IACjE,SAAS,QAAQ,WAAW,KAAK;AAAA,IAAS,YAAY,QAAQ,cAAc,KAAK;AAAA,IACjF,QAAQ,EAAE,GAAG,KAAK,QAAQ,GAAG,QAAQ,OAAO;AAAA,IAAG,UAAU,QAAQ,YAAY,KAAK;AAAA,IAClF,UAAU,EAAE,GAAG,KAAK,UAAU,GAAG,QAAQ,SAAS;AAAA,IAAG,MAAM,EAAE,GAAG,KAAK,MAAM,GAAG,QAAQ,KAAK;AAAA,EAC7F;AACF;AAEO,SAAS,mBACd,WAAuB,eAAwC,aAAqB,cACpE;AAChB,MAAI,UAAU,MAAM,mBAAmB,GAAG,aAAa;AACvD,QAAM,SAAS,kBAAkB,aAAa,YAAY;AAC1D,QAAM,SAAS,cAAc;AAC7B,YAAU,MAAM,SAAS,EAAE,SAAS,OAAO,SAAS,YAAY,OAAO,YAAY,QAAQ,MAAM,EAAE,GAAG,QAAQ,MAAM,YAAY,SAAS,EAAE,CAAC;AAC5I,QAAM,EAAE,mBAAmB,cAAc,IAAI,iBAAiB,WAAW,OAAO;AAChF,QAAMA,cAAa,QAAQ,KAAK;AAChC,SAAO,EAAE,mBAAmBA,cAAa,OAAO,mBAAmB,eAAe,SAAS,YAAAA,YAAW;AACxG;;;ACzBO,SAAS,uBAAuB,mBAAwC;AAC7E,QAAM,aAAa,SAAS,iBAAiB,yBAAyB;AACtE,aAAW,aAAa,MAAM,KAAK,UAAU,GAAG;AAC9C,UAAM,KAAK;AACX,UAAM,OAAO,MAAM,KAAK,GAAG,iBAAiB,qBAAqB,CAAC;AAgBlE,QAAI,KAAK,WAAW,EAAG;AAEvB,OAAG,UAAU,OAAO,sBAAsB;AAC1C,OAAG,gBAAgB,WAAW;AAC9B,UAAM,WAAW,GAAG,cAAc,wBAAwB;AAC1D,QAAI,SAAU,UAAS,OAAO;AAC9B,UAAM,YAAY,qBAAqB;AAMvC,UAAM,WAAW,KAAK,KAAK,CAAC,UAAW,MAAsB,aAAa,mBAAmB,MAAM,SAAS;AAC5G,UAAM,SAAS,WAAW,YAAY;AACtC,OAAG,aAAa,gBAAgB,MAAM;AACtC,eAAW,SAAS,MAAM;AACxB,YAAM,IAAI;AACV,QAAE,MAAM,UAAU,EAAE,aAAa,mBAAmB,MAAM,SAAS,KAAK;AAAA,IAC1E;AAAA,EACF;AACF;;;ACxCO,IAAM,eAAe;AAAA,EAC1B,SAAS;AAAA,EAAgB,SAAS;AAAA,EAAgB,KAAK;AAAA,EACvD,UAAU;AAAA,EAAiB,QAAQ;AAAA,EAAe,OAAO;AAAA,EACzD,cAAc;AAChB;AAEO,SAAS,cAAmC;AACjD,QAAM,MAAM,oBAAI,IAAoB;AACpC,MAAI,OAAO,aAAa,YAAa,QAAO;AAC5C,aAAW,QAAQ,SAAS,OAAO,MAAM,GAAG,GAAG;AAC7C,UAAM,UAAU,KAAK,KAAK;AAC1B,UAAM,QAAQ,QAAQ,QAAQ,GAAG;AACjC,QAAI,QAAQ,EAAG;AACf,QAAI,IAAI,QAAQ,MAAM,GAAG,KAAK,GAAG,mBAAmB,QAAQ,MAAM,QAAQ,CAAC,CAAC,CAAC;AAAA,EAC/E;AACA,SAAO;AACT;AAEO,SAAS,UAAU,MAAc,OAAe,QAAuB;AAC5E,MAAI,MAAM,GAAG,IAAI,IAAI,mBAAmB,KAAK,CAAC;AAC9C,MAAI,WAAW,UAAa,UAAU,EAAG,QAAO,aAAa,MAAM;AACnE,WAAS,SAAS;AACpB;AAEO,SAAS,aAAa,MAAoB;AAC/C,WAAS,SAAS,GAAG,IAAI;AAC3B;;;ACLA,IAAM,gBAAgB;AAEtB,IAAM,uBAAuB;AAE7B,IAAM,UAAU;AAChB,IAAM,WAAW;AACjB,IAAM,WAAW;AACjB,IAAM,oBAAoB;AAE1B,IAAM,mBAAmB;AA+BzB,IAAI,YAAsC;AAE1C,IAAI,aAAa;AAIjB,SAAS,YAAY,KAA4B;AAC/C,MAAI;AAAE,WAAO,aAAa,QAAQ,GAAG;AAAA,EAAE,QAAQ;AAAE,WAAO;AAAA,EAAK;AAC/D;AACA,SAAS,aAAa,KAAa,OAAqB;AACtD,MAAI;AAAE,iBAAa,QAAQ,KAAK,KAAK;AAAA,EAAE,QAAQ;AAAA,EAA6B;AAC9E;AACA,SAAS,cAAc,KAAmB;AACxC,MAAI;AAAE,iBAAa,WAAW,GAAG;AAAA,EAAE,QAAQ;AAAA,EAA6B;AAC1E;AAGO,SAAS,cAAuB;AACrC,MAAI,YAAY,oBAAoB,MAAM,KAAK;AAG7C,kBAAc,oBAAoB;AAClC,iBAAa,eAAe,GAAG;AAC/B,WAAO;AAAA,EACT;AACA,SAAO,YAAY,aAAa,MAAM;AACxC;AAEA,SAAS,aAAa,WAA0B;AAC9C,eAAa,eAAe,YAAY,MAAM,GAAG;AACnD;AAWO,SAAS,SAAe;AAC7B,YAAU,aAAa,QAAQ,KAAK,MAAM,KAAK,IAAI;AACnD,eAAa,aAAa,QAAQ;AACpC;AAEO,SAAS,QAAc;AAC5B,eAAa,aAAa,MAAM;AAClC;AAGO,SAAS,mBAAyB;AACvC,eAAa,aAAa,QAAQ;AACpC;AAEA,SAAS,SAAkB;AACzB,SAAO,YAAY,EAAE,IAAI,aAAa,QAAQ;AAChD;AAgBA,eAAsB,YACpB,YACA,OAAgF,CAAC,GAC/D;AAClB,QAAM,UAAU,KAAK,WAAW;AAChC,MAAI;AACF,UAAM,MAAM,MAAM,MAAM,GAAG,OAAO,kBAAkB;AAAA,MAClD,QAAQ;AAAA,MACR,SAAS,EAAE,gBAAgB,oBAAoB,GAAI,KAAK,YAAY,EAAE,gBAAgB,KAAK,UAAU,IAAI,CAAC,EAAG;AAAA,MAC7G,MAAM,KAAK,UAAU,EAAE,YAAY,QAAQ,WAAW,CAAC;AAAA,IACzD,CAAC;AACD,QAAI,CAAC,IAAI,GAAI,QAAO;AACpB,UAAM,OAAQ,MAAM,IAAI,KAAK,EAAE,MAAM,MAAM,IAAI;AAG/C,WAAO,MAAM,MAAM,cAAc;AAAA,EACnC,QAAQ;AAGN,WAAO;AAAA,EACT;AACF;AAOA,SAAS,kBAAkB,OAA0B;AACnD,QAAM,eAAe,cAAc,mCAAmC,GAAG,OAAO;AAChF,QAAM,OAAO,SAAS,cAAc,GAAG;AACvC,OAAK,YAAY;AACjB,OAAK,aAAa,QAAQ,QAAQ;AAClC,OAAK,cAAc;AACnB,QAAM,eAAe,YAAY,IAAI;AACvC;AAEA,SAAS,SAAe;AACtB,MAAI;AAAE,WAAO,SAAS,OAAO;AAAA,EAAE,QAAQ;AAAA,EAAyC;AAClF;AAIA,SAAS,gBAAsB;AAC7B,WAAS,eAAe,OAAO,GAAG,OAAO;AAEzC,WAAS,cAAc,wBAAwB,GAAG,OAAO;AAC3D;AAEA,SAAS,OAAO,WAAmB,MAAc,SAAwC;AACvF,QAAM,MAAM,SAAS,cAAc,QAAQ;AAC3C,MAAI,OAAO;AACX,MAAI,YAAY;AAChB,MAAI,cAAc;AAClB,MAAI,iBAAiB,SAAS,OAAO;AACrC,SAAO;AACT;AAEA,SAAS,0BAA0B,OAAmC;AACpE,MAAI,MAAM,YAAY,MAAO,QAAO;AACpC,SAAQ,MAAM,kBAAkB,CAAC,MAAM,cAAgB,MAAM,cAAc,MAAM;AACnF;AAEA,SAAS,aAAa,OAAyC;AAC7D,MAAI,CAAC,MAAM,WAAY,QAAO;AAC9B,SAAO,MAAM,WAAW,KAAK,CAAC,MAAM,EAAE,OAAO,MAAM,UAAU,GAAG,QAAQ;AAC1E;AAMO,SAAS,wBAAwB,OAAgC;AACtE,cAAY;AACZ,MAAI,OAAO,aAAa,YAAa;AACrC,gBAAc;AACd,MAAI,CAAC,cAAc,CAAC,0BAA0B,KAAK,EAAG;AAEtD,QAAM,SAAS,MAAM,kBAAkB,CAAC,MAAM;AAC9C,QAAM,YAAY,aAAa,QAAQ,YAAY;AAEnD,QAAM,OAAO,SAAS,cAAc,KAAK;AACzC,OAAK,KAAK;AACV,OAAK,YAAY,qBAAqB,SAAS,6BAA6B,6BAA6B;AACzG,OAAK,QAAQ,QAAQ,YAAY,cAAc;AAG/C,QAAM,OAAO,SAAS,cAAc,QAAQ;AAC5C,OAAK,OAAO;AACZ,OAAK,YAAY;AACjB,OAAK,aAAa,iBAAiB,OAAO,CAAC,SAAS,CAAC;AACrD,OAAK,aAAa,iBAAiB,QAAQ;AAC3C,QAAM,WAAW,SAAS,cAAc,MAAM;AAC9C,WAAS,YAAY;AACrB,WAAS,aAAa,eAAe,MAAM;AAC3C,WAAS,cAAc;AACvB,QAAM,YAAY,SAAS,cAAc,MAAM;AAC/C,YAAU,YAAY;AACtB,YAAU,cAAc,SAAS,iBAAiB;AAClD,OAAK,OAAO,UAAU,SAAS;AAC/B,OAAK,iBAAiB,SAAS,MAAM,YAAY,CAAC;AAClD,OAAK,SAAS,CAAC;AACf,OAAK,YAAY,IAAI;AAGrB,QAAM,QAAQ,SAAS,cAAc,SAAS;AAC9C,QAAM,KAAK;AACX,QAAM,YAAY;AAClB,QAAM,aAAa,QAAQ,QAAQ;AACnC,QAAM,aAAa,mBAAmB,QAAQ;AAC9C,QAAM,SAAS;AAEf,QAAM,SAAS,SAAS,cAAc,KAAK;AAC3C,SAAO,YAAY;AACnB,QAAM,QAAQ,SAAS,cAAc,IAAI;AACzC,QAAM,KAAK;AACX,QAAM,YAAY;AAClB,QAAM,OAAO,aAAa,KAAK;AAC/B,QAAM,cAAc,CAAC,SACjB,2BACA,OACE,mCACA;AACN,SAAO,YAAY,KAAK;AACxB,QAAM,cAAc,OAAO,8BAA8B,UAAK,MAAM,cAAc,CAAC;AACnF,cAAY,aAAa,cAAc,UAAU;AACjD,SAAO,YAAY,WAAW;AAC9B,QAAM,YAAY,MAAM;AAExB,MAAI,QAAQ;AACV,UAAM,WAAW,SAAS,cAAc,GAAG;AAC3C,aAAS,YAAY;AACrB,QAAI,MAAM;AACR,YAAM,SAAS,SAAS,cAAc,QAAQ;AAC9C,aAAO,cAAc;AACrB,eAAS,YAAY,MAAM;AAAA,IAC7B,OAAO;AACL,eAAS,cAAc;AAAA,IACzB;AACA,UAAM,YAAY,QAAQ;AAAA,EAC5B;AAEA,QAAM,OAAO,SAAS,cAAc,GAAG;AACvC,OAAK,YAAY;AACjB,OAAK,cAAc,SACf,8GACA;AACJ,QAAM,YAAY,IAAI;AAGtB,QAAM,UAAU,MAAM,aAAa,CAAC;AACpC,MAAI,UAAU,QAAQ,SAAS,GAAG;AAChC,UAAM,WAAW,SAAS,cAAc,KAAK;AAC7C,aAAS,YAAY;AACrB,UAAM,gBAAgB,SAAS,cAAc,MAAM;AACnD,kBAAc,KAAK;AACnB,kBAAc,YAAY;AAC1B,kBAAc,cAAc;AAC5B,aAAS,YAAY,aAAa;AAElC,UAAM,QAAQ,SAAS,cAAc,KAAK;AAC1C,UAAM,YAAY;AAClB,UAAM,aAAa,QAAQ,OAAO;AAClC,UAAM,aAAa,mBAAmB,iBAAiB;AACvD,eAAW,UAAU,SAAS;AAC5B,YAAM,YAAY,OAAO,OAAO,MAAM;AACtC,YAAM,YAAY,OAAO,4BAA4B,OAAO,MAAM,MAAM;AACtE,YAAI,UAAW;AACf,aAAK,YAAY,OAAO,IAAI,EAAE,SAAS,MAAM,SAAS,WAAW,MAAM,UAAU,CAAC,EAAE;AAAA,UAClF,CAAC,cAAc;AAIb,gBAAI,UAAW,QAAO;AAAA,gBACjB,mBAAkB,KAAK;AAAA,UAC9B;AAAA,QACF;AAAA,MACF,CAAC;AACD,gBAAU,QAAQ,iBAAiB,OAAO;AAC1C,gBAAU,aAAa,gBAAgB,OAAO,SAAS,CAAC;AACxD,YAAM,YAAY,SAAS;AAAA,IAC7B;AACA,aAAS,YAAY,KAAK;AAC1B,UAAM,YAAY,QAAQ;AAAA,EAC5B;AAGA,QAAM,UAAU,SAAS,cAAc,KAAK;AAC5C,UAAQ,YAAY;AACpB,MAAI,MAAM,YAAY;AACpB,YAAQ,YAAY,OAAO,4BAA4B,0BAA0B,MAAM;AAAE,YAAM;AAAG,aAAO;AAAA,IAAE,CAAC,CAAC;AAAA,EAC/G,OAAO;AACL,QAAI,OAAO,GAAG;AACZ,cAAQ,YAAY,OAAO,2BAA2B,sBAAsB,MAAM;AAAE,yBAAiB;AAAG,eAAO;AAAA,MAAE,CAAC,CAAC;AAAA,IACrH;AACA,YAAQ,YAAY,OAAO,6BAA6B,4BAA4B,MAAM;AAAE,aAAO;AAAG,aAAO;AAAA,IAAE,CAAC,CAAC;AAAA,EACnH;AACA,QAAM,YAAY,OAAO;AAEzB,QAAM,iBAAiB,WAAW,CAAC,MAAM;AACvC,QAAK,EAAoB,QAAQ,SAAU,eAAc;AAAA,EAC3D,CAAC;AAED,OAAK,YAAY,KAAK;AACtB,WAAS,KAAK,YAAY,IAAI;AAChC;AAEA,SAAS,iBAAsF;AAC7F,QAAM,OAAO,SAAS,eAAe,OAAO;AAC5C,MAAI,CAAC,KAAM,QAAO;AAClB,QAAM,OAAO,KAAK,cAA2B,yBAAyB;AACtE,QAAM,QAAQ,KAAK,cAA2B,0BAA0B;AACxE,MAAI,CAAC,QAAQ,CAAC,MAAO,QAAO;AAC5B,SAAO,EAAE,MAAM,MAAM,MAAM;AAC7B;AAEA,SAAS,YAAY,UAAyB;AAC5C,QAAM,UAAU,eAAe;AAC/B,MAAI,CAAC,QAAS;AACd,UAAQ,KAAK,QAAQ,QAAQ,WAAW,aAAa;AACrD,UAAQ,KAAK,SAAS;AACtB,UAAQ,KAAK,aAAa,iBAAiB,OAAO,QAAQ,CAAC;AAC3D,UAAQ,MAAM,SAAS,CAAC;AACxB,QAAM,cAAc,WAChB,QAAQ,MAAM,cAA2B,6BAA6B,IACtE,QAAQ;AACZ,eAAa,MAAM;AACrB;AAGO,SAAS,cAAoB;AAClC,eAAa,KAAK;AAClB,cAAY,IAAI;AAClB;AAGO,SAAS,gBAAsB;AACpC,eAAa;AACb,eAAa,IAAI;AACjB,cAAY,KAAK;AACnB;AASO,SAAS,wBAA8B;AAC5C,MAAI,OAAO,aAAa,YAAa;AACrC,eAAa;AACb,QAAM,QAA2B,aAAa;AAAA,IAC5C,gBAAgB;AAAA,IAChB,YAAY,YAAY,EAAE,IAAI,aAAa,MAAM;AAAA,IACjD,mBAAmB;AAAA,EACrB;AACA,0BAAwB,KAAK;AAC7B,iBAAe,GAAG,MAAM,cAA2B,6BAA6B,GAAG,MAAM;AAC3F;;;ACvYA,IAAM,cAAc;AAEb,SAAS,uBAAsC;AAAE,SAAO,eAAe,QAAQ,WAAW;AAAE;AAC5F,SAAS,qBAAqB,IAAkB;AAAE,iBAAe,QAAQ,aAAa,EAAE;AAAE;AAC1F,SAAS,eAAqB;AAAE,iBAAe,WAAW,WAAW;AAAE;AAEvE,SAAS,mBAAyB;AACvC,QAAM,UAAU,YAAY;AAC5B,QAAM,SAAS,QAAQ,IAAI,aAAa,YAAY;AACpD,MAAI,QAAQ;AAAE,yBAAqB,MAAM;AAAG,iBAAa,aAAa,YAAY;AAAA,EAAE;AACtF;AAEO,SAAS,oBAAoBC,eAA4B;AAC9D,WAAS,cAAc,sBAAsB,GAAG,OAAO;AACvD,QAAM,SAAS,SAAS,cAAc,KAAK;AAC3C,SAAO,YAAY;AACnB,SAAO,aAAa,QAAQ,QAAQ;AACpC,QAAM,OAAO,SAAS,cAAc,MAAM;AAC1C,OAAK,YAAY;AACjB,OAAK,cAAc;AACnB,QAAM,SAAS,SAAS,cAAc,QAAQ;AAC9C,SAAO,cAAcA;AACrB,OAAK,YAAY,MAAM;AACvB,SAAO,YAAY,IAAI;AACvB,QAAM,UAAU,SAAS,cAAc,QAAQ;AAC/C,UAAQ,YAAY;AACpB,UAAQ,OAAO;AACf,UAAQ,cAAc;AACtB,UAAQ,iBAAiB,SAAS,MAAM;AAAE,iBAAa;AAAG,WAAO,SAAS,OAAO;AAAA,EAAE,CAAC;AACpF,SAAO,YAAY,OAAO;AAC1B,WAAS,KAAK,YAAY,MAAM;AAClC;;;AC/BO,SAAS,kBAAkB,SAA4B;AAC5D,QAAM,IAAM,OAAe,QAAQ,CAAC;AACpC,IAAE,UAAU;AACX,EAAC,OAAe,OAAO;AAC1B;AAEO,SAAS,gBAAsB;AAAE,SAAO,cAAc,IAAI,YAAY,WAAW,CAAC;AAAE;AAIpF,SAAS,iBAA6B;AAC3C,QAAM,IAAI,CAAC,MAAc,SAAS,cAAc,mBAAmB,CAAC,IAAI,GAAG,aAAa,SAAS,KAAK;AACtG,SAAO,EAAE,YAAY,EAAE,aAAa,GAAG,cAAc,EAAE,eAAe,GAAG,WAAW,EAAE,YAAY,GAAG,WAAW,EAAE,YAAY,KAAK,GAAG;AACxI;;;ACbO,IAAM,iBAAiB;AAGvB,IAAM,sBAAsB;AAO5B,IAAM,sBAAsB;AAa5B,IAAM,0BAA2D;AAAA,EACtE,cAAc;AAAA,EACd,aAAa;AAAA,EACb,mBAAmB;AAAA,EACnB,cAAc;AAAA,EACd,cAAc;AAAA,EACd,UAAU;AAAA,EACV,eAAe;AAAA,EACf,YAAY;AACd;AAoCO,SAAS,kBAAuC;AACrD,QAAM,UAAU,YAAY;AAC5B,QAAM,MAAM,QAAQ,IAAI,cAAc;AACtC,MAAI,CAAC,IAAK,QAAO;AAEjB,MAAI;AACJ,MAAI;AACF,aAAS,KAAK,MAAM,GAAG;AAAA,EACzB,QAAQ;AACN,WAAO;AAAA,EACT;AACA,MAAI,OAAO,WAAW,YAAY,WAAW,KAAM,QAAO;AAC1D,QAAM,MAAM;AACZ,SAAO;AAAA,IACL,WAAW;AAAA,IACX,WAAW,IAAI,cAAc;AAAA,IAC7B,WAAW,IAAI,cAAc;AAAA,IAC7B,iBAAiB,IAAI,oBAAoB;AAAA,EAC3C;AACF;AAMO,SAAS,WACd,OACM;AACN,QAAM,QAAsB;AAAA,IAC1B,WAAW;AAAA,IACX,WAAW,MAAM,aAAa;AAAA,IAC9B,WAAW,MAAM,aAAa;AAAA,IAC9B,iBAAiB,MAAM,mBAAmB;AAAA,EAC5C;AAEA,YAAU,gBAAgB,KAAK,UAAU,KAAK,GAAG,OAAQ;AAGzD,qBAAmB,KAAK;AAC1B;AAgBO,SAAS,mBAAmB,OAAsC;AACvE,MAAI,OAAO,aAAa,YAAa,QAAO,CAAC;AAC7C,QAAM,UAAU,YAAY;AAC5B,QAAM,UAAoB,CAAC;AAC3B,aAAW,CAAC,MAAM,QAAQ,KAAK,OAAO,QAAQ,uBAAuB,GAAG;AACtE,QAAI,aAAa,YAAa;AAC9B,QAAI,SAAS,MAAM,QAAQ,MAAM,KAAM;AACvC,QAAI,uBAAuB,MAAM,QAAQ,IAAI,IAAI,CAAC,EAAG;AACrD,iBAAa,IAAI;AACjB,YAAQ,KAAK,IAAI;AAAA,EACnB;AACA,SAAO;AACT;AAiBA,SAAS,uBAAuB,MAAc,OAAoC;AAChF,MAAI,SAAS,uBAAuB,UAAU,OAAW,QAAO;AAIhE,SAAO,MAAM,WAAW,mBAAmB,KAAK,MAAM,SAAS,oBAAoB;AACrF;AAkBO,SAAS,WAAW,UAA2B,UAA8B,CAAC,GAAY;AAC/F,MAAI,QAAQ,mBAAmB,MAAO,QAAO;AAC7C,MAAI,aAAa,YAAa,QAAO;AACrC,QAAM,QAAQ,gBAAgB;AAC9B,MAAI,CAAC,MAAO,QAAO;AACnB,SAAO,MAAM,QAAQ,MAAM;AAC7B;;;AC/KA,IAAM,eAAe;AAErB,SAAS,UAAU,WAAmD;AACpE,QAAM,MAAM,eAAe,QAAQ,eAAe,SAAS;AAC3D,MAAI,CAAC,IAAK,QAAO;AACjB,MAAI;AAAE,WAAO,KAAK,MAAM,GAAG;AAAA,EAA6B,QAAQ;AAAE,WAAO;AAAA,EAAK;AAChF;AAEA,SAAS,SAAS,WAAmB,MAAqC;AACxE,iBAAe,QAAQ,eAAe,WAAW,KAAK,UAAU,IAAI,CAAC;AACvE;AAEA,eAAsB,kBACpB,UAAyB,gBACzB,UAAU,WACwB;AAClC,QAAM,SAAkC,CAAC;AAEzC,QAAM,QAAQ,SAAS,IAAI,OAAO,YAAY;AAC5C,UAAM,SAAS,UAAU,QAAQ,EAAE;AACnC,QAAI,OAAQ,QAAO;AAEnB,QAAI;AACJ,QAAI,QAAQ,SAAS,gBAAgB;AAInC,YAAM,MAAM,MAAM,MAAM,GAAG,OAAO,kBAAkB,QAAQ,EAAE,IAAI;AAAA,QAChE,QAAQ;AAAA,QACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,QAC9C,MAAM,KAAK,UAAU,EAAE,SAAS,eAAe,CAAC;AAAA,MAClD,CAAC;AACD,YAAM,OAAO,MAAM,IAAI,KAAK;AAC5B,eAAS,KAAK,QAAQ,CAAC;AAAA,IACzB,OAAO;AACL,eAAS,MAAM,QAAQ,QAAQ,cAAc;AAAA,IAC/C;AAEA,aAAS,QAAQ,IAAI,MAAM;AAC3B,WAAO;AAAA,EACT,CAAC;AAED,QAAM,UAAU,MAAM,QAAQ,IAAI,KAAK;AACvC,aAAW,KAAK,QAAS,QAAO,OAAO,QAAQ,CAAC;AAChD,SAAO;AACT;;;AC/CO,SAAS,mBACd,WACA,WACA,aACM;AAEN,cAAY,SAAS;AAGrB,MAAI,aAAa,aAAa;AAC5B,UAAM,UAAU,YAAY;AAC5B,UAAM,aAAa,eAAe;AAClC,UAAM,gBAAyC;AAAA,MAC7C,UAAU,EAAE,WAAW,WAAW,aAAa,QAAQ,IAAI,aAAa,OAAO,KAAK,IAAI,SAAS,QAAQ,IAAI,aAAa,KAAK,GAAG,UAAU,KAAK;AAAA,MACjJ,MAAM,EAAE,YAAY,QAAQ,IAAI,aAAa,MAAM,GAAG,WAAW,OAAO,mBAAmB,MAAM,YAAY,SAAS;AAAA,IACxH;AACA,UAAM,SAAS,mBAAmB,WAAW,eAAe,WAAW;AACvE,2BAAuB,OAAO,iBAAiB;AAC/C,sBAAkB;AAAA,MAChB,WAAW,OAAO,QAAQ,SAAS;AAAA,MACnC,YAAY,OAAO;AAAA,MACnB,cAAc,WAAW;AAAA,MACzB,WAAW,WAAW;AAAA,MACtB,WAAW;AAAA,MACX,YAAY,OAAO;AAAA,IACrB,CAAC;AAAA,EACH;AACF;;;AC9BA,IAAM,YAAY;AAClB,IAAM,aAAa;AAQnB,IAAM,oBAAoB,oBAAI,IAAI,CAAC,SAAS,YAAY,iBAAiB,oBAAoB,QAAQ,CAAC;AAGtG,IAAM,gBAAgB;AASf,IAAM,wBAAwB;AAG9B,IAAM,sBAAsB;AACnC,IAAM,4BAA4B;AAElC,IAAM,iBAAiB;AAOhB,SAAS,mBAAmB,aAAqE;AACtG,MAAI,OAAO,aAAa,YAAa,QAAO;AAC5C,MAAI,eAAe,gBAAgB,SAAU,QAAO;AACpD,SAAO,SAAS,cAA2B,qBAAqB;AAClE;AAuBA,SAAS,gBAAgB,KAAoD;AAC3E,SAAO,OAAO,QAAQ,WAAW,EAAE,OAAO,KAAK,OAAO,IAAI,IAAI;AAChE;AAmCA,IAAM,qBACJ;AAEF,SAAS,kBAAkB,MAAkC;AAC3D,SAAO,MAAM,KAAK,KAAK,iBAA8B,kBAAkB,CAAC,EAAE,OAAO,CAAC,OAAO,CAAC,GAAG,MAAM;AACrG;AAGA,SAAS,QAAQ,MAAmB,GAAwB;AAC1D,QAAM,YAAY,kBAAkB,IAAI;AACxC,MAAI,UAAU,WAAW,GAAG;AAC1B,MAAE,eAAe;AACjB,SAAK,MAAM;AACX;AAAA,EACF;AACA,QAAM,QAAQ,UAAU,CAAC;AACzB,QAAM,OAAO,UAAU,UAAU,SAAS,CAAC;AAC3C,QAAM,SAAS,SAAS;AACxB,QAAM,SAAS,WAAW,QAAQ,KAAK,SAAS,MAAM,KAAK,WAAW;AACtE,MAAI,EAAE,UAAU;AACd,QAAI,CAAC,UAAU,WAAW,OAAO;AAAE,QAAE,eAAe;AAAG,WAAK,MAAM;AAAA,IAAE;AAAA,EACtE,WAAW,CAAC,UAAU,WAAW,MAAM;AACrC,MAAE,eAAe;AACjB,UAAM,MAAM;AAAA,EACd;AACF;AAEO,SAAS,mBAAmB,QAA0C;AAC3E,QAAM,YAAY,OAAO,aAAa;AACtC,QAAM,cAAc,OAAO,eAAe;AAC1C,QAAM,UAAU,gBAAgB;AAGhC,MAAI,WAA8B,CAAC;AAEnC,WAAS,aAAsB;AAC7B,QAAI,cAAc,SAAU,QAAO;AACnC,QAAI,cAAc,UAAW,QAAO;AAEpC,UAAM,MAAM,aAAa,QAAQ,SAAS;AAC1C,QAAI,CAAC,IAAK,QAAO;AAEjB,QAAI,cAAc,OAAQ,QAAO;AAGjC,UAAM,KAAK,OAAO,GAAG;AACrB,WAAO,CAAC,MAAM,EAAE,KAAK,KAAK,IAAI,IAAI,KAAK;AAAA,EACzC;AAEA,WAAS,YAAkB;AACzB,iBAAa,QAAQ,WAAW,OAAO,KAAK,IAAI,CAAC,CAAC;AAAA,EACpD;AAEA,WAAS,aAAa,WAAyC;AAC7D,gBAAY,SAAS;AAKrB,QAAI;AACF,yBAAmB,WAAW,OAAO,aAAa,CAAC,GAAG,OAAO,eAAe,GAAG;AAAA,IACjF,UAAE;AACA,gBAAU;AAAA,IACZ;AAAA,EACF;AAGA,WAAS,SAAS,WAA8B;AAC9C,eAAW,QAAQ,SAAU,MAAK;AAClC,eAAW,CAAC;AACZ,cAAU,gBAAgB;AAC1B,cAAU,YAAY;AACtB,cAAU,gBAAgB,MAAM;AAChC,cAAU,gBAAgB,YAAY;AACtC,cAAU,gBAAgB,YAAY;AACtC,cAAU,gBAAgB,iBAAiB;AAC3C,cAAU,gBAAgB,UAAU;AACpC,cAAU,SAAS;AAAA,EACrB;AAOA,WAAS,QAAQ,WAA8B;AAC7C,cAAU;AACV,aAAS,SAAS;AAAA,EACpB;AAEA,WAAS,OAAO,WAA8B;AAC5C,eAAW,QAAQ,SAAU,MAAK;AAClC,eAAW,CAAC;AACZ,cAAU,gBAAgB;AAC1B,cAAU,SAAS;AAInB,UAAM,mBAAmB,YAAY,QAAQ,MAAM,GAAG;AACtD,cAAU,YAAY,kCAAkC,gBAAgB;AAExE,QAAI,SAAS;AAGX,YAAM,WAAW,SAAS,cAAc,KAAK;AAC7C,eAAS,YAAY;AACrB,eAAS,iBAAiB,SAAS,MAAM,QAAQ,SAAS,CAAC;AAC1D,OAAC,UAAU,iBAAiB,SAAS,MAAM,YAAY,QAAQ;AAChE,eAAS,KAAK,MAAM,SAAS,OAAO,CAAC;AAErC,YAAM,mBAAmB,SAAS,KAAK,MAAM;AAC7C,eAAS,KAAK,MAAM,WAAW;AAC/B,eAAS,KAAK,MAAM;AAAE,iBAAS,KAAK,MAAM,WAAW;AAAA,MAAiB,CAAC;AAEvE,YAAM,YAAY,CAAC,MAAqB;AACtC,YAAI,EAAE,QAAQ,UAAU;AAAE,YAAE,eAAe;AAAG,kBAAQ,SAAS;AAAG;AAAA,QAAO;AACzE,YAAI,EAAE,QAAQ,MAAO,SAAQ,WAAW,CAAC;AAAA,MAC3C;AACA,eAAS,iBAAiB,WAAW,WAAW,IAAI;AACpD,eAAS,KAAK,MAAM,SAAS,oBAAoB,WAAW,WAAW,IAAI,CAAC;AAE5E,gBAAU,aAAa,QAAQ,QAAQ;AACvC,gBAAU,aAAa,cAAc,MAAM;AAC3C,UAAI,OAAO,UAAU;AACnB,kBAAU,aAAa,mBAAmB,cAAc;AAAA,MAC1D,OAAO;AACL,kBAAU,aAAa,cAAc,6BAA6B;AAAA,MACpE;AACA,gBAAU,WAAW;AAAA,IACvB;AAEA,QAAI,gBAAgB,iBAAiB;AAInC,eAAS,KAAK,UAAU,IAAI,mBAAmB;AAC/C,YAAM,aAAa,MAAM;AACvB,iBAAS,gBAAgB,MAAM,YAAY,2BAA2B,GAAG,UAAU,YAAY,IAAI;AAAA,MACrG;AACA,aAAO,iBAAiB,UAAU,UAAU;AAC5C,eAAS,KAAK,MAAM;AAClB,iBAAS,KAAK,UAAU,OAAO,mBAAmB;AAClD,iBAAS,gBAAgB,MAAM,eAAe,yBAAyB;AACvE,eAAO,oBAAoB,UAAU,UAAU;AAAA,MACjD,CAAC;AAED,qBAAe,UAAU;AAAA,IAC3B;AAEA,QAAI,kBAAkB,IAAI,WAAW,GAAG;AACtC,YAAM,aAAa,SAAS,cAAc,QAAQ;AAClD,iBAAW,OAAO;AAClB,iBAAW,YAAY;AACvB,iBAAW,aAAa,cAAc,SAAS;AAC/C,iBAAW,cAAc;AACzB,iBAAW,iBAAiB,SAAS,MAAM,QAAQ,SAAS,CAAC;AAC7D,gBAAU,YAAY,UAAU;AAAA,IAClC;AAEA,QAAI,OAAO,UAAU;AACnB,YAAM,IAAI,SAAS,cAAc,IAAI;AACrC,QAAE,YAAY;AACd,QAAE,cAAc,OAAO;AACvB,UAAI,QAAS,GAAE,KAAK;AACpB,gBAAU,YAAY,CAAC;AAAA,IACzB;AAEA,QAAI,OAAO,aAAa;AACtB,YAAM,IAAI,SAAS,cAAc,GAAG;AACpC,QAAE,YAAY;AACd,QAAE,cAAc,OAAO;AACvB,gBAAU,YAAY,CAAC;AAAA,IACzB;AAEA,UAAM,OAAO,SAAS,cAAc,MAAM;AAC1C,SAAK,YAAY;AAEjB,eAAW,SAAS,OAAO,QAAQ;AACjC,YAAM,YAAY,SAAS,cAAc,KAAK;AAC9C,gBAAU,YAAY;AAEtB,YAAM,QAAQ,SAAS,cAAc,OAAO;AAC5C,YAAM,UAAU,cAAc,MAAM,EAAE;AACtC,YAAM,cAAc,MAAM;AAC1B,gBAAU,YAAY,KAAK;AAE3B,UAAI;AAEJ,UAAI,MAAM,SAAS,UAAU;AAC3B,cAAM,SAAS,SAAS,cAAc,QAAQ;AAC9C,eAAO,KAAK,cAAc,MAAM,EAAE;AAClC,eAAO,OAAO,MAAM;AACpB,YAAI,MAAM,SAAU,QAAO,WAAW;AACtC,YAAI,MAAM,SAAS;AACjB,gBAAM,cAAc,SAAS,cAAc,QAAQ;AACnD,sBAAY,QAAQ;AACpB,sBAAY,cAAc;AAC1B,iBAAO,YAAY,WAAW;AAC9B,qBAAW,OAAO,MAAM,SAAS;AAC/B,kBAAM,EAAE,OAAO,OAAAC,OAAM,IAAI,gBAAgB,GAAG;AAC5C,kBAAM,SAAS,SAAS,cAAc,QAAQ;AAC9C,mBAAO,QAAQ;AACf,mBAAO,cAAcA;AACrB,mBAAO,YAAY,MAAM;AAAA,UAC3B;AAAA,QACF;AACA,gBAAQ;AAAA,MACV,OAAO;AACL,cAAM,MAAM,SAAS,cAAc,OAAO;AAC1C,YAAI,KAAK,cAAc,MAAM,EAAE;AAC/B,YAAI,OAAO,MAAM;AACjB,YAAI,OAAO,MAAM;AACjB,YAAI,MAAM,SAAU,KAAI,WAAW;AACnC,gBAAQ;AAAA,MACV;AAEA,gBAAU,YAAY,KAAK;AAC3B,WAAK,YAAY,SAAS;AAAA,IAC5B;AAEA,UAAM,UAAU,SAAS,cAAc,KAAK;AAC5C,YAAQ,YAAY;AAEpB,UAAM,YAAY,SAAS,cAAc,QAAQ;AACjD,cAAU,OAAO;AACjB,cAAU,YAAY;AACtB,cAAU,cAAc,OAAO,eAAe;AAC9C,YAAQ,YAAY,SAAS;AAE7B,QAAI,OAAO,WAAW;AACpB,YAAM,UAAU,SAAS,cAAc,QAAQ;AAC/C,cAAQ,OAAO;AACf,cAAQ,YAAY;AACpB,cAAQ,cAAc,OAAO;AAC7B,cAAQ,iBAAiB,SAAS,MAAM,QAAQ,SAAS,CAAC;AAC1D,cAAQ,YAAY,OAAO;AAAA,IAC7B;AAEA,SAAK,YAAY,OAAO;AAExB,SAAK,iBAAiB,UAAU,CAAC,MAAM;AACrC,QAAE,eAAe;AACjB,YAAM,OAAO,IAAI,SAAS,IAAI;AAC9B,YAAM,YAAoC,CAAC;AAC3C,WAAK,QAAQ,CAAC,OAAO,QAAQ;AAC3B,kBAAU,GAAG,IAAI,OAAO,KAAK;AAAA,MAC/B,CAAC;AACD,mBAAa,SAAS;AAGtB,UAAI,cAAc,IAAI,WAAW,EAAG,UAAS,SAAS;AAAA,IACxD,CAAC;AAED,cAAU,YAAY,IAAI;AAE1B,QAAI,QAAS,WAAU,MAAM;AAAA,EAC/B;AAEA,SAAO,EAAE,YAAY,WAAW,QAAQ,aAAa;AACvD;;;AClVA,SAAS,aAAsB;AAC7B,SAAO,YAAY,EAAE,IAAI,aAAa,MAAM;AAC9C;AAEO,SAAS,2BAA2B,OAAiC,CAAC,GAAS;AACpF,QAAM,aAAa,SAAS,iBAAiB,mCAAmC;AAChF,MAAI,WAAW,WAAW,EAAG;AAE7B,QAAM,WAAW,WAAW;AAE5B,aAAW,aAAa,MAAM,KAAK,UAAU,GAAG;AAE9C,cAAU,cAAc,6BAA6B,GAAG,OAAO;AAE/D,UAAM,QAAQ,SAAS,cAAc,KAAK;AAC1C,UAAM,YAAY;AAElB,UAAM,UAAU,SAAS,cAAc,QAAQ;AAC/C,YAAQ,OAAO;AACf,YAAQ,YAAY;AACpB,YAAQ,aAAa,cAAc,yBAAyB;AAE5D,UAAM,SAAS,SAAS,cAAc,MAAM;AAC5C,WAAO,YAAY;AACnB,WAAO,cAAc,WAAW,wBAAwB;AACxD,YAAQ,YAAY,MAAM;AAE1B,QAAI,CAAC,YAAY,KAAK,cAAc;AAClC,YAAM,WAAW,SAAS,cAAc,MAAM;AAC9C,eAAS,YAAY;AACrB,eAAS,cAAc,KAAK;AAC5B,cAAQ,YAAY,QAAQ;AAAA,IAC9B;AAEA,YAAQ,iBAAiB,SAAS,MAAM,sBAAsB,CAAC;AAC/D,UAAM,YAAY,OAAO;AACzB,cAAU,YAAY,KAAK;AAAA,EAC7B;AACF;;;AChBA,SAAS,eAAe,UAA2B;AACjD,MAAI,SAAU,QAAO;AACrB,QAAM,WAAW,KAAK,SAAS;AAC/B,MAAI,CAAC,YAAY,aAAa,IAAK,QAAO;AAC1C,SAAO,GAAG,QAAQ;AACpB;AAEA,eAAsB,mBAAmB,QAA2C;AAClF,QAAM,EAAE,WAAW,aAAa,cAAc,cAAc,IAAI;AAChE,QAAM,UAAU,eAAe,OAAO,OAAO;AAC7C,mBAAiB;AACjB,QAAM,aAAa,eAAe;AAClC,QAAM,YAAY,qBAAqB;AAIvC,QAAM,kBAAkB,UACrB,OAAO,CAAC,MAAM,EAAE,WAAW,QAAQ,EACnC,IAAI,CAAC,OAAO,EAAE,IAAI,EAAE,IAAI,MAAM,EAAE,KAAK,EAAE;AAC1C,QAAM,SAAS,CAAC,OACd,KAAK,gBAAgB,KAAK,CAAC,MAAM,EAAE,OAAO,EAAE,GAAG,QAAQ,OAAO;AAEhE,MAAI,WAAW;AACb,2BAAuB,SAAS;AAGhC,wBAAoB,OAAO,SAAS,KAAK,SAAS;AAClD,+BAA2B,EAAE,cAAc,OAAO,SAAS,EAAE,CAAC;AAC9D,sBAAkB,EAAE,WAAW,WAAW,WAAW,YAAY,WAAW,cAAc,MAAM,WAAW,MAAM,WAAW,MAAM,YAAY,MAAM,CAAC;AACrJ,kBAAc;AACd;AAAA,EACF;AAEA,QAAM,UAAU,YAAY;AAI5B,QAAM,cACJ,OAAO,mBAAmB,SAAY,CAAC,IAAI,EAAE,gBAAgB,OAAO,eAAe;AACrF,QAAM,iBAAiB,WAAW,mBAAmB,WAAW;AAchE,QAAM,kBAAkB,iBAAiB,QAAQ,IAAI,aAAa,OAAO,IAAI;AAC7E,MAAI,SAAkC;AAAA,IACpC,GAAG;AAAA,IACH,UAAU,EAAE,WAAW,WAAW,aAAa,mBAAmB,IAAI,SAAS,kBAAkB,QAAQ,IAAI,aAAa,KAAK,GAAG,UAAU,KAAK;AAAA,IACjJ,MAAM,EAAE,YAAY,QAAQ,IAAI,aAAa,MAAM,GAAG,WAAW,OAAO,mBAAmB,MAAM,YAAY,SAAS;AAAA,EACxH;AAGA,MAAI,OAAO,YAAY,OAAO,SAAS,SAAS,GAAG;AACjD,UAAM,iBAAiB,MAAM,kBAAkB,OAAO,UAAU,QAAQ,OAAO;AAC/E,WAAO,WAAW,EAAE,GAAG,OAAO,UAAU,GAAG,eAAe;AAAA,EAC5D;AAEA,QAAM,SAAS,mBAAmB,WAAW,QAAQ,aAAa,YAAY;AAU9E,QAAM,SAAS,QAAQ,IAAI,aAAa,QAAQ,KAAK;AACrD,QAAM,WAAW,QAAQ,WAAW,WAAW,IAAI,OAAO,MAAM,YAAY,MAAM,KAAK,OAAO;AAC9F,QAAM,WAAW,CAAC,OAAO,cAAc,aAAa,QAAQ,gBAAgB,KAAK,CAAC,MAAM,EAAE,OAAO,QAAQ;AACzG,QAAM,sBAAsB,WAAW,WAAW,OAAO;AAEzD,yBAAuB,mBAAmB;AAC1C,0BAAwB;AAAA,IACtB,gBAAgB,wBAAwB;AAAA,IACxC,YAAY,OAAO;AAAA,IACnB,mBAAmB,OAAO,cAAc,SAAS;AAAA,IACjD,YAAY;AAAA,IACZ,WAAW;AAAA,IACX;AAAA,IACA,WAAW,KAAK,SAAS,aAAa;AAAA,IACtC,SAAS,OAAO,uBAAuB;AAAA,EACzC,CAAC;AAED,6BAA2B,EAAE,cAAc,OAAO,mBAAmB,EAAE,CAAC;AACxE,oBAAkB,EAAE,WAAW,OAAO,QAAQ,SAAS,WAAW,YAAY,qBAAqB,cAAc,WAAW,cAAc,WAAW,WAAW,WAAW,WAAW,OAAO,YAAY,OAAO,WAAW,CAAC;AAC5N,gBAAc;AASd,MAAI,CAAC,YAAY,kBAAkB,OAAO,qBAAqB,OAAO,sBAAsB,WAAW,YAAY;AACjH,UAAM,GAAG,OAAO,kBAAkB;AAAA,MAChC,QAAQ;AAAA,MACR,SAAS,EAAE,gBAAgB,oBAAoB,GAAI,KAAK,SAAS,YAAY,EAAE,gBAAgB,KAAK,QAAQ,UAAU,IAAI,CAAC,EAAG;AAAA;AAAA;AAAA;AAAA,MAI9H,MAAM,KAAK,UAAU,EAAE,YAAY,OAAO,mBAAmB,QAAQ,OAAO,CAAC;AAAA,IAC/E,CAAC,EAAE,MAAM,MAAM;AAAA,IAAC,CAAC;AAAA,EACnB;AAGA,MAAI,OAAO,cAAc;AACvB,UAAM,SAAS,mBAAmB,OAAO,YAAY;AACrD,QAAI,OAAO,WAAW,GAAG;AACvB,YAAM,WAAW,MAAM;AAIrB,cAAM,QAAQ,mBAAmB,OAAO,aAAc,WAAW;AACjE,YAAI,OAAO;AAAE,iBAAO,OAAO,KAAK;AAAG;AAAA,QAAO;AAC1C,cAAM,WAAW,SAAS,eAAe,uBAAuB;AAChE,cAAM,YAAY,YAAY,SAAS,cAAc,KAAK;AAC1D,YAAI,CAAC,UAAU,IAAI;AACjB,oBAAU,KAAK;AACf,mBAAS,KAAK,YAAY,SAAS;AAAA,QACrC;AACA,eAAO,OAAO,SAAS;AAAA,MACzB;AACA,2BAAqB,OAAO,aAAa,SAAS,QAAQ;AAAA,IAC5D;AAAA,EACF;AACF;AAOA,SAAS,qBAAqB,SAAwC,QAA0B;AAC9F,MAAI,CAAC,WAAW,QAAQ,SAAS,aAAa;AAAE,WAAO;AAAG;AAAA,EAAO;AACjE,MAAI,QAAQ,SAAS,SAAU;AAC/B,MAAI,QAAQ,SAAS,SAAS;AAC5B,WAAO,WAAW,QAAQ,KAAK,IAAI,GAAG,QAAQ,OAAO,IAAI,GAAI;AAC7D;AAAA,EACF;AAEA,QAAM,SAAS,KAAK,IAAI,KAAK,IAAI,QAAQ,OAAO,CAAC,GAAG,CAAC;AACrD,MAAI,QAAQ;AACZ,QAAM,WAAW,MAAM;AACrB,QAAI,MAAO;AACX,UAAM,aAAa,SAAS,gBAAgB,eAAe,OAAO;AAClE,UAAM,WAAW,aAAa,IAAI,OAAO,UAAU,aAAa;AAChE,QAAI,YAAY,QAAQ;AACtB,cAAQ;AACR,aAAO,oBAAoB,UAAU,QAAQ;AAC7C,aAAO;AAAA,IACT;AAAA,EACF;AACA,SAAO,iBAAiB,UAAU,UAAU,EAAE,SAAS,KAAK,CAAC;AAC7D,WAAS;AACX;;;ACvMA,IAAM,mBAAsC,CAAC;AAC7C,IAAI,SAAS;AAEN,IAAM,OAAO;AAAA,EAClB,SAAS;AAAA,EACT,SAAS;AAAA,EACT,QAAQ,CAAC;AAAA,EACT,QAAQ,IAA2B;AACjC,QAAI,QAAQ;AAAE,SAAG,IAAI;AAAA,IAAE,OAAO;AAAE,uBAAiB,KAAK,EAAE;AAAA,IAAE;AAAA,EAC5D;AAAA,EACA,IAAI;AAAA,IACF,OAAO,YAAwD;AAC7D,aAAO,EAAE,GAAG,WAAW;AAAA,IACzB;AAAA,EACF;AAAA,EACA,eAAe;AAAA,IACb,MAAM;AAAA,MACJ,QAAQ,CAAC;AAAA,MACT,OAAO,SAAgE;AACrE,eAAO,OAAO,KAAK,cAAc,KAAK,QAAQ,OAAO;AAAA,MACvD;AAAA,IACF;AAAA,EACF;AAAA,EACA,WAAW;AAAA,EACX,SAAS;AAAA,EACT,YAAY;AAAA,EACZ,OAAa;AACX,QAAI,OAAQ;AACZ,SAAK,UAAU,YAAY;AAC3B,eAAW,MAAM,kBAAkB;AAAE,SAAG,IAAI;AAAA,IAAE;AAC9C,UAAM,WAAW,KAAK,QAAQ,aAAa,MAAM,YAAY,GAAG,KAAK,QAAQ,QAAQ;AACrF,SAAK,YAAY,oBAAoB,UAAU,KAAK,QAAQ,SAAS;AACrE,SAAK,UAAU,kBAAkB,UAAU,KAAK,QAAQ,SAAS;AACjE,SAAK,aAAa;AAClB,gBAAY;AACZ,kBAAc;AAGd,iBAAa;AAGb,oBAAgB,EAAE,SAAS,KAAK,QAAQ,CAAC;AACzC,aAAS;AAAA,EACX;AACF;AAEA,IAAI,OAAO,eAAe,aAAa;AAAE,EAAC,WAAmB,OAAO;AAAK;AACzE,IAAI,OAAO,aAAa,aAAa;AACnC,MAAI,SAAS,eAAe,WAAW;AACrC,aAAS,iBAAiB,oBAAoB,MAAM,KAAK,KAAK,CAAC;AAAA,EACjE,OAAO;AACL,SAAK,KAAK;AAAA,EACZ;AACF;",
|
|
6
|
-
"names": ["isOptedOut", "audienceName", "label"]
|
|
3
|
+
"sources": ["../src/context.ts", "../src/entity.ts", "../src/feed.ts", "../src/render-feed.ts", "../src/module-system.ts", "../src/form-hooks.ts", "../src/nav/menu.ts", "../src/experiments/index.ts", "../src/audience/selfid-collector.ts", "../src/audience/selfid-api.ts", "../../core/src/audiences/types.ts", "../../core/src/audiences/evaluator/glob.ts", "../../core/src/audiences/evaluator/instant-match.ts", "../../core/src/audiences/evaluator/score.ts", "../../core/src/audiences/evaluator/condition-registry.ts", "../../core/src/audiences/evaluator/resolver.ts", "../src/audience/query-collector.ts", "../src/cookies.ts", "../src/consent.ts", "../src/audience/behavioral-collector.ts", "../src/audience/evaluate.ts", "../src/audience/dom-patcher.ts", "../src/audience/transparency.ts", "../src/audience/preview.ts", "../src/audience/data-layer.ts", "../src/audience/adapter-runner.ts", "../src/audience/selfid-submit.ts", "../src/audience/selfid-widget.ts", "../src/personalization/badge.ts", "../src/audience/index.ts", "../src/index.ts"],
|
|
4
|
+
"sourcesContent": ["// packages/kywi-js/src/context.ts\nimport type { KywiContext } from './types.js'\n\nexport function readContext(): KywiContext {\n const meta = (name: string): string => {\n if (typeof document === 'undefined') return ''\n const el = document.querySelector(`meta[name=\"kywi:${name}\"]`)\n return el?.getAttribute('content') ?? ''\n }\n return {\n siteId: meta('site-id') || document?.documentElement?.getAttribute('data-kywi-site') || '',\n locale: document?.documentElement?.lang || 'en',\n themePath: meta('theme-path') || '',\n basePath: meta('base-path') || '/',\n csrfToken: meta('csrf-token') || '',\n isEditMode: document?.body?.classList?.contains('kywi-edit-mode') ?? false,\n }\n}\n", "// packages/kywi-js/src/entity.ts\nimport type { KywiEntityObject } from './types.js'\n\nfunction createEntity(type: string, basePath: string, csrfToken: string, data: Record<string, unknown>): KywiEntityObject {\n const fields = { ...data }\n const dirty = new Map<string, unknown>()\n const errors: string[] = []\n const headers: Record<string, string> = { 'Content-Type': 'application/json' }\n if (csrfToken) headers['X-CSRF-Token'] = csrfToken\n\n const entity: KywiEntityObject = {\n get(field: string) { return fields[field] },\n set(field: string, value: unknown) { fields[field] = value; dirty.set(field, value); return entity },\n async save() {\n const id = fields['id'] as string | undefined\n const url = id ? `${basePath}/content/${type}/${id}` : `${basePath}/content/${type}`\n const res = await fetch(url, { method: id ? 'PUT' : 'POST', headers, body: JSON.stringify(Object.fromEntries(dirty)) })\n const json = await res.json() as { data?: Record<string, unknown>; error?: { message: string } }\n if (!res.ok || json.error) { errors.push(json.error?.message ?? 'Save failed'); throw entity }\n if (json.data) Object.assign(fields, json.data)\n dirty.clear()\n return entity\n },\n async delete() {\n const id = fields['id'] as string\n await fetch(`${basePath}/content/${type}/${id}`, { method: 'DELETE', headers })\n },\n getErrors() { return [...errors] },\n async loadBy(field: string, value: string) {\n const url = field === 'id' ? `${basePath}/content/${type}/${value}` : `${basePath}/content/${type}?${field}=${encodeURIComponent(value)}`\n const res = await fetch(url, { headers })\n const json = await res.json() as { data?: Record<string, unknown>; error?: { message: string } }\n if (!res.ok || json.error) { errors.push(json.error?.message ?? 'Load failed'); throw entity }\n if (json.data) Object.assign(fields, json.data)\n return entity\n },\n }\n return entity\n}\n\nexport function createEntityFactory(basePath: string, csrfToken: string) {\n return (type: string): KywiEntityObject => createEntity(type, basePath, csrfToken, {})\n}\n", "// packages/kywi-js/src/feed.ts\nimport type { KywiEntityObject, KywiFeedResult } from './types.js'\n\ninterface Filter { field: string; operator: string; value?: unknown; conjunction: 'and' | 'or' }\n\nexport type AggregateFunction = 'count' | 'sum' | 'min' | 'max' | 'avg'\nexport type AggregateResult = Record<string, unknown>\n\nclass PropBuilder {\n constructor(private builder: FeedBuilder, private field: string, private conj: 'and' | 'or') {}\n private add(op: string, value?: unknown) { this.builder._filters.push({ field: this.field, operator: op, value, conjunction: this.conj }); return this.builder }\n isEQ(v: unknown) { return this.add('eq', v) }\n isNEQ(v: unknown) { return this.add('neq', v) }\n isLT(v: unknown) { return this.add('lt', v) }\n isLTE(v: unknown) { return this.add('lte', v) }\n isGT(v: unknown) { return this.add('gt', v) }\n isGTE(v: unknown) { return this.add('gte', v) }\n beginsWith(v: unknown) { return this.add('beginsWith', v) }\n endsWith(v: unknown) { return this.add('endsWith', v) }\n contains(v: unknown) { return this.add('contains', v) }\n isNull() { return this.add('isNull') }\n isNotNull() { return this.add('isNotNull') }\n}\n\nclass FeedBuilder {\n _filters: Filter[] = []\n private _sort: { field: string; direction: 'asc' | 'desc' } | null = null\n private _limit: number | null = null\n private _page: number | null = null\n constructor(private contentType: string, private basePath: string, private csrfToken: string) {}\n\n where(): this { return this }\n prop(field: string) { return new PropBuilder(this, field, 'and') }\n andProp(field: string) { return new PropBuilder(this, field, 'and') }\n orProp(field: string) { return new PropBuilder(this, field, 'or') }\n sort(field: string, direction: 'asc' | 'desc' = 'asc'): this { this._sort = { field, direction }; return this }\n maxItems(n: number): this { this._limit = n; return this }\n page(n: number): this { this._page = n; return this }\n\n private buildBody(): Record<string, unknown> {\n const filterArray = this._filters.map(f => ({\n field: f.field,\n operator: f.operator,\n value: f.value,\n conjunction: f.conjunction,\n }))\n const body: Record<string, unknown> = { contentType: this.contentType }\n if (filterArray.length > 0) body['filters'] = filterArray\n if (this._sort) body['sort'] = this._sort\n if (this._limit !== null) body['limit'] = this._limit\n if (this._page !== null) body['page'] = this._page\n return body\n }\n\n private buildHeaders(): Record<string, string> {\n const headers: Record<string, string> = { 'Content-Type': 'application/json' }\n if (this.csrfToken) headers['X-CSRF-Token'] = this.csrfToken\n return headers\n }\n\n async aggregate(fn: AggregateFunction, field?: string): Promise<AggregateResult> {\n const body = this.buildBody()\n const agg: Record<string, string> = { function: fn }\n if (field) agg['field'] = field\n body['aggregate'] = agg\n\n const res = await fetch(`${this.basePath}/feeds/aggregate`, {\n method: 'POST',\n headers: this.buildHeaders(),\n body: JSON.stringify(body),\n })\n const json = await res.json() as { data?: AggregateResult }\n return json.data ?? {}\n }\n\n async getQuery(): Promise<KywiFeedResult> {\n const body = this.buildBody()\n const res = await fetch(`${this.basePath}/feeds/query`, {\n method: 'POST',\n headers: this.buildHeaders(),\n body: JSON.stringify(body),\n })\n // Two response envelopes are supported:\n // 1. The live @kywi-software/core server (POST /feeds/query -> jsonOk(executeFeedQuery))\n // returns { data: { items, total, page, pageSize, totalPages } }.\n // 2. A flatter { data: [ ...items ], meta: { total, page, totalPages } } shape.\n // Normalise both so the decoupled client works against the real API.\n const json = await res.json() as {\n data?: Array<Record<string, unknown>> | { items?: Array<Record<string, unknown>>; total?: number; page?: number; pageSize?: number; totalPages?: number }\n meta?: { total?: number; page?: number; totalPages?: number }\n }\n\n const payload = json.data\n const rawItems: Array<Record<string, unknown>> = Array.isArray(payload)\n ? payload\n : (payload?.items ?? [])\n const total = Array.isArray(payload) ? (json.meta?.total ?? rawItems.length) : (payload?.total ?? rawItems.length)\n const page = Array.isArray(payload) ? (json.meta?.page ?? 1) : (payload?.page ?? 1)\n const pages = Array.isArray(payload) ? (json.meta?.totalPages ?? 0) : (payload?.totalPages ?? 0)\n\n const items: KywiEntityObject[] = rawItems.map(d => ({\n get(field: string) { return d[field] },\n set() { throw new Error('Feed results are read-only') },\n save() { return Promise.reject(new Error('Feed results are read-only')) },\n delete() { return Promise.reject(new Error('Feed results are read-only')) },\n getErrors() { return [] },\n loadBy() { return Promise.reject(new Error('Use Kywi.getEntity() to load by field')) },\n }))\n\n return { items, total, page, pages }\n }\n}\n\nexport function createFeedFactory(basePath: string, csrfToken: string) {\n return (type: string) => new FeedBuilder(type, basePath, csrfToken)\n}\n", "// packages/kywi-js/src/render-feed.ts\nimport type { KywiEntityObject, KywiFeedResult } from './types.js'\n\ninterface RenderFeedOptions {\n feed: { getQuery(): Promise<KywiFeedResult> }\n container: HTMLElement\n template: (item: KywiEntityObject) => string | Node\n loadingText?: string; emptyText?: string\n wrapperTag?: string; wrapperClass?: string\n unsafeHtml?: boolean\n onRender?: (container: HTMLElement) => void\n}\n\nexport async function renderFeed(options: RenderFeedOptions): Promise<void> {\n const { feed, container, template, loadingText = '', emptyText = '', wrapperTag = 'div', wrapperClass, unsafeHtml = false, onRender } = options\n if (loadingText) container.textContent = loadingText\n const result = await feed.getQuery()\n container.textContent = ''\n if (result.items.length === 0) { if (emptyText) container.textContent = emptyText; return }\n const wrapper = document.createElement(wrapperTag)\n if (wrapperClass) wrapper.className = wrapperClass\n for (const item of result.items) {\n const rendered = template(item)\n if (rendered instanceof Node) {\n wrapper.appendChild(rendered)\n } else if (unsafeHtml) {\n const span = document.createElement('span')\n // intentional: caller explicitly opts in via unsafeHtml\n span['innerHTML'] = rendered\n wrapper.appendChild(span)\n } else {\n const span = document.createElement('span')\n span.textContent = rendered // SAFE: always textContent for string templates\n wrapper.appendChild(span)\n }\n }\n container.appendChild(wrapper)\n if (onRender) onRender(container)\n}\n", "// packages/kywi-js/src/module-system.ts\nimport { Kywi } from './index.js'\nimport type { KywiModuleContext } from './types.js'\n\nfunction readParams(el: HTMLElement): Record<string, string> {\n const params: Record<string, string> = {}\n for (const attr of Array.from(el.attributes)) {\n if (attr.name.startsWith('data-kywi-param-')) {\n params[attr.name.slice(16)] = attr.value\n }\n }\n return params\n}\n\nexport function initModules(): void {\n const elements = document.querySelectorAll('[data-kywi-module]')\n for (const el of Array.from(elements)) {\n const htmlEl = el as HTMLElement\n const moduleName = htmlEl.getAttribute('data-kywi-module')\n if (!moduleName) continue\n const def = Kywi.Module[moduleName]\n if (!def || !def.renderClient) continue\n const context: KywiModuleContext = { targetEl: htmlEl, ...readParams(htmlEl) }\n def.renderClient.call({ context, ...def })\n }\n}\n", "// packages/kywi-js/src/form-hooks.ts\nimport { Kywi } from './index.js'\n\nexport function initFormHooks(): void {\n const forms = document.querySelectorAll('[data-kywi-form]')\n for (const formEl of Array.from(forms)) {\n const htmlForm = formEl as HTMLFormElement\n const hooks = Kywi.DisplayObject.Form._hooks\n if (hooks['onAfterRender']) hooks['onAfterRender'].call({ context: { targetEl: htmlForm } })\n htmlForm.addEventListener('submit', (e) => {\n if (hooks['onSubmit']) {\n const result = hooks['onSubmit'].call({ context: { targetEl: htmlForm } })\n if (result === false) e.preventDefault()\n }\n })\n }\n}\n\nexport async function submitKywiForm(form: HTMLFormElement, endpoint: string): Promise<void> {\n const formData = new FormData(form)\n const data: Record<string, string> = {}\n formData.forEach((value, key) => { data[key] = value as string })\n\n const res = await fetch(endpoint, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(data),\n })\n const json = await res.json() as { success: boolean; errors?: Record<string, string>; data?: Record<string, unknown> }\n const hooks = Kywi.DisplayObject.Form._hooks\n\n if (!json.success && json.errors) {\n if (typeof hooks['onAfterErrorRender'] === 'function') {\n hooks['onAfterErrorRender']({ errors: json.errors, targetEl: form })\n }\n } else if (json.success && json.data) {\n if (typeof hooks['onAfterResponseRender'] === 'function') {\n hooks['onAfterResponseRender']({ response: json.data, targetEl: form })\n }\n }\n}\n", "// packages/kywi-js/src/nav/menu.ts\n//\n// Progressive enhancement for the server-rendered nav modules (`navMenu` /\n// `navigation`, kywi-cms#111). The markup is already usable without this file:\n// every link is server-rendered, panels open on `:hover` / `:focus-within`, and\n// below the breakpoint the whole bar collapses behind a native `<details>`\n// hamburger. What this adds is the polish CSS cannot express \u2014 hover intent\n// (a close delay so the pointer can cross the gap), a real click target with\n// `aria-expanded` in sync, Escape-to-close with focus returned, arrow-key\n// movement inside an open panel, outside-click dismissal, and flipping a panel\n// that would run off the right edge of the viewport.\n//\n// Dependency-free and defensive: it enhances whatever `[data-kywi-nav]` roots\n// exist and no-ops when a nav has no disclosure items.\n\n/**\n * Desktop breakpoint, in pixels. The single source shared with core's\n * `site/styles.css`, whose one media query is `(max-width: 859.98px)` \u2014 the\n * last step below this value. Change both together.\n */\nexport const NAV_BREAKPOINT = 860\n\n/** Grace period before a hover-opened panel closes, in ms. */\nconst CLOSE_DELAY_MS = 120\n\nconst ITEM_CLASS = 'kywi-nav-item--has-children'\nconst OPEN_CLASS = 'kywi-nav-item--open'\nconst TOGGLE_CLASS = 'kywi-nav-toggle'\nconst PANEL_CLASS = 'kywi-nav-panel'\nconst FLIP_CLASS = 'kywi-nav-panel--flip'\nconst READY_ATTRIBUTE = 'data-kywi-nav-ready'\n\ninterface NavEntry {\n item: HTMLElement\n toggle: HTMLElement\n panel: HTMLElement\n}\n\nfunction directChild(parent: HTMLElement, className: string): HTMLElement | null {\n for (const child of Array.from(parent.children)) {\n if (child instanceof HTMLElement && child.classList.contains(className)) return child\n }\n return null\n}\n\nfunction isDesktop(): boolean {\n if (typeof window === 'undefined') return false\n if (typeof window.matchMedia !== 'function') return true\n return window.matchMedia(`(min-width: ${NAV_BREAKPOINT}px)`).matches\n}\n\nfunction panelLinks(panel: HTMLElement): HTMLElement[] {\n return Array.from(panel.querySelectorAll<HTMLElement>('a[href], button:not([disabled])'))\n}\n\n/** Enhance one `[data-kywi-nav]` root. Returns a teardown for tests/SPAs. */\nexport function enhanceNav(nav: HTMLElement): () => void {\n const cleanups: Array<() => void> = []\n const entries: NavEntry[] = []\n\n for (const item of Array.from(nav.querySelectorAll<HTMLElement>(`.${ITEM_CLASS}`))) {\n const toggle = directChild(item, TOGGLE_CLASS)\n const panel = directChild(item, PANEL_CLASS)\n if (toggle && panel) entries.push({ item, toggle, panel })\n }\n if (entries.length === 0) return () => {}\n\n let closeTimer: ReturnType<typeof setTimeout> | undefined\n const cancelClose = () => {\n if (closeTimer !== undefined) { clearTimeout(closeTimer); closeTimer = undefined }\n }\n\n function isOpen(entry: NavEntry): boolean {\n return entry.item.classList.contains(OPEN_CLASS)\n }\n\n function close(entry: NavEntry): void {\n entry.item.classList.remove(OPEN_CLASS)\n entry.toggle.setAttribute('aria-expanded', 'false')\n entry.panel.classList.remove(FLIP_CLASS)\n }\n\n function closeAll(except?: NavEntry): void {\n for (const entry of entries) {\n // A nested fly-out keeps its ancestors open.\n if (except && (entry === except || entry.item.contains(except.item))) continue\n close(entry)\n }\n }\n\n /** Flip a panel that would overflow the right edge (measured while open). */\n function applyEdgeFlip(entry: NavEntry): void {\n if (typeof entry.panel.getBoundingClientRect !== 'function') return\n entry.panel.classList.remove(FLIP_CLASS)\n const rect = entry.panel.getBoundingClientRect()\n const viewport = window.innerWidth || document.documentElement.clientWidth || 0\n if (viewport > 0 && rect.width > 0 && rect.right > viewport - 4) {\n entry.panel.classList.add(FLIP_CLASS)\n }\n }\n\n function open(entry: NavEntry): void {\n cancelClose()\n closeAll(entry)\n entry.item.classList.add(OPEN_CLASS)\n entry.toggle.setAttribute('aria-expanded', 'true')\n applyEdgeFlip(entry)\n }\n\n function on<E extends Event>(\n target: EventTarget,\n type: string,\n handler: (event: E) => void,\n options?: AddEventListenerOptions,\n ): void {\n target.addEventListener(type, handler as EventListener, options)\n cleanups.push(() => target.removeEventListener(type, handler as EventListener, options))\n }\n\n for (const entry of entries) {\n on(entry.toggle, 'click', (event: MouseEvent) => {\n event.preventDefault()\n if (isOpen(entry)) { close(entry) } else { open(entry) }\n })\n\n // Hover intent \u2014 pointer only, and only where panels actually float.\n on(entry.item, 'mouseenter', () => {\n if (!isDesktop()) return\n open(entry)\n })\n on(entry.item, 'mouseleave', () => {\n if (!isDesktop()) return\n cancelClose()\n closeTimer = setTimeout(() => { close(entry); closeTimer = undefined }, CLOSE_DELAY_MS)\n })\n }\n\n // Keyboard focus moving into a panel opens it; moving out of the item closes\n // it. Focus landing on the toggle itself is left alone so a click (which\n // focuses first) still reads as a toggle rather than an open-then-close.\n on(nav, 'focusin', (event: FocusEvent) => {\n const target = event.target as Node | null\n if (!target) return\n for (const entry of entries) {\n if (entry.panel.contains(target)) { open(entry); return }\n }\n for (const entry of entries) {\n if (!entry.item.contains(target)) close(entry)\n }\n })\n\n on(nav, 'keydown', (event: KeyboardEvent) => {\n const target = event.target as HTMLElement | null\n if (!target) return\n const entry = entries.find((e) => e.item.contains(target))\n if (!entry) return\n\n if (event.key === 'Escape') {\n const openEntry = entries.filter((e) => e.item.contains(target) && isOpen(e)).pop()\n if (!openEntry) return\n event.preventDefault()\n close(openEntry)\n openEntry.toggle.focus()\n return\n }\n\n if (event.key !== 'ArrowDown' && event.key !== 'ArrowUp') return\n\n // On the trigger: ArrowDown opens and enters the panel, ArrowUp closes.\n if (target === entry.toggle) {\n if (event.key === 'ArrowDown') {\n event.preventDefault()\n if (!isOpen(entry)) open(entry)\n panelLinks(entry.panel)[0]?.focus()\n } else if (isOpen(entry)) {\n event.preventDefault()\n close(entry)\n }\n return\n }\n\n // Inside a panel: move between its links.\n const owner = entries.find((e) => e.panel.contains(target))\n if (!owner) return\n const links = panelLinks(owner.panel)\n const index = links.indexOf(target)\n if (index === -1) return\n event.preventDefault()\n const next = event.key === 'ArrowDown'\n ? links[(index + 1) % links.length]\n : links[(index - 1 + links.length) % links.length]\n next?.focus()\n })\n\n on(document, 'click', (event: MouseEvent) => {\n const target = event.target as Node | null\n if (target && nav.contains(target)) return\n cancelClose()\n closeAll()\n })\n\n // Crossing the breakpoint changes what \"open\" means; start from closed.\n if (typeof window.matchMedia === 'function') {\n const query = window.matchMedia(`(min-width: ${NAV_BREAKPOINT}px)`)\n const onChange = () => { cancelClose(); closeAll() }\n if (typeof query.addEventListener === 'function') {\n query.addEventListener('change', onChange)\n cleanups.push(() => query.removeEventListener('change', onChange))\n }\n }\n\n nav.setAttribute(READY_ATTRIBUTE, '')\n\n return () => {\n cancelClose()\n for (const undo of cleanups) undo()\n cleanups.length = 0\n closeAll()\n nav.removeAttribute(READY_ATTRIBUTE)\n }\n}\n\n/**\n * Enhance every nav under `root` that has not been enhanced yet. Called from\n * `Kywi.boot()`; safe to call again after injecting markup client-side.\n */\nexport function initNavMenus(root: ParentNode = document): () => void {\n if (typeof document === 'undefined') return () => {}\n const teardowns: Array<() => void> = []\n for (const nav of Array.from(root.querySelectorAll<HTMLElement>('[data-kywi-nav]'))) {\n if (nav.hasAttribute(READY_ATTRIBUTE)) continue\n teardowns.push(enhanceNav(nav))\n }\n return () => { for (const undo of teardowns) undo() }\n}\n", "// packages/kywi-js/src/experiments/index.ts\n//\n// Client-side A/B experiment beacons. Reads the server-rendered kywi:* meta\n// tags, confirms the exposure, and reports conversion signals (pageview,\n// form-submit, custom) back to the runtime endpoints. Fire-and-forget: every\n// call is best-effort and never throws into the host page.\n\nimport { readContext } from '../context.js'\nimport type { KywiContext } from '../types.js'\n\nexport interface ConversionSignal {\n type: 'pageview' | 'form_submit' | 'custom'\n url?: string\n formId?: string\n eventName?: string\n}\n\nexport interface ExperimentBootConfig {\n context?: KywiContext\n apiBase?: string\n visitorId?: string\n currentPath?: string\n /** Auto-emit a pageview conversion signal on boot (default true). */\n autoPageview?: boolean\n}\n\nexport interface ExperimentRuntime {\n /** Emit a custom conversion event, e.g. kywi.track('signup'). */\n track(eventName: string): void\n /** Emit a raw conversion signal. */\n trackConversion(signal: ConversionSignal): void\n}\n\nfunction apiBaseFrom(ctx: KywiContext): string {\n return ctx.basePath && ctx.basePath !== '/' ? `${ctx.basePath}/api/v1` : '/api/v1'\n}\n\nfunction metaContent(name: string): string | null {\n if (typeof document === 'undefined') return null\n return document.querySelector(`meta[name=\"kywi:${name}\"]`)?.getAttribute('content') ?? null\n}\n\n// A single document-level submit listener is installed once and routes to the\n// most recent boot's handler \u2014 booting twice must not double-count conversions.\nlet submitListenerInstalled = false\nlet currentFormConversion: ((formId: string) => void) | null = null\n\nfunction installFormListener(): void {\n if (submitListenerInstalled || typeof document === 'undefined') return\n submitListenerInstalled = true\n document.addEventListener(\n 'submit',\n (e) => {\n const form = e.target as HTMLElement | null\n if (!form || form.tagName !== 'FORM') return\n const formId = form.getAttribute('data-kywi-form-id') ?? form.getAttribute('id')\n if (formId && currentFormConversion) currentFormConversion(formId)\n },\n true,\n )\n}\n\nfunction beacon(url: string, body: unknown, csrfToken?: string): void {\n try {\n void fetch(url, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', ...(csrfToken ? { 'X-CSRF-Token': csrfToken } : {}) },\n body: JSON.stringify(body),\n keepalive: true,\n }).catch(() => {})\n } catch {\n /* never throw into the host page */\n }\n}\n\n/**\n * Boot the experiment beacons. Safe to call once per page load; when no\n * experiment meta is present it only wires the conversion listeners.\n */\nexport function bootExperiments(config: ExperimentBootConfig = {}): ExperimentRuntime {\n const ctx = config.context ?? readContext()\n const apiBase = config.apiBase ?? apiBaseFrom(ctx)\n const csrf = ctx.csrfToken || undefined\n const experimentId = metaContent('experiment-id')\n const visitorId = config.visitorId ?? metaContent('visitor-id') ?? undefined\n const currentPath = config.currentPath ?? (typeof location !== 'undefined' ? location.pathname : '/')\n\n function trackConversion(signal: ConversionSignal): void {\n beacon(`${apiBase}/kywi/experiments/conversion`, { visitorId, event: signal }, csrf)\n }\n function track(eventName: string): void {\n trackConversion({ type: 'custom', eventName })\n }\n\n // 1) Exposure \u2014 confirm the server-rendered variant was seen. The variant is\n // recomputed server-side, so we only send the experiment + visitor ids.\n if (experimentId) {\n beacon(`${apiBase}/kywi/experiments/exposure`, { experimentId, visitorId }, csrf)\n }\n\n // 2) Pageview conversion signal \u2014 lets pageview-goal experiments convert when\n // an enrolled visitor lands on the goal path.\n if (config.autoPageview !== false) {\n trackConversion({ type: 'pageview', url: currentPath })\n }\n\n // 3) Form-submit conversion \u2014 capture submits of any tagged form. The listener\n // is installed once; this boot becomes its active handler.\n currentFormConversion = (formId: string) => trackConversion({ type: 'form_submit', formId })\n installFormListener()\n\n const runtime: ExperimentRuntime = { track, trackConversion }\n\n // Expose on the data layer so analytics / manual conversions can reach it.\n const g = ((globalThis as unknown as { kywi?: Record<string, unknown> }).kywi ?? {})\n g['track'] = track\n g['trackConversion'] = trackConversion\n ;(globalThis as unknown as { kywi?: Record<string, unknown> }).kywi = g\n\n return runtime\n}\n", "// packages/kywi-js/src/audience/selfid-collector.ts\n//\n// The browser-side store of the visitor's self-ID answers. `Kywi.setSelfId()`\n// (selfid-api.ts) is the public way to write it; the server keeps its own copy\n// in a cookie of the same name so a server-pruned variant arm can be chosen\n// from the same answers (kywi-cms#200).\nconst SELFID_KEY = 'kywi_selfid'\n\nexport function collectSelfId(): Record<string, string> {\n const raw = readStorage(SELFID_KEY)\n if (!raw) return {}\n try {\n const parsed = JSON.parse(raw) as unknown\n if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) return {}\n return parsed as Record<string, string>\n } catch { return {} }\n}\n\nexport function storeSelfId(responses: Record<string, string>): void {\n try { localStorage.setItem(SELFID_KEY, JSON.stringify(responses)) } catch { /* private mode / quota */ }\n}\n\n/** Forget the visitor's answers entirely (the browser half of clearSelfId). */\nexport function clearStoredSelfId(): void {\n try { localStorage.removeItem(SELFID_KEY) } catch { /* private mode / quota */ }\n}\n\n/** Storage reads must never break the page \u2014 private mode throws on access. */\nfunction readStorage(key: string): string | null {\n try { return localStorage.getItem(key) } catch { return null }\n}\n", "// packages/kywi-js/src/audience/selfid-api.ts\n//\n// `Kywi.setSelfId()` / `Kywi.clearSelfId()` \u2014 the public way for a site's own\n// control to record who the visitor says they are (kywi-cms#200).\n//\n// Before this there was no supported client API at all. The built-in hello-bar\n// widget writes `localStorage['kywi_selfid']` through a module-private\n// `storeSelfId`, neither that nor `handleSelfIdSubmit` is on the `Kywi` global,\n// `POST /kywi/audience` needs an audience UUID a public page cannot discover,\n// and `GET /audiences` is admin-only. A site with its own \"What brings you\n// here?\" control had to reimplement the whole thing, server route included.\n//\n// WHAT A CALL DOES, in order \u2014 each step is load-bearing:\n//\n// 1. Merges the answers into `localStorage['kywi_selfid']`, the key\n// `collectSelfId()` reads on every boot. That is what makes `selfId.*`\n// match during the runtime's own client-side re-evaluation.\n// 2. Posts the merged bag to `POST {apiBase}/kywi/self-id`, which stores it\n// server-side (consent permitting) and pins the audience it resolves to.\n// This is the step that matters: since kywi-cms#167 the losing variant\n// arms are chosen AND PRUNED on the server, so they are not in the DOM for\n// any amount of client-side swapping to reach. Only a fresh server render\n// can show them, and the only thing that render reads is the pin.\n// 3. Reloads, so that render happens \u2014 the same pin-then-reload sequence the\n// transparency panel's audience switcher performs. Pass `reload: false` to\n// handle the re-render yourself.\n//\n// WITHOUT JAVASCRIPT, a plain `<form method=\"post\" action=\"/api/v1/kywi/self-id\">`\n// with a `returnTo` field does steps 2 and 3 on its own (the endpoint 303s\n// back), so the control still works \u2014 only step 1's browser-side copy is lost.\n\nimport { collectSelfId, storeSelfId, clearStoredSelfId } from './selfid-collector.js'\n\n/** The audience the server resolved the visitor to, if any. */\nexport interface SelfIdAudience {\n id: string\n name: string\n}\n\nexport interface SelfIdResult {\n /** The audience the answers resolved to, or null when nothing matched. */\n audience: SelfIdAudience | null\n /**\n * Whether the answers were stored SERVER-side. False for a visitor who has\n * not consented to personalization \u2014 the audience is still pinned and this\n * page still changes, the profile simply is not kept (kywi-cms#91).\n */\n persisted: boolean\n /** The answers now on record in the browser. */\n fields: Record<string, string>\n}\n\nexport interface SelfIdOptions {\n /** Mounted CMS API base. Defaults to the one `Kywi.boot()` derived. */\n apiBase?: string\n csrfToken?: string | null\n /** Reload so the server can re-render with the new pin. Default true. */\n reload?: boolean\n /**\n * Pin this audience BY ID instead of resolving one from the answers \u2014 the id\n * comes from `GET {apiBase}/kywi/audiences`, which lists the audiences an\n * editor marked public (kywi-cms#200). Use it when the page renders the\n * chooser FROM that list, so the visitor's pick lands exactly, and an\n * audience with no rules of its own is still reachable. The server validates\n * the id against the same public, site-scoped list.\n */\n audienceId?: string\n}\n\nconst DEFAULT_API_BASE = '/api/v1'\n\n/** The API base `Kywi.boot()` derived, without importing the barrel (cycle). */\nfunction resolveApiBase(explicit?: string): string {\n if (explicit) return explicit\n const basePath = (globalThis as { Kywi?: { context?: { basePath?: string } } }).Kywi?.context?.basePath\n if (!basePath || basePath === '/') return DEFAULT_API_BASE\n return `${basePath}/api/v1`\n}\n\nfunction resolveCsrfToken(explicit?: string | null): string | null {\n if (explicit !== undefined) return explicit\n return (globalThis as { Kywi?: { context?: { csrfToken?: string | null } } }).Kywi?.context?.csrfToken ?? null\n}\n\nfunction reloadPage(): void {\n try { window.location.reload() } catch { /* non-navigating test environments */ }\n}\n\n/**\n * Record the visitor's self-identification and resolve their audience.\n *\n * Answers MERGE into whatever is already on record \u2014 two controls on the same\n * page (a role picker and an industry picker) do not clobber each other \u2014 and a\n * field submitted as `''` is removed, which is what a \"Change\" / \"Clear\"\n * control sends.\n *\n * Pass `audienceId` to pin one of the site's PUBLIC audiences directly instead\n * of resolving from the answers; see {@link SelfIdOptions}.\n *\n * Resolves once the server has answered, with the audience it resolved to.\n * Never throws for a failed request: an offline visitor gets\n * `{ audience: null, persisted: false }` and no reload, so the page is not\n * dropped back onto itself with nothing changed.\n */\nexport async function setSelfId(\n fields: Record<string, string>,\n options: SelfIdOptions = {},\n): Promise<SelfIdResult> {\n const merged = { ...collectSelfId() }\n for (const [key, value] of Object.entries(fields)) {\n if (value === '') delete merged[key]\n else merged[key] = value\n }\n if (Object.keys(merged).length > 0) storeSelfId(merged)\n else clearStoredSelfId()\n\n const result = await submit(merged, options)\n if (options.reload !== false && result !== null) reloadPage()\n return result ?? { audience: null, persisted: false, fields: merged }\n}\n\n/**\n * Forget the visitor's self-identification: clears the browser copy, the\n * server-side copy and the audience pin, so resolution goes back to signals.\n * The mirror of `setSelfId`, and what a \"Change\" control calls.\n */\nexport async function clearSelfId(options: SelfIdOptions = {}): Promise<SelfIdResult> {\n clearStoredSelfId()\n const result = await submit({}, options)\n if (options.reload !== false && result !== null) reloadPage()\n return result ?? { audience: null, persisted: false, fields: {} }\n}\n\n/** POST the answers. Null when the request could not be made or was refused. */\nasync function submit(\n fields: Record<string, string>,\n options: SelfIdOptions,\n): Promise<SelfIdResult | null> {\n const csrfToken = resolveCsrfToken(options.csrfToken)\n try {\n const res = await fetch(`${resolveApiBase(options.apiBase)}/kywi/self-id`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', ...(csrfToken ? { 'X-CSRF-Token': csrfToken } : {}) },\n body: JSON.stringify({\n fields,\n ...(options.audienceId ? { audienceId: options.audienceId } : {}),\n }),\n })\n if (!res.ok) return null\n const body = (await res.json().catch(() => null)) as\n | { data?: { audience?: SelfIdAudience | null; persisted?: boolean } }\n | null\n return {\n audience: body?.data?.audience ?? null,\n persisted: body?.data?.persisted === true,\n fields,\n }\n } catch {\n return null\n }\n}\n", "// \u2500\u2500 Visitor Signals \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport interface VisitorSignals {\n utm: {\n source: string | null\n medium: string | null\n campaign: string | null\n term: string | null\n content: string | null\n }\n referrer: {\n raw: string | null\n domain: string | null\n sourceType: 'organic' | 'paid' | 'social' | 'email' | 'direct' | 'other'\n }\n session: {\n pageViewCount: number\n totalPageViews: number\n sessionDuration: number\n entryPage: string\n pagesVisited: string[]\n }\n behavioral: {\n categoryScores: Record<string, number>\n totalScore: number\n }\n /**\n * Every query parameter on the request being served, as the `query.<param>`\n * signal family (kywi-cms#196). Not stored and never gated \u2014 like the URL's\n * own `utm_*` params these are the content of this request, so a campaign link\n * (`/developers?from=marketer`) is a one-line rule that fires on the very\n * request that carries it.\n */\n query: Record<string, string>\n selfId: Record<string, string>\n identity: {\n visitorId: string\n isKnown: boolean\n maLeadId: string | null\n }\n adapters: Record<string, unknown>\n meta: {\n isOptedOut: boolean\n isPreview: boolean\n previewAudienceId: string | null\n resolvedAt: 'server' | 'client'\n }\n}\n\n// \u2500\u2500 Evaluation Result \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport interface VisitorContext {\n request: Request\n partialSignals?: Partial<VisitorSignals>\n}\n\nexport interface AudienceResult {\n winningAudienceId: string | null\n allMatchedIds: string[]\n signals: VisitorSignals\n isOptedOut: boolean\n}\n\n// \u2500\u2500 Audience & Rule Types \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport type AudienceStatus = 'active' | 'draft' | 'archived'\n\nexport interface Audience {\n id: string\n siteId: string\n name: string\n description: string | null\n priority: number\n status: AudienceStatus\n conditionGroups: ConditionGroup[]\n scoreThreshold: number | null\n /**\n * The audience's NAME may be listed to the public by\n * `GET /api/v1/kywi/audiences`, so a page can offer the visitor a choice\n * (\"What brings you here?\") without an admin token (kywi-cms#200). Off by\n * default \u2014 an audience is an internal segment definition until someone\n * deliberately publishes its name.\n *\n * Optional because this shape is also parsed from payloads written before the\n * column existed (an audience list serialized into a page, a fixture); absent\n * reads as false everywhere, and the public listing filters in SQL rather than\n * trusting this field.\n */\n public?: boolean\n createdAt: Date\n updatedAt: Date\n}\n\nexport interface ConditionGroup {\n id: string\n audienceId: string\n sortOrder: number\n conditions: Condition[]\n}\n\nexport type Condition = InstantMatchCondition | ScoreContributionCondition\n\nexport interface InstantMatchCondition {\n type: 'instant'\n id: string\n groupId: string\n signal: InstantSignalRef\n operator: InstantOperator\n value: string | string[]\n}\n\nexport type InstantSignalRef =\n | 'utm.source' | 'utm.medium' | 'utm.campaign' | 'utm.term' | 'utm.content'\n | 'referrer.domain' | 'referrer.sourceType'\n | `selfId.${string}`\n | `adapter.${string}`\n | 'identity.isKnown'\n | 'session.entryPage'\n | 'session.pagesVisited'\n | `query.${string}`\n\nexport type InstantOperator =\n | 'equals' | 'not_equals' | 'contains' | 'starts_with' | 'matches_glob'\n | 'is_one_of' | 'is_true' | 'is_false'\n | 'gte' | 'lte'\n\nexport interface ScoreContributionCondition {\n type: 'score'\n id: string\n groupId: string\n trigger: ScoreTrigger\n points: number\n threshold?: number\n category?: string\n pattern?: string\n /**\n * For `trigger: 'signalMatch'` \u2014 award `points` when an arbitrary instant\n * signal matches (e.g. `utm.campaign contains 'enterprise' \u2192 +30`). Reuses the\n * instant-match operator set so any signal (UTM / referrer / self-ID / adapter)\n * can contribute to a score, not just the five behavioral triggers.\n */\n signal?: InstantSignalRef\n operator?: InstantOperator\n value?: string | string[]\n}\n\nexport type ScoreTrigger =\n | 'pageViewCount'\n | 'totalPageViews'\n | 'urlMatch'\n | 'categoryViewed'\n | 'timeOnSite'\n | 'signalMatch'\n\n// \u2500\u2500 Self-ID Types \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport interface SelfIdField {\n id: string\n siteId: string\n label: string\n picklist: Array<{ value: string; label: string }>\n required: boolean\n sortOrder: number\n}\n\nexport interface SelfIdWidgetConfig {\n id: string\n siteId: string\n displayMode: 'hello_bar_top' | 'hello_bar_bottom' | 'modal' | 'inline' | 'drawer' | 'slide-in'\n trigger: SelfIdTrigger\n headline: string\n subheadline: string | null\n fieldIds: string[]\n submitLabel: string\n skipLabel: string | null\n frequency: 'once_visitor' | 'once_session' | 'always'\n}\n\nexport type SelfIdTrigger =\n | { type: 'immediate' }\n | { type: 'scroll'; depth: number }\n | { type: 'delay'; seconds: number }\n | { type: 'manual' }\n\n// \u2500\u2500 Preview Token \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport interface PreviewTokenPayload {\n jti: string\n audienceId: string\n expiresAt: number\n}\n\n// \u2500\u2500 Default Signals \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport function createEmptySignals(): VisitorSignals {\n return {\n utm: { source: null, medium: null, campaign: null, term: null, content: null },\n referrer: { raw: null, domain: null, sourceType: 'direct' },\n session: { pageViewCount: 0, totalPageViews: 0, sessionDuration: 0, entryPage: '/', pagesVisited: [] },\n behavioral: { categoryScores: {}, totalScore: 0 },\n query: {},\n selfId: {},\n identity: { visitorId: '', isKnown: false, maLeadId: null },\n adapters: {},\n meta: { isOptedOut: false, isPreview: false, previewAudienceId: null, resolvedAt: 'server' },\n }\n}\n", "export function matchGlob(pattern: string, value: string): boolean {\n const parts = pattern.split('*')\n if (parts.length === 1) return pattern === value\n let pos = 0\n const first = parts[0]!\n if (first !== '') {\n if (!value.startsWith(first)) return false\n pos = first.length\n }\n for (let i = 1; i < parts.length - 1; i++) {\n const part = parts[i]!\n if (part === '') continue\n const idx = value.indexOf(part, pos)\n if (idx < 0) return false\n pos = idx + part.length\n }\n const last = parts[parts.length - 1]!\n if (last !== '') {\n if (!value.endsWith(last)) return false\n if (value.length - last.length < pos) return false\n }\n return true\n}\n", "import type { VisitorSignals, InstantMatchCondition, InstantSignalRef } from '../types.js'\nimport { matchGlob } from './glob.js'\n\nexport function resolveSignalValue(signals: VisitorSignals, ref: InstantSignalRef): string | null {\n if (ref === 'utm.source') return signals.utm.source\n if (ref === 'utm.medium') return signals.utm.medium\n if (ref === 'utm.campaign') return signals.utm.campaign\n if (ref === 'utm.term') return signals.utm.term\n if (ref === 'utm.content') return signals.utm.content\n if (ref === 'referrer.domain') return signals.referrer.domain\n if (ref === 'referrer.sourceType') return signals.referrer.sourceType\n if (ref === 'identity.isKnown') return signals.identity.isKnown ? 'true' : 'false'\n if (ref === 'session.entryPage') return signals.session.entryPage\n if (ref === 'session.pagesVisited') return signals.session.pagesVisited.join(',')\n // `query.<param>` \u2014 this request's own query string (kywi-cms#196). An absent\n // param is null, so `not_equals` matches it and `equals` does not, the same\n // way an absent UTM param behaves.\n if (ref.startsWith('query.')) {\n const key = ref.slice(6)\n return signals.query[key] ?? null\n }\n if (ref.startsWith('selfId.')) {\n const key = ref.slice(7)\n return signals.selfId[key] ?? null\n }\n if (ref.startsWith('adapter.')) {\n const key = ref.slice(8)\n const val = signals.adapters[key]\n return val !== undefined && val !== null ? String(val) : null\n }\n return null\n}\n\nexport function evaluateInstantCondition(condition: InstantMatchCondition, signals: VisitorSignals): boolean {\n const actual = resolveSignalValue(signals, condition.signal)\n const expected = condition.value\n switch (condition.operator) {\n case 'equals': return actual === expected\n case 'not_equals': return actual !== expected\n case 'contains': return actual !== null && typeof expected === 'string' && actual.includes(expected)\n case 'starts_with': return actual !== null && typeof expected === 'string' && actual.startsWith(expected)\n case 'matches_glob': return actual !== null && typeof expected === 'string' && matchGlob(expected, actual)\n case 'is_one_of': return actual !== null && Array.isArray(condition.value) && condition.value.includes(actual)\n case 'is_true': return actual === 'true'\n case 'is_false': return actual === 'false'\n case 'gte': return actual !== null && typeof expected === 'string' && Number(actual) >= Number(expected)\n case 'lte': return actual !== null && typeof expected === 'string' && Number(actual) <= Number(expected)\n default: return false\n }\n}\n", "import type { VisitorSignals, ScoreContributionCondition } from '../types.js'\nimport { matchGlob } from './glob.js'\nimport { evaluateInstantCondition } from './instant-match.js'\n\nfunction evaluateOneScore(cond: ScoreContributionCondition, signals: VisitorSignals): number {\n const threshold = cond.threshold ?? 0\n switch (cond.trigger) {\n case 'pageViewCount': return signals.session.pageViewCount >= threshold ? cond.points : 0\n case 'totalPageViews': return signals.session.totalPageViews >= threshold ? cond.points : 0\n case 'timeOnSite': return signals.session.sessionDuration >= threshold ? cond.points : 0\n case 'categoryViewed': {\n const cat = cond.category ?? ''\n const score = signals.behavioral.categoryScores[cat]\n return score !== undefined && score >= threshold ? cond.points : 0\n }\n case 'urlMatch': {\n const pattern = cond.pattern ?? '*'\n const matched = signals.session.pagesVisited.some(url => matchGlob(pattern, url))\n return matched ? cond.points : 0\n }\n case 'signalMatch': {\n // Award points when an arbitrary instant signal matches. Reuses the\n // instant-match evaluator so every operator/signal is supported.\n if (!cond.signal || !cond.operator) return 0\n const matched = evaluateInstantCondition(\n {\n type: 'instant',\n id: cond.id,\n groupId: cond.groupId,\n signal: cond.signal,\n operator: cond.operator,\n value: cond.value ?? '',\n },\n signals,\n )\n return matched ? cond.points : 0\n }\n default: return 0\n }\n}\n\nexport function evaluateScoreConditions(conditions: ScoreContributionCondition[], signals: VisitorSignals): number {\n let total = 0\n for (const cond of conditions) { total += evaluateOneScore(cond, signals) }\n return total\n}\n", "import type { Condition, VisitorSignals } from '../types.js'\n\n// \u2500\u2500\u2500 Custom Condition Type Registry \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n//\n// Allows third-party code (plugins, adapters, custom app code) to register\n// additional condition type evaluators without forking the core evaluator.\n//\n// Usage:\n// import { registerConditionType } from '@kywi-software/core'\n//\n// registerConditionType('my_custom_type', (condition, signals) => {\n// // Return true if the condition matches the current visitor signals\n// return signals.adapters['my_adapter'] === condition.value\n// })\n\ntype ConditionEvaluator = (condition: Condition, signals: VisitorSignals) => boolean\n\nconst registry = new Map<string, ConditionEvaluator>()\n\n/**\n * Register a custom condition type evaluator.\n *\n * @param name - The condition `type` string this evaluator handles.\n * @param evaluate - Pure function that returns `true` when the condition matches.\n *\n * Calling this with an already-registered name overwrites the previous evaluator.\n */\nexport function registerConditionType(name: string, evaluate: ConditionEvaluator): void {\n registry.set(name, evaluate)\n}\n\n/**\n * Remove a previously registered condition type evaluator.\n * No-op if the name was never registered.\n */\nexport function unregisterConditionType(name: string): void {\n registry.delete(name)\n}\n\n/**\n * Evaluate a condition using a registered custom evaluator.\n * Returns `false` if no evaluator is registered for the condition's type.\n */\nexport function evaluateCustomCondition(condition: Condition, signals: VisitorSignals): boolean {\n const evaluator = registry.get(condition.type)\n if (!evaluator) return false\n return evaluator(condition, signals)\n}\n\n/**\n * Returns true if a custom evaluator is registered for the given condition type name.\n */\nexport function hasConditionType(name: string): boolean {\n return registry.has(name)\n}\n\n/**\n * Returns the names of all registered custom condition types.\n */\nexport function listConditionTypes(): string[] {\n return [...registry.keys()]\n}\n", "import type { Audience, VisitorSignals, ConditionGroup, InstantMatchCondition, ScoreContributionCondition } from '../types.js'\nimport { evaluateInstantCondition } from './instant-match.js'\nimport { evaluateScoreConditions } from './score.js'\nimport { hasConditionType, evaluateCustomCondition } from './condition-registry.js'\n\nexport interface ConditionTrace {\n conditionId: string\n type: 'instant' | 'score'\n passed: boolean\n scoreContributed?: number\n}\n\nexport interface EvaluationTrace {\n audienceId: string\n matched: boolean\n groupTraces: Array<{ groupId: string; matched: boolean; conditionTraces: ConditionTrace[] }>\n totalScore?: number\n}\n\nexport interface ResolveResult {\n winningAudienceId: string | null\n allMatchedIds: string[]\n traces: EvaluationTrace[]\n}\n\n// Evaluate one group: AND all instant + custom conditions, accumulate score conditions\nfunction evaluateGroup(group: ConditionGroup, signals: VisitorSignals) {\n const conditionTraces: ConditionTrace[] = []\n const instants: InstantMatchCondition[] = []\n const scores: ScoreContributionCondition[] = []\n const customs: typeof group.conditions = []\n\n for (const c of group.conditions) {\n // The Condition union only models 'instant'/'score', but the registry\n // dispatches custom condition kinds by their `type` string at runtime.\n // Capture it as a string so the custom branch stays reachable.\n const conditionType: string = c.type\n if (c.type === 'instant') instants.push(c)\n else if (c.type === 'score') scores.push(c)\n else if (hasConditionType(conditionType)) customs.push(c)\n // Unknown types with no registered evaluator are silently skipped\n }\n\n let allInstantsPassed = true\n\n for (const ic of instants) {\n const passed = evaluateInstantCondition(ic, signals)\n conditionTraces.push({ conditionId: ic.id, type: 'instant', passed })\n if (!passed) allInstantsPassed = false\n }\n\n // Custom conditions are ANDed with instant conditions\n for (const cc of customs) {\n const passed = evaluateCustomCondition(cc, signals)\n conditionTraces.push({ conditionId: cc.id, type: cc.type as 'instant', passed })\n if (!passed) allInstantsPassed = false\n }\n\n const totalScore = evaluateScoreConditions(scores, signals)\n for (const sc of scores) {\n const contributed = evaluateScoreConditions([sc], signals)\n conditionTraces.push({ conditionId: sc.id, type: 'score', passed: contributed > 0, scoreContributed: contributed })\n }\n\n return { matched: allInstantsPassed, conditionTraces, totalScore }\n}\n\nexport function resolveAudiences(audiences: Audience[], signals: VisitorSignals): ResolveResult {\n const allMatchedIds: string[] = []\n const traces: EvaluationTrace[] = []\n\n for (const audience of audiences) {\n const groupTraces: EvaluationTrace['groupTraces'] = []\n let audienceMatched = false\n let accumulatedScore = 0\n\n for (const group of audience.conditionGroups) {\n const { matched, conditionTraces, totalScore } = evaluateGroup(group, signals)\n accumulatedScore += totalScore\n groupTraces.push({ groupId: group.id, matched, conditionTraces })\n if (matched) audienceMatched = true\n }\n\n if (audience.scoreThreshold !== null) {\n const scoreThresholdMet = accumulatedScore >= audience.scoreThreshold\n const hasInstantConditions = audience.conditionGroups.some(g => g.conditions.some(c => c.type === 'instant'))\n if (hasInstantConditions) {\n audienceMatched = audienceMatched && scoreThresholdMet\n } else {\n audienceMatched = scoreThresholdMet\n }\n }\n\n traces.push({\n audienceId: audience.id,\n matched: audienceMatched,\n groupTraces,\n ...(accumulatedScore > 0 ? { totalScore: accumulatedScore } : {}),\n })\n if (audienceMatched) allMatchedIds.push(audience.id)\n }\n\n return { winningAudienceId: allMatchedIds[0] ?? null, allMatchedIds, traces }\n}\n", "// packages/kywi-js/src/audience/query-collector.ts\n//\n// The client half of the `query.<param>` signal family (kywi-cms#196). The\n// server resolves these off the request it is serving; the runtime re-evaluates\n// audiences on client signals, so it has to resolve them off `location` \u2014 a rule\n// that matched server-side would otherwise un-match the moment the client's own\n// pass ran, and the page would flip back to default content.\n\n/**\n * Every query parameter of the current URL. Repeated params keep the FIRST\n * value, matching the server's `collectQuery`. Empty off the browser.\n */\nexport function collectQuery(): Record<string, string> {\n const out: Record<string, string> = {}\n if (typeof location === 'undefined') return out\n // `forEach` rather than a for-of: the package's lib set has no DOM.Iterable,\n // so URLSearchParams is not typed as iterable here.\n new URLSearchParams(location.search).forEach((value, key) => {\n if (key !== '' && !(key in out)) out[key] = value\n })\n return out\n}\n\n/**\n * The entry page to record for a page view: the path the host handed us, with\n * this page's query string appended when the path does not already carry one.\n *\n * The query is the interesting half of a campaign landing (`?from=marketer`),\n * and the host passes a bare pathname \u2014 so without this the client's entry page\n * and the server's would disagree on every campaign link.\n */\nexport function entryPageFor(currentPath: string): string {\n if (currentPath.includes('?')) return currentPath\n const search = typeof location === 'undefined' ? '' : location.search\n return search ? `${currentPath}${search}` : currentPath\n}\n", "// packages/kywi-js/src/cookies.ts\nexport const COOKIE_NAMES = {\n SIGNALS: 'kywi_signals', VISITOR: 'kywi_visitor', UTM: 'kywi_utm',\n AUDIENCE: 'kywi_audience', OPTOUT: 'kywi_optout', KNOWN: 'kywi_known',\n PREVIEW_INIT: 'kywi_preview_init',\n} as const\n\nexport function readCookies(): Map<string, string> {\n const map = new Map<string, string>()\n if (typeof document === 'undefined') return map\n for (const pair of document.cookie.split(';')) {\n const trimmed = pair.trim()\n const eqIdx = trimmed.indexOf('=')\n if (eqIdx < 0) continue\n map.set(trimmed.slice(0, eqIdx), decodeURIComponent(trimmed.slice(eqIdx + 1)))\n }\n return map\n}\n\nexport function setCookie(name: string, value: string, maxAge?: number): void {\n let str = `${name}=${encodeURIComponent(value)}; Path=/; SameSite=Lax`\n if (maxAge !== undefined && maxAge >= 0) str += `; Max-Age=${maxAge}`\n document.cookie = str\n}\n\nexport function deleteCookie(name: string): void {\n document.cookie = `${name}=; Path=/; Max-Age=0`\n}\n", "// packages/kywi-js/src/consent.ts\nimport { readCookies, setCookie, deleteCookie } from './cookies.js'\n\nexport const CONSENT_COOKIE = 'kywi_consent' as const\n\n/** The cookie an audience pin lives in. */\nexport const AUDIENCE_PIN_COOKIE = 'kywi_audience' as const\n\n/**\n * The `kywi_audience` value prefix marking a pin the visitor chose BY HAND in\n * the transparency panel, rather than one the runtime remembered on its own.\n * Mirrors `SWITCHER_PIN_PREFIX` in core's `audiences/consent.ts`.\n */\nexport const SWITCHER_PIN_PREFIX = 'switcher:' as const\n\nexport type ConsentCategory = 'essential' | 'analytics' | 'marketing' | 'personalization'\n\n/**\n * Every cookie the engine writes \u2192 the consent category it requires.\n *\n * A MIRROR of `COOKIE_CONSENT_CATEGORY` in `@kywi-software/core`'s\n * `audiences/consent.ts`, which is the authority. This package cannot import\n * core (neither depends on the other), so the copy exists to let a decision made\n * here REVOKE the cookies it denies without a round trip.\n * `__tests__/consent-parity.test.ts` asserts the two maps are identical.\n */\nexport const COOKIE_CONSENT_CATEGORY: Record<string, ConsentCategory> = {\n kywi_consent: 'essential',\n kywi_optout: 'essential',\n kywi_preview_init: 'essential',\n kywi_signals: 'personalization',\n kywi_visitor: 'personalization',\n kywi_utm: 'personalization',\n kywi_audience: 'personalization',\n kywi_known: 'personalization',\n kywi_entry: 'personalization',\n kywi_selfid: 'personalization',\n}\n\n/** Mirrors `ConsentGateOptions` in core's `audiences/consent.ts`. */\nexport interface ConsentGateOptions {\n /**\n * `theme.personalization.requireConsent`. Defaults to true. When explicitly\n * `false` the gate is off and every category reads as allowed \u2014 for a site\n * whose consent is collected by external tooling (kywi-cms#91).\n */\n requireConsent?: boolean\n}\n\nexport interface ConsentState {\n essential: boolean\n analytics: boolean\n marketing: boolean\n personalization: boolean\n}\n\n/**\n * Read the current consent state from the `kywi_consent` cookie.\n * Returns the parsed ConsentState, or null if the cookie is missing or malformed.\n *\n * The payload is NORMALIZED, not trusted: `essential` is forced true and every\n * other category is `=== true` or false. The cookie is client-writable, so it\n * can arrive partial (written by an older version), tampered with, or carrying\n * `\"yes\"` where a boolean belongs. Casting it to `ConsentState` and hoping left\n * callers reading `state.marketing` a truthy string. This mirrors\n * `parseConsentValue` in core's `audiences/consent.ts` exactly, so the client\n * and the server can never disagree about what a given cookie means\n * (`__tests__/consent-parity.test.ts` pins that).\n *\n * Unlike core's version this does NOT decodeURIComponent: `readCookies` has\n * already decoded the value, and decoding twice would corrupt a payload\n * containing a literal `%`.\n */\nexport function getConsentState(): ConsentState | null {\n const cookies = readCookies()\n const raw = cookies.get(CONSENT_COOKIE)\n if (!raw) return null\n\n let parsed: unknown\n try {\n parsed = JSON.parse(raw)\n } catch {\n return null\n }\n if (typeof parsed !== 'object' || parsed === null) return null\n const obj = parsed as Record<string, unknown>\n return {\n essential: true,\n analytics: obj.analytics === true,\n marketing: obj.marketing === true,\n personalization: obj.personalization === true,\n }\n}\n\n/**\n * Set consent preferences. Essential is always forced to true.\n * The cookie is stored with a 1-year Max-Age.\n */\nexport function setConsent(\n prefs: Partial<Omit<ConsentState, 'essential'>>,\n): void {\n const state: ConsentState = {\n essential: true,\n analytics: prefs.analytics ?? false,\n marketing: prefs.marketing ?? false,\n personalization: prefs.personalization ?? false,\n }\n // 1 year = 365 * 24 * 60 * 60 = 31536000 seconds\n setCookie(CONSENT_COOKIE, JSON.stringify(state), 31536000)\n // A decision REVOKES as well as denies \u2014 see clearDeniedCookies. Written\n // first so the consent record survives its own sweep.\n clearDeniedCookies(state)\n}\n\n/**\n * Delete every cookie whose category `state` does not grant, and return the\n * names deleted.\n *\n * Refusing to WRITE new cookies is only half of \"Reject All\": a visitor who\n * accepted last month and rejects today is still carrying a `kywi_visitor` id,\n * and a gate that only blocked writes would keep reading it forever\n * (kywi-cms#91).\n *\n * `kywi_signals` is HttpOnly and cannot be removed from script; the attempt is\n * harmless, the server stops writing it, and it expires on its own 60-second\n * horizon. Core's server-side read paths also ignore personalization cookies\n * whenever the gate denies, so a cookie script cannot clear is inert anyway.\n */\nexport function clearDeniedCookies(state: ConsentState | null): string[] {\n if (typeof document === 'undefined') return []\n const cookies = readCookies()\n const deleted: string[] = []\n for (const [name, category] of Object.entries(COOKIE_CONSENT_CATEGORY)) {\n if (category === 'essential') continue\n if (state && state[category] === true) continue\n if (isDeliberatePreference(name, cookies.get(name))) continue\n deleteCookie(name)\n deleted.push(name)\n }\n return deleted\n}\n\n/**\n * Does this cookie's CURRENT VALUE mark it as an explicit visitor preference\n * that outranks its category?\n *\n * `kywi_audience` is the one cookie whose category depends on how it was\n * written: automatic (\"remember what I resolved\") is `personalization`, but an\n * audience the visitor picked by hand in the transparency panel is `essential`,\n * on the same argument as `kywi_optout`. The category map is static and cannot\n * express that, so the sweep asks the value.\n *\n * Without this, rejecting personalization would delete a choice the visitor made\n * deliberately and which the server is under orders to keep honouring \u2014 the\n * revocation would quietly undo the exemption. Mirrors the same predicate in\n * core's `audiences/consent.ts`.\n */\nfunction isDeliberatePreference(name: string, value: string | undefined): boolean {\n if (name !== AUDIENCE_PIN_COOKIE || value === undefined) return false\n // The prefix alone is not a pin \u2014 `switcher:` with no id decodes to nothing\n // (parseAudiencePin rejects it), so exempting it would strand a cookie nobody\n // will ever honour.\n return value.startsWith(SWITCHER_PIN_PREFIX) && value.length > SWITCHER_PIN_PREFIX.length\n}\n\n/**\n * Check whether a specific consent category has been granted.\n *\n * `essential` is ALWAYS granted, decision or not \u2014 necessary cookies (the\n * consent record itself, the personalization opt-out flag) are never gated, and\n * before kywi-cms#91 that was true only by accident because nothing called this\n * function at all. Every other category needs an explicit `true`, so an\n * undecided visitor grants nothing and an unknown category is denied.\n *\n * `requireConsent: false` (from `theme.personalization.requireConsent`) turns the\n * gate off entirely for a site whose consent is collected by external tooling.\n *\n * The authoritative cookie\u2192category map lives in `@kywi-software/core`'s\n * `audiences/consent.ts` (`COOKIE_CONSENT_CATEGORY`) \u2014 this package cannot\n * import core, so `__tests__/consent-parity.test.ts` asserts the two agree.\n */\nexport function hasConsent(category: ConsentCategory, options: ConsentGateOptions = {}): boolean {\n if (options.requireConsent === false) return true\n if (category === 'essential') return true\n const state = getConsentState()\n if (!state) return false\n return state[category] === true\n}\n", "// packages/kywi-js/src/audience/behavioral-collector.ts\nimport { entryPageFor } from './query-collector.js'\nimport { hasConsent, type ConsentGateOptions } from '../consent.js'\n\nconst SESSION_KEY = 'kywi_session'\nconst TOTAL_VIEWS_KEY = 'kywi_total_views'\nconst CATEGORY_SCORES_KEY = 'kywi_category_scores'\nconst SESSION_START_KEY = 'kywi_session_start'\n\ninterface SessionData { pageViewCount: number; entryPage: string; pagesVisited: string[] }\n\nexport interface BehavioralResult {\n session: { pageViewCount: number; totalPageViews: number; sessionDuration: number; entryPage: string; pagesVisited: string[] }\n behavioral: { categoryScores: Record<string, number>; totalScore: number }\n}\n\nfunction getSession(): SessionData {\n const raw = sessionStorage.getItem(SESSION_KEY)\n if (raw) { try { return JSON.parse(raw) as SessionData } catch { /* fall through */ } }\n return { pageViewCount: 0, entryPage: '', pagesVisited: [] }\n}\n\nfunction getSessionStart(): number {\n const raw = sessionStorage.getItem(SESSION_START_KEY)\n if (raw) return Number(raw)\n const now = Date.now()\n sessionStorage.setItem(SESSION_START_KEY, String(now))\n return now\n}\n\nfunction getCategoryScores(): Record<string, number> {\n const raw = localStorage.getItem(CATEGORY_SCORES_KEY)\n if (raw) { try { return JSON.parse(raw) as Record<string, number> } catch { /* fall through */ } }\n return {}\n}\n\nexport function collectBehavioral(\n currentPath: string,\n category?: string,\n gate: ConsentGateOptions = {},\n): BehavioralResult {\n const session = getSession()\n session.pageViewCount += 1\n // Path + query, so `session.entryPage` says the same thing here as it does on\n // the server (kywi-cms#196) \u2014 a campaign link's params are the point of it.\n //\n // STORING it needs personalization consent, exactly like the `kywi_entry`\n // cookie the server writes: an entry page carried from one page view to the\n // next is the same tracking whichever store it sits in. Without consent the\n // CURRENT page is still reported as the entry page for this evaluation \u2014 the\n // server does the same with its own fallback \u2014 so a campaign landing still\n // personalizes, it just is not remembered (review nit 16).\n const entryPage = session.entryPage || entryPageFor(currentPath)\n if (!session.entryPage && hasConsent('personalization', gate)) session.entryPage = entryPage\n session.pagesVisited.push(currentPath)\n sessionStorage.setItem(SESSION_KEY, JSON.stringify(session))\n\n const totalViews = (Number(localStorage.getItem(TOTAL_VIEWS_KEY)) || 0) + 1\n localStorage.setItem(TOTAL_VIEWS_KEY, String(totalViews))\n\n const sessionStart = getSessionStart()\n const categoryScores = getCategoryScores()\n if (category) {\n categoryScores[category] = (categoryScores[category] ?? 0) + 1\n localStorage.setItem(CATEGORY_SCORES_KEY, JSON.stringify(categoryScores))\n }\n\n return {\n session: { pageViewCount: session.pageViewCount, totalPageViews: totalViews, sessionDuration: Math.floor((Date.now() - sessionStart) / 1000), entryPage, pagesVisited: session.pagesVisited },\n behavioral: { categoryScores, totalScore: 0 },\n }\n}\n", "// packages/kywi-js/src/audience/evaluate.ts\nimport type { Audience, VisitorSignals, AudienceResult } from '@kywi-software/core/audiences/types'\nimport { createEmptySignals } from '@kywi-software/core/audiences/types'\nimport { resolveAudiences } from '@kywi-software/core/evaluator'\nimport { collectBehavioral } from './behavioral-collector.js'\nimport { collectSelfId } from './selfid-collector.js'\nimport { collectQuery } from './query-collector.js'\nimport type { ConsentGateOptions } from '../consent.js'\n\nfunction merge(base: VisitorSignals, partial: Partial<VisitorSignals>): VisitorSignals {\n return {\n utm: partial.utm ?? base.utm, referrer: partial.referrer ?? base.referrer,\n session: partial.session ?? base.session, behavioral: partial.behavioral ?? base.behavioral,\n query: { ...base.query, ...partial.query },\n selfId: { ...base.selfId, ...partial.selfId }, identity: partial.identity ?? base.identity,\n adapters: { ...base.adapters, ...partial.adapters }, meta: { ...base.meta, ...partial.meta },\n }\n}\n\nexport function evaluateClientSide(\n audiences: Audience[], serverSignals: Partial<VisitorSignals>, currentPath: string, pageCategory?: string,\n /**\n * `theme.personalization.requireConsent` (kywi-cms#91), passed through to the\n * behavioral collector so what it REMEMBERS between page views follows the\n * same rule the server's cookies do. Omitted, consent is required \u2014 the\n * compliant default.\n */\n gate: ConsentGateOptions = {},\n): AudienceResult {\n let signals = merge(createEmptySignals(), serverSignals)\n const client = collectBehavioral(currentPath, pageCategory, gate)\n const selfId = collectSelfId()\n // `query` comes from location, not from the server signal bag: the runtime can\n // run on a page the server signals were not resolved for (a client-side\n // navigation), and the URL is always the truth about the URL (kywi-cms#196).\n signals = merge(signals, { session: client.session, behavioral: client.behavioral, query: collectQuery(), selfId, meta: { ...signals.meta, resolvedAt: 'client' } })\n const { winningAudienceId, allMatchedIds } = resolveAudiences(audiences, signals)\n const isOptedOut = signals.meta.isOptedOut\n return { winningAudienceId: isOptedOut ? null : winningAudienceId, allMatchedIds, signals, isOptedOut }\n}\n", "// packages/kywi-js/src/audience/dom-patcher.ts\nexport function patchVariantContainers(winningAudienceId: string | null): void {\n const containers = document.querySelectorAll('.kywi-variant-container')\n for (const container of Array.from(containers)) {\n const el = container as HTMLElement\n const arms = Array.from(el.querySelectorAll('[data-kywi-variant]'))\n\n // Only module-level containers (VariantContainerModule in\n // packages/core/src/layout/modules-extended.tsx) render every arm into the\n // DOM for this client-side swap \u2014 each tagged `[data-kywi-variant]`, with at\n // least a \"default\" arm present even while loading. Section-level containers\n // (SectionRenderer's VariantContainerRenderer in packages/core/src/layout/\n // renderer.tsx, and components/personalization/VariantContainer.tsx) share\n // the `.kywi-variant-container` selector but are fully server-resolved: they\n // render only the winning arm's markup directly, so they never have any\n // `[data-kywi-variant]` children. That \"has arms to toggle\" structure is the\n // most robust discriminator available (unlike a class or `data-mode`\n // attribute, it can't drift out of sync with what this function actually\n // needs to do) \u2014 skip anything without arms so we never overwrite a\n // server-resolved container's `data-variant` with a client-side audience id\n // that disagrees with the content already rendered (kywi-cms#99).\n if (arms.length === 0) continue\n\n el.classList.remove('kywi-variant-loading')\n el.removeAttribute('aria-busy')\n const skeleton = el.querySelector('.kywi-variant-skeleton')\n if (skeleton) skeleton.remove()\n const requested = winningAudienceId ?? 'default'\n // The page's globally-resolved audience need not match one of THIS\n // container's own arms \u2014 it may only be bound to 'default' and some other\n // audience entirely. Hiding every arm because none matches `requested`\n // would blank out the container's own default content (kywi-cms#172), so\n // fall back to showing 'default' when nothing on this container matches.\n const hasMatch = arms.some((child) => (child as HTMLElement).getAttribute('data-kywi-variant') === requested)\n const target = hasMatch ? requested : 'default'\n el.setAttribute('data-variant', target)\n for (const child of arms) {\n const h = child as HTMLElement\n h.style.display = h.getAttribute('data-kywi-variant') === target ? '' : 'none'\n }\n }\n}\n", "// packages/kywi-js/src/audience/transparency.ts\n//\n// The transparency surface: the single global control through which a visitor\n// sees THAT the page is personalized, WHICH audience it was personalized for,\n// and can switch audience, reset to automatic resolution, or turn\n// personalization off entirely (issue #105).\n//\n// Three states:\n// 1. nothing \u2014 not personalizing and no match available\n// 2. collapsed pill \u2014 small, unobtrusive, click/Enter expands\n// 3. expanded panel \u2014 audience name, switcher, reset, opt-out/opt-in\n//\n// Collapsing is NOT dismissal: there is no permanent suppression any more (a\n// promise of transparency the visitor can silence forever is not one). The\n// collapsed/expanded choice is remembered in localStorage; the legacy\n// `kywi_transparency_dismissed` flag is migrated to \"collapsed\".\n//\n// All styling lives in `@kywi-software/core`'s site/styles.css (token-driven,\n// overridable) \u2014 this module ships no stylesheet of its own.\n\nimport { COOKIE_NAMES, readCookies, setCookie, deleteCookie } from '../cookies.js'\n\nconst COLLAPSED_KEY = 'kywi_transparency_collapsed'\n/** Pre-#105 permanent-dismissal flag; read once and migrated to \"collapsed\". */\nconst LEGACY_DISMISSED_KEY = 'kywi_transparency_dismissed'\n\nconst ROOT_ID = 'kywi-transparency'\nconst PANEL_ID = 'kywi-transparency-panel'\nconst TITLE_ID = 'kywi-transparency-title'\nconst SWITCHER_LABEL_ID = 'kywi-transparency-switcher-label'\n\nconst DEFAULT_API_BASE = '/api/v1'\n\n/** An audience the visitor can be shown as, or switch to. */\nexport interface TransparencyAudience {\n id: string\n name: string\n}\n\nexport interface TransparencyState {\n /** The page is currently rendering an audience's variant content. */\n isPersonalized: boolean\n isOptedOut: boolean\n /** At least one audience matched \u2014 i.e. personalization is available here. */\n hasMatchAvailable: boolean\n /** The audience the page was personalized for (resolves the displayed name). */\n audienceId?: string | null\n /** Active audiences, by name \u2014 powers the name lookup and the switcher. */\n audiences?: TransparencyAudience[]\n /** Mounted CMS API base (e.g. `/api/v1`); the switcher posts to `{apiBase}/kywi/audience`. */\n apiBase?: string\n csrfToken?: string | null\n /**\n * `personalization.transparencyNotice.enabled`. When false the surface never\n * appears on its own; `openTransparencyPanel()` (e.g. a personalizationBadge\n * click) can still open it deliberately.\n */\n enabled?: boolean\n}\n\n// The last state boot handed us, so a programmatic open() can re-render the\n// panel with real data instead of an empty shell.\nlet lastState: TransparencyState | null = null\n// Set by openTransparencyPanel(): render even when the automatic gate says no.\nlet forcedOpen = false\n\n/* \u2500\u2500 storage (never let a blocked/full localStorage break the page) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\nfunction readStorage(key: string): string | null {\n try { return localStorage.getItem(key) } catch { return null }\n}\nfunction writeStorage(key: string, value: string): void {\n try { localStorage.setItem(key, value) } catch { /* private mode / quota */ }\n}\nfunction removeStorage(key: string): void {\n try { localStorage.removeItem(key) } catch { /* private mode / quota */ }\n}\n\n/** True when the surface should render as the pill. Defaults to collapsed. */\nexport function isCollapsed(): boolean {\n if (readStorage(LEGACY_DISMISSED_KEY) === '1') {\n // Migration: a visitor who permanently dismissed the old bar gets the pill,\n // not silence. Rewriting the flag makes the migration a one-time cost.\n removeStorage(LEGACY_DISMISSED_KEY)\n writeStorage(COLLAPSED_KEY, '1')\n return true\n }\n return readStorage(COLLAPSED_KEY) !== '0'\n}\n\nfunction setCollapsed(collapsed: boolean): void {\n writeStorage(COLLAPSED_KEY, collapsed ? '1' : '0')\n}\n\n/* \u2500\u2500 visitor preferences \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n//\n// CONSENT: `kywi_optout` is a `necessary` cookie in the map core owns\n// (`audiences/consent.ts`), so none of the writes below are gated on\n// `hasConsent()` (kywi-cms#91). It records a SUPPRESSION \u2014 \"stop personalizing\n// me\" \u2014 and refusing to remember that until the visitor first accepts cookies\n// would invert the point of the control. Deleting `kywi_audience` is likewise\n// ungated: dropping data never needs permission.\n\nexport function optOut(): void {\n setCookie(COOKIE_NAMES.OPTOUT, '1', 365 * 24 * 3600)\n deleteCookie(COOKIE_NAMES.AUDIENCE)\n}\n\nexport function optIn(): void {\n deleteCookie(COOKIE_NAMES.OPTOUT)\n}\n\n/** Clears the manual audience pin so resolution goes back to signals. */\nexport function resetToAutomatic(): void {\n deleteCookie(COOKIE_NAMES.AUDIENCE)\n}\n\nfunction hasPin(): boolean {\n return readCookies().has(COOKIE_NAMES.AUDIENCE)\n}\n\n/**\n * Pins the visitor to `audienceId` via the public runtime route \u2014 the same\n * endpoint the boot path uses, but marked `source: 'switcher'`. That flag is\n * what tells the server this is the visitor operating a control by hand rather\n * than the runtime remembering something automatically, which is why it is\n * honoured as an `essential` preference and not gated on personalization consent\n * (kywi-cms#91; the same argument as kywi_optout \u2014 see the block above).\n *\n * The server re-validates the id against the active audiences.\n *\n * Returns whether the pin will SURVIVE this page: false when the server declined\n * to store it (an older server that predates `source`, or a refusal), so the\n * caller can say so instead of reloading into an unchanged page.\n */\nexport async function pinAudience(\n audienceId: string,\n opts: { apiBase?: string | undefined; csrfToken?: string | null | undefined } = {},\n): Promise<boolean> {\n const apiBase = opts.apiBase ?? DEFAULT_API_BASE\n try {\n const res = await fetch(`${apiBase}/kywi/audience`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', ...(opts.csrfToken ? { 'X-CSRF-Token': opts.csrfToken } : {}) },\n body: JSON.stringify({ audienceId, source: 'switcher' }),\n })\n if (!res.ok) return false\n const body = (await res.json().catch(() => null)) as { data?: { persisted?: boolean } } | null\n // A server that predates `persisted` omits it; it also predates the gate, so\n // it did store the pin. Absent means stored.\n return body?.data?.persisted !== false\n } catch {\n // Offline / blocked \u2014 nothing was stored, so do not reload into a page that\n // would look unchanged for no stated reason.\n return false\n }\n}\n\n/**\n * Report a pin the server declined to store, in place of the reload that would\n * otherwise follow. Replaces any previous message so repeated attempts do not\n * stack up, and is announced politely for screen readers.\n */\nfunction showSwitcherError(group: HTMLElement): void {\n group.parentElement?.querySelector('.kywi-transparency-switcher-error')?.remove()\n const note = document.createElement('p')\n note.className = 'kywi-transparency-switcher-error'\n note.setAttribute('role', 'status')\n note.textContent = \"Couldn't save that choice. It won't be remembered on the next page.\"\n group.parentElement?.appendChild(note)\n}\n\nfunction reload(): void {\n try { window.location.reload() } catch { /* non-navigating test environments */ }\n}\n\n/* \u2500\u2500 rendering \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\nfunction removeSurface(): void {\n document.getElementById(ROOT_ID)?.remove()\n // Pre-#105 markup, in case a stale bar is still on the page.\n document.querySelector('.kywi-transparency-bar')?.remove()\n}\n\nfunction button(className: string, text: string, onClick: () => void): HTMLButtonElement {\n const btn = document.createElement('button')\n btn.type = 'button'\n btn.className = className\n btn.textContent = text\n btn.addEventListener('click', onClick)\n return btn\n}\n\nfunction shouldRenderAutomatically(state: TransparencyState): boolean {\n if (state.enabled === false) return false\n return (state.isPersonalized && !state.isOptedOut) || (state.isOptedOut && state.hasMatchAvailable)\n}\n\nfunction audienceName(state: TransparencyState): string | null {\n if (!state.audienceId) return null\n return state.audiences?.find((a) => a.id === state.audienceId)?.name ?? null\n}\n\n/**\n * Renders (or removes) the transparency surface for `state`. Safe to call on\n * every boot; it replaces whatever is currently mounted.\n */\nexport function renderTransparencyPanel(state: TransparencyState): void {\n lastState = state\n if (typeof document === 'undefined') return\n removeSurface()\n if (!forcedOpen && !shouldRenderAutomatically(state)) return\n\n const active = state.isPersonalized && !state.isOptedOut\n const collapsed = forcedOpen ? false : isCollapsed()\n\n const root = document.createElement('div')\n root.id = ROOT_ID\n root.className = `kywi-transparency ${active ? 'kywi-transparency-active' : 'kywi-transparency-opted-out'}`\n root.dataset.state = collapsed ? 'collapsed' : 'expanded'\n\n /* pill */\n const pill = document.createElement('button')\n pill.type = 'button'\n pill.className = 'kywi-transparency-pill'\n pill.setAttribute('aria-expanded', String(!collapsed))\n pill.setAttribute('aria-controls', PANEL_ID)\n const pillIcon = document.createElement('span')\n pillIcon.className = 'kywi-transparency-pill-icon'\n pillIcon.setAttribute('aria-hidden', 'true')\n pillIcon.textContent = '\u2726'\n const pillLabel = document.createElement('span')\n pillLabel.className = 'kywi-transparency-pill-label'\n pillLabel.textContent = active ? 'Personalized' : 'Personalization off'\n pill.append(pillIcon, pillLabel)\n pill.addEventListener('click', () => expandPanel())\n pill.hidden = !collapsed\n root.appendChild(pill)\n\n /* panel */\n const panel = document.createElement('section')\n panel.id = PANEL_ID\n panel.className = 'kywi-transparency-panel'\n panel.setAttribute('role', 'dialog')\n panel.setAttribute('aria-labelledby', TITLE_ID)\n panel.hidden = collapsed\n\n const header = document.createElement('div')\n header.className = 'kywi-transparency-header'\n const title = document.createElement('h2')\n title.id = TITLE_ID\n title.className = 'kywi-transparency-title'\n const name = audienceName(state)\n title.textContent = !active\n ? 'Personalization is off'\n : name\n ? 'This page is personalized for:'\n : 'This page is personalized'\n header.appendChild(title)\n const collapseBtn = button('kywi-transparency-collapse', '\u2715', () => collapsePanel())\n collapseBtn.setAttribute('aria-label', 'Collapse')\n header.appendChild(collapseBtn)\n panel.appendChild(header)\n\n if (active) {\n const audience = document.createElement('p')\n audience.className = 'kywi-transparency-audience'\n if (name) {\n const strong = document.createElement('strong')\n strong.textContent = name\n audience.appendChild(strong)\n } else {\n audience.textContent = 'Based on your browsing context.'\n }\n panel.appendChild(audience)\n }\n\n const note = document.createElement('p')\n note.className = 'kywi-transparency-note'\n note.textContent = active\n ? 'Parts of this page are chosen to match this audience. You can switch, reset, or turn personalization off.'\n : 'Personalization is turned off, so you are seeing the default version of this page.'\n panel.appendChild(note)\n\n /* audience switcher \u2014 pointless while opted out (opt-out beats a pin) */\n const options = state.audiences ?? []\n if (active && options.length > 0) {\n const switcher = document.createElement('div')\n switcher.className = 'kywi-transparency-switcher'\n const switcherLabel = document.createElement('span')\n switcherLabel.id = SWITCHER_LABEL_ID\n switcherLabel.className = 'kywi-transparency-switcher-label'\n switcherLabel.textContent = 'See this page as'\n switcher.appendChild(switcherLabel)\n\n const group = document.createElement('div')\n group.className = 'kywi-transparency-options'\n group.setAttribute('role', 'group')\n group.setAttribute('aria-labelledby', SWITCHER_LABEL_ID)\n for (const option of options) {\n const isCurrent = option.id === state.audienceId\n const optionBtn = button('kywi-transparency-option', option.name, () => {\n if (isCurrent) return\n void pinAudience(option.id, { apiBase: state.apiBase, csrfToken: state.csrfToken }).then(\n (persisted) => {\n // Reloading on a pin the server did not store would drop the visitor\n // back on the same page with no explanation \u2014 say what happened\n // instead (kywi-cms#91).\n if (persisted) reload()\n else showSwitcherError(group)\n },\n )\n })\n optionBtn.dataset.kywiAudienceId = option.id\n optionBtn.setAttribute('aria-pressed', String(isCurrent))\n group.appendChild(optionBtn)\n }\n switcher.appendChild(group)\n panel.appendChild(switcher)\n }\n\n /* actions */\n const actions = document.createElement('div')\n actions.className = 'kywi-transparency-actions'\n if (state.isOptedOut) {\n actions.appendChild(button('kywi-transparency-opt-in', 'Enable personalization', () => { optIn(); reload() }))\n } else {\n if (hasPin()) {\n actions.appendChild(button('kywi-transparency-reset', 'Reset to automatic', () => { resetToAutomatic(); reload() }))\n }\n actions.appendChild(button('kywi-transparency-opt-out', 'Turn off personalization', () => { optOut(); reload() }))\n }\n panel.appendChild(actions)\n\n panel.addEventListener('keydown', (e) => {\n if ((e as KeyboardEvent).key === 'Escape') collapsePanel()\n })\n\n root.appendChild(panel)\n document.body.appendChild(root)\n}\n\nfunction currentSurface(): { root: HTMLElement; pill: HTMLElement; panel: HTMLElement } | null {\n const root = document.getElementById(ROOT_ID)\n if (!root) return null\n const pill = root.querySelector<HTMLElement>('.kywi-transparency-pill')\n const panel = root.querySelector<HTMLElement>('.kywi-transparency-panel')\n if (!pill || !panel) return null\n return { root, pill, panel }\n}\n\nfunction setExpanded(expanded: boolean): void {\n const surface = currentSurface()\n if (!surface) return\n surface.root.dataset.state = expanded ? 'expanded' : 'collapsed'\n surface.pill.hidden = expanded\n surface.pill.setAttribute('aria-expanded', String(expanded))\n surface.panel.hidden = !expanded\n const focusTarget = expanded\n ? surface.panel.querySelector<HTMLElement>('.kywi-transparency-collapse')\n : surface.pill\n focusTarget?.focus()\n}\n\n/** Expands the panel and remembers the choice. */\nexport function expandPanel(): void {\n setCollapsed(false)\n setExpanded(true)\n}\n\n/** Collapses back to the pill and remembers the choice \u2014 never full suppression. */\nexport function collapsePanel(): void {\n forcedOpen = false\n setCollapsed(true)\n setExpanded(false)\n}\n\n/**\n * Opens the transparency panel from anywhere \u2014 the programmatic entry point the\n * personalizationBadge module's status pill calls (#107), so opt-out and\n * audience switching live in exactly one surface. Renders the panel even when\n * the automatic gate (or `transparencyNotice.enabled: false`) would hide it,\n * because opening it is an explicit visitor action.\n */\nexport function openTransparencyPanel(): void {\n if (typeof document === 'undefined') return\n forcedOpen = true\n const state: TransparencyState = lastState ?? {\n isPersonalized: false,\n isOptedOut: readCookies().has(COOKIE_NAMES.OPTOUT),\n hasMatchAvailable: false,\n }\n renderTransparencyPanel(state)\n currentSurface()?.panel.querySelector<HTMLElement>('.kywi-transparency-collapse')?.focus()\n}\n\n/** Test seam: forget the cached boot state and any forced-open flag. */\nexport function resetTransparencyState(): void {\n lastState = null\n forcedOpen = false\n}\n", "// packages/kywi-js/src/audience/preview.ts\nimport { COOKIE_NAMES, readCookies, deleteCookie } from '../cookies.js'\n\nconst PREVIEW_KEY = 'kywi_preview_audience'\n\nexport function getPreviewAudienceId(): string | null { return sessionStorage.getItem(PREVIEW_KEY) }\nexport function setPreviewAudienceId(id: string): void { sessionStorage.setItem(PREVIEW_KEY, id) }\nexport function clearPreview(): void { sessionStorage.removeItem(PREVIEW_KEY) }\n\nexport function checkPreviewInit(): void {\n const cookies = readCookies()\n const initId = cookies.get(COOKIE_NAMES.PREVIEW_INIT)\n if (initId) { setPreviewAudienceId(initId); deleteCookie(COOKIE_NAMES.PREVIEW_INIT) }\n}\n\nexport function renderPreviewBanner(audienceName: string): void {\n document.querySelector('.kywi-preview-banner')?.remove()\n const banner = document.createElement('div')\n banner.className = 'kywi-preview-banner'\n banner.setAttribute('role', 'status')\n const text = document.createElement('span')\n text.className = 'kywi-preview-text'\n text.textContent = 'Previewing as: '\n const strong = document.createElement('strong')\n strong.textContent = audienceName\n text.appendChild(strong)\n banner.appendChild(text)\n const exitBtn = document.createElement('button')\n exitBtn.className = 'kywi-preview-exit'\n exitBtn.type = 'button'\n exitBtn.textContent = 'Exit Preview'\n exitBtn.addEventListener('click', () => { clearPreview(); window.location.reload() })\n banner.appendChild(exitBtn)\n document.body.appendChild(banner)\n}\n", "// packages/kywi-js/src/audience/data-layer.ts\nimport type { KywiVisitor } from '../types.js'\n\nexport function populateDataLayer(visitor: KywiVisitor): void {\n const g = ((window as any).kywi ?? {}) as { visitor?: KywiVisitor }\n g.visitor = visitor\n ;(window as any).kywi = g\n}\n\nexport function fireKywiReady(): void { window.dispatchEvent(new CustomEvent('kywiReady')) }\n\nexport interface ServerMeta { audienceId: string | null; experimentId: string | null; variantId: string | null; visitorId: string }\n\nexport function readServerMeta(): ServerMeta {\n const m = (n: string) => document.querySelector(`meta[name=\"kywi:${n}\"]`)?.getAttribute('content') ?? null\n return { audienceId: m('audience-id'), experimentId: m('experiment-id'), variantId: m('variant-id'), visitorId: m('visitor-id') ?? '' }\n}\n", "// packages/kywi-js/src/audience/adapter-runner.ts\nimport type { VisitorSignals } from '@kywi-software/core/audiences/types'\n\ninterface PullAdapter {\n id: string\n side: 'client' | 'server-proxy'\n fields: string[]\n collect: (signals: Partial<VisitorSignals>) => Promise<Record<string, unknown>>\n}\n\nconst CACHE_PREFIX = 'kywi_adapter_'\n\nfunction getCached(adapterId: string): Record<string, unknown> | null {\n const raw = sessionStorage.getItem(CACHE_PREFIX + adapterId)\n if (!raw) return null\n try { return JSON.parse(raw) as Record<string, unknown> } catch { return null }\n}\n\nfunction setCache(adapterId: string, data: Record<string, unknown>): void {\n sessionStorage.setItem(CACHE_PREFIX + adapterId, JSON.stringify(data))\n}\n\nexport async function runClientAdapters(\n adapters: PullAdapter[], currentSignals: Partial<VisitorSignals>,\n apiBase = '/api/v1',\n): Promise<Record<string, unknown>> {\n const merged: Record<string, unknown> = {}\n\n const tasks = adapters.map(async (adapter) => {\n const cached = getCached(adapter.id)\n if (cached) return cached\n\n let result: Record<string, unknown>\n if (adapter.side === 'server-proxy') {\n // The proxy lives in the CMS runtime namespace (`{apiBase}/kywi/adapters/:id`),\n // alongside the audience-assignment and identity routes \u2014 not a bare\n // `/api/kywi/...` path, which would miss a handler mounted at `/api/v1`.\n const res = await fetch(`${apiBase}/kywi/adapters/${adapter.id}`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ signals: currentSignals }),\n })\n const json = await res.json() as { data?: Record<string, unknown> }\n result = json.data ?? {}\n } else {\n result = await adapter.collect(currentSignals)\n }\n\n setCache(adapter.id, result)\n return result\n })\n\n const results = await Promise.all(tasks)\n for (const r of results) Object.assign(merged, r)\n return merged\n}\n", "// packages/kywi-js/src/audience/selfid-submit.ts\nimport { storeSelfId } from './selfid-collector.js'\nimport { evaluateClientSide } from './evaluate.js'\nimport { patchVariantContainers } from './dom-patcher.js'\nimport { populateDataLayer, fireKywiReady, readServerMeta } from './data-layer.js'\nimport { readCookies, COOKIE_NAMES } from '../cookies.js'\nimport type { Audience, VisitorSignals } from '@kywi-software/core/audiences/types'\n\nexport function handleSelfIdSubmit(\n responses: Record<string, string>,\n audiences?: Audience[],\n currentPath?: string,\n): void {\n // 1. Store responses (replaces previous)\n storeSelfId(responses)\n\n // 2. Re-evaluate if audiences provided\n if (audiences && currentPath) {\n const cookies = readCookies()\n const serverMeta = readServerMeta()\n const serverSignals: Partial<VisitorSignals> = {\n identity: { visitorId: serverMeta.visitorId || cookies.get(COOKIE_NAMES.VISITOR) || '', isKnown: cookies.has(COOKIE_NAMES.KNOWN), maLeadId: null },\n meta: { isOptedOut: cookies.has(COOKIE_NAMES.OPTOUT), isPreview: false, previewAudienceId: null, resolvedAt: 'server' },\n }\n const result = evaluateClientSide(audiences, serverSignals, currentPath)\n patchVariantContainers(result.winningAudienceId)\n populateDataLayer({\n visitorId: result.signals.identity.visitorId,\n audienceId: result.winningAudienceId,\n experimentId: serverMeta.experimentId,\n variantId: serverMeta.variantId,\n isPreview: false,\n isOptedOut: result.isOptedOut,\n })\n }\n}\n", "// packages/kywi-js/src/audience/selfid-widget.ts\nimport { storeSelfId } from './selfid-collector.js'\nimport { handleSelfIdSubmit } from './selfid-submit.js'\nimport type { Audience } from '@kywi-software/core/audiences/types'\n\nconst SHOWN_KEY = 'kywi_selfid_shown'\nconst MS_PER_DAY = 86_400_000\n\n/**\n * Display modes that render their own explicit dismiss control (#97, #106).\n * Every overlay mode is dismissible \u2014 an overlay a visitor cannot close is a\n * trap. Only `inline` is excluded: it is part of the host's page content and\n * has no viewport-covering surface to escape from.\n */\nconst DISMISSIBLE_MODES = new Set(['modal', 'slide-in', 'hello_bar_top', 'hello_bar_bottom', 'drawer'])\n\n/** Modes that float over the page and therefore close themselves on submit. */\nconst OVERLAY_MODES = DISMISSIBLE_MODES\n\n/**\n * Host mount contract for `inline` mode (#106): when the page contains an\n * element matching this selector, the widget renders INTO it instead of into\n * the runtime's own `#kywi-selfid-container` (which is appended to the end of\n * `<body>`, i.e. after the footer). Hosts opt in by putting\n * `<div data-kywi-selfid-mount></div>` wherever the card belongs.\n */\nexport const SELFID_MOUNT_SELECTOR = '[data-kywi-selfid-mount]'\n\n/** Body class + custom property published while a top hello bar is on screen. */\nexport const HELLO_BAR_TOP_CLASS = 'kywi-selfid-hello-bar-top-open'\nconst HELLO_BAR_HEIGHT_PROPERTY = '--kywi-selfid-hello-bar-height'\n\nconst MODAL_LABEL_ID = 'kywi-selfid-headline'\n\n/**\n * The host slot for `inline` mode, when one exists. Returns null for every\n * other display mode \u2014 modal/drawer/hello-bar/slide-in position themselves\n * against the viewport, so a content slot would be meaningless for them.\n */\nexport function resolveSelfIdMount(displayMode?: SelfIdWidgetConfig['displayMode']): HTMLElement | null {\n if (typeof document === 'undefined') return null\n if (displayMode && displayMode !== 'inline') return null\n return document.querySelector<HTMLElement>(SELFID_MOUNT_SELECTOR)\n}\n\n/** A picklist option: the value submitted, and the label shown to the visitor. */\nexport interface SelfIdFieldOption {\n value: string\n label: string\n}\n\nexport interface SelfIdFieldConfig {\n id: string\n label: string\n type: 'text' | 'select' | 'email'\n /**\n * Either `{value, label}` pairs (current server shape \u2014 see\n * resolveSelfIdWidget in the create-kywi-app scaffold) or bare strings (older\n * config shape, kept for backward compatibility). A bare string is treated as\n * both its own value and its own label.\n */\n options?: Array<string | SelfIdFieldOption>\n required?: boolean\n}\n\n/** Normalize a picklist option to `{value, label}`, treating a bare string as both. */\nfunction normalizeOption(opt: string | SelfIdFieldOption): SelfIdFieldOption {\n return typeof opt === 'string' ? { value: opt, label: opt } : opt\n}\n\n/** When the widget is shown, once frequency allows. Defaults to immediate. */\nexport type SelfIdWidgetTrigger =\n | { type: 'immediate' }\n | { type: 'scroll'; depth: number }\n | { type: 'delay'; seconds: number }\n | { type: 'manual' }\n\nexport interface SelfIdWidgetConfig {\n fields: SelfIdFieldConfig[]\n headline?: string\n subheadline?: string\n submitLabel?: string\n skipLabel?: string\n displayMode?: 'modal' | 'inline' | 'slide-in' | 'hello_bar_top' | 'hello_bar_bottom' | 'drawer'\n frequency?: 'once' | 'session' | 'daily' | 'always'\n trigger?: SelfIdWidgetTrigger\n /**\n * The full audience rule definitions to re-evaluate client-side after a\n * submission. These flow straight into `handleSelfIdSubmit` \u2192\n * `evaluateClientSide` \u2192 `resolveAudiences`, which iterates each audience's\n * `conditionGroups`, so full `Audience` objects are required (not id strings).\n */\n audiences?: Audience[]\n currentPath?: string\n}\n\nexport interface SelfIdWidget {\n shouldShow(): boolean\n markShown(): void\n render(container: HTMLElement): void\n handleSubmit(responses: Record<string, string>): void\n}\n\nconst FOCUSABLE_SELECTOR =\n 'button:not([disabled]), [href], input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex=\"-1\"])'\n\nfunction focusableElements(root: HTMLElement): HTMLElement[] {\n return Array.from(root.querySelectorAll<HTMLElement>(FOCUSABLE_SELECTOR)).filter((el) => !el.hidden)\n}\n\n/** Keeps Tab inside `root`, wrapping at both ends. */\nfunction trapTab(root: HTMLElement, e: KeyboardEvent): void {\n const focusable = focusableElements(root)\n if (focusable.length === 0) {\n e.preventDefault()\n root.focus()\n return\n }\n const first = focusable[0]!\n const last = focusable[focusable.length - 1]!\n const active = document.activeElement as HTMLElement | null\n const inside = active !== null && root.contains(active) && active !== root\n if (e.shiftKey) {\n if (!inside || active === first) { e.preventDefault(); last.focus() }\n } else if (!inside || active === last) {\n e.preventDefault()\n first.focus()\n }\n}\n\nexport function createSelfIdWidget(config: SelfIdWidgetConfig): SelfIdWidget {\n const frequency = config.frequency ?? 'once'\n const displayMode = config.displayMode ?? 'inline'\n const isModal = displayMode === 'modal'\n\n /** Undo callbacks for everything render() attached outside the container. */\n let cleanups: Array<() => void> = []\n\n function shouldShow(): boolean {\n if (frequency === 'always') return true\n if (frequency === 'session') return false\n\n const raw = localStorage.getItem(SHOWN_KEY)\n if (!raw) return true\n\n if (frequency === 'once') return false\n\n // 'daily' \u2014 true if more than 24h since last shown\n const ts = Number(raw)\n return !isNaN(ts) && Date.now() - ts > MS_PER_DAY\n }\n\n function markShown(): void {\n localStorage.setItem(SHOWN_KEY, String(Date.now()))\n }\n\n function handleSubmit(responses: Record<string, string>): void {\n storeSelfId(responses)\n // Attempt the immediate variant swap, but never let a personalization/resolver\n // error skip markShown() \u2014 otherwise a `frequency: 'once'` widget re-shows on\n // every subsequent page load forever (issue #80). The `finally` keeps the\n // frequency gate honored even if `handleSelfIdSubmit` throws.\n try {\n handleSelfIdSubmit(responses, config.audiences ?? [], config.currentPath ?? '/')\n } finally {\n markShown()\n }\n }\n\n /** Removes the widget and everything it attached to the document. */\n function teardown(container: HTMLElement): void {\n for (const undo of cleanups) undo()\n cleanups = []\n container.replaceChildren()\n container.className = ''\n container.removeAttribute('role')\n container.removeAttribute('aria-modal')\n container.removeAttribute('aria-label')\n container.removeAttribute('aria-labelledby')\n container.removeAttribute('tabindex')\n container.hidden = true\n }\n\n /**\n * Visitor closed the widget without answering. Dismissing honors the\n * frequency gate exactly like skipping does, so a `once` widget does not\n * reappear on the next page.\n */\n function dismiss(container: HTMLElement): void {\n markShown()\n teardown(container)\n }\n\n function render(container: HTMLElement): void {\n for (const undo of cleanups) undo()\n cleanups = []\n container.replaceChildren()\n container.hidden = false\n // Style hook uses kebab-case (kywi-selfid-hello-bar-top) even though the\n // config value is snake_case (hello_bar_top), matching the existing\n // kywi-selfid-slide-in convention.\n const displayModeClass = displayMode.replace(/_/g, '-')\n container.className = `kywi-selfid-widget kywi-selfid-${displayModeClass}`\n\n if (isModal) {\n // A real backdrop ELEMENT (the old ::before pseudo could not be clicked):\n // clicking it dismisses without submitting.\n const backdrop = document.createElement('div')\n backdrop.className = 'kywi-selfid-backdrop'\n backdrop.addEventListener('click', () => dismiss(container))\n ;(container.parentElement ?? document.body).appendChild(backdrop)\n cleanups.push(() => backdrop.remove())\n\n const previousOverflow = document.body.style.overflow\n document.body.style.overflow = 'hidden'\n cleanups.push(() => { document.body.style.overflow = previousOverflow })\n\n const onKeydown = (e: KeyboardEvent) => {\n if (e.key === 'Escape') { e.preventDefault(); dismiss(container); return }\n if (e.key === 'Tab') trapTab(container, e)\n }\n document.addEventListener('keydown', onKeydown, true)\n cleanups.push(() => document.removeEventListener('keydown', onKeydown, true))\n\n container.setAttribute('role', 'dialog')\n container.setAttribute('aria-modal', 'true')\n if (config.headline) {\n container.setAttribute('aria-labelledby', MODAL_LABEL_ID)\n } else {\n container.setAttribute('aria-label', 'Personalize your experience')\n }\n container.tabIndex = -1\n }\n\n if (displayMode === 'hello_bar_top') {\n // Offset hook (#106): hosts push their own content down with\n // `body.kywi-selfid-hello-bar-top-open` and/or the measured height in\n // `--kywi-selfid-hello-bar-height` \u2014 no hardcoded header height anywhere.\n document.body.classList.add(HELLO_BAR_TOP_CLASS)\n const syncHeight = () => {\n document.documentElement.style.setProperty(HELLO_BAR_HEIGHT_PROPERTY, `${container.offsetHeight}px`)\n }\n window.addEventListener('resize', syncHeight)\n cleanups.push(() => {\n document.body.classList.remove(HELLO_BAR_TOP_CLASS)\n document.documentElement.style.removeProperty(HELLO_BAR_HEIGHT_PROPERTY)\n window.removeEventListener('resize', syncHeight)\n })\n // Measured once the card's children exist (i.e. after render() returns).\n queueMicrotask(syncHeight)\n }\n\n if (DISMISSIBLE_MODES.has(displayMode)) {\n const dismissBtn = document.createElement('button')\n dismissBtn.type = 'button'\n dismissBtn.className = 'kywi-selfid-dismiss'\n dismissBtn.setAttribute('aria-label', 'Dismiss')\n dismissBtn.textContent = '\u00D7'\n dismissBtn.addEventListener('click', () => dismiss(container))\n container.appendChild(dismissBtn)\n }\n\n if (config.headline) {\n const h = document.createElement('h3')\n h.className = 'kywi-selfid-headline'\n h.textContent = config.headline\n if (isModal) h.id = MODAL_LABEL_ID\n container.appendChild(h)\n }\n\n if (config.subheadline) {\n const p = document.createElement('p')\n p.className = 'kywi-selfid-subheadline'\n p.textContent = config.subheadline\n container.appendChild(p)\n }\n\n const form = document.createElement('form')\n form.className = 'kywi-selfid-form'\n\n for (const field of config.fields) {\n const fieldWrap = document.createElement('div')\n fieldWrap.className = 'kywi-selfid-field'\n\n const label = document.createElement('label')\n label.htmlFor = `kywi-field-${field.id}`\n label.textContent = field.label\n fieldWrap.appendChild(label)\n\n let input: HTMLInputElement | HTMLSelectElement\n\n if (field.type === 'select') {\n const select = document.createElement('select')\n select.id = `kywi-field-${field.id}`\n select.name = field.id\n if (field.required) select.required = true\n if (field.options) {\n const placeholder = document.createElement('option')\n placeholder.value = ''\n placeholder.textContent = ''\n select.appendChild(placeholder)\n for (const opt of field.options) {\n const { value, label } = normalizeOption(opt)\n const option = document.createElement('option')\n option.value = value\n option.textContent = label\n select.appendChild(option)\n }\n }\n input = select\n } else {\n const inp = document.createElement('input')\n inp.id = `kywi-field-${field.id}`\n inp.name = field.id\n inp.type = field.type\n if (field.required) inp.required = true\n input = inp\n }\n\n fieldWrap.appendChild(input)\n form.appendChild(fieldWrap)\n }\n\n const actions = document.createElement('div')\n actions.className = 'kywi-selfid-actions'\n\n const submitBtn = document.createElement('button')\n submitBtn.type = 'submit'\n submitBtn.className = 'kywi-selfid-submit'\n submitBtn.textContent = config.submitLabel ?? 'Submit'\n actions.appendChild(submitBtn)\n\n if (config.skipLabel) {\n const skipBtn = document.createElement('button')\n skipBtn.type = 'button'\n skipBtn.className = 'kywi-selfid-skip'\n skipBtn.textContent = config.skipLabel\n skipBtn.addEventListener('click', () => dismiss(container))\n actions.appendChild(skipBtn)\n }\n\n form.appendChild(actions)\n\n form.addEventListener('submit', (e) => {\n e.preventDefault()\n const data = new FormData(form)\n const responses: Record<string, string> = {}\n data.forEach((value, key) => {\n responses[key] = String(value)\n })\n handleSubmit(responses)\n // An overlay must not stay on screen over the freshly personalized page \u2014\n // and for the modal, leaving it up would also leave the scroll lock on.\n if (OVERLAY_MODES.has(displayMode)) teardown(container)\n })\n\n container.appendChild(form)\n\n if (isModal) container.focus()\n }\n\n return { shouldShow, markShown, render, handleSubmit }\n}\n", "// packages/kywi-js/src/personalization/badge.ts\n//\n// The `personalizationBadge` layout module's runtime half (#107). It is a\n// PASSIVE status pill \u2014 \"Personalization active/off\", plus the audience name\n// when there is one \u2014 that opens the global transparency panel on click. It\n// deliberately embeds no opt-in/opt-out controls of its own: opting out and\n// switching audience live in exactly one surface (see audience/transparency.ts),\n// so a badge placed on a page can never disagree with the global panel.\n//\n// CONSENT: the badge writes no cookies at all \u2014 it only READS `kywi_optout` to\n// choose its label, and reads need no consent. The panel it opens writes only\n// `necessary`-category preference cookies, so nothing here is gated on\n// `hasConsent()` (kywi-cms#91). Issue #91 listed this file as an ungated\n// `kywi_optout` writer; that write had already moved to\n// `audience/transparency.ts` by the time the fix landed.\n\nimport { COOKIE_NAMES, readCookies } from '../cookies.js'\nimport { openTransparencyPanel } from '../audience/transparency.js'\n\nexport interface PersonalizationBadgeInfo {\n /** The audience the page was personalized for, if any. */\n audienceName?: string | null\n}\n\nfunction isOptedOut(): boolean {\n return readCookies().has(COOKIE_NAMES.OPTOUT)\n}\n\nexport function renderPersonalizationBadge(info: PersonalizationBadgeInfo = {}): void {\n const containers = document.querySelectorAll('[data-kywi-personalization-badge]')\n if (containers.length === 0) return\n\n const optedOut = isOptedOut()\n\n for (const container of Array.from(containers)) {\n // Clear existing badge to avoid duplication\n container.querySelector('.kywi-personalization-badge')?.remove()\n\n const badge = document.createElement('div')\n badge.className = 'kywi-personalization-badge'\n\n const trigger = document.createElement('button')\n trigger.type = 'button'\n trigger.className = 'kywi-badge-open'\n trigger.setAttribute('aria-label', 'Personalization details')\n\n const status = document.createElement('span')\n status.className = 'kywi-badge-status'\n status.textContent = optedOut ? 'Personalization off' : 'Personalization active'\n trigger.appendChild(status)\n\n if (!optedOut && info.audienceName) {\n const audience = document.createElement('span')\n audience.className = 'kywi-badge-audience'\n audience.textContent = info.audienceName\n trigger.appendChild(audience)\n }\n\n trigger.addEventListener('click', () => openTransparencyPanel())\n badge.appendChild(trigger)\n container.appendChild(badge)\n }\n}\n", "// packages/kywi-js/src/audience/index.ts\nimport type { Audience, VisitorSignals } from '@kywi-software/core/audiences/types'\nimport { evaluateClientSide } from './evaluate.js'\nimport { patchVariantContainers } from './dom-patcher.js'\nimport { renderTransparencyPanel } from './transparency.js'\nimport { checkPreviewInit, getPreviewAudienceId, renderPreviewBanner } from './preview.js'\nimport { populateDataLayer, fireKywiReady, readServerMeta } from './data-layer.js'\nimport { readCookies, COOKIE_NAMES } from '../cookies.js'\nimport { hasConsent, type ConsentGateOptions } from '../consent.js'\nimport { runClientAdapters } from './adapter-runner.js'\nimport { Kywi } from '../index.js'\nimport { createSelfIdWidget, resolveSelfIdMount, type SelfIdWidgetConfig } from './selfid-widget.js'\nimport { renderPersonalizationBadge } from '../personalization/badge.js'\n\nexport interface AudienceBootConfig {\n audiences: Audience[]\n currentPath: string\n pageCategory?: string\n serverSignals?: Partial<VisitorSignals>\n adapters?: Array<{ id: string; side: 'client' | 'server-proxy'; fields: string[]; collect: (s: Partial<VisitorSignals>) => Promise<Record<string, unknown>> }>\n selfIdWidget?: SelfIdWidgetConfig\n /**\n * Base path of the mounted CMS API (e.g. `/api/v1`). Runtime calls\n * (server-proxy adapters, audience assignment) are made under `{apiBase}/kywi/*`.\n * Defaults to the same `{basePath}/api/v1` derivation the entity/feed factories\n * use, so it matches the reference app's mount out of the box.\n */\n apiBase?: string\n /**\n * `personalization.transparencyNotice.enabled`. When false, the global\n * transparency surface never appears on its own \u2014 the visitor can still open\n * it deliberately through a `personalizationBadge` module. Defaults to true;\n * the host page passes the resolved config value through.\n */\n transparencyNotice?: boolean\n /**\n * `theme.personalization.requireConsent` (kywi-cms#91). Defaults to true \u2014\n * the resolved audience is only remembered for a visitor who consented to\n * personalization. `false` turns that gate off for a site whose consent is\n * collected by external tooling; the server applies the same flag, so the two\n * must agree or the request is made and then refused persistence.\n */\n requireConsent?: boolean\n}\n\n/** The mounted CMS API base \u2014 mirrors the derivation in Kywi.boot(). */\nfunction resolveApiBase(explicit?: string): string {\n if (explicit) return explicit\n const basePath = Kywi.context?.basePath\n if (!basePath || basePath === '/') return '/api/v1'\n return `${basePath}/api/v1`\n}\n\nexport async function bootAudienceEngine(config: AudienceBootConfig): Promise<void> {\n const { audiences, currentPath, pageCategory, serverSignals } = config\n const apiBase = resolveApiBase(config.apiBase)\n checkPreviewInit()\n const serverMeta = readServerMeta()\n const previewId = getPreviewAudienceId()\n\n // Audiences the visitor can be shown as \u2014 the switcher's option list and the\n // lookup that turns a resolved audience id into a name the visitor recognizes.\n const activeAudiences = audiences\n .filter((a) => a.status === 'active')\n .map((a) => ({ id: a.id, name: a.name }))\n const nameOf = (id: string | null): string | null =>\n id ? activeAudiences.find((a) => a.id === id)?.name ?? null : null\n\n if (previewId) {\n patchVariantContainers(previewId)\n // Show the audience's name, falling back to the raw id for an audience that\n // is not in this page's list (e.g. previewing a draft audience).\n renderPreviewBanner(nameOf(previewId) ?? previewId)\n renderPersonalizationBadge({ audienceName: nameOf(previewId) })\n populateDataLayer({ visitorId: serverMeta.visitorId, audienceId: previewId, experimentId: null, variantId: null, isPreview: true, isOptedOut: false })\n fireKywiReady()\n return\n }\n\n const cookies = readCookies()\n // `theme.personalization.requireConsent` for this page, resolved once and used\n // by every gate below. Built conditionally: under `exactOptionalPropertyTypes`\n // an explicit `undefined` is not the same as an absent key.\n const consentGate: ConsentGateOptions =\n config.requireConsent === undefined ? {} : { requireConsent: config.requireConsent }\n const mayPersonalize = hasConsent('personalization', consentGate)\n\n // `kywi_visitor` and `kywi_known` are `personalization` cookies, so the client\n // reads them through the same gate the server does (kywi-cms#91). Without it\n // this fallback quietly undid the server's own gating: the server hands an\n // unconsented visitor a throwaway id precisely so they stop being recognised,\n // and boot would then recognise them anyway off a cookie left over from before\n // they were ever asked. (`clearDeniedCookies` sweeps these on an explicit\n // Reject, but never for a visitor who simply has not answered.)\n //\n // `serverMeta.visitorId` is NOT gated: the server already applied the rule\n // when it chose what to stamp into the page, and that id describes THIS\n // request rather than stored tracking. `kywi_optout` is `essential` and is\n // never gated either \u2014 a suppression preference outranks the gate by design.\n const storedVisitorId = mayPersonalize ? cookies.get(COOKIE_NAMES.VISITOR) : undefined\n let merged: Partial<VisitorSignals> = {\n ...serverSignals,\n identity: { visitorId: serverMeta.visitorId || storedVisitorId || '', isKnown: mayPersonalize && cookies.has(COOKIE_NAMES.KNOWN), maLeadId: null },\n meta: { isOptedOut: cookies.has(COOKIE_NAMES.OPTOUT), isPreview: false, previewAudienceId: null, resolvedAt: 'server' },\n }\n\n // Run client-side adapters if configured\n if (config.adapters && config.adapters.length > 0) {\n const adapterResults = await runClientAdapters(config.adapters, merged, apiBase)\n merged.adapters = { ...merged.adapters, ...adapterResults }\n }\n\n const result = evaluateClientSide(audiences, merged, currentPath, pageCategory, consentGate)\n\n // A manual pin (kywi_audience \u2014 written by the transparency panel's switcher,\n // or by this boot path persisting a client-resolved audience) beats signal\n // resolution, matching the server engine's precedence. Without this the client\n // would re-resolve from signals and immediately overwrite the visitor's own\n // choice on the very next page view.\n // The cookie records HOW it was pinned (`switcher:<id>` for a hand-picked\n // audience, a bare id for an automatic one) so the SERVER can apply the right\n // consent rule \u2014 see core's parseAudiencePin. Here only the id matters.\n const rawPin = cookies.get(COOKIE_NAMES.AUDIENCE) ?? null\n const pinnedId = rawPin?.startsWith('switcher:') ? rawPin.slice('switcher:'.length) || null : rawPin\n const isPinned = !result.isOptedOut && pinnedId !== null && activeAudiences.some((a) => a.id === pinnedId)\n const effectiveAudienceId = isPinned ? pinnedId : result.winningAudienceId\n\n patchVariantContainers(effectiveAudienceId)\n renderTransparencyPanel({\n isPersonalized: effectiveAudienceId !== null,\n isOptedOut: result.isOptedOut,\n hasMatchAvailable: result.allMatchedIds.length > 0,\n audienceId: effectiveAudienceId,\n audiences: activeAudiences,\n apiBase,\n csrfToken: Kywi.context?.csrfToken ?? null,\n enabled: config.transparencyNotice !== false,\n })\n // Render the status pill wherever a personalizationBadge module was placed.\n renderPersonalizationBadge({ audienceName: nameOf(effectiveAudienceId) })\n populateDataLayer({ visitorId: result.signals.identity.visitorId, audienceId: effectiveAudienceId, experimentId: serverMeta.experimentId, variantId: serverMeta.variantId, isPreview: false, isOptedOut: result.isOptedOut })\n fireKywiReady()\n\n // Remember the client-resolved audience so the NEXT server render can use it \u2014\n // but only for a visitor who consented to personalization. `kywi_audience` is\n // a `personalization` cookie in core's map, and unlike the transparency\n // panel's switcher this write is automatic, which is exactly the tracking\n // consent governs (kywi-cms#91). Without consent the swap above still happens\n // for this page; it simply is not remembered. The server enforces the same\n // rule, so this only avoids a request that would be refused persistence.\n if (!isPinned && mayPersonalize && result.winningAudienceId && result.winningAudienceId !== serverMeta.audienceId) {\n fetch(`${apiBase}/kywi/audience`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', ...(Kywi.context?.csrfToken ? { 'X-CSRF-Token': Kywi.context.csrfToken } : {}) },\n // Explicitly the AUTOMATIC write, so the server applies the\n // personalization gate to it (kywi-cms#91). The transparency panel's\n // hand-operated switcher sends source:'switcher' and is exempt.\n body: JSON.stringify({ audienceId: result.winningAudienceId, source: 'boot' }),\n }).catch(() => {})\n }\n\n // Self-ID widget \u2014 mount when frequency allows, timed by its trigger.\n if (config.selfIdWidget) {\n const widget = createSelfIdWidget(config.selfIdWidget)\n if (widget.shouldShow()) {\n const doRender = () => {\n // Inline mode renders into the host's own slot when one is declared\n // ([data-kywi-selfid-mount]); otherwise it falls back to the runtime's\n // container at the end of <body>, like the overlay modes.\n const mount = resolveSelfIdMount(config.selfIdWidget!.displayMode)\n if (mount) { widget.render(mount); return }\n const existing = document.getElementById('kywi-selfid-container')\n const container = existing ?? document.createElement('div')\n if (!container.id) {\n container.id = 'kywi-selfid-container'\n document.body.appendChild(container)\n }\n widget.render(container)\n }\n scheduleSelfIdWidget(config.selfIdWidget.trigger, doRender)\n }\n }\n}\n\n/**\n * Show the self-ID widget per its trigger: immediately, after a delay, or once\n * the visitor scrolls past a depth fraction (0-1). `manual` never auto-shows \u2014\n * the host page decides. An absent trigger behaves as immediate.\n */\nfunction scheduleSelfIdWidget(trigger: SelfIdWidgetConfig['trigger'], render: () => void): void {\n if (!trigger || trigger.type === 'immediate') { render(); return }\n if (trigger.type === 'manual') return\n if (trigger.type === 'delay') {\n window.setTimeout(render, Math.max(0, trigger.seconds) * 1000)\n return\n }\n // scroll: fire once when scrolled past `depth` (fraction of scrollable height).\n const target = Math.min(Math.max(trigger.depth, 0), 1)\n let fired = false\n const onScroll = () => {\n if (fired) return\n const scrollable = document.documentElement.scrollHeight - window.innerHeight\n const progress = scrollable > 0 ? window.scrollY / scrollable : 1\n if (progress >= target) {\n fired = true\n window.removeEventListener('scroll', onScroll)\n render()\n }\n }\n window.addEventListener('scroll', onScroll, { passive: true })\n onScroll()\n}\n", "// packages/kywi-js/src/index.ts\nimport type { KywiContext, KywiModuleDefinition } from './types.js'\nimport { readContext } from './context.js'\nimport { createEntityFactory } from './entity.js'\nimport { createFeedFactory } from './feed.js'\nimport { renderFeed } from './render-feed.js'\nimport { initModules } from './module-system.js'\nimport { initFormHooks } from './form-hooks.js'\nimport { initNavMenus } from './nav/menu.js'\nimport { bootExperiments } from './experiments/index.js'\nimport { setSelfId, clearSelfId } from './audience/selfid-api.js'\n\ntype PreInitCallback = (kywi: typeof Kywi) => void\nconst preInitCallbacks: PreInitCallback[] = []\nlet booted = false\n\nexport const Kywi = {\n version: '0.1.0',\n context: null as unknown as KywiContext,\n Module: {} as Record<string, KywiModuleDefinition>,\n preInit(fn: PreInitCallback): void {\n if (booted) { fn(Kywi) } else { preInitCallbacks.push(fn) }\n },\n UI: {\n extend(definition: KywiModuleDefinition): KywiModuleDefinition {\n return { ...definition }\n },\n },\n DisplayObject: {\n Form: {\n _hooks: {} as Record<string, (...args: unknown[]) => unknown>,\n extend(methods: Record<string, (...args: unknown[]) => unknown>): void {\n Object.assign(Kywi.DisplayObject.Form._hooks, methods)\n },\n },\n },\n /**\n * Record who the visitor says they are, and resolve their audience\n * (kywi-cms#200). Writes the browser-side answers, posts them to the\n * consent-gated runtime endpoint, and reloads so the SERVER can re-render\n * with the resolved audience pinned \u2014 which is the only way to reach a\n * variant arm, since every losing arm is pruned server-side (#167).\n *\n * On the global because a site's own \"What brings you here?\" control needs\n * it: the built-in widget's write path is module-private.\n */\n setSelfId,\n /** Forget the visitor's self-identification and un-pin their audience. */\n clearSelfId,\n getEntity: null as unknown as (type: string) => any,\n getFeed: null as unknown as (type: string) => any,\n renderFeed: null as unknown as (options: any) => void,\n boot(): void {\n if (booted) return\n Kywi.context = readContext()\n for (const fn of preInitCallbacks) { fn(Kywi) }\n const basePath = Kywi.context.basePath === '/' ? '/api/v1' : `${Kywi.context.basePath}/api/v1`\n Kywi.getEntity = createEntityFactory(basePath, Kywi.context.csrfToken)\n Kywi.getFeed = createFeedFactory(basePath, Kywi.context.csrfToken)\n Kywi.renderFeed = renderFeed\n initModules()\n initFormHooks()\n // Nav modules render fully usable without JS; this layers hover intent,\n // Escape/arrow keys and viewport-edge flipping on top (kywi-cms#111).\n initNavMenus()\n // A/B experiment beacons: confirm exposure of the server-rendered variant\n // and wire conversion signals (pageview / form-submit / kywi.track).\n bootExperiments({ context: Kywi.context })\n booted = true\n },\n}\n\nif (typeof globalThis !== 'undefined') { (globalThis as any).Kywi = Kywi }\nif (typeof document !== 'undefined') {\n if (document.readyState === 'loading') {\n document.addEventListener('DOMContentLoaded', () => Kywi.boot())\n } else {\n Kywi.boot()\n }\n}\n\nexport { bootAudienceEngine } from './audience/index.js'\nexport {\n setSelfId,\n clearSelfId,\n type SelfIdAudience,\n type SelfIdOptions,\n type SelfIdResult,\n} from './audience/selfid-api.js'\nexport {\n openTransparencyPanel,\n renderTransparencyPanel,\n type TransparencyState,\n type TransparencyAudience,\n} from './audience/transparency.js'\nexport { bootExperiments, type ExperimentRuntime, type ConversionSignal } from './experiments/index.js'\nexport { initNavMenus, enhanceNav, NAV_BREAKPOINT } from './nav/menu.js'\n// Cookie consent (kywi-cms#91). The shipped `cookieConsent` layout module drives\n// this itself; these are exported so a site with its OWN banner can record a\n// decision the engine's gated cookie writes will honour.\nexport {\n getConsentState,\n setConsent,\n hasConsent,\n clearDeniedCookies,\n COOKIE_CONSENT_CATEGORY,\n CONSENT_COOKIE,\n AUDIENCE_PIN_COOKIE,\n SWITCHER_PIN_PREFIX,\n type ConsentCategory,\n type ConsentGateOptions,\n type ConsentState,\n} from './consent.js'\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["isOptedOut", "DEFAULT_API_BASE", "readStorage", "audienceName", "label", "resolveApiBase"]
|
|
7
7
|
}
|