@consenti/ui 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +888 -0
- package/dist/angular.d.cts +481 -0
- package/dist/angular.d.ts +481 -0
- package/dist/angular.js +74 -0
- package/dist/angular.js.map +1 -0
- package/dist/angular.mjs +53 -0
- package/dist/angular.mjs.map +1 -0
- package/dist/chunk-ARALKXMB.mjs +20 -0
- package/dist/chunk-ARALKXMB.mjs.map +1 -0
- package/dist/hmac-N6H7F2K2.mjs +26 -0
- package/dist/hmac-N6H7F2K2.mjs.map +1 -0
- package/dist/http-VHOGZ6ZJ.mjs +7 -0
- package/dist/http-VHOGZ6ZJ.mjs.map +1 -0
- package/dist/index.css +1 -0
- package/dist/index.d.ts +770 -0
- package/dist/index.mjs +2221 -0
- package/dist/index.mjs.map +1 -0
- package/dist/index.umd.js +2305 -0
- package/dist/index.umd.js.map +1 -0
- package/dist/react.d.cts +452 -0
- package/dist/react.d.ts +452 -0
- package/dist/react.js +61 -0
- package/dist/react.js.map +1 -0
- package/dist/react.mjs +40 -0
- package/dist/react.mjs.map +1 -0
- package/dist/style.d.ts +2 -0
- package/dist/testing.d.cts +109 -0
- package/dist/testing.d.ts +109 -0
- package/dist/testing.js +95 -0
- package/dist/testing.js.map +1 -0
- package/dist/testing.mjs +74 -0
- package/dist/testing.mjs.map +1 -0
- package/dist/vue.d.cts +446 -0
- package/dist/vue.d.ts +446 -0
- package/dist/vue.js +73 -0
- package/dist/vue.js.map +1 -0
- package/dist/vue.mjs +52 -0
- package/dist/vue.mjs.map +1 -0
- package/package.json +97 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/styles/consenti-css.ts","../src/utils/ssr.ts","../src/utils/broadcast.ts","../src/utils/receipt.ts","../src/utils/cookie.ts","../src/utils/storage.ts","../src/core/consent-store.ts","../src/core/event-bus.ts","../src/utils/locale.ts","../src/core/profile-resolver.ts","../src/core/gpc.ts","../src/ui/buttons.ts","../src/ui/overlay.ts","../src/ui/banner.ts","../src/ui/focus-trap.ts","../src/ui/modal.ts","../src/ui/renderer.ts","../src/core/consenti-setup.ts","../src/plugins/plugin.base.ts","../src/profile/consenti-profile.ts","../src/utils/consent-script.ts","../src/utils/cookie-trigger.ts"],"sourcesContent":["// Auto-generated by scripts/build-css.mjs — do not edit manually.\n// Run `npm run build --workspace=apps/ui` to regenerate.\nexport const CONSENTI_CSS = `:root{--consenti-color-bg: #ffffff;--consenti-color-text: #1a2e4a;--consenti-color-primary: #1565c0;--consenti-color-primary-text: #ffffff;--consenti-color-secondary: #f0f4f8;--consenti-color-secondary-text: #1a2e4a;--consenti-color-border: #dbe4ee;--consenti-color-overlay: rgba(26, 52, 96, 0.55);--consenti-font-family: system-ui, -apple-system, sans-serif;--consenti-font-size-base: 14px;--consenti-font-size-heading: 16px;--consenti-font-weight-heading: 600;--consenti-line-height: 1.5;--consenti-spacing-sm: 8px;--consenti-spacing-md: 16px;--consenti-spacing-lg: 24px;--consenti-border-radius: 8px;--consenti-border-radius-btn: 4px;--consenti-shadow: 0 4px 24px rgba(21, 101, 192, 0.14);--consenti-color-accent: #d32f2f;--consenti-color-accent-text: #ffffff;--consenti-toggle-bg-on: #43a047;--consenti-toggle-bg-off: #c8d6e8;--consenti-toggle-knob: #ffffff;--consenti-toggle-width: 44px;--consenti-toggle-height: 24px;--consenti-z-banner: 9999;--consenti-z-overlay: 9998;--consenti-z-modal: 10000}.consenti-root--dark{--consenti-color-bg: #1e2535;--consenti-color-text: #e2e8f0;--consenti-color-primary: #4a9eff;--consenti-color-primary-text: #ffffff;--consenti-color-secondary: #2a3447;--consenti-color-secondary-text: #cbd5e1;--consenti-color-border: #374151;--consenti-color-overlay: rgba(0, 0, 0, 0.65);--consenti-shadow: 0 4px 24px rgba(0, 0, 0, 0.4);--consenti-toggle-bg-off: #4b5563}.consenti-overlay{position:fixed;inset:0;background:var(--consenti-color-overlay);z-index:var(--consenti-z-overlay)}.consenti-btn{display:inline-flex;align-items:center;justify-content:center;padding:var(--consenti-spacing-sm) var(--consenti-spacing-md);border:none;border-radius:var(--consenti-border-radius-btn);font-family:var(--consenti-font-family);font-size:var(--consenti-font-size-base);font-weight:500;line-height:1;cursor:pointer;text-decoration:none;transition:opacity .15s ease}.consenti-btn:focus-visible{outline:2px solid var(--consenti-color-primary);outline-offset:2px}.consenti-btn:hover{opacity:.9}.consenti-btn--primary{background:var(--consenti-color-primary);color:var(--consenti-color-primary-text)}.consenti-btn--secondary{background:var(--consenti-color-secondary);color:var(--consenti-color-secondary-text)}.consenti-btn--text{background:rgba(0,0,0,0);color:var(--consenti-color-primary);padding-left:0;padding-right:0;text-decoration:underline}.consenti-btn--accent{background:var(--consenti-color-accent);color:var(--consenti-color-accent-text)}.consenti-btn--close{background:rgba(0,0,0,0);color:var(--consenti-color-text);font-size:20px;line-height:1;padding:4px 8px;border-radius:50%}.consenti-btn--close:hover{background:var(--consenti-color-secondary);opacity:1}.consenti-banner{position:fixed;left:0;right:0;z-index:var(--consenti-z-banner);background:var(--consenti-color-bg);color:var(--consenti-color-text);font-family:var(--consenti-font-family);font-size:var(--consenti-font-size-base);line-height:var(--consenti-line-height);box-shadow:var(--consenti-shadow);padding:var(--consenti-spacing-lg);display:flex;flex-wrap:wrap;align-items:center;gap:var(--consenti-spacing-md)}.consenti-banner--top{top:0;border-bottom:1px solid var(--consenti-color-border)}@media(prefers-reduced-motion: no-preference){.consenti-banner--top{animation:consenti-slide-down .2s ease-out}}.consenti-banner--bottom{bottom:0;border-top:1px solid var(--consenti-color-border)}@media(prefers-reduced-motion: no-preference){.consenti-banner--bottom{animation:consenti-slide-up .2s ease-out}}.consenti-banner--middle{top:50%;transform:translateY(-50%);left:50%;right:auto;transform:translate(-50%, -50%);width:min(600px,90vw);border-radius:var(--consenti-border-radius)}.consenti-banner--left-bottom{bottom:var(--consenti-spacing-lg);left:var(--consenti-spacing-lg);right:auto;width:min(400px,90vw);border-radius:var(--consenti-border-radius)}.consenti-banner--right-bottom{bottom:var(--consenti-spacing-lg);right:var(--consenti-spacing-lg);left:auto;width:min(400px,90vw);border-radius:var(--consenti-border-radius)}.consenti-banner__text-container{flex:1;min-width:200px}.consenti-banner__heading{font-size:var(--consenti-font-size-heading);font-weight:var(--consenti-font-weight-heading);margin:0 0 var(--consenti-spacing-sm);width:100%}.consenti-banner__text{margin:0}.consenti-banner__links{display:flex;flex-wrap:wrap;gap:var(--consenti-spacing-sm);margin-top:var(--consenti-spacing-sm)}.consenti-banner__link{font-size:.85em;text-decoration:underline;text-underline-offset:2px}.consenti-banner__controls{position:absolute;top:var(--consenti-spacing-sm);right:var(--consenti-spacing-sm);display:flex;align-items:center;gap:4px}.consenti-banner__buttons{display:flex;flex-wrap:wrap;gap:var(--consenti-spacing-sm);align-items:center}.consenti-banner--top .consenti-banner__buttons,.consenti-banner--bottom .consenti-banner__buttons{margin-left:2vw}@keyframes consenti-slide-up{from{transform:translateY(100%)}to{transform:translateY(0)}}@keyframes consenti-slide-down{from{transform:translateY(-100%)}to{transform:translateY(0)}}.consenti-modal{position:fixed;inset:0;z-index:var(--consenti-z-modal);display:flex;font-family:var(--consenti-font-family);font-size:var(--consenti-font-size-base);color:var(--consenti-color-text);line-height:var(--consenti-line-height)}.consenti-modal--passthrough{pointer-events:none}.consenti-modal--passthrough .consenti-modal__container{pointer-events:auto}.consenti-modal--center{align-items:center;justify-content:center}.consenti-modal--left{align-items:stretch;justify-content:flex-start}.consenti-modal--left .consenti-modal__container{border-radius:0 var(--consenti-border-radius) var(--consenti-border-radius) 0;height:100%;max-height:100vh}.consenti-modal--right{align-items:stretch;justify-content:flex-end}.consenti-modal--right .consenti-modal__container{border-radius:0;height:100%;max-height:100vh}.consenti-modal__overlay{position:absolute;inset:0;background:var(--consenti-color-overlay)}.consenti-modal__container{position:relative;z-index:1;background:var(--consenti-color-bg);border-radius:var(--consenti-border-radius);box-shadow:var(--consenti-shadow);width:min(560px,90vw);max-height:85vh;display:flex;flex-direction:column;overflow:hidden}@media(prefers-reduced-motion: no-preference){.consenti-modal__container{animation:consenti-fade-in .15s ease-out}}.consenti-modal__header{display:flex;align-items:center;justify-content:space-between;padding:var(--consenti-spacing-md) var(--consenti-spacing-lg);border-bottom:1px solid var(--consenti-color-border);flex-shrink:0}.consenti-modal__heading{font-size:var(--consenti-font-size-heading);font-weight:var(--consenti-font-weight-heading);margin:0;flex:1}.consenti-modal__header-controls{display:flex;align-items:center;gap:4px;flex-shrink:0}.consenti-modal__close{flex-shrink:0}.consenti-modal__links{display:flex;flex-wrap:wrap;gap:var(--consenti-spacing-sm);padding:var(--consenti-spacing-sm) var(--consenti-spacing-lg)}.consenti-modal__link{font-size:.85em;text-decoration:underline;text-underline-offset:2px}.consenti-modal__subheading{padding:var(--consenti-spacing-sm) var(--consenti-spacing-lg) 0;margin:0;color:#6b7280;font-size:13px}.consenti-modal__text{padding:var(--consenti-spacing-sm) var(--consenti-spacing-lg)}.consenti-modal__body{overflow-y:auto;flex:1;padding:0 var(--consenti-spacing-lg)}.consenti-modal__categories{display:flex;flex-direction:column;gap:var(--consenti-spacing-md);padding:var(--consenti-spacing-md) 0}.consenti-modal__buttons{display:flex;flex-wrap:wrap;gap:var(--consenti-spacing-sm);padding:var(--consenti-spacing-md) var(--consenti-spacing-lg);border-top:1px solid var(--consenti-color-border);flex-shrink:0}.consenti-modal__receipt-option{margin:0 var(--consenti-spacing-lg);padding-top:var(--consenti-spacing-md);border-top:1px solid var(--consenti-color-border);flex-shrink:0}.consenti-modal__receipt-label{display:flex;align-items:center;gap:var(--consenti-spacing-sm);font-size:var(--consenti-font-size-base);cursor:pointer}.consenti-modal__receipt-checkbox{width:16px;height:16px;flex-shrink:0;cursor:pointer}.consenti-modal__receipt-desc{margin:4px 0 0 24px;font-size:12px;color:#6b7280}@keyframes consenti-fade-in{from{opacity:0;transform:scale(0.97)}to{opacity:1;transform:scale(1)}}.consenti-category{padding:var(--consenti-spacing-md) 0;border-bottom:1px solid var(--consenti-color-border)}.consenti-category:last-child{border-bottom:none}.consenti-category--mandatory .consenti-category__toggle{opacity:.6;cursor:default}.consenti-category__header{display:flex;align-items:center;justify-content:space-between;gap:var(--consenti-spacing-md);margin-bottom:var(--consenti-spacing-sm)}.consenti-category__heading{font-size:var(--consenti-font-size-heading);font-weight:var(--consenti-font-weight-heading);margin:0}.consenti-category__text{color:#4b5563;font-size:13px;margin:0}.consenti-category__li-desc{margin:var(--consenti-spacing-sm) 0 0;font-size:12px;color:#6b7280;font-style:italic}.consenti-category__toggle{flex-shrink:0;position:relative;width:var(--consenti-toggle-width);height:var(--consenti-toggle-height);border-radius:calc(var(--consenti-toggle-height)/2);background:var(--consenti-toggle-bg-off);border:none;cursor:pointer;transition:background .2s ease;padding:0}.consenti-category__toggle:focus-visible{outline:2px solid var(--consenti-color-primary);outline-offset:2px}.consenti-category__toggle[aria-checked=true]{background:var(--consenti-toggle-bg-on)}.consenti-category__toggle--mandatory{cursor:default}.consenti-category__toggle-knob{position:absolute;top:2px;left:2px;width:calc(var(--consenti-toggle-height) - 4px);height:calc(var(--consenti-toggle-height) - 4px);border-radius:50%;background:var(--consenti-toggle-knob);transition:transform .2s ease;display:block}[aria-checked=true] .consenti-category__toggle-knob{transform:translateX(calc(var(--consenti-toggle-width) - var(--consenti-toggle-height)))}.consenti-locale-switcher{position:relative;display:inline-flex}.consenti-locale-switcher__btn{font-size:1rem;line-height:1;cursor:pointer}.consenti-locale-switcher__list{position:absolute;top:calc(100% + 4px);right:0;z-index:calc(var(--consenti-z-modal) + 1);background:var(--consenti-color-bg);border:1px solid var(--consenti-color-border);border-radius:var(--consenti-border-radius);box-shadow:var(--consenti-shadow);list-style:none;margin:0;padding:4px 0;min-width:80px}.consenti-locale-switcher__list li{display:block}.consenti-locale-switcher__option{display:block;width:100%;padding:6px 12px;text-align:left;font-size:var(--consenti-font-size-base);color:var(--consenti-color-text);background:none;border:none;cursor:pointer}.consenti-locale-switcher__option:hover{background:var(--consenti-color-secondary)}.consenti-locale-switcher__option--active{font-weight:600;color:var(--consenti-color-primary)}.consenti-locale-switcher--modal .consenti-locale-switcher__list{right:0}`\n","/**\n * Server-Side Rendering (SSR) guards.\n *\n * Every access to browser globals (`window`, `document`, `navigator`) must go through\n * these helpers. `new ConsentiSetup(config)` called during SSR — e.g. in a Next.js or\n * Nuxt server render — silently no-ops because `isClient()` returns `false` and the\n * constructor returns early before touching any DOM API.\n */\n\n/**\n * Returns `true` when running in a browser environment with `window` and `document`\n * available. Returns `false` in Node.js, Deno, edge runtimes, and test environments\n * that do not provide a DOM.\n */\nexport const isClient = (): boolean =>\n typeof window !== 'undefined' && typeof document !== 'undefined'\n\n/**\n * Returns the `window` object when running client-side, or `null` in SSR contexts.\n * Prefer this over bare `window` access in code that might run during SSR.\n */\nexport const safeWindow = (): (Window & typeof globalThis) | null =>\n isClient() ? window : null\n\n/**\n * Returns the `document` object when running client-side, or `null` in SSR contexts.\n */\nexport const safeDocument = (): Document | null =>\n isClient() ? document : null\n\n/**\n * Returns the `navigator` object when running client-side and `navigator` is defined,\n * or `null` otherwise. Some non-browser environments expose `window` but not `navigator`.\n */\nexport const safeNavigator = (): Navigator | null =>\n isClient() && typeof navigator !== 'undefined' ? navigator : null\n","/**\n * Cross-tab consent synchronisation via the `BroadcastChannel` API.\n *\n * When a user interacts with the consent widget in one tab (accept, deny, delete),\n * `ConsentiChannel` broadcasts the change to all other open tabs on the same origin.\n * Receiving tabs update their in-memory state and hide the banner without requiring\n * a page reload.\n *\n * `BroadcastChannel` is not available in very old browsers or in Safari < 15.4.\n * The `isSupported` guard ensures `ConsentiChannel` fails silently in those cases\n * — consent still works, cross-tab sync is just unavailable.\n */\n\nimport type { ConsentiMessage, ConsentValue } from '../types'\nimport { isClient } from './ssr'\n\nexport type { ConsentiMessage }\n\n/**\n * Wrapper around the native `BroadcastChannel` API for Consenti inter-tab messaging.\n *\n * All tabs with an active `ConsentiSetup` instance for the same origin subscribe\n * on the shared channel name `'consenti'` and receive each other's consent events.\n */\nexport class ConsentiChannel {\n private channel: BroadcastChannel | null = null\n private onMessageCallback: ((msg: ConsentiMessage) => void) | undefined\n\n /** `true` when `BroadcastChannel` is available in the current environment. */\n get isSupported(): boolean {\n return isClient() && typeof BroadcastChannel !== 'undefined'\n }\n\n /**\n * Opens the `BroadcastChannel` and registers a message handler.\n * Silently no-ops if `BroadcastChannel` is unsupported.\n *\n * @param onMessage - Callback invoked with each received `ConsentiMessage`.\n */\n connect(onMessage: (msg: ConsentiMessage) => void): void {\n if (!this.isSupported) return\n this.onMessageCallback = onMessage\n this.channel = new BroadcastChannel('consenti')\n this.channel.onmessage = (event: MessageEvent<ConsentiMessage>) => {\n if (event.data?.type) {\n onMessage(event.data)\n }\n }\n this.channel.onmessageerror = () => {\n console.warn('[Consenti] BroadcastChannel message error — ignoring')\n }\n }\n\n /**\n * Posts a message to all other tabs connected to the `'consenti'` channel.\n * The sending tab does not receive its own message.\n *\n * @param message - The `ConsentiMessage` to broadcast.\n */\n send(message: ConsentiMessage): void {\n this.channel?.postMessage(message)\n }\n\n /** Closes the channel and removes the message callback. */\n disconnect(): void {\n this.channel?.close()\n this.channel = null\n this.onMessageCallback = void 0\n }\n}\n\n// Re-export so callers can use ConsentValue without going through types\nexport type { ConsentValue }\n","/**\n * Consent receipt generation and download.\n *\n * When `core.allowReceipt: true`, the preference modal shows an opt-in checkbox.\n * If the user checks it before saving, `downloadReceipt()` is called after\n * `submitConsent()` to download a JSON file to their device.\n *\n * The receipt is a portable record of the user's choices at a specific point in time.\n * If `core.signCookies` is enabled, the receipt also includes the HMAC signature as\n * tamper evidence.\n */\n\nimport type { ConsentReceipt, ConsentValue } from '../types'\n\n/** Parameters required to construct a consent receipt. */\nexport interface ReceiptParams {\n /** Visitor UUID from the `consenti_uid` cookie. */\n visitorId: string\n /** Numeric profile ID. */\n profileId: number\n /** Profile schema version at the time of consent. */\n profileVersion: number\n /** Locale active when consent was given. */\n locale: string\n /** The full consent map. */\n consent: ConsentValue\n /** HMAC-SHA256 hex signature, if `core.signCookies` is enabled. */\n signature?: string | undefined\n}\n\n/**\n * Builds a `ConsentReceipt` from the given parameters.\n * Sets `version: '1.0'` and `issuedAt` to the current time.\n *\n * @param params - Receipt content. See `ReceiptParams`.\n */\nexport function generateReceipt(params: ReceiptParams): ConsentReceipt {\n const receipt: ConsentReceipt = {\n version: '1.0',\n issuedAt: new Date().toISOString(),\n visitorId: params.visitorId,\n profileId: params.profileId,\n profileVersion: params.profileVersion,\n locale: params.locale,\n consent: params.consent,\n }\n if (params.signature) receipt.signature = params.signature\n return receipt\n}\n\n/**\n * Triggers a browser file download of the receipt as a pretty-printed JSON file.\n *\n * The filename is `consenti-receipt-{YYYY-MM-DD}.json`.\n * Uses `Blob` + `URL.createObjectURL` + a synthetic anchor click —\n * this approach is supported in all ES2020 target browsers.\n *\n * Silently returns when called in an SSR context.\n *\n * @param receipt - The receipt object returned by `generateReceipt()`.\n */\nexport function downloadReceipt(receipt: ConsentReceipt): void {\n if (typeof window === 'undefined' || typeof document === 'undefined') return\n\n const json = JSON.stringify(receipt, null, 2)\n const blob = new Blob([json], { type: 'application/json' })\n const url = URL.createObjectURL(blob)\n\n const anchor = document.createElement('a')\n anchor.href = url\n anchor.download = `consenti-receipt-${receipt.issuedAt.slice(0, 10)}.json`\n anchor.style.display = 'none'\n\n document.body.appendChild(anchor)\n anchor.click()\n document.body.removeChild(anchor)\n\n setTimeout(() => URL.revokeObjectURL(url), 1000)\n}\n","/**\n * Low-level `document.cookie` read/write helpers.\n *\n * All functions are SSR-safe — they return early or return `null` when `isClient()`\n * is `false`. Consumers should prefer the `ConsentStorage` class which abstracts\n * over both cookies and `localStorage`.\n */\n\nimport type { CookieOptions } from '../types'\nimport { isClient } from './ssr'\n\n/**\n * Reads a single cookie value by name.\n *\n * @param name - Cookie name (unencoded).\n * @returns The decoded cookie value, or `null` if the cookie is absent or SSR context.\n */\nexport function getCookie(name: string): string | null {\n if (!isClient()) return null\n const match = document.cookie.match(new RegExp(`(?:^|;\\\\s*)${escapeRegExp(name)}=([^;]*)`))\n return match ? decodeURIComponent(match[1] ?? '') : null\n}\n\n/**\n * Writes a cookie to `document.cookie`.\n *\n * The `Secure` flag is auto-detected from `window.location.protocol` when `opts.secure`\n * is not explicitly provided — cookies are set without `Secure` on `http://` to allow\n * local development without HTTPS.\n *\n * @param name - Cookie name (unencoded).\n * @param value - Cookie value (unencoded — will be URI-encoded).\n * @param opts - Optional cookie attributes.\n */\nexport function setCookie(name: string, value: string, opts: CookieOptions = {}): void {\n if (!isClient()) return\n const secure =\n opts.secure ??\n (typeof window !== 'undefined' &&\n typeof window.location !== 'undefined' &&\n window.location.protocol === 'https:')\n const parts = [\n `${encodeURIComponent(name)}=${encodeURIComponent(value)}`,\n `Path=${opts.path ?? '/'}`,\n `SameSite=${opts.sameSite ?? 'Lax'}`,\n opts.maxAge != null ? `Max-Age=${opts.maxAge}` : '',\n secure ? 'Secure' : '',\n opts.domain ? `Domain=${opts.domain}` : '',\n ]\n document.cookie = parts.filter(Boolean).join('; ')\n}\n\n/**\n * Deletes a cookie by setting `Max-Age=0`.\n *\n * @param name - Cookie name (unencoded).\n * @param opts - Optional `path` and `domain` attributes — must match the original `setCookie` call.\n */\nexport function deleteCookie(name: string, opts: Pick<CookieOptions, 'path' | 'domain'> = {}): void {\n setCookie(name, '', { ...opts, maxAge: 0 })\n}\n\n/** Escapes special regex characters in a cookie name so it can be used in a `RegExp`. */\nfunction escapeRegExp(str: string): string {\n return str.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')\n}\n","/**\n * Unified storage abstraction for consent data.\n *\n * `ConsentStorage` normalises `document.cookie` and `localStorage` behind a single\n * `read / write / remove` interface so the rest of the codebase does not need to\n * know which persistence backend is active.\n *\n * `'cookie'` is the default and recommended mode — consent written to a cookie is\n * visible across subdomains when a `Domain` attribute is set.\n * `'localStorage'` is origin-scoped and cannot be shared with subdomains; it is also\n * incompatible with API mode (which sets consent via `Set-Cookie` on the server).\n */\n\nimport type { CookieOptions } from '../types'\nimport { isClient } from './ssr'\nimport { getCookie, setCookie, deleteCookie } from './cookie'\n\n/** The two supported storage backends. */\nexport type StorageMode = 'cookie' | 'localStorage'\n\n/**\n * Abstraction layer over `document.cookie` and `localStorage`.\n *\n * All operations are SSR-safe — they return early without throwing when `isClient()` is `false`.\n */\nexport class ConsentStorage {\n /**\n * @param mode - `'cookie'` (default) or `'localStorage'`. Falls back to `'cookie'` if\n * `localStorage` is requested but unavailable in the current environment.\n */\n constructor(private mode: StorageMode) {\n if (mode === 'localStorage' && isClient() && typeof localStorage === 'undefined') {\n console.warn('[Consenti] localStorage is not available, falling back to cookie storage')\n this.mode = 'cookie'\n }\n }\n\n /**\n * Reads a value from storage.\n *\n * @param key - Cookie name or localStorage key.\n * @returns The stored string value, or `null` if absent or SSR context.\n */\n read(key: string): string | null {\n if (!isClient()) return null\n if (this.mode === 'localStorage') {\n return localStorage.getItem(key)\n }\n return getCookie(key)\n }\n\n /**\n * Writes a value to storage.\n *\n * @param key - Cookie name or localStorage key.\n * @param value - String value to persist.\n * @param opts - Cookie attributes (ignored in localStorage mode).\n */\n write(key: string, value: string, opts: CookieOptions = {}): void {\n if (!isClient()) return\n if (this.mode === 'localStorage') {\n localStorage.setItem(key, value)\n } else {\n setCookie(key, value, opts)\n }\n }\n\n /**\n * Removes a value from storage.\n *\n * @param key - Cookie name or localStorage key.\n * @param opts - Cookie `path` / `domain` attributes (must match the original write call).\n */\n remove(key: string, opts: Pick<CookieOptions, 'path' | 'domain'> = {}): void {\n if (!isClient()) return\n if (this.mode === 'localStorage') {\n localStorage.removeItem(key)\n } else {\n deleteCookie(key, opts)\n }\n }\n}\n","/**\n * Consent cookie read/write operations.\n *\n * Cookie name format : `consenti_{userId}_{profileId}`\n * Cookie value format: `t:{epochSeconds}::{cookieId}:{status}|...`\n * Signed value format: `t:{epochSeconds}::{pairs}::sig:{hmac-hex}`\n *\n * The compact `t:{seconds}` timestamp replaces the previous ISO-8601 format to reduce\n * cookie size. Old `ConsentTimestamp:{ISO}` cookies are parsed transparently for\n * backwards compatibility.\n *\n * Visitor identity is derived from the consent cookie name (no separate `consenti_uid`\n * cookie is written). This avoids dropping identifiers before the user has given consent,\n * which is required by TTDSG §25 (Germany) and similar ePrivacy laws.\n *\n * The store supports both `document.cookie` and `localStorage` via `ConsentStorage`.\n * An in-memory cache avoids repeated cookie reads on the hot path (e.g. `hasConsent()`\n * called in render loops by the React/Vue hooks).\n */\n\nimport type { ConsentValue, ParsedConsent, CookieOptions } from '../types'\nimport { ConsentStorage } from '../utils/storage'\n\nconst COOKIE_NAME_PREFIX = 'consenti'\n\n/**\n * Builds the full cookie name for a visitor + profile combination.\n *\n * @param userId - UUID assigned to this visitor.\n * @param profileId - Numeric profile ID from `core.profileId`.\n */\nexport function buildConsentCookieName(userId: string, profileId: number): string {\n return `${COOKIE_NAME_PREFIX}_${userId}_${profileId}`\n}\n\n/**\n * Serialises a `ConsentValue` map into the compact cookie value string (without signature).\n *\n * @param consent - The consent map to serialise.\n * @returns A string in the form `t:{epochSeconds}::{id}:{status}|...`\n */\nexport function buildCookieValue(consent: ConsentValue): string {\n const epochSeconds = Math.floor(Date.now() / 1000)\n const pairs = Object.entries(consent)\n .map(([id, status]) => `${id}:${status}`)\n .join('|')\n return `t:${epochSeconds}::${pairs}`\n}\n\n/**\n * Parses a raw cookie string back into a `ParsedConsent` object.\n *\n * Handles both the current compact format (`t:{epochSeconds}`) and the legacy\n * ISO-8601 format (`ConsentTimestamp:{ISO}`) for backwards compatibility.\n *\n * Returns `null` if the value is malformed, missing required segments, or contains\n * an unrecognised consent status. Callers should treat `null` as \"no valid consent\".\n *\n * @param value - Raw string read from the cookie / localStorage.\n */\nexport function parseConsentCookie(value: string): ParsedConsent | null {\n const parts = value.split('::')\n if (parts.length < 2) return null\n\n const tsPart = parts[0] ?? ''\n let timestamp: string\n\n if (tsPart.startsWith('t:')) {\n // Compact format: t:{epochSeconds}\n const epochStr = tsPart.slice(2)\n const epoch = parseInt(epochStr, 10)\n if (!epochStr || isNaN(epoch)) return null\n timestamp = new Date(epoch * 1000).toISOString()\n } else if (tsPart.startsWith('ConsentTimestamp:')) {\n // Legacy ISO-8601 format\n timestamp = tsPart.replace('ConsentTimestamp:', '')\n if (!timestamp) return null\n } else {\n return null\n }\n\n const consentPart = parts[1] ?? ''\n const sigPart = parts[2]\n\n const consent: ConsentValue = {}\n for (const pair of consentPart.split('|')) {\n const colonIdx = pair.indexOf(':')\n if (colonIdx === -1) return null\n const id = pair.slice(0, colonIdx)\n const status = pair.slice(colonIdx + 1)\n if (!id || (status !== 'granted' && status !== 'denied' && status !== 'objected')) {\n return null\n }\n consent[id] = status\n }\n\n const result: ParsedConsent = { timestamp, consent }\n if (sigPart?.startsWith('sig:')) {\n result.signature = sigPart.slice(4)\n }\n\n return result\n}\n\n/**\n * Manages reading and writing the per-profile consent cookie for a single visitor.\n *\n * One `ConsentStore` instance is created per `ConsentiSetup` and lives for the\n * lifetime of the widget. An in-memory cache (`cachedConsent`, `cachedTimestamp`)\n * prevents repeat cookie reads on every call to `hasConsent()` / `getConsent()`.\n */\nexport class ConsentStore {\n private storage: ConsentStorage\n private cachedConsent: ConsentValue | null = null\n private cachedTimestamp: string | null = null\n\n /**\n * @param userId - Visitor UUID (from `consenti_uid` cookie).\n * @param profileId - Numeric profile ID.\n * @param storageMode - `'cookie'` (default, cross-subdomain) or `'localStorage'` (origin-scoped).\n * @param cookieDomains - Comma-separated domain list. First entry used as `Domain` attribute.\n */\n constructor(\n private userId: string,\n private profileId: number,\n storageMode: 'cookie' | 'localStorage',\n private cookieDomains?: string,\n ) {\n this.storage = new ConsentStorage(storageMode)\n }\n\n /** The full cookie / localStorage key for this visitor + profile combination. */\n get cookieName(): string {\n return buildConsentCookieName(this.userId, this.profileId)\n }\n\n /**\n * Reads and parses the stored consent record.\n * Updates the in-memory cache on success.\n *\n * @returns The parsed consent or `null` if absent / malformed.\n */\n read(): ParsedConsent | null {\n const raw = this.storage.read(this.cookieName)\n if (!raw) return null\n const parsed = parseConsentCookie(raw)\n if (parsed) {\n this.cachedConsent = parsed.consent\n this.cachedTimestamp = parsed.timestamp\n }\n return parsed\n }\n\n /**\n * Reads, parses, and cryptographically verifies the stored consent record.\n *\n * If the cookie has a `::sig:{hmac}` segment, the signature is verified against\n * the raw value using HMAC-SHA256. A mismatch means the cookie was tampered with —\n * the cookie is deleted and `null` is returned so the widget re-prompts for consent.\n *\n * If the cookie has no signature segment, it is treated as valid (backwards compatible\n * with cookies written before `signCookies` was enabled).\n *\n * @param signingKey - The HMAC key (from profile API response or `core.cookieSigningKey`).\n * @returns The parsed consent, or `null` if absent / malformed / signature invalid.\n */\n async readAndVerify(signingKey: string): Promise<ParsedConsent | null> {\n const raw = this.storage.read(this.cookieName)\n if (!raw) return null\n\n const parts = raw.split('::')\n if (parts.length < 2) return null\n\n const parsed = parseConsentCookie(raw)\n if (!parsed) return null\n\n const hasSig = parts.length === 3 && (parts[2] ?? '').startsWith('sig:')\n if (hasSig) {\n const { verifyValue } = await import('../utils/hmac')\n const valueToVerify = (parts[0] ?? '') + '::' + (parts[1] ?? '')\n const sig = (parts[2] ?? '').slice(4)\n const valid = await verifyValue(valueToVerify, sig, signingKey)\n if (!valid) {\n console.warn('[Consenti] Cookie signature mismatch — consent cleared and banner will re-appear')\n this.delete()\n return null\n }\n }\n\n this.cachedConsent = parsed.consent\n this.cachedTimestamp = parsed.timestamp\n return parsed\n }\n\n /**\n * Writes a consent record to storage.\n *\n * @param consent - The consent map to persist.\n * @param signature - Optional pre-computed HMAC hex string. Use `writeAndSign()` instead\n * if you need signing — it guarantees the signature covers the exact\n * value that gets written (timestamps match).\n */\n write(consent: ConsentValue, signature?: string): void {\n let value = buildCookieValue(consent)\n if (signature) {\n value = `${value}::sig:${signature}`\n }\n const opts = this.buildCookieOpts()\n this.storage.write(this.cookieName, value, opts)\n this.cachedConsent = consent\n this.cachedTimestamp = new Date().toISOString()\n }\n\n /**\n * Builds the cookie value, signs it with HMAC-SHA256 in a single atomic step,\n * and writes the signed value to storage.\n *\n * Using this instead of `write(consent, signature)` ensures the signature\n * covers exactly the value that is written — there is no timestamp drift\n * between building, signing, and writing.\n *\n * @param consent - The consent map to persist.\n * @param signingKey - The HMAC key string.\n * @returns The hex-encoded signature, stored in `this.lastSignature` by the caller.\n */\n async writeAndSign(consent: ConsentValue, signingKey: string): Promise<string> {\n const { signValue } = await import('../utils/hmac')\n const rawValue = buildCookieValue(consent)\n const sig = await signValue(rawValue, signingKey)\n const finalValue = `${rawValue}::sig:${sig}`\n const opts = this.buildCookieOpts()\n this.storage.write(this.cookieName, finalValue, opts)\n this.cachedConsent = consent\n this.cachedTimestamp = new Date().toISOString()\n return sig\n }\n\n /** Removes the consent record from storage and clears the in-memory cache. */\n delete(): void {\n const opts = this.buildCookieOpts()\n this.storage.remove(this.cookieName, opts)\n this.cachedConsent = null\n this.cachedTimestamp = null\n }\n\n /** Returns `true` if a valid consent record exists (uses cache when available). */\n hasConsent(): boolean {\n if (this.cachedConsent) return true\n return this.read() !== null\n }\n\n /** Returns the stored consent map, or `null` if no consent has been given. */\n getConsent(): ConsentValue | null {\n if (this.cachedConsent) return this.cachedConsent\n return this.read()?.consent ?? null\n }\n\n /** Returns the `Date` of the last consent submission, or `false` if no consent exists. */\n getConsentDate(): Date | false {\n if (this.cachedTimestamp) return new Date(this.cachedTimestamp)\n const parsed = this.read()\n if (!parsed) return false\n return new Date(parsed.timestamp)\n }\n\n /**\n * Updates the in-memory cache without writing to storage.\n * Used by the cross-tab BroadcastChannel handler to keep other tabs in sync.\n */\n setInMemory(consent: ConsentValue): void {\n this.cachedConsent = consent\n this.cachedTimestamp = new Date().toISOString()\n }\n\n /** Clears the in-memory cache. Used when consent is deleted from another tab. */\n clearInMemory(): void {\n this.cachedConsent = null\n this.cachedTimestamp = null\n }\n\n /**\n * Derives the visitor UUID from an existing consent cookie, or generates a new one\n * in memory.\n *\n * No separate identifier cookie is written before consent. Storing a tracking\n * identifier before the user has given consent violates TTDSG §25 (Germany) and\n * equivalent ePrivacy laws in the EU. The UUID is embedded in the consent cookie\n * name and only persisted when consent is first saved (via `write()` / `writeAndSign()`).\n *\n * @param profileId - The active profile ID, used to locate an existing consent cookie.\n * @param storageMode - The configured storage mode (`'cookie'` or `'localStorage'`).\n */\n static getOrCreateUserId(profileId: number, storageMode: 'cookie' | 'localStorage'): string {\n if (typeof document === 'undefined') return crypto.randomUUID()\n\n // Look for an existing consent cookie matching `consenti_*_{profileId}`.\n const prefix = `${COOKIE_NAME_PREFIX}_`\n const suffix = `_${profileId}`\n const all = storageMode === 'localStorage'\n ? ConsentStore._localStorageKeys()\n : ConsentStore._cookieNames()\n\n for (const name of all) {\n if (name.startsWith(prefix) && name.endsWith(suffix)) {\n const inner = name.slice(prefix.length, name.length - suffix.length)\n // Validate it looks like a UUID (8-4-4-4-12 hex groups).\n if (/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(inner)) {\n return inner\n }\n }\n }\n\n return crypto.randomUUID()\n }\n\n /** Returns all document.cookie names. */\n private static _cookieNames(): string[] {\n return document.cookie.split(';').map((c) => c.split('=')[0]?.trim() ?? '')\n }\n\n /** Returns all localStorage keys (empty array when unavailable). */\n private static _localStorageKeys(): string[] {\n if (typeof localStorage === 'undefined') return []\n return Object.keys(localStorage)\n }\n\n private buildCookieOpts(): CookieOptions {\n const opts: CookieOptions = {\n path: '/',\n sameSite: 'Lax',\n maxAge: 365 * 24 * 60 * 60,\n }\n if (this.cookieDomains) {\n const domain = this.cookieDomains.split(',')[0]?.trim()\n if (domain) opts.domain = domain\n }\n return opts\n }\n}\n","/**\n * Custom DOM event dispatcher with optional Google Tag Manager integration.\n *\n * Every user-visible action in the widget (banner shown, consent saved, modal opened)\n * dispatches a `CustomEvent` on `window` so host applications can react without\n * coupling to Consenti internals.\n *\n * When `config.utils.gtm` is configured, the same events are also pushed to\n * `window.dataLayer` (or a custom variable name) so GTM triggers can fire.\n *\n * Event names all use the `consenti:` namespace prefix to avoid collisions.\n */\n\nimport type { ConsentValue, GtmConfig, ConsentEvent } from '../types'\nimport { isClient, safeWindow } from '../utils/ssr'\n\n/** All event names dispatched by the widget. */\nexport type EventName =\n | 'consenti:bannerInitialized'\n | 'consenti:bannerVisibility'\n | 'consenti:modalVisibility'\n | 'consenti:consentBeingSubmitted'\n | 'consenti:consentSubmitted'\n | 'consenti:closeRequest'\n\n/**\n * Dispatches `CustomEvent` on `window` and optionally pushes to the GTM dataLayer.\n *\n * One `EventBus` instance is created per `ConsentiSetup` and shared across the\n * banner, modal, and consent submission flows.\n */\nexport class EventBus {\n /**\n * @param profileId - Profile ID included in all GTM pushes.\n * @param gtmConfig - Optional GTM integration settings.\n */\n constructor(\n private profileId: number,\n private gtmConfig?: GtmConfig,\n ) {}\n\n /**\n * Dispatches a `consenti:*` custom event on `window` and, if GTM is configured,\n * pushes the event to `window.dataLayer`.\n *\n * Silently no-ops in SSR contexts.\n *\n * @param name - The event name.\n * @param detail - Event payload attached to `event.detail` and included in the GTM push.\n */\n dispatch(name: EventName, detail: ConsentEvent = {}): void {\n if (!isClient()) return\n const event = new CustomEvent(name, { detail, bubbles: true })\n window.dispatchEvent(event)\n this.pushToGTM(name, detail)\n }\n\n private pushToGTM(name: string, detail: ConsentEvent): void {\n const gtm = this.gtmConfig\n if (!gtm) return\n\n const win = safeWindow() as (Window & { [key: string]: unknown }) | null\n if (!win) return\n\n const layerName = gtm.dataLayer ?? 'dataLayer'\n const layer = win[layerName]\n if (!Array.isArray(layer)) return\n\n const shouldPush = !gtm.events || gtm.events.length === 0 || gtm.events.includes(name)\n if (!shouldPush) return\n\n layer.push({\n event: name,\n consenti: {\n profileId: this.profileId,\n consent: detail.consent,\n consentId: detail.consentId,\n consentAction: detail.consentAction,\n },\n })\n\n // Push Google Consent Mode v2 cookieless-ping flags alongside consent submissions.\n // Only emitted when gtag.js or google_tag_manager is detected on the page to\n // prevent no-op pushes that pollute the dataLayer on non-Google sites.\n if (name === 'consenti:consentSubmitted' && this.hasGtag(win)) {\n if (gtm.urlPassthrough) {\n layer.push({ url_passthrough: true })\n }\n if (gtm.adsDataRedaction) {\n // ads_data_redaction: true tells Google to strip identifying data from ad pings\n // when the user has denied ad_storage.\n const adConsentDenied = detail.consent?.['ad_storage'] !== 'granted'\n layer.push({ ads_data_redaction: adConsentDenied })\n }\n }\n }\n\n /**\n * Returns `true` when gtag.js or Google Tag Manager is detected on the page.\n * Used to gate GCM v2 cookieless-ping pushes so they are not emitted when\n * Google services are absent.\n */\n private hasGtag(win: Window & { [key: string]: unknown }): boolean {\n return typeof win['gtag'] === 'function' || typeof win['google_tag_manager'] !== 'undefined'\n }\n}\n\n// Re-export so callers do not need a separate import from types\nexport type { ConsentValue }\n","/**\n * Locale resolution and deep-merge utilities.\n *\n * Consenti resolves translations in three steps:\n * 1. Exact match — `'fr-CA'` → use `translations['fr-CA']`\n * 2. Language prefix — `'fr-CA'` → try `translations['fr']` if `'fr-CA'` is absent\n * 3. Default locale — fall back to `translations[defaultLocale]`\n *\n * Missing keys in the resolved locale are filled from the default locale via `deepMerge`,\n * so partial translations are safe — only the keys that differ need to be specified.\n */\n\nimport type { ProfileTranslations, LocaleTranslations } from '../types'\n\n/**\n * Resolves the best-matching `LocaleTranslations` for a requested locale.\n *\n * The result is a deep merge of the default locale (base) and the resolved locale\n * (override), so any missing keys in the override are inherited from the base.\n *\n * @param translations - All available locale translations from a `ProfileConfig`.\n * @param requested - The desired locale (e.g. `'fr-CA'`).\n * @param defaultLocale - The fallback locale key (e.g. `'en'`). Must exist in `translations`.\n * @throws `Error` if `defaultLocale` is not found in `translations`.\n */\nexport function resolveLocale(\n translations: ProfileTranslations,\n requested: string,\n defaultLocale: string,\n): LocaleTranslations {\n const base = translations[defaultLocale]\n if (!base) {\n throw new Error(`[Consenti] No translations found for defaultLocale \"${defaultLocale}\"`)\n }\n\n // 1. Exact match\n if (translations[requested]) {\n return deepMerge(base, translations[requested] as Partial<LocaleTranslations>)\n }\n\n // 2. Language prefix (e.g. 'fr' from 'fr-CA')\n const lang = requested.split('-')[0] ?? ''\n if (lang !== requested && translations[lang]) {\n return deepMerge(base, translations[lang] as Partial<LocaleTranslations>)\n }\n\n // 3. Default locale fallback\n return base\n}\n\n/**\n * Recursively merges `override` into `base`, returning a new object.\n *\n * Rules:\n * - `undefined` and `null` values in `override` are skipped (base values are kept).\n * - Arrays are replaced wholesale — individual array items are not merged.\n * - Plain objects are merged recursively.\n * - All other values are replaced by the override value.\n *\n * @param base - The base object (default locale translations).\n * @param override - The partial override (requested locale translations).\n */\nexport function deepMerge<T extends object>(base: T, override: Partial<T>): T {\n const result = { ...base } as Record<string, unknown>\n\n for (const key in override) {\n const overrideVal = override[key]\n if (overrideVal === undefined || overrideVal === null) continue\n\n if (\n typeof overrideVal === 'object' &&\n !Array.isArray(overrideVal) &&\n typeof result[key] === 'object' &&\n result[key] !== null &&\n !Array.isArray(result[key])\n ) {\n result[key] = deepMerge(result[key] as object, overrideVal as Partial<object>)\n } else {\n result[key] = overrideVal\n }\n }\n\n return result as T\n}\n","/**\n * Profile resolution — fetches or looks up the active consent profile.\n *\n * Resolution order:\n * 1. **API mode** (`config.api.enabled = true`)\n * → Fetch `GET /consenti/api/v1/profile/{id}?locale={locale}` from the backend.\n * The server resolves locale and returns a flat `ResolvedProfile`.\n * Falls through to step 2 on network failure.\n * 2. **Local registry** (`config.core.profileId > 0`)\n * → Look up a profile registered via `new ConsentiProfile(config)`.\n * Locale resolution is performed client-side via `resolveLocale()`.\n * 3. **Built-in default** (`config.core.profileId === 0`)\n * → Return the minimal English GDPR profile with necessary/analytics/marketing cookies.\n */\n\nimport type { ConsentiConfig, ResolvedProfile, ProfileConfig, PublicProfileResponse } from '../types'\nimport { httpRequest } from '../utils/http'\nimport { resolveLocale } from '../utils/locale'\n\n/** Module-level registry populated by `ConsentiProfile` constructor calls. */\nconst profileRegistry = new Map<number, ProfileConfig>()\n\n/**\n * Registers a local profile so it can be resolved by `resolveProfile()`.\n * Called automatically by `ConsentiProfile` — consumers do not call this directly.\n *\n * @param id - The numeric profile ID.\n * @param config - The `ProfileConfig` to register.\n */\nexport function registerProfile(id: number, config: ProfileConfig): void {\n profileRegistry.set(id, config)\n}\n\n/**\n * Returns a previously registered local profile, or `undefined` if not found.\n *\n * @param id - The numeric profile ID to look up.\n */\nexport function getRegisteredProfile(id: number): ProfileConfig | undefined {\n return profileRegistry.get(id)\n}\n\n/**\n * Built-in default profile — used when `core.profileId === 0` (or omitted) and no API\n * is configured. Covers the four Google Consent Mode v2 purposes plus a necessary\n * functional bucket, giving a realistic starting point for any GTM-backed site.\n */\nexport const DEFAULT_PROFILE: ResolvedProfile = {\n id: 0,\n version: 1,\n defaultLocale: 'en',\n cookies: [\n { id: 'functionality_storage', mandatory: true },\n {\n id: 'analytics_storage',\n listenGpc: true,\n expiry: 365,\n },\n {\n id: 'ad_storage',\n listenGpc: true,\n expiry: 365,\n },\n {\n id: 'ad_user_data',\n listenGpc: true,\n expiry: 365,\n },\n {\n id: 'ad_personalization',\n listenGpc: true,\n expiry: 365,\n },\n ],\n mainBanner: {\n position: 'bottom',\n heading: 'We value your privacy',\n htmlText:\n 'We use cookies to enhance your browsing experience, serve personalised ads or content, ' +\n 'and analyse our traffic. By clicking <strong>Accept All</strong> you consent to our use ' +\n 'of cookies. You may change your preferences at any time.',\n buttons: [\n { text: 'Accept All', cookies: '*', style: 'primary', action: 'custom' },\n { text: 'Reject All', cookies: '!', style: 'primary', action: 'custom' },\n { text: 'Manage Preferences', style: 'secondary', action: 'manage' },\n ],\n },\n preferenceModal: {\n heading: 'Cookie Preferences',\n subheading: 'Choose which cookies you allow us to use.',\n htmlText:\n 'We use different types of cookies to optimise your experience. You can choose to ' +\n 'enable or disable each category below. Necessary cookies cannot be disabled as they ' +\n 'are required for core site functionality.',\n showClose: true,\n overlayOpacity: 50,\n categories: [\n {\n id: 'cat-necessary',\n heading: 'Strictly Necessary',\n htmlText:\n 'These cookies are essential for the website to function correctly. ' +\n 'They include authentication, security, and accessibility features. ' +\n 'They cannot be disabled.',\n mandatory: true,\n cookies: ['functionality_storage'],\n },\n {\n id: 'cat-analytics',\n heading: 'Analytics',\n htmlText:\n 'These cookies help us understand how visitors interact with our website by ' +\n 'collecting and reporting information anonymously. This allows us to improve the ' +\n 'site over time. (e.g. Google Analytics)',\n cookies: ['analytics_storage'],\n },\n {\n id: 'cat-advertising',\n heading: 'Advertising',\n htmlText:\n 'These cookies are used to deliver advertisements more relevant to you and your ' +\n 'interests. They are also used to limit the number of times you see an ad and to ' +\n 'help measure the effectiveness of advertising campaigns. ' +\n '(e.g. Google Ads, Meta Pixel)',\n cookies: ['ad_storage', 'ad_user_data', 'ad_personalization'],\n },\n ],\n buttons: [\n { text: 'Accept All', cookies: '*', style: 'primary', action: 'custom' },\n { text: 'Save Preferences', style: 'primary', action: 'submit' },\n { text: 'Reject All', cookies: '!', style: 'primary', action: 'custom' },\n ],\n },\n}\n\n/**\n * Resolves the active `ResolvedProfile` for the given widget configuration.\n *\n * @param config - The full `ConsentiConfig` from `new ConsentiSetup(config)`.\n * @returns A fully resolved profile ready for rendering.\n * @throws `Error` if `profileId > 0` and the profile is not registered nor reachable via API.\n */\nexport async function resolveProfile(config: ConsentiConfig): Promise<ResolvedProfile> {\n const locale = config.core.locale ?? 'en'\n const profileId = config.core.profileId ?? 0\n\n if (config.api?.enabled) {\n try {\n const base = config.api.baseUrl ?? (typeof window !== 'undefined' ? window.location.origin : '')\n const url = `${base}/consenti/api/v1/profiles/${profileId}/${encodeURIComponent(locale)}`\n const apiProfile = await httpRequest<PublicProfileResponse>(url, {}, config.api.authToken)\n return {\n id: profileId,\n version: apiProfile.version,\n defaultLocale: apiProfile.defaultLocale,\n locales: apiProfile.locales,\n cookies: apiProfile.cookies,\n mainBanner: apiProfile.mainBanner,\n preferenceModal: apiProfile.preferenceModal,\n ...(apiProfile.gpcBanner != null ? { gpcBanner: apiProfile.gpcBanner } : {}),\n }\n } catch {\n console.warn('[Consenti] API profile fetch failed, falling back to local profile')\n }\n }\n\n if (profileId > 0) {\n const local = profileRegistry.get(profileId)\n if (local) {\n const resolved = resolveLocale(local.translations ?? {}, locale, local.defaultLocale)\n return {\n id: profileId,\n version: 1,\n defaultLocale: local.defaultLocale,\n locales: Object.keys(local.translations ?? {}),\n cookies: local.cookies ?? [],\n mainBanner: resolved.mainBanner,\n ...(resolved.gpcBanner ? { gpcBanner: resolved.gpcBanner } : {}),\n preferenceModal: resolved.preferenceModal,\n ...(local.darkMode !== undefined ? { darkMode: local.darkMode } : {}),\n }\n }\n throw new Error(\n `[Consenti] Profile ${profileId} not found. ` +\n `Register it with ConsentiProfile or enable the API.`,\n )\n }\n\n return DEFAULT_PROFILE\n}\n","/**\n * Global Privacy Control (GPC) detection and consent application.\n *\n * GPC is a browser-level privacy signal defined at https://globalprivacycontrol.org/.\n * When `navigator.globalPrivacyControl === true`, the user has indicated they do\n * not want their personal data sold or shared. CCPA and GDPR both recognise GPC\n * as a legally meaningful signal.\n *\n * Consenti supports three modes via `core.autoHonorGPC`:\n * - `false` → GPC signal ignored entirely (default)\n * - `true` → GPC-flagged cookies pre-denied; GPC banner shown (or main banner)\n * - `'strict'` → GPC-flagged cookies pre-denied; consent written silently; no banner\n *\n * Only cookies with `listenGpc: true` on their `Cookie` config are affected.\n * Mandatory cookies are never denied regardless of GPC.\n */\n\nimport type { Cookie, ConsentValue } from '../types'\nimport { safeNavigator } from '../utils/ssr'\n\n/**\n * Detects whether the Global Privacy Control signal is active in the current browser.\n *\n * Supported natively in Brave and via extensions in Firefox. The property is\n * intentionally not on the standard `Navigator` type yet, so we cast to check it.\n *\n * @returns `true` if `navigator.globalPrivacyControl === true`, `false` in all other cases\n * including SSR environments where `navigator` is unavailable.\n */\nexport function detectGPC(): boolean {\n const nav = safeNavigator()\n if (!nav) return false\n return (\n 'globalPrivacyControl' in nav &&\n (nav as Navigator & { globalPrivacyControl: boolean }).globalPrivacyControl === true\n )\n}\n\n/**\n * Applies the GPC signal to a partial consent object.\n *\n * For every cookie with `listenGpc: true` that is not mandatory:\n * - Consent-basis cookies → `'denied'`\n * - Legitimate-interest cookies (`type: 'legitimate_interest'`) → `'objected'`\n *\n * Mandatory cookies are always forced to `'granted'`.\n *\n * @param cookies - The full cookie list from the resolved profile.\n * @param currentConsent - Existing partial consent to use as a base (typically `{}`).\n * @param mode - The `autoHonorGPC` mode (`true` or `'strict'`). If falsy,\n * returns `currentConsent` unchanged.\n * @returns A new `ConsentValue` with GPC denials applied.\n */\nexport function applyGPCToConsent(\n cookies: Cookie[],\n currentConsent: Partial<ConsentValue>,\n mode: boolean | 'strict',\n): ConsentValue {\n if (!mode || !detectGPC()) return { ...currentConsent } as ConsentValue\n\n const result: ConsentValue = { ...currentConsent } as ConsentValue\n\n for (const cookie of cookies) {\n if (cookie.mandatory) continue\n if (cookie.listenGpc) {\n // LI cookies use 'objected' as the refusal status per the GDPR Art. 21 framework\n result[cookie.id] = cookie.type === 'legitimate_interest' ? 'objected' : 'denied'\n }\n }\n\n // Mandatory cookies always win, regardless of GPC\n for (const cookie of cookies) {\n if (cookie.mandatory) result[cookie.id] = 'granted'\n }\n\n return result\n}\n","/**\n * Button construction and click routing.\n *\n * `style` maps directly to the BEM modifier class (visual only).\n * `action` determines the click handler:\n *\n * | `action` | Effect |\n * |---|---|\n * | `'custom'` | Grant/deny via `cookies`: `'*'` all, `'!'` deny, `string[]` specific |\n * | `'submit'` | Save the current modal toggle state |\n * | `'manage'` | Open the preference modal |\n * | `'close'` | Dismiss the current surface without saving |\n */\n\nimport type { Button } from '../types'\n\n/**\n * Callbacks wired to every interactive button rendered by the widget.\n * Implemented by `ConsentiSetup.buildHandlers()` for each rendering context\n * (`'banner'` or `'modal'`).\n */\nexport interface ButtonClickHandler {\n /** Grant all cookies (non-mandatory and mandatory). */\n onGrantAll: () => void\n /** Deny all non-mandatory cookies; grant mandatory ones. */\n onDenyAll: () => void\n /** Save the current toggle state (modal context) or grant mandatory only (banner context). */\n onSubmit: () => void\n /** Open the preference modal. */\n onManage: () => void\n /** Close the current UI surface without saving. */\n onClose: () => void\n /** Grant only the specified cookie IDs plus all mandatory cookies. */\n onGrantSpecific: (cookieIds: string[]) => void\n}\n\n/**\n * Constructs a `<button>` element from a `Button` config and registers a click handler.\n *\n * @param btn - Button configuration from the resolved profile.\n * @param handlers - Click handler implementations.\n * @returns A fully wired `HTMLButtonElement`.\n */\nexport function buildButton(btn: Button, handlers: ButtonClickHandler): HTMLButtonElement {\n const el = document.createElement('button')\n el.type = 'button'\n el.className = `consenti-btn consenti-btn--${btn.style}`\n el.textContent = btn.text\n\n el.addEventListener('click', () => {\n switch (btn.action) {\n case 'custom': {\n const cookies = btn.cookies\n if (cookies === '*') {\n handlers.onGrantAll()\n } else if (cookies === '!') {\n handlers.onDenyAll()\n } else if (Array.isArray(cookies) && cookies.length > 0) {\n handlers.onGrantSpecific(cookies)\n }\n break\n }\n case 'submit':\n handlers.onSubmit()\n break\n case 'manage':\n handlers.onManage()\n break\n case 'close':\n handlers.onClose()\n break\n }\n })\n\n return el\n}\n","/**\n * Full-page semi-transparent overlay shown behind the banner or modal.\n * Rendered as a sibling element inside `#consenti-root` so it does not interfere\n * with the host page's stacking context.\n *\n * Opacity is driven by an inline style (the only inline style in the widget) because\n * the value is dynamic per-profile config and cannot be expressed as a CSS custom\n * property without JavaScript.\n */\n\n/**\n * Creates a `<div class=\"consenti-overlay\">` element.\n *\n * @param opacity - Overlay opacity as a percentage (0–100). Converted to a 0–1 CSS value.\n * @param onClick - Optional click handler (used by modal to close on backdrop click).\n * @returns The constructed overlay element. Not yet inserted into the DOM.\n */\nexport function createOverlay(opacity: number, onClick?: () => void): HTMLElement {\n const el = document.createElement('div')\n el.className = 'consenti-overlay'\n el.style.opacity = String(opacity / 100)\n el.setAttribute('aria-hidden', 'true')\n if (onClick) {\n el.addEventListener('click', onClick)\n }\n return el\n}\n","/**\n * Cookie consent banner DOM builder.\n *\n * Constructs the banner element from a `MainBanner` config and wires up button handlers.\n * The banner is accessible: it carries `role=\"region\"`, `aria-label`, and `aria-live`\n * so screen reader users are informed when it appears.\n *\n * Position variants (bottom, top, middle, left-bottom, right-bottom) are applied via\n * BEM modifier classes and handled entirely in CSS.\n *\n * Buttons with `action === 'link'` are rendered as `<a>` elements in\n * `.consenti-banner__links` inside the text container, separate from action buttons.\n */\n\nimport type { MainBanner, Button } from '../types'\nimport type { ButtonClickHandler } from './buttons'\nimport { buildButton } from './buttons'\nimport { createOverlay } from './overlay'\n\n/**\n * Builds and manages the consent banner DOM element.\n *\n * One `Banner` instance is created per `ConsentiSetup`. Call `build()` to construct\n * the DOM, then `show()` / `hide()` to toggle visibility without rebuilding.\n */\nexport class Banner {\n private el: HTMLElement | null = null\n private overlayEl: HTMLElement | null = null\n\n /**\n * Constructs the banner DOM from the given config and returns the root element.\n * The element is not yet inserted into the document — the caller appends it to\n * the mount root via `Renderer.mount()`.\n *\n * @param config - Banner layout and content config from the resolved profile.\n * @param handlers - Click handlers wired to each button.\n * @param privacyPolicyUrl - Optional URL appended as a \"Privacy Policy\" link after the body text.\n * @param locales - All locale codes available on the profile (for the locale switcher).\n * @param activeLocale - The currently active locale code.\n * @param onLocaleSwitch - Callback invoked when the user selects a different locale.\n * @returns The constructed banner `<div>` element.\n */\n build(\n config: MainBanner,\n handlers: ButtonClickHandler,\n privacyPolicyUrl?: string,\n locales?: string[],\n activeLocale?: string,\n onLocaleSwitch?: (locale: string) => void,\n ): HTMLElement {\n const banner = document.createElement('div')\n banner.id = 'consenti-banner'\n banner.className = `consenti-banner consenti-banner--${config.position}`\n banner.setAttribute('role', 'region')\n banner.setAttribute('aria-label', 'Cookie consent')\n banner.setAttribute('aria-live', 'polite')\n\n const textContainer = document.createElement('div')\n textContainer.className = `consenti-banner__text-container`\n const linksContainer = document.createElement('div')\n linksContainer.className = 'consenti-banner__links'\n\n if (config.heading) {\n const headingTag = config.headingTag ?? 'h2'\n const heading = document.createElement(headingTag)\n heading.id = 'consenti-banner-heading'\n heading.className = 'consenti-banner__heading'\n heading.textContent = config.heading\n textContainer.appendChild(heading)\n }\n\n const text = document.createElement('div')\n text.className = 'consenti-banner__text'\n text.innerHTML = config.htmlText\n\n if (privacyPolicyUrl) {\n const link = document.createElement('a')\n link.href = privacyPolicyUrl\n link.className = 'consenti-banner__link consenti-banner__link--privacy consenti-btn consenti-btn--text'\n link.textContent = 'Privacy Policy'\n link.setAttribute('target', '_blank')\n link.setAttribute('rel', 'noopener noreferrer')\n linksContainer.appendChild(link)\n }\n\n textContainer.appendChild(text)\n\n // Render link-action buttons as styled anchors below the body text\n const linkButtons = config.buttons.filter((b): b is Button & { action: 'link' } => b.action === 'link')\n if (linkButtons.length > 0) {\n for (const btn of linkButtons) {\n if (!btn.url) continue\n const anchor = document.createElement('a')\n anchor.href = btn.url\n anchor.className = `consenti-banner__link consenti-btn consenti-btn--${btn.style ?? 'text'}`\n anchor.textContent = btn.text\n anchor.setAttribute('target', '_blank')\n anchor.setAttribute('rel', 'noopener noreferrer')\n linksContainer.appendChild(anchor)\n }\n }\n if (linkButtons.length > 0 || privacyPolicyUrl) {\n textContainer.appendChild(linksContainer)\n }\n\n banner.appendChild(textContainer)\n\n const actionButtons = config.buttons.filter(b => b.action !== 'link')\n if (actionButtons.length > 0) {\n const btns = document.createElement('div')\n btns.className = 'consenti-banner__buttons'\n for (const btn of actionButtons) {\n btns.appendChild(buildButton(btn, handlers))\n }\n banner.appendChild(btns)\n }\n\n // Controls group: locale switcher + close button (both positioned absolute top-right)\n const hasClose = config.showClose\n const hasLocaleSwitcher = config.showLocaleSwitcher && locales && locales.length > 1\n\n if (hasClose || hasLocaleSwitcher) {\n const controls = document.createElement('div')\n controls.className = 'consenti-banner__controls'\n\n if (hasLocaleSwitcher && locales && onLocaleSwitch) {\n controls.appendChild(buildLocaleSwitcher(locales, activeLocale, onLocaleSwitch))\n }\n\n if (hasClose) {\n const close = document.createElement('button')\n close.type = 'button'\n close.className = 'consenti-banner__close consenti-btn consenti-btn--close'\n close.setAttribute('aria-label', 'Close cookie banner')\n close.textContent = '×'\n close.addEventListener('click', handlers.onClose)\n controls.appendChild(close)\n }\n\n banner.appendChild(controls)\n }\n\n if (config.overlayOpacity && config.overlayOpacity > 0) {\n this.overlayEl = createOverlay(config.overlayOpacity)\n }\n\n this.el = banner\n return banner\n }\n\n /** Returns the overlay element (if `overlayOpacity > 0`), or `null`. */\n getOverlay(): HTMLElement | null {\n return this.overlayEl\n }\n\n /** Makes the banner and its overlay visible after a `hide()` call. */\n show(): void {\n if (this.el) this.el.style.display = ''\n if (this.overlayEl) this.overlayEl.style.display = ''\n }\n\n /** Hides the banner and its overlay without removing them from the DOM. */\n hide(): void {\n if (this.el) this.el.style.display = 'none'\n if (this.overlayEl) this.overlayEl.style.display = 'none'\n }\n\n /** Removes the banner and overlay from the DOM entirely. */\n remove(): void {\n this.el?.remove()\n this.overlayEl?.remove()\n this.el = null\n this.overlayEl = null\n }\n}\n\n/**\n * Builds a locale switcher button + dropdown list element.\n * Clicking the globe button toggles the dropdown; selecting a locale calls `onSwitch`.\n */\nfunction buildLocaleSwitcher(\n locales: string[],\n activeLocale: string | undefined,\n onSwitch: (locale: string) => void,\n): HTMLElement {\n const wrapper = document.createElement('div')\n wrapper.className = 'consenti-locale-switcher'\n\n const btn = document.createElement('button')\n btn.type = 'button'\n btn.className = 'consenti-locale-switcher__btn consenti-btn consenti-btn--close'\n btn.setAttribute('aria-label', 'Switch language')\n btn.setAttribute('aria-haspopup', 'listbox')\n btn.textContent = '🌐'\n\n const list = document.createElement('ul')\n list.className = 'consenti-locale-switcher__list'\n list.setAttribute('role', 'listbox')\n list.setAttribute('aria-label', 'Select language')\n list.hidden = true\n\n for (const locale of locales) {\n const item = document.createElement('li')\n item.setAttribute('role', 'option')\n item.setAttribute('aria-selected', String(locale === activeLocale))\n\n const localeBtn = document.createElement('button')\n localeBtn.type = 'button'\n localeBtn.className = `consenti-locale-switcher__option${locale === activeLocale ? ' consenti-locale-switcher__option--active' : ''}`\n localeBtn.textContent = locale\n localeBtn.addEventListener('click', (e) => {\n e.stopPropagation()\n list.hidden = true\n if (locale !== activeLocale) onSwitch(locale)\n })\n\n item.appendChild(localeBtn)\n list.appendChild(item)\n }\n\n btn.addEventListener('click', (e) => {\n e.stopPropagation()\n list.hidden = !list.hidden\n })\n\n // Close dropdown when clicking outside\n document.addEventListener('click', () => { list.hidden = true }, { capture: true, once: false })\n\n wrapper.appendChild(btn)\n wrapper.appendChild(list)\n return wrapper\n}\n","/**\n * WCAG 2.1 focus trap for modal dialogs.\n *\n * When a modal is open, keyboard focus must remain within it (WCAG 2.1 SC 2.1.2\n * \"No Keyboard Trap\" requires that keyboard focus can be moved *to* and *from* a\n * component — trapping focus inside a modal while it is open is the correct pattern).\n *\n * Additionally, pressing Escape dispatches a `consenti:closeRequest` CustomEvent on the\n * container so the modal's close handler can react without a direct reference back here.\n */\n\n/** Selector covering all standard interactive focusable elements. */\nconst FOCUSABLE_SELECTORS = [\n 'a[href]',\n 'button:not([disabled])',\n 'input:not([disabled])',\n 'select:not([disabled])',\n 'textarea:not([disabled])',\n '[tabindex]:not([tabindex=\"-1\"])',\n '[contenteditable=\"true\"]',\n].join(', ')\n\n/**\n * Traps keyboard focus within `container`.\n *\n * - Tab cycles from last focusable element back to first.\n * - Shift+Tab cycles from first focusable element to last.\n * - Escape dispatches `consenti:closeRequest` on the container.\n * - Focus is moved to the first focusable element immediately on call.\n *\n * @param container - The modal root element to trap focus inside.\n * @returns A cleanup function — call it when the modal closes to remove event listeners.\n */\nexport function trapFocus(container: HTMLElement): () => void {\n const getFocusable = () =>\n Array.from(container.querySelectorAll<HTMLElement>(FOCUSABLE_SELECTORS)).filter(\n (el) => !el.closest('[aria-hidden=\"true\"]'),\n )\n\n const handleKeydown = (e: KeyboardEvent): void => {\n if (e.key !== 'Tab') return\n const els = getFocusable()\n if (els.length === 0) return\n const first = els[0]!\n const last = els[els.length - 1]!\n\n if (e.shiftKey) {\n if (document.activeElement === first || document.activeElement === container) {\n e.preventDefault()\n last.focus()\n }\n } else {\n if (document.activeElement === last) {\n e.preventDefault()\n first.focus()\n }\n }\n }\n\n const handleEscape = (e: KeyboardEvent): void => {\n if (e.key === 'Escape') {\n container.dispatchEvent(new CustomEvent('consenti:closeRequest', { bubbles: true }))\n }\n }\n\n document.addEventListener('keydown', handleKeydown)\n document.addEventListener('keydown', handleEscape)\n\n getFocusable()[0]?.focus()\n\n return () => {\n document.removeEventListener('keydown', handleKeydown)\n document.removeEventListener('keydown', handleEscape)\n }\n}\n","/**\n * Preference modal DOM builder and state manager.\n *\n * The modal renders a list of consent category toggles (`role=\"switch\"`) in an\n * accessible dialog (`role=\"dialog\"`, `aria-modal=\"true\"`). Each toggle reflects\n * the user's current consent for that category and can be flipped before saving.\n *\n * Legitimate-interest (LI) categories use `'objected'` as their off-state rather than\n * `'denied'`, per the GDPR Art. 21 framework for LI objection.\n *\n * A consent receipt checkbox is optionally rendered when `core.allowReceipt: true`.\n * It resets to unchecked each time the modal is opened so the user must opt in per submission.\n */\n\nimport type { PreferenceModal, Category, ConsentValue, Button } from '../types'\nimport type { ButtonClickHandler } from './buttons'\nimport { buildButton } from './buttons'\nimport { createOverlay } from './overlay'\nimport { trapFocus } from './focus-trap'\n\n/**\n * Builds and manages the preference modal DOM.\n * One `Modal` instance is created per `ConsentiSetup` and reused across openings.\n */\nexport class Modal {\n private el: HTMLElement | null = null\n private triggerEl: HTMLElement | null = null\n private trapCleanup: (() => void) | null = null\n /** Map of category ID → toggle button element, used to read state on submit. */\n private toggleMap = new Map<string, HTMLButtonElement>()\n private receiptCheckbox: HTMLInputElement | null = null\n /** Live toggle state — updated on each click so `getToggleConsent()` reads the latest. */\n private toggleState = new Map<string, boolean>()\n /** When `true`, the modal wrapper is passthrough (overlayOpacity === 0). */\n private passthrough = false\n\n /**\n * Constructs the modal DOM from the given config.\n * The modal is not inserted into the document until `open()` is called.\n *\n * @param config - Modal layout and content config from the resolved profile.\n * @param initialConsent - Current stored consent, used to pre-set toggle states.\n * @param handlers - Click handlers wired to each button.\n * @param allowReceipt - When `true`, renders the consent receipt download checkbox.\n * @param dpdpaConfig - DPDPA grievance notice config.\n * @param locales - All available locale codes (for locale switcher).\n * @param activeLocale - The currently active locale code.\n * @param onLocaleSwitch - Callback invoked when the user selects a different locale.\n */\n build(\n config: PreferenceModal,\n initialConsent: ConsentValue,\n handlers: ButtonClickHandler,\n allowReceipt: boolean,\n dpdpaConfig?: { dataFiduciary: string; grievanceEmail: string; purposeDescription?: string },\n locales?: string[],\n activeLocale?: string,\n onLocaleSwitch?: (locale: string) => void,\n ): void {\n const position = config.position ?? 'center'\n const wrapper = document.createElement('div')\n wrapper.id = 'consenti-modal'\n wrapper.className = `consenti-modal consenti-modal--${position}`\n wrapper.setAttribute('role', 'dialog')\n wrapper.setAttribute('aria-modal', 'true')\n\n const overlayOpacity = config.overlayOpacity ?? 50\n this.passthrough = overlayOpacity === 0\n if (overlayOpacity > 0) {\n // When `persistent: true`, clicking the overlay does not close the modal —\n // the user must use one of the footer buttons.\n const overlayClickHandler = config.persistent ? undefined : () => handlers.onClose()\n const overlay = createOverlay(overlayOpacity, overlayClickHandler)\n overlay.className = 'consenti-modal__overlay'\n wrapper.appendChild(overlay)\n } else {\n // No overlay — let pointer events pass through the wrapper to the page beneath.\n wrapper.classList.add('consenti-modal--passthrough')\n }\n\n const container = document.createElement('div')\n container.className = 'consenti-modal__container'\n\n const header = document.createElement('div')\n header.className = 'consenti-modal__header'\n\n if (config.heading) {\n const headingTag = config.headingTag ?? 'h2'\n const heading = document.createElement(headingTag)\n heading.id = 'consenti-modal-heading'\n heading.className = 'consenti-modal__heading'\n heading.textContent = config.heading\n header.appendChild(heading)\n wrapper.setAttribute('aria-labelledby', 'consenti-modal-heading')\n }\n\n // Header controls: locale switcher + close\n const hasClose = config.showClose !== false\n const hasLocaleSwitcher = config.showLocaleSwitcher && locales && locales.length > 1\n\n if (hasLocaleSwitcher || hasClose) {\n const headerControls = document.createElement('div')\n headerControls.className = 'consenti-modal__header-controls'\n\n if (hasLocaleSwitcher && locales && onLocaleSwitch) {\n headerControls.appendChild(buildModalLocaleSwitcher(locales, activeLocale, onLocaleSwitch))\n }\n\n if (hasClose) {\n const close = document.createElement('button')\n close.type = 'button'\n close.className = 'consenti-modal__close consenti-btn consenti-btn--close'\n close.setAttribute('aria-label', 'Close preference modal')\n close.textContent = '×'\n close.addEventListener('click', handlers.onClose)\n headerControls.appendChild(close)\n }\n\n header.appendChild(headerControls)\n }\n container.appendChild(header)\n\n if (config.subheading) {\n const sub = document.createElement('p')\n sub.className = 'consenti-modal__subheading'\n sub.textContent = config.subheading\n container.appendChild(sub)\n }\n\n if (config.htmlText) {\n const desc = document.createElement('div')\n desc.className = 'consenti-modal__text'\n desc.innerHTML = config.htmlText\n container.appendChild(desc)\n }\n\n // Render link-action buttons as styled anchors below the intro text\n const linkButtons = config.buttons.filter((b): b is Button & { action: 'link' } => b.action === 'link')\n if (linkButtons.length > 0) {\n const linksContainer = document.createElement('div')\n linksContainer.className = 'consenti-modal__links'\n for (const btn of linkButtons) {\n if (!btn.url) continue\n const anchor = document.createElement('a')\n anchor.href = btn.url\n anchor.className = `consenti-modal__link consenti-btn consenti-btn--${btn.style ?? 'text'}`\n anchor.textContent = btn.text\n anchor.setAttribute('target', '_blank')\n anchor.setAttribute('rel', 'noopener noreferrer')\n linksContainer.appendChild(anchor)\n }\n container.appendChild(linksContainer)\n }\n\n const body = document.createElement('div')\n body.className = 'consenti-modal__body'\n\n const categories = document.createElement('div')\n categories.className = 'consenti-modal__categories'\n\n for (const cat of config.categories) {\n const catEl = this.buildCategory(cat, initialConsent)\n categories.appendChild(catEl)\n }\n body.appendChild(categories)\n container.appendChild(body)\n\n if (allowReceipt) {\n container.appendChild(this.buildReceiptOption())\n }\n\n if (dpdpaConfig) {\n const notice = document.createElement('div')\n notice.className = 'consenti-modal__dpdpa-notice'\n notice.innerHTML = [\n `<strong>Data Fiduciary:</strong> ${dpdpaConfig.dataFiduciary}.`,\n dpdpaConfig.purposeDescription ? ` <strong>Purpose:</strong> ${dpdpaConfig.purposeDescription}.` : '',\n ` You may withdraw consent at any time or contact our Grievance Officer at`,\n ` <a href=\"mailto:${dpdpaConfig.grievanceEmail}\">${dpdpaConfig.grievanceEmail}</a>.`,\n ].join('')\n container.appendChild(notice)\n }\n\n const btns = document.createElement('div')\n btns.className = 'consenti-modal__buttons'\n for (const btn of config.buttons.filter(b => b.action !== 'link')) {\n btns.appendChild(buildButton(btn, handlers))\n }\n container.appendChild(btns)\n\n wrapper.appendChild(container)\n\n wrapper.addEventListener('consenti:closeRequest', () => handlers.onClose())\n\n this.el = wrapper\n }\n\n private buildCategory(cat: Category, initialConsent: ConsentValue): HTMLElement {\n const isLI = cat.type === 'legitimate_interest'\n const isMandatory = cat.mandatory === true\n\n let defaultOn: boolean\n if (isMandatory) {\n defaultOn = true\n } else if (isLI) {\n // LI categories are \"on\" unless the user has actively objected\n defaultOn = initialConsent[cat.cookies[0] ?? ''] !== 'objected'\n } else {\n defaultOn = initialConsent[cat.cookies[0] ?? ''] === 'granted'\n }\n this.toggleState.set(cat.id, defaultOn)\n\n const wrapper = document.createElement('div')\n wrapper.className = `consenti-category${isMandatory ? ' consenti-category--mandatory' : ''}`\n\n const header = document.createElement('div')\n header.className = 'consenti-category__header'\n\n const headingTag = cat.headingTag ?? 'h3'\n const heading = document.createElement(headingTag)\n heading.id = `consenti-cat-${cat.id}-heading`\n heading.className = 'consenti-category__heading'\n heading.textContent = cat.heading\n header.appendChild(heading)\n\n const toggle = document.createElement('button')\n toggle.type = 'button'\n toggle.className = `consenti-category__toggle${isMandatory ? ' consenti-category__toggle--mandatory' : ''}`\n toggle.setAttribute('role', 'switch')\n toggle.setAttribute('aria-checked', String(defaultOn))\n toggle.setAttribute('aria-labelledby', `consenti-cat-${cat.id}-heading`)\n if (isMandatory) toggle.setAttribute('aria-disabled', 'true')\n\n const knob = document.createElement('span')\n knob.className = 'consenti-category__toggle-knob'\n knob.setAttribute('aria-hidden', 'true')\n toggle.appendChild(knob)\n\n if (!isMandatory) {\n toggle.addEventListener('click', () => {\n const current = this.toggleState.get(cat.id) ?? false\n const next = !current\n this.toggleState.set(cat.id, next)\n toggle.setAttribute('aria-checked', String(next))\n })\n }\n\n this.toggleMap.set(cat.id, toggle)\n header.appendChild(toggle)\n wrapper.appendChild(header)\n\n const text = document.createElement('div')\n text.className = 'consenti-category__text'\n text.innerHTML = cat.htmlText\n wrapper.appendChild(text)\n\n if (isLI && cat.legitimateInterest?.description) {\n const li = document.createElement('p')\n li.className = 'consenti-category__li-desc'\n li.textContent = cat.legitimateInterest.description\n wrapper.appendChild(li)\n }\n\n return wrapper\n }\n\n private buildReceiptOption(): HTMLElement {\n const wrapper = document.createElement('div')\n wrapper.className = 'consenti-modal__receipt-option'\n\n const label = document.createElement('label')\n label.className = 'consenti-modal__receipt-label'\n\n const checkbox = document.createElement('input')\n checkbox.type = 'checkbox'\n checkbox.className = 'consenti-modal__receipt-checkbox'\n checkbox.id = 'consenti-receipt-checkbox'\n checkbox.setAttribute('aria-describedby', 'consenti-receipt-desc')\n this.receiptCheckbox = checkbox\n\n label.appendChild(checkbox)\n label.append(' Get a copy of my consent choices (JSON)')\n wrapper.appendChild(label)\n\n const desc = document.createElement('p')\n desc.id = 'consenti-receipt-desc'\n desc.className = 'consenti-modal__receipt-desc'\n desc.textContent = 'A JSON file will be downloaded to your device when you save your preferences.'\n wrapper.appendChild(desc)\n\n return wrapper\n }\n\n /**\n * Appends the modal to `document.body` and activates the focus trap.\n * The receipt checkbox (if present) is reset to unchecked.\n *\n * @param triggerEl - The element that opened the modal. Focus returns here on `close()`.\n */\n open(triggerEl?: HTMLElement): void {\n if (!this.el) return\n this.triggerEl = triggerEl ?? (document.activeElement as HTMLElement)\n if (this.receiptCheckbox) this.receiptCheckbox.checked = false\n document.body.appendChild(this.el)\n // Skip focus trap when overlay is transparent — the user can interact with the\n // page behind the modal panel so trapping keyboard focus would be counterproductive.\n if (!this.passthrough) {\n this.trapCleanup = trapFocus(this.el)\n }\n }\n\n /**\n * Removes the modal from `document.body`, deactivates the focus trap,\n * and returns focus to `triggerEl`.\n */\n close(): void {\n this.trapCleanup?.()\n this.trapCleanup = null\n this.el?.remove()\n this.triggerEl?.focus()\n this.triggerEl = null\n }\n\n /** Returns `true` if the modal is currently mounted in the document. */\n isOpen(): boolean {\n return this.el?.isConnected === true\n }\n\n /** Returns `true` if the receipt download checkbox is checked at the time of calling. */\n isReceiptRequested(): boolean {\n return this.receiptCheckbox?.checked ?? false\n }\n\n /**\n * Reads the current toggle state map and converts it to a `ConsentValue`.\n *\n * Called by the submit handler to build the consent object before writing to storage.\n *\n * @param categories - The category list from the resolved profile (provides cookie IDs and type).\n */\n getToggleConsent(categories: Category[]): ConsentValue {\n const consent: ConsentValue = {}\n for (const cat of categories) {\n const isLI = cat.type === 'legitimate_interest'\n const isMandatory = cat.mandatory === true\n const on = this.toggleState.get(cat.id) ?? false\n\n for (const cookieId of cat.cookies) {\n if (isMandatory) {\n consent[cookieId] = 'granted'\n } else if (isLI) {\n // LI refusal is 'objected' not 'denied' — distinct status for Art. 21 records\n consent[cookieId] = on ? 'granted' : 'objected'\n } else {\n consent[cookieId] = on ? 'granted' : 'denied'\n }\n }\n }\n return consent\n }\n\n /** Removes the modal element from the DOM and clears all internal state. */\n destroy(): void {\n this.trapCleanup?.()\n this.el?.remove()\n this.el = null\n this.toggleMap.clear()\n this.toggleState.clear()\n this.receiptCheckbox = null\n }\n}\n\n/** Builds a compact locale switcher for the modal header. */\nfunction buildModalLocaleSwitcher(\n locales: string[],\n activeLocale: string | undefined,\n onSwitch: (locale: string) => void,\n): HTMLElement {\n const wrapper = document.createElement('div')\n wrapper.className = 'consenti-locale-switcher consenti-locale-switcher--modal'\n\n const btn = document.createElement('button')\n btn.type = 'button'\n btn.className = 'consenti-locale-switcher__btn consenti-btn consenti-btn--close'\n btn.setAttribute('aria-label', 'Switch language')\n btn.setAttribute('aria-haspopup', 'listbox')\n btn.textContent = '🌐'\n\n const list = document.createElement('ul')\n list.className = 'consenti-locale-switcher__list'\n list.setAttribute('role', 'listbox')\n list.setAttribute('aria-label', 'Select language')\n list.hidden = true\n\n for (const locale of locales) {\n const item = document.createElement('li')\n item.setAttribute('role', 'option')\n item.setAttribute('aria-selected', String(locale === activeLocale))\n\n const localeBtn = document.createElement('button')\n localeBtn.type = 'button'\n localeBtn.className = `consenti-locale-switcher__option${locale === activeLocale ? ' consenti-locale-switcher__option--active' : ''}`\n localeBtn.textContent = locale\n localeBtn.addEventListener('click', (e) => {\n e.stopPropagation()\n list.hidden = true\n if (locale !== activeLocale) onSwitch(locale)\n })\n\n item.appendChild(localeBtn)\n list.appendChild(item)\n }\n\n btn.addEventListener('click', (e) => {\n e.stopPropagation()\n list.hidden = !list.hidden\n })\n\n document.addEventListener('click', () => { list.hidden = true }, { capture: true, once: false })\n\n wrapper.appendChild(btn)\n wrapper.appendChild(list)\n return wrapper\n}\n","/**\n * DOM mount point and screen reader utilities.\n *\n * All Consenti UI elements (banner, modal, overlay) are inserted into a single\n * `<div id=\"consenti-root\">` appended to `document.body`. This keeps the DOM\n * footprint predictable and makes cleanup trivial — `unmount()` removes the root\n * and any injected `<style>` tag in one call.\n *\n * The `announceToScreenReader()` method manages a visually-hidden ARIA live region\n * (`#consenti-announcer`) used to announce non-visual state changes (e.g.\n * \"Your cookie preferences have been saved.\").\n */\n\nimport { isClient } from '../utils/ssr'\n\nconst ROOT_ID = 'consenti-root'\n\n/**\n * Manages the `#consenti-root` mount point and related DOM infrastructure.\n * One `Renderer` instance is created per `ConsentiSetup`.\n */\nexport class Renderer {\n private root: HTMLElement | null = null\n private styleEl: HTMLElement | null = null\n\n /**\n * Returns the mount-point element, creating `#consenti-root` if needed.\n *\n * When `rootEl` is supplied:\n * - `string` → resolved via `document.querySelector`. Throws if no match.\n * - `HTMLElement` → used directly. Throws if not attached to the document.\n * On subsequent calls the previously stored root is returned immediately.\n *\n * @throws `Error` if called in SSR or if a supplied `rootEl` cannot be resolved.\n */\n mount(rootEl?: string | HTMLElement): HTMLElement {\n if (!isClient()) throw new Error('[Consenti] Renderer.mount() called in SSR context')\n\n if (this.root) return this.root\n\n if (rootEl !== undefined) {\n if (typeof rootEl === 'string') {\n const found = document.querySelector<HTMLElement>(rootEl)\n if (!found) throw new Error(`[Consenti] rootEl selector \"${rootEl}\" did not match any element.`)\n this.root = found\n } else {\n if (!document.contains(rootEl)) throw new Error('[Consenti] rootEl element is not attached to the document.')\n this.root = rootEl\n }\n return this.root\n }\n\n let root = document.getElementById(ROOT_ID)\n if (!root) {\n root = document.createElement('div')\n root.id = ROOT_ID\n document.body.appendChild(root)\n }\n this.root = root\n return root\n }\n\n /**\n * Injects a `<style id=\"consenti-styles\">` tag into `<head>` with the given CSS string.\n * No-ops if the style element already exists (idempotent).\n *\n * @param css - The CSS content to inject.\n */\n injectStyles(css: string): void {\n if (!isClient()) return\n if (this.styleEl) return\n\n const style = document.createElement('style')\n style.id = 'consenti-styles'\n style.textContent = css\n document.head.appendChild(style)\n this.styleEl = style\n }\n\n /** Removes `#consenti-root` and the injected `<style>` tag from the document. */\n unmount(): void {\n this.root?.remove()\n this.styleEl?.remove()\n this.root = null\n this.styleEl = null\n }\n\n /**\n * Announces a message to screen readers via a visually-hidden `aria-live=\"polite\"` region.\n *\n * The region is created on first call and reused. The message is cleared then\n * re-set via `requestAnimationFrame` to guarantee the change is detected by\n * AT even if the same string is announced twice in a row.\n *\n * @param message - The message to announce (plain text, not HTML).\n */\n announceToScreenReader(message: string): void {\n if (!isClient()) return\n let announcer = document.getElementById('consenti-announcer')\n if (!announcer) {\n announcer = document.createElement('div')\n announcer.id = 'consenti-announcer'\n announcer.setAttribute('role', 'status')\n announcer.setAttribute('aria-live', 'polite')\n announcer.setAttribute('aria-atomic', 'true')\n // Visually hidden but still in the accessibility tree\n announcer.style.cssText =\n 'position:absolute;width:1px;height:1px;padding:0;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0;'\n document.body.appendChild(announcer)\n }\n announcer.textContent = ''\n requestAnimationFrame(() => {\n if (announcer) announcer.textContent = message\n })\n }\n}\n","/**\n * Main entry point for the Consenti widget.\n *\n * Instantiate one `ConsentiSetup` per page (or per profile ID if you run multiple\n * consent contexts on the same page). The constructor is safe to call during SSR —\n * it silently no-ops when `window` is not defined.\n *\n * @example\n * ```ts\n * const widget = new ConsentiSetup({\n * core: { profileId: 0 },\n * })\n * await widget.ready\n * console.log(widget.hasConsent())\n * ```\n */\n\nimport type {\n ConsentiConfig,\n ConsentValue,\n ConsentAction,\n ResolvedProfile,\n ConsentiPlugin,\n ConsentiWidgetAPI,\n ConsentiMessage,\n} from '../types'\nimport { CONSENTI_CSS } from '../styles/consenti-css'\nimport { isClient } from '../utils/ssr'\nimport { ConsentStorage } from '../utils/storage'\nimport { ConsentiChannel } from '../utils/broadcast'\nimport { generateReceipt, downloadReceipt } from '../utils/receipt'\nimport { ConsentStore } from './consent-store'\nimport { EventBus } from './event-bus'\nimport { resolveProfile } from './profile-resolver'\nimport { detectGPC, applyGPCToConsent } from './gpc'\nimport { Banner } from '../ui/banner'\nimport { Modal } from '../ui/modal'\nimport { Renderer } from '../ui/renderer'\nimport type { ButtonClickHandler } from '../ui/buttons'\n\nexport class ConsentiSetup implements ConsentiWidgetAPI {\n private initialized = false\n private initializing = false\n private profile: ResolvedProfile | null = null\n private consentStore: ConsentStore | null = null\n private eventBus: EventBus | null = null\n private banner: Banner | null = null\n private modal: Modal | null = null\n private renderer: Renderer | null = null\n private broadcastChannel = new ConsentiChannel()\n private readyResolve!: () => void\n private readyPromise: Promise<void>\n private plugins: ConsentiPlugin[] = []\n /** Signature from the most recent `writeAndSign` call. Included in consent receipts. */\n private lastSignature: string | undefined\n /** Tracks which banner variant is currently visible, or null when hidden. */\n private _bannerVariant: 'main' | 'gpc' | null = null\n\n /**\n * Creates and initialises the consent widget.\n *\n * Construction is synchronous; the async `init()` is launched in the background.\n * Use `await widget.ready` or `widget.onReady(cb)` to run code after init completes.\n *\n * @param config - Widget configuration. See `ConsentiConfig` for all options.\n */\n constructor(private config: ConsentiConfig) {\n this.readyPromise = new Promise<void>((resolve) => {\n this.readyResolve = resolve\n })\n\n if (!isClient()) return\n\n if (config.api?.enabled && config.core.storage === 'localStorage') {\n console.warn(\n '[Consenti] localStorage storage mode cannot be used with API mode ' +\n '(API sets cookies via Set-Cookie header). Falling back to cookie storage.',\n )\n config.core.storage = 'cookie'\n }\n\n if (config.autoInit !== false) {\n this.init().catch((err) => console.warn('[Consenti] Init error:', err))\n }\n }\n\n private profileId(): number {\n return this.config.core.profileId ?? 0\n }\n\n /**\n * Starts widget initialisation. Called automatically unless `autoInit: false` is set.\n * Can be called explicitly after construction when `autoInit: false`, or after `destroy()`\n * to re-initialise the same instance.\n */\n async init(): Promise<void> {\n if (!isClient()) return\n if (this.initializing) return\n this.initializing = true\n // Reset ready promise so callers can await this init cycle\n this.initialized = false\n this.readyPromise = new Promise<void>((resolve) => { this.readyResolve = resolve })\n\n try {\n const storageMode = this.config.core.storage ?? 'cookie'\n const userId = this.config.core.userId\n ?? ConsentStore.getOrCreateUserId(this.profileId(), storageMode)\n\n this.profile = await resolveProfile(this.config)\n\n // Apply profileOverride — shallow-merge top-level keys only\n if (this.config.profileOverride) {\n this.profile = { ...this.profile, ...this.config.profileOverride }\n }\n\n this.eventBus = new EventBus(this.profileId(), this.config.utils?.gtm)\n this.consentStore = new ConsentStore(\n userId,\n this.profileId(),\n storageMode,\n this.config.core.cookieDomains,\n )\n this.renderer = new Renderer()\n if (!this.config.core.disableCssTemplate && CONSENTI_CSS) {\n this.renderer.injectStyles(CONSENTI_CSS)\n }\n this.banner = new Banner()\n this.modal = new Modal()\n\n this.applyTheme()\n this.applyDarkMode()\n this.broadcastChannel.connect((msg) => this.handleBroadcastMessage(msg))\n\n await this.runPlugins()\n\n // Determine signing key: API-returned key takes precedence, then config fallback\n const signingKey = this.profile.cookieSigningKey ?? this.config.core.cookieSigningKey\n\n const existing =\n this.config.core.signCookies && signingKey\n ? await this.consentStore.readAndVerify(signingKey)\n : this.consentStore.read()\n const hasExisting = existing !== null\n\n this.eventBus.dispatch('consenti:bannerInitialized', {\n profileId: this.profileId(),\n hasExistingConsent: hasExisting,\n })\n\n // GPC handling — evaluated before normal banner logic\n const gpcMode = this.config.core.autoHonorGPC ?? false\n if (gpcMode && detectGPC()) {\n const gpcConsent = applyGPCToConsent(this.profile.cookies, {}, gpcMode)\n\n if (gpcMode === 'strict') {\n // Silent mode: write consent immediately, dispatch event, skip any banner\n await this.submitConsent(gpcConsent, true)\n this.initialized = true\n this.readyResolve()\n return\n }\n\n // Non-strict: only apply GPC denial on first visit.\n // If the user has already explicitly given consent, respect that choice and\n // fall through to normal banner logic (which will skip the banner since\n // hasExisting is true). Re-running the GPC write on every page load would\n // overwrite the user's explicit consent after they've reviewed via the GPC banner.\n if (!hasExisting) {\n // Persist GPC denial immediately so cookies are dropped now, then show\n // the GPC banner for the user to review/adjust.\n // BroadcastChannel is same-window-exempt, so the banner in this tab is\n // not hidden by the consentUpdated handler received in other tabs.\n const gpcSigningKey = this.profile.cookieSigningKey ?? this.config.core.cookieSigningKey\n if (this.config.core.signCookies && gpcSigningKey && this.consentStore) {\n this.lastSignature = await this.consentStore.writeAndSign(gpcConsent, gpcSigningKey)\n } else {\n this.consentStore?.write(gpcConsent)\n }\n this.broadcastChannel.send({\n type: 'consentUpdated',\n consent: gpcConsent,\n profileId: this.profileId(),\n })\n this.eventBus?.dispatch('consenti:consentSubmitted', {\n consent: gpcConsent,\n gpcDetected: true,\n consentId: crypto.randomUUID(),\n consentAction: this.deriveAction(gpcConsent, false),\n pageUrl: window.location.href,\n })\n this.showBanner(true)\n this.initialized = true\n this.readyResolve()\n return\n }\n }\n\n const regulation = this.config.core.regulation ?? 'gdpr'\n\n // CCPA opt-out: silently write all-granted on first visit; no banner.\n if (regulation === 'ccpa' && !hasExisting) {\n const defaultConsent: ConsentValue = {}\n for (const cookie of this.profile.cookies) {\n defaultConsent[cookie.id] = 'granted'\n }\n const ccpaSigKey = this.profile.cookieSigningKey ?? this.config.core.cookieSigningKey\n if (this.config.core.signCookies && ccpaSigKey) {\n await this.consentStore.writeAndSign(defaultConsent, ccpaSigKey)\n } else {\n this.consentStore.write(defaultConsent)\n }\n this.initialized = true\n this.readyResolve()\n return\n }\n\n // CPRA: opt-out for non-sensitive; opt-in required for sensitive data.\n // GPC triggers Do Not Sell + Do Not Share simultaneously (CPRA s.1798.135).\n if (regulation === 'cpra' && !hasExisting) {\n const gpcActive = this.config.core.autoHonorGPC && detectGPC()\n const defaultConsent: ConsentValue = {}\n for (const cookie of this.profile.cookies) {\n if (cookie.mandatory) { defaultConsent[cookie.id] = 'granted'; continue }\n if (cookie.cpraCategory === 'sensitive') {\n // Sensitive data always requires explicit opt-in even under CPRA\n defaultConsent[cookie.id] = 'denied'\n } else if (gpcActive && (cookie.cpraCategory === 'sale' || cookie.cpraCategory === 'sharing')) {\n defaultConsent[cookie.id] = 'denied'\n } else {\n // Non-sensitive CPRA cookies default to granted (opt-out model)\n defaultConsent[cookie.id] = 'granted'\n }\n }\n const sigKey = this.profile.cookieSigningKey ?? this.config.core.cookieSigningKey\n if (this.config.core.signCookies && sigKey) {\n await this.consentStore.writeAndSign(defaultConsent, sigKey)\n } else {\n this.consentStore.write(defaultConsent)\n }\n this.initialized = true\n this.readyResolve()\n return\n }\n\n // DPDPA: opt-in (same as GDPR — show banner). GPC is not a recognised opt-out under DPDPA.\n // (GDPR and DPDPA both fall through to showBanner below.)\n\n if (!hasExisting) {\n this.showBanner()\n }\n\n this.initialized = true\n this.readyResolve()\n } finally {\n this.initializing = false\n }\n }\n\n /** Applies the `consenti-root--dark` class based on config or profile flag. */\n private applyDarkMode(): void {\n if (!this.renderer) return\n const isDark = this.config.darkMode ?? this.profile?.darkMode ?? false\n const root = this.renderer.mount(this.config.rootEl)\n if (isDark) {\n root.classList.add('consenti-root--dark')\n } else {\n root.classList.remove('consenti-root--dark')\n }\n }\n\n /**\n * Injects CSS custom property overrides supplied via `core.theme` into the\n * Consenti root element so they cascade down to all widget elements.\n */\n private applyTheme(): void {\n const theme = this.config.core.theme\n if (!theme || !this.renderer) return\n\n const map: Record<string, string | undefined> = {\n '--consenti-color-bg': theme.bgColor,\n '--consenti-color-text': theme.textColor,\n '--consenti-color-primary': theme.primaryColor,\n '--consenti-color-primary-text': theme.primaryTextColor,\n '--consenti-color-secondary': theme.secondaryColor,\n '--consenti-color-secondary-text': theme.secondaryTextColor,\n '--consenti-color-border': theme.borderColor,\n '--consenti-font-family': theme.fontFamily,\n '--consenti-font-size-base': theme.fontSizeBase,\n '--consenti-font-size-heading': theme.fontSizeHeading,\n '--consenti-border-radius': theme.borderRadius,\n '--consenti-border-radius-btn': theme.buttonBorderRadius,\n '--consenti-toggle-bg-on': theme.toggleBgOn,\n '--consenti-toggle-bg-off': theme.toggleBgOff,\n '--consenti-color-accent': theme.accentColor,\n '--consenti-color-accent-text': theme.accentTextColor,\n }\n\n const root = this.renderer.mount(this.config.rootEl)\n for (const [prop, value] of Object.entries(map)) {\n if (value !== undefined) root.style.setProperty(prop, value)\n }\n\n if (theme.fontSizeMultiplier) {\n // Multiply base and heading font sizes if a multiplier is given\n const mult = parseFloat(theme.fontSizeMultiplier)\n if (!isNaN(mult)) {\n const baseSize = parseFloat(getComputedStyle(root).getPropertyValue('--consenti-font-size-base') || '14')\n const headingSize = parseFloat(getComputedStyle(root).getPropertyValue('--consenti-font-size-heading') || '16')\n root.style.setProperty('--consenti-font-size-base', `${Math.round(baseSize * mult)}px`)\n root.style.setProperty('--consenti-font-size-heading', `${Math.round(headingSize * mult)}px`)\n }\n }\n }\n\n private async runPlugins(): Promise<void> {\n this.plugins = this.config.plugins ?? []\n for (const plugin of this.plugins) {\n try {\n await plugin.initialize(this)\n } catch (err) {\n console.warn('[Consenti] Plugin initialize error:', err)\n }\n }\n }\n\n /**\n * Invokes an optional plugin hook on all registered plugins.\n * Async hooks are awaited; errors are caught and logged so a broken plugin\n * never interferes with the consent flow.\n *\n * @param invoke - Function that calls the desired hook method on a single plugin.\n */\n private async callPluginHook(invoke: (plugin: ConsentiPlugin, widget: ConsentiWidgetAPI) => void | Promise<void>): Promise<void> {\n for (const plugin of this.plugins) {\n try {\n await invoke(plugin, this)\n } catch (err) {\n console.warn('[Consenti] Plugin hook failed:', err)\n }\n }\n }\n\n private buildHandlers(context: 'banner' | 'modal'): ButtonClickHandler {\n return {\n onGrantAll: () => {\n const consent: ConsentValue = {}\n for (const cookie of this.profile?.cookies ?? []) {\n consent[cookie.id] = 'granted'\n }\n void this.submitConsent(consent)\n if (context === 'banner') { this.banner?.hide(); this._bannerVariant = null }\n },\n onDenyAll: () => {\n const consent: ConsentValue = {}\n for (const cookie of this.profile?.cookies ?? []) {\n consent[cookie.id] = cookie.mandatory ? 'granted' : 'denied'\n }\n void this.submitConsent(consent)\n if (context === 'banner') { this.banner?.hide(); this._bannerVariant = null }\n },\n onSubmit: () => {\n if (context === 'modal' && this.modal && this.profile) {\n const consent = this.modal.getToggleConsent(this.profile.preferenceModal.categories)\n void this.submitConsent(consent)\n this.hideModal()\n } else {\n const consent: ConsentValue = {}\n for (const cookie of this.profile?.cookies ?? []) {\n if (cookie.mandatory) consent[cookie.id] = 'granted'\n }\n void this.submitConsent(consent)\n if (context === 'banner') { this.banner?.hide(); this._bannerVariant = null }\n }\n },\n onManage: () => {\n this.showModal()\n },\n onClose: () => {\n if (context === 'modal') {\n this.hideModal()\n } else {\n this.banner?.hide()\n this._bannerVariant = null\n this.broadcastChannel.send({ type: 'bannerClosed' })\n this.eventBus?.dispatch('consenti:bannerVisibility', { show: false, action: true })\n void this.callPluginHook((p) => p.onBannerHide?.())\n }\n },\n onGrantSpecific: (cookieIds: string[]) => {\n const consent: ConsentValue = {}\n for (const cookie of this.profile?.cookies ?? []) {\n if (cookie.mandatory || cookieIds.includes(cookie.id)) {\n consent[cookie.id] = 'granted'\n } else {\n consent[cookie.id] = 'denied'\n }\n }\n void this.submitConsent(consent)\n if (context === 'banner') this.hideBanner()\n },\n }\n }\n\n /**\n * Classifies a consent submission as `accept_all`, `reject_all`, `custom`, or `update`.\n * Used to enrich events and API payloads with intent metadata.\n */\n private deriveAction(fullConsent: ConsentValue, hadExistingConsent: boolean): ConsentAction {\n if (hadExistingConsent) return 'update'\n const nonMandatory = this.profile?.cookies.filter((c) => !c.mandatory) ?? []\n if (nonMandatory.length === 0) return 'custom'\n const allGranted = nonMandatory.every((c) => fullConsent[c.id] === 'granted')\n const allDenied = nonMandatory.every(\n (c) => fullConsent[c.id] === 'denied' || fullConsent[c.id] === 'objected',\n )\n if (allGranted) return 'accept_all'\n if (allDenied) return 'reject_all'\n return 'custom'\n }\n\n private handleBroadcastMessage(msg: ConsentiMessage): void {\n switch (msg.type) {\n case 'consentUpdated':\n if (msg.profileId !== this.profileId()) return\n this.consentStore?.setInMemory(msg.consent)\n this.banner?.hide()\n if (this.modal?.isOpen()) this.modal.close()\n this.eventBus?.dispatch('consenti:consentSubmitted', {\n consent: msg.consent,\n fromBroadcast: true,\n })\n break\n case 'bannerClosed':\n this.banner?.hide()\n this._bannerVariant = null\n break\n case 'consentDeleted':\n this.consentStore?.clearInMemory()\n break\n }\n }\n\n // ─── Public API ──────────────────────────────────────────────────────────────\n\n /**\n * Returns `true` if the visitor has saved consent for the active profile.\n * Always returns `false` during SSR.\n */\n hasConsent(): boolean {\n if (!isClient()) return false\n return this.consentStore?.hasConsent() ?? false\n }\n\n /**\n * Returns the stored consent map, or `null` if no consent has been saved.\n * Always returns `null` during SSR.\n */\n getConsent(): ConsentValue | null {\n if (!isClient()) return null\n return this.consentStore?.getConsent() ?? null\n }\n\n /**\n * Returns the `Date` when consent was last saved, or `false` if no consent exists.\n * Always returns `false` during SSR.\n */\n getConsentDate(): Date | false {\n if (!isClient()) return false\n return this.consentStore?.getConsentDate() ?? false\n }\n\n /**\n * Returns the consent map in Google Consent Mode v2 format, ready to pass to\n * `gtag('consent', 'update', ...)`. Returns `null` if no consent has been saved.\n *\n * Maps Consenti cookie IDs to the seven Google Consent Mode v2 parameters.\n * Cookies whose IDs exactly match a GCM v2 key are forwarded directly.\n * Non-GCM cookies are ignored by this method (they appear in `getConsent()` only).\n */\n getGTMConsent(): Record<string, string> | null {\n const consent = this.getConsent()\n if (!consent) return null\n\n // The seven Google Consent Mode v2 parameters with their safe defaults.\n const gcmKeys = [\n 'ad_storage',\n 'analytics_storage',\n 'ad_user_data',\n 'ad_personalization',\n 'functionality_storage',\n 'personalization_storage',\n 'security_storage',\n ] as const\n\n const result: Record<string, string> = {}\n\n for (const key of gcmKeys) {\n const raw = consent[key]\n // Map Consenti statuses to GCM values: 'objected' is treated as 'denied' for GCM\n if (raw === 'granted') {\n result[key] = 'granted'\n } else if (raw === 'denied' || raw === 'objected') {\n result[key] = 'denied'\n } else {\n // Key not in consent map — use a safe default\n const safeDefault = (key === 'functionality_storage' || key === 'security_storage')\n ? 'granted'\n : 'denied'\n result[key] = safeDefault\n }\n }\n\n return result\n }\n\n /**\n * Records and persists the user's consent choices.\n *\n * Mandatory cookies are always forced to `'granted'` regardless of what is passed.\n * Non-mandatory cookies not present in `consent` default to `'denied'`.\n *\n * @param consent - Partial or full map of cookie ID → status.\n * @param gpcDerived - Internal flag set to `true` when called from the GPC strict path.\n */\n async submitConsent(consent: Partial<ConsentValue>, gpcDerived = false): Promise<void> {\n if (!isClient() || !this.profile || !this.consentStore) return\n\n const regulation = this.config.core.regulation ?? 'gdpr'\n const hadExistingConsent = this.consentStore.hasConsent()\n const consentId = crypto.randomUUID()\n const pageUrl = window.location.href\n\n const fullConsent: ConsentValue = {}\n for (const cookie of this.profile.cookies) {\n if (cookie.mandatory) {\n fullConsent[cookie.id] = 'granted'\n } else if (regulation === 'cpra') {\n // CPRA: sensitive cookies default to denied (opt-in); all others default to granted (opt-out)\n const fallback = cookie.cpraCategory === 'sensitive' ? 'denied' : 'granted'\n fullConsent[cookie.id] = consent[cookie.id] ?? fallback\n } else {\n // CCPA: default granted (opt-out); GDPR/DPDPA: default denied (opt-in)\n fullConsent[cookie.id] = consent[cookie.id] ?? (regulation === 'ccpa' ? 'granted' : 'denied')\n }\n }\n\n const consentAction = this.deriveAction(fullConsent, hadExistingConsent)\n\n this.eventBus?.dispatch('consenti:consentBeingSubmitted', { consent: fullConsent, consentId })\n\n let apiResponse: unknown\n if (this.config.api?.enabled) {\n try {\n const base = this.config.api.baseUrl ?? window.location.origin\n const { httpRequest } = await import('../utils/http')\n apiResponse = await httpRequest(\n `${base}/consenti/api/v1/consent`,\n {\n method: 'POST',\n body: JSON.stringify({\n profileId: this.profileId(),\n consent: fullConsent,\n locale: this.config.core.locale ?? 'en',\n gpcDetected: gpcDerived || detectGPC(),\n consentId,\n action: consentAction,\n pageUrl,\n }),\n },\n this.config.api.authToken,\n )\n } catch (err) {\n console.warn('[Consenti] API consent submission failed:', err)\n }\n }\n\n // Sign and write atomically so the signature covers the exact persisted value\n const signingKey = this.profile.cookieSigningKey ?? this.config.core.cookieSigningKey\n if (this.config.core.signCookies && signingKey) {\n this.lastSignature = await this.consentStore.writeAndSign(fullConsent, signingKey)\n } else {\n this.consentStore.write(fullConsent)\n }\n\n const receiptRequested = this.modal?.isReceiptRequested() ?? false\n if (receiptRequested && this.config.core.allowReceipt) {\n // Cookie name format: consenti_{userId}_{profileId} — extract the UUID segment.\n const nameParts = this.consentStore.cookieName.split('_')\n // nameParts: ['consenti', '{uuid-segment1}', ..., '{profileId}'] but UUID contains hyphens\n // so join everything except first and last parts for the UUID.\n const visitorId = nameParts.slice(1, -1).join('_')\n const receiptParams = {\n visitorId,\n profileId: this.profileId(),\n profileVersion: this.profile.version,\n locale: this.config.core.locale ?? 'en',\n consent: fullConsent,\n ...(this.lastSignature ? { signature: this.lastSignature } : {}),\n }\n const receipt = generateReceipt(receiptParams)\n downloadReceipt(receipt)\n }\n\n this.broadcastChannel.send({\n type: 'consentUpdated',\n consent: fullConsent,\n profileId: this.profileId(),\n })\n\n this.eventBus?.dispatch('consenti:consentSubmitted', {\n consent: fullConsent,\n apiResponse,\n gpcDetected: gpcDerived,\n consentId,\n consentAction,\n pageUrl,\n })\n\n await this.callPluginHook((p) => p.onConsentSubmit?.(fullConsent))\n\n this.renderer?.announceToScreenReader('Your cookie preferences have been saved.')\n\n // Hide any visible UI after consent is saved so programmatic submitConsent()\n // calls also dismiss the banner/modal, not just button-triggered submissions.\n this.hideBanner()\n if (this.modal?.isOpen()) this.hideModal()\n }\n\n /**\n * Returns the currently visible banner variant, or `false` if no banner is shown.\n * Useful for programmatically checking state before calling `showBanner` / `hideBanner`.\n */\n bannerVisibility(): 'main' | 'gpc' | false {\n return this._bannerVariant ?? false\n }\n\n /**\n * Returns `'preference'` when the preference modal is open, or `false` when closed.\n */\n modalVisibility(): 'preference' | false {\n return this.modal?.isOpen() ? 'preference' : false\n }\n\n /**\n * Mounts and shows the consent banner.\n *\n * No-ops if the requested variant is already visible. If a different variant is\n * currently shown (e.g. switching from main to GPC), the existing banner is replaced.\n *\n * @param useGpcVariant - When `true` and the profile has a `gpcBanner`, that variant\n * is shown instead of `mainBanner`. Used by the GPC flow.\n */\n showBanner(useGpcVariant = false): void {\n if (!isClient() || !this.profile || !this.banner || !this.renderer) return\n const variant: 'main' | 'gpc' = useGpcVariant && this.profile.gpcBanner ? 'gpc' : 'main'\n\n // Skip if the same variant is already visible.\n if (this._bannerVariant === variant) return\n\n const bannerConfig = variant === 'gpc' ? this.profile.gpcBanner! : this.profile.mainBanner\n const root = this.renderer.mount(this.config.rootEl)\n const handlers = this.buildHandlers('banner')\n\n // Remove any previously rendered banner before building a new one so that we do\n // not accumulate duplicate elements and so new button handlers are correctly wired.\n this.banner.remove()\n\n const locales = this.profile.locales\n const activeLocale = this.config.core.locale ?? 'en'\n const bannerEl = this.banner.build(\n bannerConfig,\n handlers,\n this.config.core.privacyPolicyUrl,\n locales,\n activeLocale,\n (locale) => this.switchLocale(locale),\n )\n root.appendChild(bannerEl)\n const overlay = this.banner.getOverlay()\n if (overlay) root.appendChild(overlay)\n this._bannerVariant = variant\n this.eventBus?.dispatch('consenti:bannerVisibility', { show: true, action: false })\n void this.callPluginHook((p) => p.onBannerShow?.())\n }\n\n /** Hides the banner and notifies other tabs via BroadcastChannel. No-op if already hidden. */\n hideBanner(): void {\n if (this._bannerVariant === null) return\n this.banner?.hide()\n this._bannerVariant = null\n this.broadcastChannel.send({ type: 'bannerClosed' })\n this.eventBus?.dispatch('consenti:bannerVisibility', { show: false, action: true })\n this.renderer?.announceToScreenReader('Cookie banner dismissed.')\n void this.callPluginHook((p) => p.onBannerHide?.())\n }\n\n /**\n * Opens the preference modal and hides the banner while the modal is open.\n * No-ops if the modal is already open.\n * If the modal is closed without saving consent, the banner is re-shown automatically\n * (see `hideModal()`). Only one banner or modal is visible at a time.\n *\n * @param triggerEl - The element that triggered the modal open. Focus returns here on close.\n */\n showModal(triggerEl?: HTMLElement): void {\n if (!isClient() || !this.profile || !this.modal) return\n if (this.modal.isOpen()) return\n\n const handlers = this.buildHandlers('modal')\n const existing = this.consentStore?.getConsent() ?? {}\n const regulation = this.config.core.regulation ?? 'gdpr'\n const dpdpaCfg = regulation === 'dpdpa' ? this.profile.dpdpa : undefined\n const locales = this.profile.locales\n const activeLocale = this.config.core.locale ?? 'en'\n this.modal.build(\n this.profile.preferenceModal,\n existing,\n handlers,\n this.config.core.allowReceipt ?? false,\n dpdpaCfg,\n locales,\n activeLocale,\n (locale) => { this.hideModal(); this.switchLocale(locale) },\n )\n // Hide banner while modal is open so only one panel is visible at a time.\n this.banner?.hide()\n this.modal.open(triggerEl)\n // The modal is appended to document.body (outside #consenti-root), so the\n // consenti-root--dark class must be mirrored onto the modal element itself.\n const isDark = this.config.darkMode ?? this.profile?.darkMode ?? false\n document.getElementById('consenti-modal')?.classList.toggle('consenti-root--dark', isDark)\n this.eventBus?.dispatch('consenti:modalVisibility', { show: true, action: true })\n void this.callPluginHook((p) => p.onModalShow?.())\n }\n\n /**\n * Closes the preference modal and returns focus to the element that opened it.\n * No-ops if the modal is already closed.\n * If the user has not yet given consent, the banner is re-shown automatically.\n */\n hideModal(): void {\n if (!this.modal?.isOpen()) return\n this.modal.close()\n this.eventBus?.dispatch('consenti:modalVisibility', { show: false, action: true })\n void this.callPluginHook((p) => p.onModalHide?.())\n\n // If no consent on record, bring the banner back so the user still sees the notice.\n if (!this.consentStore?.hasConsent() && this._bannerVariant !== null) {\n this.banner?.show()\n } else if (!this.consentStore?.hasConsent()) {\n this.showBanner()\n }\n }\n\n /**\n * Registers a callback to run once the widget has finished initialising.\n * If the widget is already initialised, the callback is invoked synchronously.\n *\n * @param callback - Function to call when the widget is ready.\n */\n onReady(callback: () => void): void {\n if (this.initialized) {\n callback()\n } else {\n void this.readyPromise.then(callback)\n }\n }\n\n /**\n * A `Promise` that resolves once the widget has finished initialising.\n * Equivalent to wrapping `onReady()` in a `Promise`.\n *\n * @example\n * ```ts\n * await widget.ready\n * console.log('Widget ready')\n * ```\n */\n get ready(): Promise<void> {\n return this.readyPromise\n }\n\n /**\n * Deletes the stored consent record and notifies other tabs.\n * The banner will reappear on the next page load (or immediately if the\n * receiving tab calls `showBanner()` in response to `consentDeleted`).\n */\n async deleteConsent(): Promise<void> {\n if (!isClient() || !this.consentStore) return\n\n if (this.config.api?.enabled) {\n try {\n const base = this.config.api.baseUrl ?? window.location.origin\n const visitorId = this.consentStore.cookieName\n const { httpRequest } = await import('../utils/http')\n await httpRequest(\n `${base}/consenti/api/v1/consent/${visitorId}`,\n { method: 'DELETE' },\n this.config.api?.authToken,\n )\n } catch (err) {\n console.warn('[Consenti] API consent deletion failed:', err)\n }\n }\n\n this.consentStore.delete()\n this.broadcastChannel.send({ type: 'consentDeleted' })\n }\n\n /**\n * Removes existing consent and immediately re-shows the consent banner so the user\n * can review and update their preferences.\n *\n * Equivalent to calling `deleteConsent()` followed by `showBanner()`, but in a single\n * method so callers do not need to coordinate the async deletion with the banner display.\n *\n * Use this to power \"Review my consent\" links in footers or privacy dashboards.\n */\n async reConsent(): Promise<void> {\n await this.deleteConsent()\n this.showBanner()\n }\n\n /**\n * Returns the fully resolved profile for the current session, or `null` during the\n * async init phase (before the profile has been fetched/resolved).\n *\n * Plugins can use this to read cookie definitions, banner config, modal categories,\n * and the profile version without accessing internal state.\n */\n getProfile(): ResolvedProfile | null {\n return this.profile\n }\n\n /**\n * Returns the `#consenti-root` container element that wraps all widget DOM,\n * or `null` before the widget has mounted or after `destroy()`.\n *\n * Plugins can use this to append custom elements, apply extra CSS variables,\n * or observe mutations within the widget container.\n */\n getRootElement(): HTMLElement | null {\n if (!isClient()) return null\n if (this.config.rootEl) {\n if (typeof this.config.rootEl === 'string') {\n return document.querySelector<HTMLElement>(this.config.rootEl)\n }\n return this.config.rootEl\n }\n return document.getElementById('consenti-root')\n }\n\n /**\n * Returns the `#consenti-banner` element when it is present in the DOM, or `null`.\n * The element may be hidden (`display: none`) — check `bannerVisibility()` for visibility state.\n *\n * Useful for adding extra content, custom event listeners, or mutation observers on the banner.\n */\n getBannerElement(): HTMLElement | null {\n if (!isClient()) return null\n return document.getElementById('consenti-banner')\n }\n\n /**\n * Returns the `#consenti-modal` element when the modal has been built, or `null`.\n * The element is built on first `showModal()` call and reused on subsequent opens.\n *\n * Useful for injecting extra content into the modal or listening to modal-specific events.\n */\n getModalElement(): HTMLElement | null {\n if (!isClient()) return null\n return document.getElementById('consenti-modal')\n }\n\n /**\n * Switches the active locale, re-resolves the profile, and re-renders the widget.\n * Called by the locale switcher UI when the user selects a different language.\n *\n * @param locale - BCP 47 locale code to switch to (e.g. `'fr'`, `'de-AT'`).\n */\n switchLocale(locale: string): void {\n if (!isClient()) return\n this.config.core.locale = locale\n // Teardown current UI and re-init with new locale\n this.banner?.remove()\n if (this.modal?.isOpen()) this.modal.close()\n this._bannerVariant = null\n this.profile = null\n this.initialized = false\n this.readyPromise = new Promise<void>((resolve) => { this.readyResolve = resolve })\n this.init().catch((err) => console.warn('[Consenti] Locale switch error:', err))\n }\n\n /**\n * Destroys the widget — removes all DOM elements, disconnects the BroadcastChannel,\n * and calls `destroy()` on all registered plugins.\n *\n * After `destroy()`, the instance should not be used again.\n */\n destroy(): void {\n for (const plugin of this.plugins) {\n try { plugin.destroy() } catch { /* ignore — plugin cleanup must not throw */ }\n }\n this.modal?.destroy()\n this.renderer?.unmount()\n this.broadcastChannel.disconnect()\n this.banner = null\n this.modal = null\n this.renderer = null\n this.consentStore = null\n this.eventBus = null\n this.initialized = false\n }\n}\n","import type { ConsentValue, ConsentiWidgetAPI } from '../types'\n\n/**\n * Abstract base class for Consenti UI plugins.\n *\n * Extend this class to hook into the widget lifecycle. Each plugin receives the\n * full `ConsentiWidgetAPI` in `initialize()` — call any public method, inspect\n * the resolved profile, or manipulate the widget DOM.\n *\n * Errors thrown in `initialize()` or `destroy()` are caught by `ConsentiSetup`\n * and logged as warnings — a broken plugin never blocks the consent flow.\n *\n * @example\n * ```ts\n * import { ConsentiPlugin } from '@consenti/ui'\n * import type { ConsentiWidgetAPI } from '@consenti/ui'\n *\n * class AnalyticsPlugin extends ConsentiPlugin {\n * initialize(widget: ConsentiWidgetAPI) {\n * window.addEventListener('consenti:consentSubmitted', () => {\n * const consent = widget.getConsent()\n * myAnalytics.track('consent_saved', consent)\n * })\n * }\n *\n * destroy() {\n * // remove listeners, clear timers, etc.\n * }\n * }\n *\n * class BannerStylePlugin extends ConsentiPlugin {\n * initialize(widget: ConsentiWidgetAPI) {\n * widget.onReady(() => {\n * const banner = widget.getBannerElement()\n * if (banner) banner.style.setProperty('--my-custom-prop', 'red')\n * })\n * }\n * destroy() {}\n * }\n *\n * const widget = new ConsentiSetup({\n * core: { profileId: 1 },\n * plugins: [new AnalyticsPlugin(), new BannerStylePlugin()],\n * })\n * ```\n */\nexport abstract class ConsentiPlugin {\n /**\n * Called once after `ConsentiSetup` has finished its async `init()`.\n * May be async — the widget awaits the result before continuing.\n *\n * @param widget - The full public API of the initialised widget.\n */\n abstract initialize(widget: ConsentiWidgetAPI): void | Promise<void>\n\n /**\n * Called when `widget.destroy()` is invoked.\n * Use this to remove event listeners, cancel timers, and release resources.\n */\n abstract destroy(): void\n\n /**\n * Called after consent has been saved to storage and dispatched to the API.\n * Receives the full, normalised consent map (mandatory cookies always `'granted'`).\n *\n * @param consent - The complete consent record that was saved.\n */\n onConsentSubmit?(consent: ConsentValue): void | Promise<void>\n\n /** Called after the consent banner has been mounted and made visible. */\n onBannerShow?(): void | Promise<void>\n\n /** Called after the consent banner has been hidden. */\n onBannerHide?(): void | Promise<void>\n\n /** Called after the preference modal has been opened. */\n onModalShow?(): void | Promise<void>\n\n /** Called after the preference modal has been closed. */\n onModalHide?(): void | Promise<void>\n}\n","/**\n * Client-side profile builder for API-less deployments.\n *\n * `ConsentiProfile` provides a type-safe way to define consent profiles directly\n * in application code, without running a backend. Each instance is registered in\n * the module-level profile registry so `ConsentiSetup` can resolve it by ID.\n *\n * IDs are auto-assigned starting from 1000 to avoid conflicts with API-backed profiles\n * (which use small sequential integers assigned by the database).\n *\n * @example\n * ```ts\n * import { ConsentiProfile, ConsentiSetup } from '@consenti/ui'\n *\n * const profile = new ConsentiProfile({\n * cookies: [\n * { id: 'necessary', mandatory: true },\n * { id: 'analytics' },\n * ],\n * defaultLocale: 'en',\n * translations: {\n * en: {\n * mainBanner: {\n * position: 'bottom',\n * htmlText: 'We use cookies.',\n * buttons: [{ text: 'Accept All', type: 'primary', cookies: '*' }],\n * },\n * preferenceModal: {\n * categories: [],\n * buttons: [{ text: 'Save', type: 'submit' }],\n * },\n * },\n * },\n * })\n *\n * const widget = new ConsentiSetup({ core: { profileId: profile.getId() } })\n * ```\n */\n\nimport type { ProfileConfig } from '../types'\nimport { registerProfile } from '../core/profile-resolver'\n\n/** Starting point for auto-assigned local profile IDs. */\nlet nextLocalId = 1000\n\n/**\n * Defines a local consent profile and registers it for use by `ConsentiSetup`.\n */\nexport class ConsentiProfile {\n private id: number\n\n /**\n * @param config - Full profile configuration including cookies, locales, and UI text.\n */\n constructor(private config: ProfileConfig) {\n this.id = nextLocalId++\n registerProfile(this.id, config)\n }\n\n /** Returns the auto-assigned numeric profile ID. Pass this to `core.profileId`. */\n getId(): number {\n return this.id\n }\n\n /** Returns the raw `ProfileConfig` passed to the constructor. */\n toJSON(): ProfileConfig {\n return this.config\n }\n}\n","/**\n * ConsentScript — conditionally loads/unloads a third-party `<script>` tag\n * based on a specific consent cookie's status.\n *\n * Instantiate after the widget is ready. The script is injected into `<head>`\n * when the target cookie's consent is `'granted'` and removed when it is\n * revoked. Ideal for lazy-loading analytics, ad pixels, or chat widgets without\n * manual `consenti:consentSubmitted` listeners.\n *\n * @example\n * ```ts\n * import { ConsentiSetup, ConsentScript } from '@consenti/ui'\n *\n * const widget = new ConsentiSetup({ core: { profileId: 0 } })\n * await widget.ready\n *\n * const cs = new ConsentScript({\n * cookieId: 'analytics_storage',\n * widget,\n * src: 'https://www.googletagmanager.com/gtag/js?id=G-XXXXXXX',\n * attributes: { async: '' },\n * onLoad: () => console.log('Analytics loaded'),\n * onRevoke: () => console.log('Analytics removed'),\n * })\n * ```\n */\n\nimport type { ConsentiSetup } from '../core/consenti-setup'\nimport { isClient } from './ssr'\n\n/** Options for constructing a {@link ConsentScript} instance. */\nexport interface ConsentScriptOptions {\n /** The consent cookie ID to watch (e.g. `'analytics_storage'`). */\n cookieId: string\n /** The `ConsentiSetup` widget instance to read consent from. */\n widget: ConsentiSetup\n /** Remote script `src` URL to inject when consent is granted. */\n src?: string\n /** Inline JavaScript content to execute when consent is granted. */\n innerHTML?: string\n /**\n * Additional HTML attributes applied to the injected `<script>` element.\n * Set a key to an empty string for boolean attributes (e.g. `{ async: '', defer: '' }`).\n */\n attributes?: Record<string, string>\n /** Called after the `<script>` element has been appended to `<head>`. */\n onLoad?: () => void\n /** Called after the `<script>` element has been removed from the DOM. */\n onRevoke?: () => void\n}\n\n/**\n * Conditionally injects and removes a `<script>` element based on a specific\n * cookie's consent status.\n *\n * The check runs immediately on construction so existing consent is honoured\n * without waiting for the next `consenti:consentSubmitted` event. It then\n * re-evaluates on every future consent change.\n *\n * Call {@link ConsentScript.destroy} when the trigger is no longer needed\n * (e.g. on SPA route unmount) to remove the event listener and the injected\n * element.\n */\nexport class ConsentScript {\n private scriptEl: HTMLScriptElement | null = null\n private readonly handler: () => void\n\n constructor(private readonly options: ConsentScriptOptions) {\n // Assign handler unconditionally to satisfy TypeScript strict property initialisation.\n this.handler = () => this.evaluate()\n\n if (!isClient()) return\n\n window.addEventListener('consenti:consentSubmitted', this.handler)\n\n // Evaluate immediately so any existing consent is reflected at construction time.\n this.evaluate()\n }\n\n private evaluate(): void {\n const consent = this.options.widget.getConsent()\n const granted = consent?.[this.options.cookieId] === 'granted'\n\n if (granted && !this.scriptEl) {\n this.inject()\n } else if (!granted && this.scriptEl) {\n this.revoke()\n }\n }\n\n private inject(): void {\n const script = document.createElement('script')\n if (this.options.src) script.src = this.options.src\n if (this.options.innerHTML) script.innerHTML = this.options.innerHTML\n for (const [key, value] of Object.entries(this.options.attributes ?? {})) {\n script.setAttribute(key, value)\n }\n document.head.appendChild(script)\n this.scriptEl = script\n this.options.onLoad?.()\n }\n\n private revoke(): void {\n this.scriptEl?.remove()\n this.scriptEl = null\n this.options.onRevoke?.()\n }\n\n /**\n * Removes the event listener and the injected `<script>` element (if present).\n * Call this when the widget is destroyed or the script is no longer needed.\n */\n destroy(): void {\n if (!isClient()) return\n window.removeEventListener('consenti:consentSubmitted', this.handler)\n this.revoke()\n }\n}\n","/**\n * CookieTrigger — a pre-built accessible UI element that reopens the Consenti\n * preference panel when clicked.\n *\n * Use this to add a \"Cookie Settings\" link in your site footer or cookie policy\n * page without wiring up the click handler manually or importing the widget module\n * into your layout component.\n *\n * @example\n * ```ts\n * import { ConsentiSetup, CookieTrigger } from '@consenti/ui'\n *\n * const widget = new ConsentiSetup({ core: { profileId: 0 } })\n *\n * // Attach to an existing element\n * new CookieTrigger({ widget, el: '#footer-cookie-settings' })\n *\n * // — or — create a new button and place it yourself\n * const trigger = new CookieTrigger({ widget, label: 'Cookie Settings' })\n * document.querySelector('.site-footer')?.appendChild(trigger.getElement())\n * ```\n */\n\nimport type { ConsentiSetup } from '../core/consenti-setup'\nimport { isClient } from './ssr'\n\n/** Options for constructing a {@link CookieTrigger} instance. */\nexport interface CookieTriggerOptions {\n /** The widget instance to control when the trigger is clicked. */\n widget: ConsentiSetup\n /**\n * Element to attach the click handler to.\n * - `HTMLElement` — used directly.\n * - `string` — treated as a CSS selector; the first matching element is used.\n * - Omitted — a new `<button>` is created. Retrieve it with {@link CookieTrigger.getElement}\n * and append it to the DOM at the desired location.\n */\n el?: HTMLElement | string\n /**\n * Which panel opens on click.\n * - `'modal'` (default) — preference modal.\n * - `'banner'` — main consent banner.\n */\n action?: 'modal' | 'banner'\n /**\n * Label for the auto-created button element.\n * Ignored when `el` points to an existing element. Defaults to `'Cookie Settings'`.\n */\n label?: string\n}\n\n/**\n * Attaches a click handler that opens the Consenti preference panel to a DOM\n * element, or creates an accessible `<button>` when no existing element is provided.\n *\n * The created button uses the `.consenti-btn` and `.consenti-btn--text` classes so\n * it inherits the widget's theme automatically.\n */\nexport class CookieTrigger {\n private readonly el: HTMLElement\n private readonly clickHandler: () => void\n /** Whether this instance created the element (used to decide if `destroy()` removes it). */\n private readonly created: boolean\n\n constructor(options: CookieTriggerOptions) {\n const action = options.action ?? 'modal'\n\n this.clickHandler = () => {\n if (action === 'modal') options.widget.showModal()\n else options.widget.showBanner()\n }\n\n if (!options.el) {\n const btn = document.createElement('button')\n btn.type = 'button'\n btn.className = 'consenti-btn consenti-btn--text consenti-cookie-trigger'\n btn.textContent = options.label ?? 'Cookie Settings'\n btn.setAttribute('aria-label', options.label ?? 'Open cookie settings')\n this.el = btn\n this.created = true\n } else if (typeof options.el === 'string') {\n if (!isClient()) {\n // SSR — create a placeholder that is never appended.\n this.el = document.createElement('span')\n this.created = false\n return\n }\n const found = document.querySelector<HTMLElement>(options.el)\n if (!found) {\n throw new Error(\n `[Consenti] CookieTrigger: element not found for selector \"${options.el}\"`,\n )\n }\n this.el = found\n this.created = false\n } else {\n this.el = options.el\n this.created = false\n }\n\n this.el.addEventListener('click', this.clickHandler)\n }\n\n /**\n * Returns the trigger element.\n * When no `el` was provided, append the returned element to the desired location\n * in the DOM after construction.\n */\n getElement(): HTMLElement {\n return this.el\n }\n\n /**\n * Removes the click handler and, if this instance auto-created the button,\n * removes it from the DOM.\n */\n destroy(): void {\n this.el.removeEventListener('click', this.clickHandler)\n if (this.created) this.el.remove()\n }\n}\n"],"mappings":"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","names":["httpRequest"]}
|