@consenti/ui 0.1.0 → 0.2.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 +325 -117
- package/dist/angular.d.cts +233 -34
- package/dist/angular.d.ts +233 -34
- package/dist/angular.js +1 -73
- package/dist/angular.js.map +1 -1
- package/dist/angular.mjs +1 -52
- package/dist/angular.mjs.map +1 -1
- package/dist/chunk-K2UN3O67.mjs +2 -0
- package/dist/{chunk-ARALKXMB.mjs.map → chunk-K2UN3O67.mjs.map} +1 -1
- package/dist/hmac-UKMRMKN6.mjs +2 -0
- package/dist/{hmac-N6H7F2K2.mjs.map → hmac-UKMRMKN6.mjs.map} +1 -1
- package/dist/http-UU2NIW6M.mjs +2 -0
- package/dist/index.css +1 -1
- package/dist/index.d.ts +217 -25
- package/dist/index.mjs +1 -2220
- package/dist/index.mjs.map +1 -1
- package/dist/index.umd.js +1 -2304
- package/dist/index.umd.js.map +1 -1
- package/dist/react.d.cts +233 -29
- package/dist/react.d.ts +233 -29
- package/dist/react.js +1 -60
- package/dist/react.js.map +1 -1
- package/dist/react.mjs +1 -39
- package/dist/react.mjs.map +1 -1
- package/dist/testing.js +1 -94
- package/dist/testing.js.map +1 -1
- package/dist/testing.mjs +1 -73
- package/dist/testing.mjs.map +1 -1
- package/dist/vue.d.cts +244 -53
- package/dist/vue.d.ts +244 -53
- package/dist/vue.js +1 -72
- package/dist/vue.js.map +1 -1
- package/dist/vue.mjs +1 -51
- package/dist/vue.mjs.map +1 -1
- package/package.json +13 -5
- package/dist/chunk-ARALKXMB.mjs +0 -20
- package/dist/hmac-N6H7F2K2.mjs +0 -26
- package/dist/http-VHOGZ6ZJ.mjs +0 -7
- /package/dist/{http-VHOGZ6ZJ.mjs.map → http-UU2NIW6M.mjs.map} +0 -0
package/dist/index.umd.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/utils/hmac.ts","../src/utils/http.ts","../src/index.ts","../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/core/profile-resolver.ts","../src/utils/locale.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":["/**\n * HMAC-SHA256 signing utilities using the native Web Crypto API.\n *\n * Used to sign consent cookies so that tampering (e.g. a user manually editing\n * their cookie to fake a broader consent) is detectable on the next page load.\n * The signing key is sourced from the API profile response or from\n * `core.cookieSigningKey` in the widget config.\n *\n * All operations are async because `crypto.subtle` is Promise-based.\n * This module is tree-shaken out of bundles that do not use `core.signCookies`.\n */\n\n/**\n * Imports a raw UTF-8 string key as a non-extractable `CryptoKey` for HMAC-SHA256.\n * The key is not extractable — it can only be used for sign/verify operations.\n */\nasync function importHmacKey(rawKey: string): Promise<CryptoKey> {\n const keyData = new TextEncoder().encode(rawKey)\n return crypto.subtle.importKey(\n 'raw',\n keyData,\n { name: 'HMAC', hash: 'SHA-256' },\n false,\n ['sign', 'verify'],\n )\n}\n\n/**\n * Signs a string value with HMAC-SHA256 and returns the lowercase hex-encoded signature.\n *\n * @param value - The plaintext string to sign (the raw cookie value without `::sig:` segment).\n * @param key - The signing key as a UTF-8 string.\n * @returns Lowercase hex string, e.g. `\"a3f9c2e1b8d0...\"`\n */\nexport async function signValue(value: string, key: string): Promise<string> {\n const cryptoKey = await importHmacKey(key)\n const data = new TextEncoder().encode(value)\n const signature = await crypto.subtle.sign('HMAC', cryptoKey, data)\n return Array.from(new Uint8Array(signature))\n .map((b) => b.toString(16).padStart(2, '0'))\n .join('')\n}\n\n/**\n * Verifies a HMAC-SHA256 signature by re-computing the expected signature and comparing.\n *\n * Note: comparison is not constant-time, but this runs entirely client-side against\n * data the user already controls, so timing attacks offer no practical advantage.\n *\n * @param value - The original plaintext string that was signed.\n * @param signature - The hex-encoded signature to verify.\n * @param key - The signing key as a UTF-8 string.\n * @returns `true` if the signature matches, `false` otherwise.\n */\nexport async function verifyValue(value: string, signature: string, key: string): Promise<boolean> {\n const expected = await signValue(value, key)\n return expected === signature\n}\n","/**\n * Minimal `fetch` wrapper used for all Consenti API calls.\n *\n * Adds `Content-Type: application/json` and, when `authToken` is provided,\n * an `Authorization: Bearer <token>` header. Throws a descriptive `Error` on\n * non-2xx responses so callers can catch and fall back gracefully.\n *\n * This module is tree-shaken out of bundles that do not use API mode.\n */\n\n/**\n * Sends an HTTP request and returns the parsed JSON response body.\n *\n * @param url - The request URL.\n * @param options - Standard `RequestInit` options (method, body, headers, etc.).\n * @param authToken - Optional Bearer token. Added as `Authorization: Bearer <token>` when present.\n * @returns Parsed JSON body typed as `T`.\n * @throws `Error` with HTTP status and URL on non-2xx responses.\n */\nexport async function httpRequest<T>(\n url: string,\n options: RequestInit = {},\n authToken?: string,\n): Promise<T> {\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n ...(options.headers as Record<string, string> | undefined),\n }\n if (authToken) {\n headers['Authorization'] = `Bearer ${authToken}`\n }\n const res = await fetch(url, { ...options, headers })\n if (!res.ok) {\n throw new Error(`[Consenti] HTTP ${res.status} ${res.statusText} — ${url}`)\n }\n return res.json() as Promise<T>\n}\n","/**\n * Public API entry point for `@consenti/ui`.\n *\n * This file is the only entry point for the ESM (`dist/index.mjs`) and UMD\n * (`dist/index.umd.js`) bundles. It exports exactly what end-users should import.\n *\n * React and Vue integrations are in separate subpath exports to enable full tree-shaking:\n * - `@consenti/ui/react` — `useConsent()` hook for Next.js\n * - `@consenti/ui/vue` — `useConsent()` composable for Nuxt\n *\n * Importing from `@consenti/ui` never pulls in React or Vue code.\n */\n\nexport { ConsentiSetup } from './core/consenti-setup'\nexport { ConsentiPlugin } from './plugins/plugin.base'\nexport { ConsentiProfile } from './profile/consenti-profile'\nexport { ConsentScript } from './utils/consent-script'\nexport { CookieTrigger } from './utils/cookie-trigger'\n\nexport type { ConsentScriptOptions } from './utils/consent-script'\nexport type { CookieTriggerOptions } from './utils/cookie-trigger'\n\nexport type {\n ConsentiConfig,\n CoreConfig,\n ApiConfig,\n GtmConfig,\n UtilsConfig,\n ThemeConfig,\n Cookie,\n Button,\n ButtonStyle,\n ButtonAction,\n MainBanner,\n GpcBanner,\n PreferenceModal,\n Category,\n ProfileConfig,\n ProfileTranslations,\n LocaleTranslations,\n ConsentValue,\n ConsentStatus,\n ConsentAction,\n ConsentRecord,\n ConsentReceipt,\n ConsentEvent,\n ParsedConsent,\n CookieOptions,\n ConsentiMessage,\n ResolvedProfile,\n ConsentiWidgetAPI,\n} from './types'\n","// 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 * 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 * 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 * 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":["verifyValue","signValue","httpRequest"]}
|
|
1
|
+
{"version":3,"sources":["../src/utils/hmac.ts","../src/utils/http.ts","../src/index.ts","../src/styles/consenti-css.ts","../src/utils/ssr.ts","../src/utils/console.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/core/profile-resolver.ts","../src/utils/locale.ts","../src/utils/geo-hints.ts","../../../packages/utils/src/compliance.ts","../src/profiles/general-privacy-consent.ts","../src/profiles/notice-only.ts","../src/profiles/opt-in.ts","../src/profiles/opt-in-brazil.ts","../src/profiles/opt-in-china.ts","../src/profiles/opt-in-dpdpa.ts","../src/profiles/opt-out.ts","../src/profiles/opt-out-strict.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":["/**\n * HMAC-SHA256 signing utilities using the native Web Crypto API.\n *\n * Used to sign consent cookies so that tampering (e.g. a user manually editing\n * their cookie to fake a broader consent) is detectable on the next page load.\n * The signing key is sourced from the API profile response or from\n * `core.cookieSigningKey` in the widget config.\n *\n * All operations are async because `crypto.subtle` is Promise-based.\n * This module is tree-shaken out of bundles that do not use `core.signCookies`.\n */\n\n/**\n * Imports a raw UTF-8 string key as a non-extractable `CryptoKey` for HMAC-SHA256.\n * The key is not extractable — it can only be used for sign/verify operations.\n */\nasync function importHmacKey(rawKey: string): Promise<CryptoKey> {\n const keyData = new TextEncoder().encode(rawKey)\n return crypto.subtle.importKey(\n 'raw',\n keyData,\n { name: 'HMAC', hash: 'SHA-256' },\n false,\n ['sign', 'verify'],\n )\n}\n\n/**\n * Signs a string value with HMAC-SHA256 and returns the lowercase hex-encoded signature.\n *\n * @param value - The plaintext string to sign (the raw cookie value without `::sig:` segment).\n * @param key - The signing key as a UTF-8 string.\n * @returns Lowercase hex string, e.g. `\"a3f9c2e1b8d0...\"`\n */\nexport async function signValue(value: string, key: string): Promise<string> {\n const cryptoKey = await importHmacKey(key)\n const data = new TextEncoder().encode(value)\n const signature = await crypto.subtle.sign('HMAC', cryptoKey, data)\n return Array.from(new Uint8Array(signature))\n .map((b) => b.toString(16).padStart(2, '0'))\n .join('')\n}\n\n/**\n * Verifies a HMAC-SHA256 signature by re-computing the expected signature and comparing.\n *\n * Note: comparison is not constant-time, but this runs entirely client-side against\n * data the user already controls, so timing attacks offer no practical advantage.\n *\n * @param value - The original plaintext string that was signed.\n * @param signature - The hex-encoded signature to verify.\n * @param key - The signing key as a UTF-8 string.\n * @returns `true` if the signature matches, `false` otherwise.\n */\nexport async function verifyValue(value: string, signature: string, key: string): Promise<boolean> {\n const expected = await signValue(value, key)\n return expected === signature\n}\n","/**\n * Minimal `fetch` wrapper used for all Consenti API calls.\n *\n * Adds `Content-Type: application/json` and, when `authToken` is provided,\n * an `Authorization: Bearer <token>` header. Throws a descriptive `Error` on\n * non-2xx responses so callers can catch and fall back gracefully.\n *\n * This module is tree-shaken out of bundles that do not use API mode.\n */\n\n/**\n * Sends an HTTP request and returns the parsed JSON response body.\n *\n * @param url - The request URL.\n * @param options - Standard `RequestInit` options (method, body, headers, etc.).\n * @param authToken - Optional Bearer token. Added as `Authorization: Bearer <token>` when present.\n * @returns Parsed JSON body typed as `T`.\n * @throws `Error` with HTTP status and URL on non-2xx responses.\n */\nexport async function httpRequest<T>(\n url: string,\n options: RequestInit = {},\n authToken?: string,\n): Promise<T> {\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n ...(options.headers as Record<string, string> | undefined),\n }\n if (authToken) {\n headers['Authorization'] = `Bearer ${authToken}`\n }\n const res = await fetch(url, { ...options, headers })\n if (!res.ok) {\n throw new Error(`[Consenti] HTTP ${res.status} ${res.statusText} — ${url}`)\n }\n return res.json() as Promise<T>\n}\n","/**\n * Public API entry point for `@consenti/ui`.\n *\n * This file is the only entry point for the ESM (`dist/index.mjs`) and UMD\n * (`dist/index.umd.js`) bundles. It exports exactly what end-users should import.\n *\n * React and Vue integrations are in separate subpath exports to enable full tree-shaking:\n * - `@consenti/ui/react` — `useConsent()` hook for Next.js\n * - `@consenti/ui/vue` — `useConsent()` composable for Nuxt\n *\n * Importing from `@consenti/ui` never pulls in React or Vue code.\n */\n\nexport { ConsentiSetup } from './core/consenti-setup'\nexport { ConsentiPlugin } from './plugins/plugin.base'\nexport { ConsentiProfile } from './profile/consenti-profile'\nexport { ConsentScript } from './utils/consent-script'\nexport { CookieTrigger } from './utils/cookie-trigger'\n\nexport type { ConsentScriptOptions } from './utils/consent-script'\nexport type { CookieTriggerOptions } from './utils/cookie-trigger'\n\nexport type {\n ConsentiConfig,\n CoreConfig,\n ApiConfig,\n GtmConfig,\n UtilsConfig,\n ThemeConfig,\n Cookie,\n Button,\n ButtonStyle,\n ButtonAction,\n MainBanner,\n GpcBanner,\n PreferenceModal,\n Category,\n ProfileConfig,\n ProfileTranslations,\n LocaleTranslations,\n ConsentValue,\n ConsentStatus,\n ConsentAction,\n ConsentRecord,\n ConsentReceipt,\n ConsentEvent,\n ParsedConsent,\n CookieOptions,\n ConsentiMessage,\n ResolvedProfile,\n ConsentiWidgetAPI,\n ConsentiEventName,\n} from './types'\n","// 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 = `#consenti-root{position:relative}.consenti-d-none{display:none}: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-secondary-border: #1a2e4a;--consenti-color-overlay: rgba(26, 52, 96, 0.55);--consenti-color-accent: #d32f2f;--consenti-color-accent-text: #ffffff;--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: 0;--consenti-shadow: 0 4px 24px rgba(21, 101, 192, 0.14);--consenti-toggle-bg-on: #43a047;--consenti-toggle-bg-off: #9ca3af;--consenti-toggle-knob: #ffffff;--consenti-toggle-width: 52px;--consenti-toggle-height: 28px;--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: #6b7280}.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-primary);border:1px solid var(--consenti-color-primary)}.consenti-btn--secondary:hover{background:var(--consenti-color-primary);color:var(--consenti-color-secondary);border:1px solid var(--consenti-color-secondary)}.consenti-btn--text{background:rgba(0,0,0,0);color:var(--consenti-color-primary);padding-left:0;padding-right:0;text-decoration:underline;text-underline-offset:4px}.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:500px}.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}.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;margin-block:var(--consenti-spacing-md)}.consenti-banner__powered-by{position:absolute;right:10px;bottom:2px;opacity:.2;color:var(--consenti-color-primary)}@media(max-width: 576px){.consenti-banner{padding:var(--consenti-spacing-md);gap:var(--consenti-spacing-sm)}.consenti-banner--left-bottom,.consenti-banner--right-bottom{left:0;right:0;bottom:0;width:100%;border-radius:0;border-top:1px solid var(--consenti-color-border)}.consenti-banner--middle{width:95vw}.consenti-banner__buttons{width:100%;flex-direction:row;align-items:stretch;margin-bottom:var(--consenti-spacing-md)}.consenti-banner__text-container{min-width:0;width:100%}}@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--fullscreen{align-items:flex-start;justify-content:flex-start}.consenti-modal--fullscreen .consenti-modal__container{width:100vw;max-width:100vw;height:100dvh;max-height:100dvh;border-radius:0;margin:0}.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;justify-content:space-between;padding:var(--consenti-spacing-md) var(--consenti-spacing-lg);border-bottom:1px solid var(--consenti-color-border);flex-shrink:0;position:relative;flex-direction:column}.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;position:absolute;right:15px;top:10px}.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-top:var(--consenti-spacing-sm);margin:0;color:#6b7280;font-size:13px}.consenti-modal__text{padding:var(--consenti-spacing-sm) 0}.consenti-modal__body{overflow-y:auto;flex:1;padding:var(--consenti-spacing-sm) var(--consenti-spacing-lg);position:relative}.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);border-top:1px solid var(--consenti-color-border);flex-shrink:0}.consenti-modal__receipt-option{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}.consenti-modal__powered-by{opacity:.5;color:var(--consenti-color-primary)}@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:#949dab;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-icon{position:absolute;top:50%;transform:translateY(-50%);width:10px;height:10px;display:flex;align-items:center;justify-content:center;pointer-events:none;transition:opacity .15s ease;color:hsla(0,0%,100%,.9)}.consenti-category__toggle-icon svg{width:10px;height:10px;overflow:visible}.consenti-category__toggle-icon--check{left:8px;opacity:0}[aria-checked=true] .consenti-category__toggle-icon--check{opacity:1}.consenti-category__toggle-icon--cross{right:8px;opacity:1}[aria-checked=true] .consenti-category__toggle-icon--cross{opacity:0}.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","type ConsoleLevel = 'info' | 'log' | 'warning' | 'error'\n\nlet _levels: ConsoleLevel[] = ['error']\n\nexport function initConsole(levels: ConsoleLevel[]): void {\n _levels = levels\n}\n\nexport const logger = {\n info: (...args: unknown[]) => { if (_levels.includes('info')) console.info('[Consenti]', ...args) },\n log: (...args: unknown[]) => { if (_levels.includes('log')) console.log('[Consenti]', ...args) },\n warn: (...args: unknown[]) => { if (_levels.includes('warning')) console.warn('[Consenti]', ...args) },\n error: (...args: unknown[]) => { if (_levels.includes('error')) console.error('[Consenti]', ...args) },\n}\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'\nimport { logger } from './console'\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 logger.warn('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'\nimport { logger } from './console'\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 logger.warn('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, Cookie } from '../types'\nimport { ConsentStorage } from '../utils/storage'\nimport { logger } from '../utils/console'\n\nexport function resolveConsentMaxAge(cookies: Cookie[]): number {\n const minDays = cookies\n .map(c => c.expiry ?? 365)\n .reduce((min, d) => Math.min(min, d), 365)\n return minDays * 24 * 60 * 60\n}\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 private cookies: Cookie[] = []\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 /** Set profile cookies so max-age can be derived from the shortest expiry. */\n setProfileCookies(cookies: Cookie[]): void {\n this.cookies = cookies\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 logger.warn('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: this.cookies.length > 0 ? resolveConsentMaxAge(this.cookies) : 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","import type { ConsentiConfig, ResolvedProfile, ProfileConfig, PublicProfileResponse } from '../types'\nimport { httpRequest } from '../utils/http'\nimport { resolveLocale } from '../utils/locale'\nimport { logger } from '../utils/console'\nimport { getBrowserGeoHints } from '../utils/geo-hints'\nimport { COMPLIANCE_GROUP_IDS, EMBEDDED_COMPLIANCE_MAP } from '@consenti/utils'\nimport generalPrivacyConsent from '../profiles/general-privacy-consent'\nimport noticeOnly from '../profiles/notice-only'\nimport optIn from '../profiles/opt-in'\nimport optInBrazil from '../profiles/opt-in-brazil'\nimport optInChina from '../profiles/opt-in-china'\nimport optInDpdpa from '../profiles/opt-in-dpdpa'\nimport optOut from '../profiles/opt-out'\nimport optOutStrict from '../profiles/opt-out-strict'\n\nconst PREBUILT_PROFILE_MAP: Record<string, ResolvedProfile> = {\n 'general-privacy-consent': generalPrivacyConsent,\n 'notice-only': noticeOnly,\n 'opt-in': optIn,\n 'opt-in-brazil': optInBrazil,\n 'opt-in-china': optInChina,\n 'opt-in-dpdpa': optInDpdpa,\n 'opt-out': optOut,\n 'opt-out-strict': optOutStrict,\n}\n\n// ─── Local profile registry ───────────────────────────────────────────────────\n\nconst profileRegistry = new Map<string, ProfileConfig>()\n\nexport function registerProfile(id: number, config: ProfileConfig): void {\n profileRegistry.set(String(id), config)\n}\n\nexport function getRegisteredProfile(id: number): ProfileConfig | undefined {\n return profileRegistry.get(String(id))\n}\n\n// ─── Default fallback profile ─────────────────────────────────────────────────\n\nexport const DEFAULT_PROFILE: ResolvedProfile = {\n id: 0,\n version: 1,\n defaultLocale: 'en',\n gpcMode: 'honor',\n cookies: [\n { id: 'functionality_storage', mandatory: true },\n { id: 'analytics_storage', listenGpc: true, expiry: 365 },\n { id: 'ad_storage', listenGpc: true, expiry: 365 },\n { id: 'ad_user_data', listenGpc: true, expiry: 365 },\n { id: 'ad_personalization', listenGpc: true, expiry: 365 },\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 Optional', cookies: '!', style: 'primary', action: 'custom' },\n { text: 'Customize', style: 'text', action: 'manage' },\n ],\n },\n gpcBanner: {\n position: 'bottom',\n heading: 'Your Privacy Preference Has Been Recognized',\n htmlText:\n 'Your browser has sent a Global Privacy Control (GPC) signal. ' +\n \"We've applied your privacy preference where required by applicable law. \" +\n 'You can review or update your cookie choices at any time.',\n buttons: [\n { text: 'Review Preferences', style: 'primary', action: 'manage' },\n { text: 'Continue', style: 'secondary', action: 'submit' },\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. (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 interests. ' +\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: 'Reject Optional', cookies: '!', style: 'primary', action: 'custom' },\n { text: 'Save Selection', style: 'secondary', action: 'submit' },\n ],\n },\n}\n\n// ─── mapPublicProfileToResolved ───────────────────────────────────────────────\n\nexport function mapPublicProfileToResolved(resp: PublicProfileResponse): ResolvedProfile {\n let gpcMode: 'ignore' | 'honor' | 'strict' | undefined\n if (resp.gpcMode === true) gpcMode = 'honor'\n else if (resp.gpcMode === false) gpcMode = 'ignore'\n else if (resp.gpcMode) gpcMode = resp.gpcMode as 'ignore' | 'honor' | 'strict'\n\n return {\n id: 0,\n profileUuid: resp.id,\n version: resp.version,\n defaultLocale: resp.defaultLocale,\n locales: resp.locales,\n cookies: resp.cookies,\n mainBanner: resp.mainBanner,\n preferenceModal: resp.preferenceModal,\n ...(resp.gpcBanner ? { gpcBanner: resp.gpcBanner } : {}),\n ...(gpcMode ? { gpcMode } : {}),\n ...(resp.darkMode !== undefined ? { darkMode: resp.darkMode } : {}),\n ...(resp.complianceGroup ? { complianceGroup: resp.complianceGroup } : {}),\n ...(resp.complianceConfig ? { complianceConfig: resp.complianceConfig } : {}),\n }\n}\n\n// ─── sessionStorage cache for /resolve-profile ───────────────────────────────\n\nconst CACHE_KEY_PREFIX = '__consenti_resolved_'\nconst CACHE_TTL_MS = 60 * 60 * 1000\n\ninterface ResolvedUrlCache {\n filePath: string\n version: number\n complianceGroup: string\n tenantId: string\n locale: string\n expiresAt: number\n}\n\nexport function getCachedResolution(\n tenantId: string,\n locale: string,\n cacheEnabled: boolean,\n): ResolvedUrlCache | null {\n if (!cacheEnabled) return null\n const key = `${CACHE_KEY_PREFIX}${tenantId}_${locale}`\n try {\n const raw = sessionStorage.getItem(key)\n if (!raw) return null\n const cached = JSON.parse(raw) as ResolvedUrlCache\n if (Date.now() > cached.expiresAt) { sessionStorage.removeItem(key); return null }\n return cached\n } catch { return null }\n}\n\nexport function setCachedResolution(\n tenantId: string,\n locale: string,\n cacheEnabled: boolean,\n data: Omit<ResolvedUrlCache, 'expiresAt'>,\n): void {\n if (!cacheEnabled) return\n const key = `${CACHE_KEY_PREFIX}${tenantId}_${locale}`\n try {\n sessionStorage.setItem(key, JSON.stringify({ ...data, expiresAt: Date.now() + CACHE_TTL_MS }))\n } catch { /* ignore storage failures */ }\n}\n\n// ─── Domain allowlist check ───────────────────────────────────────────────────\n\nfunction isDomainAllowed(profile: PublicProfileResponse, trustDomain: boolean): boolean {\n if (trustDomain) return true\n const origins = profile.allowedOrigins\n if (!origins || origins.length === 0) return true\n const current = window.location.origin\n return origins.some(o => o === current || o === '*')\n}\n\n// ─── Pre-built profile loader ─────────────────────────────────────────────────\n\nfunction loadPrebuiltProfile(complianceGroup: string): ResolvedProfile | null {\n return PREBUILT_PROFILE_MAP[complianceGroup] ?? null\n}\n\nfunction isPrebuiltAllowed(complianceGroup: string, config: ConsentiConfig): boolean {\n const setting = config.core.usePrebuiltProfiles ?? 'all'\n if (setting === 'all') return true\n return (setting as string[]).includes(complianceGroup)\n}\n\n// ─── Geo hint → complianceGroup ───────────────────────────────────────────────\n\nfunction resolveGroupFromHints(tz: string, lang: string): string {\n // Try to extract a country code from the BCP47 language tag (e.g. 'de-DE' → 'DE')\n const parts = lang.replace(/_/g, '-').split('-')\n const tag = parts.length >= 2 ? parts[parts.length - 1]?.toUpperCase() : undefined\n const countryCode = tag && /^[A-Z]{2}$/.test(tag) ? tag : undefined\n\n if (countryCode) {\n const entry = (EMBEDDED_COMPLIANCE_MAP.countries as Record<string, { default: string }>)[countryCode]\n if (entry?.default) return entry.default\n }\n\n // If language has no country subtag, try the timezone IANA region (e.g. 'Europe/Berlin' → 'DE' area)\n const tzParts = tz.split('/')\n if (tzParts[0] === 'Europe') return 'opt-in'\n if (tzParts[0] === 'America') {\n const city = tzParts[1] ?? ''\n if (['Los_Angeles', 'Denver', 'Chicago', 'New_York', 'Phoenix', 'Anchorage', 'Honolulu'].includes(city)) {\n return 'opt-out'\n }\n }\n\n // Default: opt-in (GDPR) — strictest, safe for any unknown jurisdiction\n return 'opt-in'\n}\n\n// ─── Scenario helpers ─────────────────────────────────────────────────────────\n\nfunction effectiveLocale(config: ConsentiConfig): string {\n const l = config.core.locale\n return (!l || l === 'auto') ? navigator.language : l\n}\n\nfunction effectiveTenantId(config: ConsentiConfig): string {\n return config.api?.tenantId ?? config.core.tenantId ?? 'default'\n}\n\nfunction isKnownComplianceGroup(value: string): boolean {\n return (COMPLIANCE_GROUP_IDS as readonly string[]).includes(value)\n}\n\nasync function fetchProfileJson(\n url: string,\n authToken: string | undefined,\n): Promise<PublicProfileResponse | null> {\n try {\n return await httpRequest<PublicProfileResponse>(url, {}, authToken)\n } catch {\n return null\n }\n}\n\n/** Scenario 1A — api disabled, compliance.type = 'auto' (client-side geo) */\nasync function scenario1A(config: ConsentiConfig): Promise<ResolvedProfile> {\n const { tz, lang } = getBrowserGeoHints()\n // Derive complianceGroup from tz + lang.\n // 'opt-in' (GDPR) is the strictest default — safe for any jurisdiction.\n // Full geo-routing via ComplianceMapData is a future enhancement to this path.\n const complianceGroup = resolveGroupFromHints(tz, lang)\n\n if (isPrebuiltAllowed(complianceGroup, config)) {\n const profile = loadPrebuiltProfile(complianceGroup)\n if (profile) return profile\n }\n return DEFAULT_PROFILE\n}\n\n/** Scenario 1B — api disabled, compliance.type = known ComplianceGroupId */\nasync function scenario1B(complianceGroup: string, config: ConsentiConfig): Promise<ResolvedProfile> {\n if (isPrebuiltAllowed(complianceGroup, config)) {\n const profile = loadPrebuiltProfile(complianceGroup)\n if (profile) return profile\n }\n return DEFAULT_PROFILE\n}\n\n/** Scenario 2A — api enabled, compliance.type = 'auto' (server geo-resolve) */\nasync function scenario2A(config: ConsentiConfig): Promise<ResolvedProfile> {\n const locale = effectiveLocale(config)\n const tenantId = effectiveTenantId(config)\n const cacheEnabled = config.core.cacheResolvedProfiles !== false\n const base = config.api?.baseUrl ?? window.location.origin\n const trustDomain = config.api?.trustDomain ?? false\n\n const cached = getCachedResolution(tenantId, locale, cacheEnabled)\n if (cached) {\n const json = await fetchProfileJson(cached.filePath, config.api?.authToken)\n if (json) {\n if (!isDomainAllowed(json, trustDomain)) {\n logger.error(`Domain ${window.location.origin} is not in allowedOrigins for this profile. Falling back to pre-built profile.`)\n return scenario1A(config)\n }\n return mapPublicProfileToResolved(json)\n }\n }\n\n const { tz, lang } = getBrowserGeoHints()\n const resolveUrl =\n `${base}/consenti/api/v1/resolve-profile` +\n `?tz=${encodeURIComponent(tz)}&lang=${encodeURIComponent(lang)}` +\n `&locale=${encodeURIComponent(locale)}&tenantId=${encodeURIComponent(tenantId)}`\n\n let filePath: string\n let complianceGroup: string\n let version: number\n\n try {\n const resolved = await httpRequest<{\n filePath: string\n tenantId: string\n profileId: string\n complianceGroup: string\n locale: string\n version: number\n warning?: string\n }>(resolveUrl, {}, config.api?.authToken)\n\n if (resolved.warning === 'locale_not_found') {\n logger.info('Locale not found; using default locale.')\n }\n\n filePath = resolved.filePath\n complianceGroup = resolved.complianceGroup\n version = resolved.version\n\n setCachedResolution(tenantId, locale, cacheEnabled, {\n filePath,\n version,\n complianceGroup,\n tenantId,\n locale,\n })\n } catch {\n logger.warn('resolve-profile failed; falling back to pre-built profile.')\n return scenario1A(config)\n }\n\n const json = await fetchProfileJson(filePath, config.api?.authToken)\n ?? await fetchProfileJson(\n `${base}/consenti/api/v1/profiles/${tenantId}/${complianceGroup}/default.json`,\n config.api?.authToken,\n )\n\n if (!json) {\n logger.warn('Profile fetch failed; falling back to pre-built profile.')\n return scenario1A(config)\n }\n\n if (!isDomainAllowed(json, trustDomain)) {\n logger.error(`Domain ${window.location.origin} is not in allowedOrigins for this profile. Falling back to pre-built profile.`)\n return scenario1A(config)\n }\n\n return mapPublicProfileToResolved(json)\n}\n\n/** Scenario 2B — api enabled, compliance.type = known ComplianceGroupId */\nasync function scenario2B(complianceGroup: string, config: ConsentiConfig): Promise<ResolvedProfile> {\n const locale = effectiveLocale(config)\n const tenantId = effectiveTenantId(config)\n const base = config.api?.baseUrl ?? window.location.origin\n const trustDomain = config.api?.trustDomain ?? false\n\n const group = config.api?.complianceGroup ?? complianceGroup\n\n const json = await fetchProfileJson(\n `${base}/consenti/api/v1/profiles/${tenantId}/${group}/${encodeURIComponent(locale)}.json`,\n config.api?.authToken,\n ) ?? await fetchProfileJson(\n `${base}/consenti/api/v1/profiles/${tenantId}/${group}/default.json`,\n config.api?.authToken,\n )\n\n if (!json) {\n logger.warn(`Could not fetch profile for ${group}; falling back to pre-built.`)\n return scenario1B(group, config)\n }\n\n if (!isDomainAllowed(json, trustDomain)) {\n logger.error(`Domain ${window.location.origin} is not in allowedOrigins for this profile. Falling back to pre-built profile.`)\n return scenario1B(group, config)\n }\n\n return mapPublicProfileToResolved(json)\n}\n\n/** Scenario Local — locally registered profile */\nasync function scenarioLocal(typeValue: string, config: ConsentiConfig): Promise<ResolvedProfile> {\n if (config.api?.enabled) {\n logger.warn('Local profile used instead of server profile. Check compliance.type config.')\n }\n\n const locale = effectiveLocale(config)\n const local = Array.from(profileRegistry.values()).find(p => p.complianceGroup === typeValue)\n\n if (local) {\n const resolved = resolveLocale(local.translations ?? {}, locale, local.defaultLocale)\n return {\n id: 0,\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 ...(local.complianceGroup ? { complianceGroup: local.complianceGroup } : {}),\n }\n }\n\n // Fall back to auto path for the current api mode\n return config.api?.enabled ? scenario2A(config) : scenario1A(config)\n}\n\n// ─── resolveProfile dispatcher ────────────────────────────────────────────────\n\nexport async function resolveProfile(config: ConsentiConfig): Promise<ResolvedProfile> {\n const complianceType = config.compliance?.type ?? 'auto'\n const apiEnabled = config.api?.enabled ?? false\n\n try {\n if (complianceType === 'auto') {\n return apiEnabled ? await scenario2A(config) : await scenario1A(config)\n }\n\n if (isKnownComplianceGroup(complianceType)) {\n return apiEnabled\n ? await scenario2B(complianceType, config)\n : await scenario1B(complianceType, config)\n }\n\n // Unknown type — treat as local profile key or fall back to auto\n return await scenarioLocal(complianceType, config)\n } catch {\n return DEFAULT_PROFILE\n }\n}\n","import { DeepPartial } from '@consenti/types';\n\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])\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])\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: DeepPartial<T>): T {\n const result = { ...base } as Record<string, T[keyof T]>\n\n for (const key in override) {\n const overrideVal = override[key]\n if (overrideVal === undefined || overrideVal === null) continue\n\n const baseVal = result[key]\n\n if (\n typeof overrideVal === 'object' &&\n !Array.isArray(overrideVal) &&\n typeof baseVal === 'object' &&\n baseVal !== null &&\n !Array.isArray(baseVal)\n ) {\n result[key] = deepMerge(\n baseVal as T[keyof T] & object,\n overrideVal as DeepPartial<T[keyof T] & object>,\n ) as T[keyof T]\n } else {\n result[key] = overrideVal as T[keyof T]\n }\n }\n\n return result as T\n}\n","export function getBrowserGeoHints(): { tz: string; lang: string; langs: string[] } {\n return {\n tz: Intl.DateTimeFormat().resolvedOptions().timeZone,\n lang: navigator.language,\n langs: Array.from(navigator.languages),\n }\n}\n","/**\n * Single source of truth for all compliance groups, regulations, and country mappings.\n *\n * This is DATA, not types. The `Compliance` and `ComplianceGroupId` literal types\n * derived from the ID arrays below live in @consenti/types (packages/types/src/compliance.ts),\n * via a type-only import — that keeps @consenti/types free of runtime code.\n *\n * ─── HOW TO UPDATE ────────────────────────────────────────────────────────────\n *\n * ADD A NEW REGULATION:\n * 1. Add regulation ID string to `COMPLIANCE_REGULATION_IDS` below\n * (the `Compliance` type in @consenti/types auto-updates from it).\n * 2. Add it to the appropriate group's `compliances` array below.\n * 3. Add / update the country entry in `EMBEDDED_COMPLIANCE_MAP` below.\n * That is all — no other files need touching.\n *\n * ADD A NEW COMPLIANCE GROUP:\n * 1. Add the kebab-case ID to `COMPLIANCE_GROUP_IDS` below.\n * 2. Add a full group entry to `COMPLIANCE_GROUPS` below (label, compliances, flags).\n * 3. Add a validation rule set to `COMPLIANCE_VALIDATION_RULES` below.\n * 4. Map affected countries in `EMBEDDED_COMPLIANCE_MAP` to the new group.\n *\n * ADD OR UPDATE A COUNTRY:\n * Edit the entry in `EMBEDDED_COMPLIANCE_MAP.countries` below. That is the ONLY\n * place that needs changing for country-level jurisdiction updates.\n */\n\n// ─── Regulation IDs ───────────────────────────────────────────────────────────\n\nexport const COMPLIANCE_REGULATION_IDS = [\n // European Union\n 'gdpr', 'eprivacy',\n // United Kingdom\n 'uk-gdpr', 'pecr',\n // Switzerland\n 'revfadp',\n // Turkey\n 'kvkk',\n // North America — Canada\n 'pipeda', 'law25',\n // North America — US (California)\n 'ccpa', 'cpra',\n // North America — US state laws\n 'vcdpa', 'cpa-co', 'ctdpa', 'ucpa', 'icdpa-ia', 'icdpa-in', 'tipa',\n 'mcdpa-mt', 'mcdpa-mn', 'ocpa', 'tdpsa', 'dpdpa-de', 'njdpa', 'nhpa',\n 'ndpa-ne', 'modpa', 'kcdpa', 'ridtpa',\n // Latin America\n 'lgpd', 'lfpdppp', 'lpdp-ar', 'lpdp-pe', 'law1581-co', 'law18331-uy',\n 'law19628-cl', 'lopdp-ec', 'lpdp-cr', 'data-protection-pa', 'data-protection-do',\n // Asia — India\n 'dpdpa',\n // Asia — China\n 'pipl', 'dsl-cn', 'csl-cn',\n // Asia — Pacific\n 'appi', 'pipa-kr', 'pdpa-sg', 'pdpa-th', 'pdpa-my', 'dpa-ph', 'pdp-id',\n 'pdpd-vn', 'privacy-au', 'privacy-nz', 'pdpl-hk', 'pdpa-mo', 'pdpl-tw',\n 'privacy-bn', 'privacy-lk', 'privacy-np',\n // Middle East\n 'pdpl-sa', 'uae-pdpl', 'difc-dpl', 'adgm-dpr', 'qpdl-qa', 'pdpl-bh',\n 'pdpl-om', 'privacy-kw', 'privacy-il',\n // Africa\n 'popia', 'ndpa-ng', 'dpa-ke', 'dpa-gh', 'dpa-ug', 'dpa-rw', 'dpa-mu',\n 'dpa-bw', 'dpa-zm', 'dpa-zw', 'dpa-eg', 'dpa-ma', 'dpa-tn', 'dpa-dz',\n // Oceania\n 'privacy-fj', 'privacy-pg',\n] as const\n\n// ─── Compliance Group IDs ─────────────────────────────────────────────────────\n\nexport const COMPLIANCE_GROUP_IDS = [\n 'opt-in',\n 'opt-out',\n 'opt-out-strict',\n 'opt-in-dpdpa',\n 'opt-in-china',\n 'opt-in-brazil',\n 'general-privacy-consent',\n 'notice-only',\n] as const\n\n// Local alias for the shape checks below. The public `ComplianceGroupId` type\n// (derived from the same array) is re-exported from @consenti/types.\ntype ComplianceGroupId = typeof COMPLIANCE_GROUP_IDS[number]\n\n// ─── Compliance Groups ────────────────────────────────────────────────────────\n\nexport const COMPLIANCE_GROUPS = {\n 'opt-out-strict': {\n label: 'Opt-out Strict (CPRA / California)',\n description: 'California CPRA: opt-out with sale/sharing/sensitive categories required. GPC must be honored. Stricter than general US state laws.',\n compliances: ['cpra'],\n defaultGpc: 'honor',\n gpcAutoHonor: true,\n allowsLegitimateInterest: false,\n allowsTcf: false,\n requiresCpraCategory: true,\n requiresDpdpaDisclosure: false,\n },\n 'opt-out': {\n label: 'Opt-out (US State Laws)',\n description: 'US state privacy laws. Collect first, allow opt-out of sale/share. Covers CCPA and all state-level equivalents.',\n compliances: [\n 'ccpa',\n 'vcdpa', 'cpa-co', 'ctdpa', 'ucpa', 'icdpa-ia', 'icdpa-in', 'tipa',\n 'mcdpa-mt', 'mcdpa-mn', 'ocpa', 'tdpsa', 'dpdpa-de', 'njdpa', 'nhpa',\n 'ndpa-ne', 'modpa', 'kcdpa', 'ridtpa',\n ],\n defaultGpc: 'honor',\n gpcAutoHonor: true,\n allowsLegitimateInterest: false,\n allowsTcf: false,\n requiresCpraCategory: false,\n requiresDpdpaDisclosure: false,\n },\n 'notice-only': {\n label: 'Notice Only',\n description: 'A privacy notice is generally sufficient. No dedicated cookie banner law is encoded for this jurisdiction.',\n compliances: ['privacy-fj', 'privacy-pg'],\n defaultGpc: 'ignore',\n gpcAutoHonor: false,\n allowsLegitimateInterest: true,\n allowsTcf: false,\n requiresCpraCategory: false,\n requiresDpdpaDisclosure: false,\n },\n 'opt-in': {\n label: 'Standard Opt-in (GDPR)',\n description: 'GDPR / ePrivacy style: prior consent required before non-essential cookies. Granular consent, easy reject, easy withdrawal.',\n compliances: [\n 'gdpr', 'eprivacy', 'uk-gdpr', 'pecr', 'revfadp', 'kvkk', 'pdpa-th',\n 'pdpl-sa', 'uae-pdpl', 'difc-dpl', 'adgm-dpr', 'qpdl-qa', 'pdpl-bh', 'pdpl-om',\n 'law25',\n ],\n defaultGpc: 'ignore',\n gpcAutoHonor: false,\n allowsLegitimateInterest: true,\n allowsTcf: true,\n requiresCpraCategory: false,\n requiresDpdpaDisclosure: false,\n },\n 'opt-in-dpdpa': {\n label: 'Opt-in (India DPDPA)',\n description: \"India's Digital Personal Data Protection Act. Opt-in consent with data fiduciary disclosure. No legitimate interest basis.\",\n compliances: ['dpdpa'],\n defaultGpc: 'ignore',\n gpcAutoHonor: false,\n allowsLegitimateInterest: false,\n allowsTcf: false,\n requiresCpraCategory: false,\n requiresDpdpaDisclosure: true,\n },\n 'opt-in-china': {\n label: 'Opt-in (China PIPL / DSL)',\n description: 'China PIPL + DSL + CSL: strict opt-in with purpose specification, data minimisation, and cross-border transfer controls.',\n compliances: ['pipl', 'dsl-cn', 'csl-cn'],\n defaultGpc: 'ignore',\n gpcAutoHonor: false,\n allowsLegitimateInterest: false,\n allowsTcf: false,\n requiresCpraCategory: false,\n requiresDpdpaDisclosure: false,\n },\n 'opt-in-brazil': {\n label: 'Opt-in (Brazil LGPD)',\n description: 'Brazil LGPD: opt-in consent required. Legitimate interest is a valid basis. Analytics and advertising may differ in treatment.',\n compliances: ['lgpd'],\n defaultGpc: 'ignore',\n gpcAutoHonor: false,\n allowsLegitimateInterest: true,\n allowsTcf: false,\n requiresCpraCategory: false,\n requiresDpdpaDisclosure: false,\n },\n 'general-privacy-consent': {\n label: 'General Privacy Consent',\n description: 'Privacy notice or consent rules exist but the jurisdiction does not have a GDPR/PECR-style strict cookie banner law. Documenting legal basis is best practice.',\n compliances: [\n 'pipeda',\n 'popia', 'appi', 'pipa-kr',\n 'pdpa-sg', 'pdpa-my', 'dpa-ph', 'pdp-id', 'pdpd-vn',\n 'privacy-au', 'privacy-nz', 'pdpl-hk', 'pdpa-mo', 'pdpl-tw',\n 'privacy-bn', 'privacy-lk', 'privacy-np', 'privacy-kw', 'privacy-il',\n 'ndpa-ng', 'dpa-ke', 'dpa-gh', 'dpa-ug', 'dpa-rw', 'dpa-mu',\n 'dpa-bw', 'dpa-zm', 'dpa-zw', 'dpa-eg', 'dpa-ma', 'dpa-tn', 'dpa-dz',\n 'lfpdppp', 'lpdp-ar', 'lpdp-pe', 'law1581-co', 'law18331-uy',\n 'law19628-cl', 'lopdp-ec', 'lpdp-cr', 'data-protection-pa', 'data-protection-do',\n ],\n defaultGpc: 'ignore',\n gpcAutoHonor: false,\n allowsLegitimateInterest: true,\n allowsTcf: false,\n requiresCpraCategory: false,\n requiresDpdpaDisclosure: false,\n },\n} satisfies Record<ComplianceGroupId, {\n label: string\n description: string\n compliances: readonly string[]\n defaultGpc: 'ignore' | 'honor' | 'strict'\n gpcAutoHonor: boolean\n allowsLegitimateInterest: boolean\n allowsTcf: boolean\n requiresCpraCategory: boolean\n requiresDpdpaDisclosure: boolean\n}>\n\n// ─── GPC Options ──────────────────────────────────────────────────────────────\n\nexport const GPC_OPTIONS = [\n { value: 'ignore', label: 'Ignore', description: 'Do not check or respond to the GPC browser signal.' },\n { value: 'honor', label: 'Honor', description: 'Respect GPC as a valid opt-out signal. Show GPC variant banner once.' },\n { value: 'strict', label: 'Strict', description: 'Silently deny all non-mandatory cookies when GPC is detected. No banner shown.' },\n]\n\n// ─── Legal Basis Options ──────────────────────────────────────────────────────\n\nexport const LEGAL_BASIS_OPTIONS = [\n { value: 'mandatory', label: 'Strictly Necessary', description: 'Required for the service to function. No consent needed.' },\n { value: 'consent', label: 'Consent', description: 'Requires explicit user agreement. User can withdraw at any time.' },\n { value: 'legitimate_interest', label: 'Legitimate Interest', description: 'Based on legitimate business interest. User has the right to object.' },\n]\n\n// ─── CPRA Categories ──────────────────────────────────────────────────────────\n\nexport const CPRA_CATEGORIES = [\n { value: 'sale', label: 'Sale', description: 'Sale of personal data to third parties for their own use.' },\n { value: 'sharing', label: 'Sharing', description: 'Sharing for cross-context behavioral advertising (even without payment).' },\n { value: 'sensitive', label: 'Sensitive', description: 'Sensitive personal information (geolocation, health, race, biometrics, etc.). Requires opt-in even in California.' },\n]\n\n// ─── TCF Purposes (IAB TCF 2.2) ──────────────────────────────────────────────\n\nexport const TCF_PURPOSES = [\n { id: 1, name: 'Store and/or access information on a device', description: \"Cookies, device identifiers, or other information can be stored or accessed on your device.\" },\n { id: 2, name: 'Select basic ads', description: \"Ads can be shown based on the content you're viewing, the app you're using, or your approximate location.\" },\n { id: 3, name: 'Create a personalised ads profile', description: \"A profile can be built about you and your interests to show you personalised ads.\" },\n { id: 4, name: 'Select personalised ads', description: \"Personalised ads can be shown to you based on a profile about you.\" },\n { id: 5, name: 'Create a personalised content profile', description: \"A profile can be built about you and your interests to show you personalised content.\" },\n { id: 6, name: 'Select personalised content', description: \"Personalised content can be shown to you based on a profile about you.\" },\n { id: 7, name: 'Measure ad performance', description: \"The performance and effectiveness of ads that you see or interact with can be measured.\" },\n { id: 8, name: 'Measure content performance', description: \"The performance and effectiveness of content that you see or interact with can be measured.\" },\n { id: 9, name: 'Apply market research to generate audience insights', description: \"Market research can be used to learn more about the audiences who visit sites or use apps.\" },\n { id: 10, name: 'Develop and improve products', description: \"Your data can be used to improve existing systems and software, and to develop new products.\" },\n { id: 11, name: 'Use limited data to select content', description: \"Content can be selected based on limited data such as the website you are visiting.\" },\n]\n\nexport const TCF_SPECIAL_FEATURES = [\n { id: 1, name: 'Use precise geolocation data', description: \"Your precise location (within a radius of less than 500 metres) can be used.\" },\n { id: 2, name: 'Actively scan device characteristics for identification', description: \"Your device can be identified based on a scan of your device's unique combination of characteristics.\" },\n]\n\n// ─── Compliance Validation Rules ──────────────────────────────────────────────\n// check() returns true when a violation exists (i.e., the rule is broken)\n\nexport const COMPLIANCE_VALIDATION_RULES: Record<string, {\n errors: Array<{\n rule: string\n field: string\n check: (cookie: Record<string, unknown>) => boolean\n message: (cookieId: string) => string\n }>\n warnings: Array<{\n rule: string\n field: string\n check: (cookie: Record<string, unknown>) => boolean\n message: (cookieId: string) => string\n suggestion: string\n }>\n}> = {\n 'opt-in': {\n errors: [\n {\n rule: 'legal-basis-required',\n field: 'legalBasis',\n check: (c) => !c['legalBasis'],\n message: (id) => `Cookie \"${id}\" must have a legal basis set (Consent, Legitimate Interest, or Strictly Necessary) for GDPR compliance.`,\n },\n {\n rule: 'li-description-required',\n field: 'legitimateInterest.description',\n check: (c) => c['legalBasis'] === 'legitimate_interest' && !(c['legitimateInterest'] as Record<string, unknown> | undefined)?.['description'],\n message: (id) => `Cookie \"${id}\" uses Legitimate Interest but has no balancing test description. GDPR requires documenting your LI assessment.`,\n },\n ],\n warnings: [\n {\n rule: 'li-description-recommended',\n field: 'legitimateInterest.description',\n check: (c) => c['legalBasis'] === 'legitimate_interest' && !(c['legitimateInterest'] as Record<string, unknown> | undefined)?.['description'],\n message: (id) => `Cookie \"${id}\": adding a Legitimate Interest description improves transparency and is a GDPR best practice.`,\n suggestion: 'Add a brief description of your legitimate interest balancing test.',\n },\n ],\n },\n\n 'opt-out': {\n errors: [],\n warnings: [\n {\n rule: 'gpc-recommended',\n field: 'listenGpc',\n check: (c) => c['legalBasis'] !== 'mandatory' && !c['listenGpc'],\n message: (id) => `Cookie \"${id}\" does not listen for the GPC signal. US state privacy laws require honoring GPC for opt-out of sale/sharing.`,\n suggestion: 'Enable \"Listen for GPC signal\" on this cookie.',\n },\n ],\n },\n\n 'opt-out-strict': {\n errors: [\n {\n rule: 'cpra-category-required',\n field: 'cpraCategory',\n check: (c) => c['legalBasis'] !== 'mandatory' && !c['cpraCategory'],\n message: (id) => `Cookie \"${id}\" must have a CPRA category (Sale, Sharing, or Sensitive) assigned. Required under CPRA for all non-necessary cookies.`,\n },\n ],\n warnings: [\n {\n rule: 'gpc-strict-required',\n field: 'listenGpc',\n check: (c) => c['legalBasis'] !== 'mandatory' && !c['listenGpc'],\n message: (id) => `Cookie \"${id}\" does not listen for the GPC signal. Under CPRA, non-mandatory cookies must honor GPC as a Do Not Sell/Share signal.`,\n suggestion: 'Enable \"Listen for GPC signal\" on this cookie.',\n },\n ],\n },\n\n 'opt-in-dpdpa': {\n errors: [\n {\n rule: 'no-li-allowed',\n field: 'legalBasis',\n check: (c) => c['legalBasis'] === 'legitimate_interest',\n message: (id) => `Cookie \"${id}\" uses Legitimate Interest, which is not a valid legal basis under India's DPDPA. Use Consent or Strictly Necessary.`,\n },\n {\n rule: 'legal-basis-required',\n field: 'legalBasis',\n check: (c) => !c['legalBasis'],\n message: (id) => `Cookie \"${id}\" must have a legal basis set (Consent or Strictly Necessary) for DPDPA compliance.`,\n },\n ],\n warnings: [],\n },\n\n 'opt-in-china': {\n errors: [\n {\n rule: 'legal-basis-required',\n field: 'legalBasis',\n check: (c) => !c['legalBasis'],\n message: (id) => `Cookie \"${id}\" must have a legal basis set for China PIPL compliance. Consent is the primary valid basis.`,\n },\n ],\n warnings: [\n {\n rule: 'li-limited-under-pipl',\n field: 'legalBasis',\n check: (c) => c['legalBasis'] === 'legitimate_interest',\n message: (id) => `Cookie \"${id}\" uses Legitimate Interest. Under China's PIPL, LI is narrowly scoped and data processing purposes must be strictly documented.`,\n suggestion: 'Consider using Consent for this cookie to ensure PIPL compliance. Document the specific legitimate interest basis if retaining LI.',\n },\n ],\n },\n\n 'opt-in-brazil': {\n errors: [\n {\n rule: 'legal-basis-required',\n field: 'legalBasis',\n check: (c) => !c['legalBasis'],\n message: (id) => `Cookie \"${id}\" must have a legal basis set. Brazil's LGPD requires one of ten legal bases; Consent or Legitimate Interest are most common for cookies.`,\n },\n ],\n warnings: [\n {\n rule: 'li-description-recommended',\n field: 'legitimateInterest.description',\n check: (c) => c['legalBasis'] === 'legitimate_interest' && !(c['legitimateInterest'] as Record<string, unknown> | undefined)?.['description'],\n message: (id) => `Cookie \"${id}\" uses Legitimate Interest. LGPD requires documenting the basis and conducting an impact assessment.`,\n suggestion: 'Add a Legitimate Interest description to document the LGPD basis and your impact assessment.',\n },\n ],\n },\n\n 'general-privacy-consent': {\n errors: [],\n warnings: [\n {\n rule: 'legal-basis-recommended',\n field: 'legalBasis',\n check: (c) => !c['legalBasis'],\n message: (id) => `Cookie \"${id}\" has no legal basis documented. While a strict cookie banner may not be legally required in this jurisdiction, documenting legal basis is a privacy best practice.`,\n suggestion: 'Set a legal basis to improve compliance documentation and future-proof against stricter enforcement.',\n },\n ],\n },\n\n 'notice-only': {\n errors: [],\n warnings: [],\n },\n}\n\n// ─── Embedded Jurisdiction → Compliance Group Map ─────────────────────────────\n// Version tag: update when regulations or mappings change materially.\n// HOW TO UPDATE: edit or add a country entry below. That is the ONLY file change needed.\n//\n// locales: BCP 47 tags for the most commonly used web/IT locales in that country.\n// Only well-supported, widely-used locales are listed. English-speaking countries\n// use 'en' (no country suffix). No country-specific English variants (en-XX) anywhere.\n\nexport const EMBEDDED_COMPLIANCE_MAP = {\n version: '2026-07',\n countries: {\n // ─── Europe — EU 27 ───────────────────────────────────────────────────────\n AT: { name: 'Austria', type: 'country', complianceGroup: 'opt-in', default: 'opt-in', description: 'Austria (GDPR)', locales: ['de-AT'] },\n BE: { name: 'Belgium', type: 'country', complianceGroup: 'opt-in', default: 'opt-in', description: 'Belgium (GDPR)', locales: ['nl-BE', 'fr-BE'] },\n BG: { name: 'Bulgaria', type: 'country', complianceGroup: 'opt-in', default: 'opt-in', description: 'Bulgaria (GDPR)', locales: ['bg-BG'] },\n HR: { name: 'Croatia', type: 'country', complianceGroup: 'opt-in', default: 'opt-in', description: 'Croatia (GDPR)', locales: ['hr-HR'] },\n CY: { name: 'Cyprus', type: 'country', complianceGroup: 'opt-in', default: 'opt-in', description: 'Cyprus (GDPR)', locales: ['el-CY'] },\n CZ: { name: 'Czechia', type: 'country', complianceGroup: 'opt-in', default: 'opt-in', description: 'Czechia (GDPR)', locales: ['cs-CZ'] },\n DK: { name: 'Denmark', type: 'country', complianceGroup: 'opt-in', default: 'opt-in', description: 'Denmark (GDPR)', locales: ['da-DK'] },\n EE: { name: 'Estonia', type: 'country', complianceGroup: 'opt-in', default: 'opt-in', description: 'Estonia (GDPR)', locales: ['et-EE'] },\n FI: { name: 'Finland', type: 'country', complianceGroup: 'opt-in', default: 'opt-in', description: 'Finland (GDPR)', locales: ['fi-FI'] },\n FR: { name: 'France', type: 'country', complianceGroup: 'opt-in', default: 'opt-in', description: 'France (GDPR)', locales: ['fr-FR'] },\n DE: { name: 'Germany', type: 'country', complianceGroup: 'opt-in', default: 'opt-in', description: 'Germany (GDPR)', locales: ['de-DE'] },\n GR: { name: 'Greece', type: 'country', complianceGroup: 'opt-in', default: 'opt-in', description: 'Greece (GDPR)', locales: ['el-GR'] },\n HU: { name: 'Hungary', type: 'country', complianceGroup: 'opt-in', default: 'opt-in', description: 'Hungary (GDPR)', locales: ['hu-HU'] },\n IE: { name: 'Ireland', type: 'country', complianceGroup: 'opt-in', default: 'opt-in', description: 'Ireland (GDPR)', locales: ['en'] },\n IT: { name: 'Italy', type: 'country', complianceGroup: 'opt-in', default: 'opt-in', description: 'Italy (GDPR)', locales: ['it-IT'] },\n LV: { name: 'Latvia', type: 'country', complianceGroup: 'opt-in', default: 'opt-in', description: 'Latvia (GDPR)', locales: ['lv-LV'] },\n LT: { name: 'Lithuania', type: 'country', complianceGroup: 'opt-in', default: 'opt-in', description: 'Lithuania (GDPR)', locales: ['lt-LT'] },\n LU: { name: 'Luxembourg', type: 'country', complianceGroup: 'opt-in', default: 'opt-in', description: 'Luxembourg (GDPR)', locales: ['fr-LU', 'de-LU'] },\n MT: { name: 'Malta', type: 'country', complianceGroup: 'opt-in', default: 'opt-in', description: 'Malta (GDPR)', locales: ['mt-MT', 'en'] },\n NL: { name: 'Netherlands', type: 'country', complianceGroup: 'opt-in', default: 'opt-in', description: 'Netherlands (GDPR)', locales: ['nl-NL'] },\n PL: { name: 'Poland', type: 'country', complianceGroup: 'opt-in', default: 'opt-in', description: 'Poland (GDPR)', locales: ['pl-PL'] },\n PT: { name: 'Portugal', type: 'country', complianceGroup: 'opt-in', default: 'opt-in', description: 'Portugal (GDPR)', locales: ['pt-PT'] },\n RO: { name: 'Romania', type: 'country', complianceGroup: 'opt-in', default: 'opt-in', description: 'Romania (GDPR)', locales: ['ro-RO'] },\n SK: { name: 'Slovakia', type: 'country', complianceGroup: 'opt-in', default: 'opt-in', description: 'Slovakia (GDPR)', locales: ['sk-SK'] },\n SI: { name: 'Slovenia', type: 'country', complianceGroup: 'opt-in', default: 'opt-in', description: 'Slovenia (GDPR)', locales: ['sl-SI'] },\n ES: { name: 'Spain', type: 'country', complianceGroup: 'opt-in', default: 'opt-in', description: 'Spain (GDPR)', locales: ['es-ES'] },\n SE: { name: 'Sweden', type: 'country', complianceGroup: 'opt-in', default: 'opt-in', description: 'Sweden (GDPR)', locales: ['sv-SE'] },\n // ─── Europe — EEA non-EU ──────────────────────────────────────────────────\n IS: { name: 'Iceland', type: 'country', complianceGroup: 'opt-in', default: 'opt-in', description: 'Iceland (GDPR via EEA)', locales: ['is-IS'] },\n LI: { name: 'Liechtenstein', type: 'country', complianceGroup: 'opt-in', default: 'opt-in', description: 'Liechtenstein (GDPR via EEA)', locales: ['de-LI'] },\n NO: { name: 'Norway', type: 'country', complianceGroup: 'opt-in', default: 'opt-in', description: 'Norway (GDPR via EEA)', locales: ['nb-NO'] },\n // ─── Europe — GDPR-aligned candidate/observer countries ──────────────────\n AD: { name: 'Andorra', type: 'country', complianceGroup: 'opt-in', default: 'opt-in', description: 'Andorra (GDPR-aligned)', locales: ['ca-AD'] },\n AL: { name: 'Albania', type: 'country', complianceGroup: 'opt-in', default: 'opt-in', description: 'Albania (GDPR-aligned)', locales: ['sq-AL'] },\n BA: { name: 'Bosnia and Herzegovina', type: 'country', complianceGroup: 'opt-in', default: 'opt-in', description: 'Bosnia and Herzegovina (GDPR-aligned)', locales: ['bs-BA', 'sr-Latn-BA'] },\n GG: { name: 'Guernsey', type: 'country', complianceGroup: 'opt-in', default: 'opt-in', description: 'Guernsey (DPA based on GDPR)', locales: ['en'] },\n GI: { name: 'Gibraltar', type: 'country', complianceGroup: 'opt-in', default: 'opt-in', description: 'Gibraltar (DPA based on UK GDPR)', locales: ['en'] },\n GL: { name: 'Greenland', type: 'country', complianceGroup: 'opt-in', default: 'opt-in', description: 'Greenland (Danish GDPR implementation)', locales: ['da-GL'] },\n MC: { name: 'Monaco', type: 'country', complianceGroup: 'opt-in', default: 'opt-in', description: 'Monaco (GDPR-aligned)', locales: ['fr-MC'] },\n ME: { name: 'Montenegro', type: 'country', complianceGroup: 'opt-in', default: 'opt-in', description: 'Montenegro (GDPR-aligned)', locales: ['sr-Latn-ME'] },\n MK: { name: 'North Macedonia', type: 'country', complianceGroup: 'opt-in', default: 'opt-in', description: 'North Macedonia (GDPR-aligned)', locales: ['mk-MK'] },\n RS: { name: 'Serbia', type: 'country', complianceGroup: 'opt-in', default: 'opt-in', description: 'Serbia (GDPR-aligned)', locales: ['sr-RS'] },\n SM: { name: 'San Marino', type: 'country', complianceGroup: 'opt-in', default: 'opt-in', description: 'San Marino (GDPR-aligned)', locales: ['it-SM'] },\n TR: { name: 'Türkiye', type: 'country', complianceGroup: 'opt-in', default: 'opt-in', description: 'Türkiye (KVKK)', locales: ['tr-TR'] },\n // ─── North America ────────────────────────────────────────────────────────\n CA: {\n name: 'Canada',\n type: 'country',\n complianceGroup: 'general-privacy-consent',\n default: 'general-privacy-consent',\n description: 'Canada (PIPEDA / Law 25)',\n locales: ['en', 'fr-CA'],\n overriddenRegions: {\n QC: { name: 'Quebec', type: 'region', complianceGroup: 'opt-in', description: 'Quebec — Law 25 (GDPR-style opt-in)' },\n },\n },\n US: {\n name: 'United States',\n type: 'country',\n // complianceGroup: base when state known but has no specific law\n // default: used when state CANNOT be detected (use most strict = opt-out-strict)\n complianceGroup: 'opt-out',\n default: 'opt-out-strict',\n description: 'United States — opt-out-strict (CPRA) used when state cannot be detected',\n locales: ['en', 'es-US'],\n overriddenRegions: {\n CA: { name: 'California', type: 'region', complianceGroup: 'opt-out-strict', description: 'California — CPRA/CCPA' },\n VA: { name: 'Virginia', type: 'region', complianceGroup: 'opt-out', description: 'Virginia — VCDPA' },\n CO: { name: 'Colorado', type: 'region', complianceGroup: 'opt-out', description: 'Colorado — CPA' },\n CT: { name: 'Connecticut', type: 'region', complianceGroup: 'opt-out', description: 'Connecticut — CTDPA' },\n UT: { name: 'Utah', type: 'region', complianceGroup: 'opt-out', description: 'Utah — UCPA' },\n TX: { name: 'Texas', type: 'region', complianceGroup: 'opt-out', description: 'Texas — TDPSA' },\n DE: { name: 'Delaware', type: 'region', complianceGroup: 'opt-out', description: 'Delaware — DPDPA' },\n NJ: { name: 'New Jersey', type: 'region', complianceGroup: 'opt-out', description: 'New Jersey — NJDPA' },\n OR: { name: 'Oregon', type: 'region', complianceGroup: 'opt-out', description: 'Oregon — OCPA' },\n MN: { name: 'Minnesota', type: 'region', complianceGroup: 'opt-out', description: 'Minnesota — MCDPA' },\n MD: { name: 'Maryland', type: 'region', complianceGroup: 'opt-out', description: 'Maryland — MODPA' },\n KY: { name: 'Kentucky', type: 'region', complianceGroup: 'opt-out', description: 'Kentucky — KCDPA' },\n TN: { name: 'Tennessee', type: 'region', complianceGroup: 'opt-out', description: 'Tennessee — TIPA' },\n NE: { name: 'Nebraska', type: 'region', complianceGroup: 'opt-out', description: 'Nebraska — NDPA' },\n IA: { name: 'Iowa', type: 'region', complianceGroup: 'opt-out', description: 'Iowa — ICDPA' },\n IN: { name: 'Indiana', type: 'region', complianceGroup: 'opt-out', description: 'Indiana — ICDPA' },\n MT: { name: 'Montana', type: 'region', complianceGroup: 'opt-out', description: 'Montana — MCDPA' },\n NH: { name: 'New Hampshire', type: 'region', complianceGroup: 'opt-out', description: 'New Hampshire — NHPA' },\n RI: { name: 'Rhode Island', type: 'region', complianceGroup: 'opt-out', description: 'Rhode Island — RIDTPA' },\n },\n },\n // ─── Latin America ────────────────────────────────────────────────────────\n AR: { name: 'Argentina', type: 'country', complianceGroup: 'general-privacy-consent', default: 'general-privacy-consent', description: 'Argentina (LPDP)', locales: ['es-AR'] },\n BO: { name: 'Bolivia', type: 'country', complianceGroup: 'general-privacy-consent', default: 'general-privacy-consent', description: 'Bolivia', locales: ['es-BO'] },\n BR: { name: 'Brazil', type: 'country', complianceGroup: 'opt-in-brazil', default: 'opt-in-brazil', description: 'Brazil (LGPD)', locales: ['pt-BR'] },\n CL: { name: 'Chile', type: 'country', complianceGroup: 'general-privacy-consent', default: 'general-privacy-consent', description: 'Chile (Law 19.628)', locales: ['es-CL'] },\n CO: { name: 'Colombia', type: 'country', complianceGroup: 'general-privacy-consent', default: 'general-privacy-consent', description: 'Colombia (Law 1581)', locales: ['es-CO'] },\n CR: { name: 'Costa Rica', type: 'country', complianceGroup: 'general-privacy-consent', default: 'general-privacy-consent', description: 'Costa Rica (LPDP)', locales: ['es-CR'] },\n DO: { name: 'Dominican Republic', type: 'country', complianceGroup: 'general-privacy-consent', default: 'general-privacy-consent', description: 'Dominican Republic', locales: ['es-DO'] },\n EC: { name: 'Ecuador', type: 'country', complianceGroup: 'general-privacy-consent', default: 'general-privacy-consent', description: 'Ecuador (LOPDP)', locales: ['es-EC'] },\n GF: { name: 'French Guiana', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'French Guiana', locales: ['fr-GF'] },\n GT: { name: 'Guatemala', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Guatemala', locales: ['es-GT'] },\n GY: { name: 'Guyana', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Guyana', locales: ['en'] },\n HN: { name: 'Honduras', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Honduras', locales: ['es-HN'] },\n HT: { name: 'Haiti', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Haiti', locales: ['fr-HT'] },\n JM: { name: 'Jamaica', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Jamaica', locales: ['en'] },\n MX: { name: 'Mexico', type: 'country', complianceGroup: 'general-privacy-consent', default: 'general-privacy-consent', description: 'Mexico (LFPDPPP)', locales: ['es-MX'] },\n NI: { name: 'Nicaragua', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Nicaragua', locales: ['es-NI'] },\n PA: { name: 'Panama', type: 'country', complianceGroup: 'general-privacy-consent', default: 'general-privacy-consent', description: 'Panama', locales: ['es-PA'] },\n PE: { name: 'Peru', type: 'country', complianceGroup: 'general-privacy-consent', default: 'general-privacy-consent', description: 'Peru (LPDP)', locales: ['es-PE'] },\n PY: { name: 'Paraguay', type: 'country', complianceGroup: 'general-privacy-consent', default: 'general-privacy-consent', description: 'Paraguay', locales: ['es-PY'] },\n SR: { name: 'Suriname', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Suriname', locales: ['nl-SR'] },\n SV: { name: 'El Salvador', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'El Salvador', locales: ['es-SV'] },\n TT: { name: 'Trinidad and Tobago', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Trinidad and Tobago', locales: ['en'] },\n UY: { name: 'Uruguay', type: 'country', complianceGroup: 'general-privacy-consent', default: 'general-privacy-consent', description: 'Uruguay (Law 18.331)', locales: ['es-UY'] },\n VE: { name: 'Venezuela', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Venezuela', locales: ['es-VE'] },\n // ─── Caribbean ────────────────────────────────────────────────────────────\n AG: { name: 'Antigua and Barbuda', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Antigua and Barbuda', locales: ['en'] },\n BB: { name: 'Barbados', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Barbados', locales: ['en'] },\n BL: { name: 'Saint Barthélemy', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Saint Barthélemy', locales: ['fr-BL'] },\n BM: { name: 'Bermuda', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Bermuda', locales: ['en'] },\n BS: { name: 'Bahamas', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Bahamas', locales: ['en'] },\n CU: { name: 'Cuba', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Cuba', locales: ['es-CU'] },\n DM: { name: 'Dominica', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Dominica', locales: ['en'] },\n GD: { name: 'Grenada', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Grenada', locales: ['en'] },\n KN: { name: 'Saint Kitts and Nevis', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Saint Kitts and Nevis', locales: ['en'] },\n LC: { name: 'Saint Lucia', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Saint Lucia', locales: ['en'] },\n PM: { name: 'Saint Pierre and Miquelon', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Saint Pierre and Miquelon', locales: ['fr-PM'] },\n PR: { name: 'Puerto Rico', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Puerto Rico', locales: ['es-PR', 'en'] },\n VC: { name: 'Saint Vincent and the Grenadines', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Saint Vincent and the Grenadines', locales: ['en'] },\n // ─── Asia — India ─────────────────────────────────────────────────────────\n IN: { name: 'India', type: 'country', complianceGroup: 'opt-in-dpdpa', default: 'opt-in-dpdpa', description: 'India (DPDPA)', locales: ['hi-IN', 'en', 'bn-IN', 'ta-IN'] },\n // ─── Asia — China ─────────────────────────────────────────────────────────\n CN: { name: 'China', type: 'country', complianceGroup: 'opt-in-china', default: 'opt-in-china', description: 'China (PIPL + DSL + CSL)', locales: ['zh-CN'] },\n // ─── Asia — Southeast Asia ────────────────────────────────────────────────\n BN: { name: 'Brunei', type: 'country', complianceGroup: 'general-privacy-consent', default: 'general-privacy-consent', description: 'Brunei (Personal Data Protection Order)', locales: ['ms-BN', 'en'] },\n ID: { name: 'Indonesia', type: 'country', complianceGroup: 'general-privacy-consent', default: 'general-privacy-consent', description: 'Indonesia (PDP Law)', locales: ['id-ID'] },\n KH: { name: 'Cambodia', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Cambodia', locales: ['km-KH'] },\n LA: { name: 'Laos', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Laos', locales: ['lo-LA'] },\n MM: { name: 'Myanmar', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Myanmar', locales: ['my-MM'] },\n MY: { name: 'Malaysia', type: 'country', complianceGroup: 'general-privacy-consent', default: 'general-privacy-consent', description: 'Malaysia (PDPA)', locales: ['ms-MY', 'zh-MY', 'en'] },\n PH: { name: 'Philippines', type: 'country', complianceGroup: 'general-privacy-consent', default: 'general-privacy-consent', description: 'Philippines (DPA)', locales: ['fil-PH', 'en'] },\n SG: { name: 'Singapore', type: 'country', complianceGroup: 'general-privacy-consent', default: 'general-privacy-consent', description: 'Singapore (PDPA)', locales: ['zh-SG', 'ms-SG', 'ta-SG', 'en'] },\n TH: { name: 'Thailand', type: 'country', complianceGroup: 'opt-in', default: 'opt-in', description: 'Thailand (PDPA — GDPR-style opt-in)', locales: ['th-TH'] },\n TL: { name: 'Timor-Leste', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Timor-Leste', locales: ['pt-TL'] },\n VN: { name: 'Vietnam', type: 'country', complianceGroup: 'general-privacy-consent', default: 'general-privacy-consent', description: 'Vietnam (PDPD)', locales: ['vi-VN'] },\n // ─── Asia — East Asia ─────────────────────────────────────────────────────\n HK: { name: 'Hong Kong', type: 'country', complianceGroup: 'general-privacy-consent', default: 'general-privacy-consent', description: 'Hong Kong SAR (PDPO)', locales: ['zh-HK'] },\n JP: { name: 'Japan', type: 'country', complianceGroup: 'general-privacy-consent', default: 'general-privacy-consent', description: 'Japan (APPI)', locales: ['ja-JP'] },\n KP: { name: 'North Korea', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'North Korea', locales: ['ko-KP'] },\n KR: { name: 'South Korea', type: 'country', complianceGroup: 'general-privacy-consent', default: 'general-privacy-consent', description: 'South Korea (PIPA)', locales: ['ko-KR'] },\n MN: { name: 'Mongolia', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Mongolia', locales: ['mn-MN'] },\n MO: { name: 'Macau', type: 'country', complianceGroup: 'general-privacy-consent', default: 'general-privacy-consent', description: 'Macau SAR (PDPA)', locales: ['zh-MO', 'pt-MO'] },\n TW: { name: 'Taiwan', type: 'country', complianceGroup: 'general-privacy-consent', default: 'general-privacy-consent', description: 'Taiwan (PDPL)', locales: ['zh-TW'] },\n // ─── Asia — South Asia ────────────────────────────────────────────────────\n AF: { name: 'Afghanistan', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Afghanistan', locales: ['fa-AF'] },\n BD: { name: 'Bangladesh', type: 'country', complianceGroup: 'general-privacy-consent', default: 'general-privacy-consent', description: 'Bangladesh', locales: ['bn-BD'] },\n BT: { name: 'Bhutan', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Bhutan', locales: ['dz-BT'] },\n IO: { name: 'British Indian Ocean Territory', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'British Indian Ocean Territory', locales: ['en'] },\n LK: { name: 'Sri Lanka', type: 'country', complianceGroup: 'general-privacy-consent', default: 'general-privacy-consent', description: 'Sri Lanka', locales: ['si-LK', 'ta-LK'] },\n MV: { name: 'Maldives', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Maldives', locales: ['dv-MV'] },\n NP: { name: 'Nepal', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Nepal', locales: ['ne-NP'] },\n PK: { name: 'Pakistan', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Pakistan', locales: ['ur-PK', 'en'] },\n // ─── Asia — Central Asia ──────────────────────────────────────────────────\n AM: { name: 'Armenia', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Armenia', locales: ['hy-AM'] },\n AZ: { name: 'Azerbaijan', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Azerbaijan', locales: ['az-AZ'] },\n GE: { name: 'Georgia', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Georgia', locales: ['ka-GE'] },\n KG: { name: 'Kyrgyzstan', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Kyrgyzstan', locales: ['ky-KG', 'ru-KG'] },\n KZ: { name: 'Kazakhstan', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Kazakhstan', locales: ['kk-KZ', 'ru-KZ'] },\n TJ: { name: 'Tajikistan', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Tajikistan', locales: ['tg-TJ'] },\n TM: { name: 'Turkmenistan', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Turkmenistan', locales: ['tk-TM'] },\n UZ: { name: 'Uzbekistan', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Uzbekistan', locales: ['uz-UZ'] },\n // ─── Middle East ──────────────────────────────────────────────────────────\n AE: { name: 'United Arab Emirates', type: 'country', complianceGroup: 'general-privacy-consent', default: 'general-privacy-consent', description: 'United Arab Emirates (UAE PDPL / DIFC DPL / ADGM DPR)', locales: ['ar-AE'] },\n BH: { name: 'Bahrain', type: 'country', complianceGroup: 'general-privacy-consent', default: 'general-privacy-consent', description: 'Bahrain (PDPL)', locales: ['ar-BH'] },\n IL: { name: 'Israel', type: 'country', complianceGroup: 'general-privacy-consent', default: 'general-privacy-consent', description: 'Israel (Privacy Protection Law)', locales: ['he-IL', 'ar-IL'] },\n IQ: { name: 'Iraq', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Iraq', locales: ['ar-IQ'] },\n IR: { name: 'Iran', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Iran', locales: ['fa-IR'] },\n JO: { name: 'Jordan', type: 'country', complianceGroup: 'general-privacy-consent', default: 'general-privacy-consent', description: 'Jordan', locales: ['ar-JO'] },\n KW: { name: 'Kuwait', type: 'country', complianceGroup: 'general-privacy-consent', default: 'general-privacy-consent', description: 'Kuwait', locales: ['ar-KW'] },\n LB: { name: 'Lebanon', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Lebanon', locales: ['ar-LB'] },\n OM: { name: 'Oman', type: 'country', complianceGroup: 'general-privacy-consent', default: 'general-privacy-consent', description: 'Oman (PDPL)', locales: ['ar-OM'] },\n PS: { name: 'Palestine', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Palestine', locales: ['ar-PS'] },\n QA: { name: 'Qatar', type: 'country', complianceGroup: 'general-privacy-consent', default: 'general-privacy-consent', description: 'Qatar (PDPL)', locales: ['ar-QA'] },\n SA: { name: 'Saudi Arabia', type: 'country', complianceGroup: 'general-privacy-consent', default: 'general-privacy-consent', description: 'Saudi Arabia (PDPL)', locales: ['ar-SA'] },\n SY: { name: 'Syria', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Syria', locales: ['ar-SY'] },\n YE: { name: 'Yemen', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Yemen', locales: ['ar-YE'] },\n // ─── Africa — West ────────────────────────────────────────────────────────\n BF: { name: 'Burkina Faso', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Burkina Faso', locales: ['fr-BF'] },\n BJ: { name: 'Benin', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Benin', locales: ['fr-BJ'] },\n CI: { name: \"Côte d'Ivoire\", type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: \"Côte d'Ivoire\", locales: ['fr-CI'] },\n CM: { name: 'Cameroon', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Cameroon', locales: ['fr-CM', 'en'] },\n CV: { name: 'Cabo Verde', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Cabo Verde', locales: ['pt-CV'] },\n GA: { name: 'Gabon', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Gabon', locales: ['fr-GA'] },\n GH: { name: 'Ghana', type: 'country', complianceGroup: 'general-privacy-consent', default: 'general-privacy-consent', description: 'Ghana (DPA)', locales: ['en'] },\n GM: { name: 'Gambia', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Gambia', locales: ['en'] },\n GN: { name: 'Guinea', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Guinea', locales: ['fr-GN'] },\n GQ: { name: 'Equatorial Guinea', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Equatorial Guinea', locales: ['es-GQ', 'fr-GQ'] },\n GW: { name: 'Guinea-Bissau', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Guinea-Bissau', locales: ['pt-GW'] },\n LR: { name: 'Liberia', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Liberia', locales: ['en'] },\n ML: { name: 'Mali', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Mali', locales: ['fr-ML'] },\n MR: { name: 'Mauritania', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Mauritania', locales: ['ar-MR'] },\n NE: { name: 'Niger', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Niger', locales: ['fr-NE'] },\n NG: { name: 'Nigeria', type: 'country', complianceGroup: 'general-privacy-consent', default: 'general-privacy-consent', description: 'Nigeria (NDPA)', locales: ['en'] },\n SL: { name: 'Sierra Leone', type: 'country', complianceGroup: 'general-privacy-consent', default: 'general-privacy-consent', description: 'Sierra Leone', locales: ['en'] },\n SN: { name: 'Senegal', type: 'country', complianceGroup: 'general-privacy-consent', default: 'general-privacy-consent', description: 'Senegal', locales: ['fr-SN'] },\n TG: { name: 'Togo', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Togo', locales: ['fr-TG'] },\n // ─── Africa — East ────────────────────────────────────────────────────────\n BI: { name: 'Burundi', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Burundi', locales: ['fr-BI'] },\n DJ: { name: 'Djibouti', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Djibouti', locales: ['fr-DJ', 'ar-DJ'] },\n ER: { name: 'Eritrea', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Eritrea', locales: ['ti-ER'] },\n ET: { name: 'Ethiopia', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Ethiopia', locales: ['am-ET'] },\n KE: { name: 'Kenya', type: 'country', complianceGroup: 'general-privacy-consent', default: 'general-privacy-consent', description: 'Kenya (DPA)', locales: ['sw-KE', 'en'] },\n KM: { name: 'Comoros', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Comoros', locales: ['ar-KM', 'fr-KM'] },\n MG: { name: 'Madagascar', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Madagascar', locales: ['fr-MG'] },\n MU: { name: 'Mauritius', type: 'country', complianceGroup: 'general-privacy-consent', default: 'general-privacy-consent', description: 'Mauritius (DPA)', locales: ['en', 'fr-MU'] },\n MW: { name: 'Malawi', type: 'country', complianceGroup: 'general-privacy-consent', default: 'general-privacy-consent', description: 'Malawi', locales: ['en'] },\n MZ: { name: 'Mozambique', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Mozambique', locales: ['pt-MZ'] },\n RW: { name: 'Rwanda', type: 'country', complianceGroup: 'general-privacy-consent', default: 'general-privacy-consent', description: 'Rwanda (DPA)', locales: ['rw-RW', 'en'] },\n SC: { name: 'Seychelles', type: 'country', complianceGroup: 'general-privacy-consent', default: 'general-privacy-consent', description: 'Seychelles', locales: ['fr-SC', 'en'] },\n SO: { name: 'Somalia', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Somalia', locales: ['so-SO'] },\n SS: { name: 'South Sudan', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'South Sudan', locales: ['en'] },\n ST: { name: 'São Tomé and Príncipe', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'São Tomé and Príncipe', locales: ['pt-ST'] },\n TZ: { name: 'Tanzania', type: 'country', complianceGroup: 'general-privacy-consent', default: 'general-privacy-consent', description: 'Tanzania', locales: ['sw-TZ', 'en'] },\n UG: { name: 'Uganda', type: 'country', complianceGroup: 'general-privacy-consent', default: 'general-privacy-consent', description: 'Uganda (DPA)', locales: ['en'] },\n // ─── Africa — Southern ────────────────────────────────────────────────────\n AO: { name: 'Angola', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Angola', locales: ['pt-AO'] },\n BW: { name: 'Botswana', type: 'country', complianceGroup: 'general-privacy-consent', default: 'general-privacy-consent', description: 'Botswana (DPA)', locales: ['en'] },\n LS: { name: 'Lesotho', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Lesotho', locales: ['en'] },\n NA: { name: 'Namibia', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Namibia', locales: ['en'] },\n SZ: { name: 'Eswatini', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Eswatini', locales: ['en'] },\n ZA: { name: 'South Africa', type: 'country', complianceGroup: 'general-privacy-consent', default: 'general-privacy-consent', description: 'South Africa (POPIA)', locales: ['en', 'af-ZA'] },\n ZM: { name: 'Zambia', type: 'country', complianceGroup: 'general-privacy-consent', default: 'general-privacy-consent', description: 'Zambia (DPA)', locales: ['en'] },\n ZW: { name: 'Zimbabwe', type: 'country', complianceGroup: 'general-privacy-consent', default: 'general-privacy-consent', description: 'Zimbabwe (DPA)', locales: ['en'] },\n // ─── Africa — North ───────────────────────────────────────────────────────\n DZ: { name: 'Algeria', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Algeria', locales: ['ar-DZ', 'fr-DZ'] },\n EG: { name: 'Egypt', type: 'country', complianceGroup: 'general-privacy-consent', default: 'general-privacy-consent', description: 'Egypt (DPA)', locales: ['ar-EG'] },\n LY: { name: 'Libya', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Libya', locales: ['ar-LY'] },\n MA: { name: 'Morocco', type: 'country', complianceGroup: 'general-privacy-consent', default: 'general-privacy-consent', description: 'Morocco', locales: ['ar-MA', 'fr-MA'] },\n SD: { name: 'Sudan', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Sudan', locales: ['ar-SD'] },\n TN: { name: 'Tunisia', type: 'country', complianceGroup: 'general-privacy-consent', default: 'general-privacy-consent', description: 'Tunisia (DPA)', locales: ['ar-TN', 'fr-TN'] },\n // ─── Africa — Central ─────────────────────────────────────────────────────\n CD: { name: 'DR Congo', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'DR Congo', locales: ['fr-CD'] },\n CF: { name: 'Central African Republic', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Central African Republic', locales: ['fr-CF'] },\n CG: { name: 'Republic of Congo', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Republic of Congo', locales: ['fr-CG'] },\n TD: { name: 'Chad', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Chad', locales: ['fr-TD', 'ar-TD'] },\n // ─── Europe — Eastern ─────────────────────────────────────────────────────\n BY: { name: 'Belarus', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Belarus', locales: ['be-BY', 'ru-BY'] },\n MD: { name: 'Moldova', type: 'country', complianceGroup: 'general-privacy-consent', default: 'general-privacy-consent', description: 'Moldova', locales: ['ro-MD'] },\n RU: { name: 'Russia', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Russia', locales: ['ru-RU'] },\n UA: { name: 'Ukraine', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Ukraine', locales: ['uk-UA'] },\n VA: { name: 'Holy See', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Holy See', locales: ['it-VA'] },\n // ─── Oceania ──────────────────────────────────────────────────────────────\n AU: { name: 'Australia', type: 'country', complianceGroup: 'general-privacy-consent', default: 'general-privacy-consent', description: 'Australia (Privacy Act)', locales: ['en'] },\n FJ: { name: 'Fiji', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Fiji', locales: ['en'] },\n FM: { name: 'Micronesia', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Micronesia', locales: ['en'] },\n KI: { name: 'Kiribati', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Kiribati', locales: ['en'] },\n MH: { name: 'Marshall Islands', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Marshall Islands', locales: ['en'] },\n MP: { name: 'Northern Mariana Islands', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Northern Mariana Islands', locales: ['en'] },\n NC: { name: 'New Caledonia', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'New Caledonia', locales: ['fr-NC'] },\n NF: { name: 'Norfolk Island', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Norfolk Island', locales: ['en'] },\n NR: { name: 'Nauru', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Nauru', locales: ['en'] },\n NU: { name: 'Niue', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Niue', locales: ['en'] },\n NZ: { name: 'New Zealand', type: 'country', complianceGroup: 'general-privacy-consent', default: 'general-privacy-consent', description: 'New Zealand (Privacy Act)', locales: ['en'] },\n PF: { name: 'French Polynesia', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'French Polynesia', locales: ['fr-PF'] },\n PG: { name: 'Papua New Guinea', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Papua New Guinea', locales: ['en'] },\n PW: { name: 'Palau', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Palau', locales: ['en'] },\n SB: { name: 'Solomon Islands', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Solomon Islands', locales: ['en'] },\n TO: { name: 'Tonga', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Tonga', locales: ['en'] },\n TV: { name: 'Tuvalu', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Tuvalu', locales: ['en'] },\n VU: { name: 'Vanuatu', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Vanuatu', locales: ['fr-VU', 'en'] },\n WS: { name: 'Samoa', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Samoa', locales: ['en'] },\n // ─── Europe — GDPR-equivalent / adequacy ─────────────────────────────────\n UK: { name: 'United Kingdom', type: 'country', complianceGroup: 'opt-in', default: 'opt-in', description: 'United Kingdom (UK GDPR + PECR)', locales: ['en'] },\n CH: { name: 'Switzerland', type: 'country', complianceGroup: 'opt-in', default: 'opt-in', description: 'Switzerland (revFADP)', locales: ['de-CH', 'fr-CH', 'it-CH'] },\n },\n} satisfies {\n version: string\n countries: Record<string, {\n name: string\n type: 'country'\n complianceGroup: ComplianceGroupId\n default: ComplianceGroupId\n description: string\n locales: readonly string[]\n overriddenRegions?: Record<string, {\n name: string\n type: 'region'\n complianceGroup: ComplianceGroupId\n description: string\n }>\n }>\n}\n\nexport const COUNTRY_CODES = Object.entries(EMBEDDED_COMPLIANCE_MAP.countries).reduce(function (acc, [c, d]) {\n acc[c] = d.name;\n return acc;\n}, {} as Record<string, string>);\n\nexport const LOCALES = Object.values(EMBEDDED_COMPLIANCE_MAP.countries).reduce(function (acc, c) {\n c.locales.forEach(l => {\n acc[l] = c.name;\n })\n return acc;\n}, {} as Record<string, string>);\n","import type { ResolvedProfile } from '../types'\n\nconst profile: ResolvedProfile = {\n id: 0,\n version: 1,\n defaultLocale: 'en',\n complianceGroup: 'general-privacy-consent',\n gpcMode: 'honor',\n cookies: [\n { id: 'necessary', legalBasis: 'mandatory' },\n { id: 'analytics', legalBasis: 'consent', listenGpc: true, expiry: 365 },\n { id: 'marketing', legalBasis: 'consent', listenGpc: true, expiry: 365 },\n { id: 'functional', legalBasis: 'consent', expiry: 365 },\n ],\n mainBanner: {\n position: 'bottom',\n heading: 'We value your privacy',\n htmlText:\n 'We use cookies and similar technologies to enhance your browsing experience, ' +\n 'personalise content and ads, and analyse our traffic. ' +\n 'By clicking <strong>Accept All</strong>, you consent to our use of these technologies. ' +\n 'You can change your preferences at any time. ' +\n 'See our <a href=\"/privacy-policy\">Privacy Policy</a> for more information.',\n buttons: [\n { text: 'Accept All', cookies: '*', style: 'primary', action: 'custom' },\n { text: 'Reject Optional', cookies: '!', style: 'secondary', action: 'custom' },\n { text: 'Manage Preferences', style: 'text', action: 'manage' },\n ],\n },\n gpcBanner: {\n position: 'bottom',\n heading: 'Your Privacy Preference Has Been Recognized',\n htmlText:\n 'Your browser sent a Global Privacy Control (GPC) signal indicating you prefer to ' +\n 'limit data collection. We have applied your preference. ' +\n 'You can review or update your choices at any time.',\n buttons: [\n { text: 'Review Preferences', style: 'primary', action: 'manage' },\n { text: 'Continue', style: 'secondary', action: 'submit' },\n ],\n },\n preferenceModal: {\n position: 'right',\n heading: 'Cookie Preferences',\n subheading: 'Choose which types of cookies you allow us to use. You can change these settings at any time.',\n htmlText:\n 'We use different types of cookies to optimise your experience on our website. ' +\n 'Click on the different category headings to find out more and change your default settings. ' +\n 'Note that blocking some types of cookies may impact your experience of the site ' +\n 'and the services we are able to offer.',\n showClose: true,\n overlayOpacity: 50,\n categories: [\n {\n id: 'cat-necessary',\n heading: 'Strictly Necessary',\n htmlText:\n 'These cookies are necessary for the website to function and cannot be switched off. ' +\n 'They are usually only set in response to actions you have taken such as setting your ' +\n 'privacy preferences, logging in, or filling in forms.',\n mandatory: true,\n cookies: ['necessary'],\n },\n {\n id: 'cat-functional',\n heading: 'Functional',\n htmlText:\n 'These cookies enable enhanced functionality and personalisation. They may be set ' +\n 'by us or by third parties whose services we have added to our pages.',\n cookies: ['functional'],\n },\n {\n id: 'cat-analytics',\n heading: 'Analytics',\n htmlText:\n 'These cookies help us understand how our website is being used, or how effective ' +\n 'our marketing campaigns are. All information these cookies collect is aggregated ' +\n 'and therefore anonymous.',\n cookies: ['analytics'],\n },\n {\n id: 'cat-marketing',\n heading: 'Marketing',\n htmlText:\n 'These cookies are used by our advertising partners to build a profile of your interests ' +\n 'and show you relevant advertisements on other sites. They work by uniquely identifying ' +\n 'your browser and device. If you do not allow these cookies, you will not experience ' +\n 'targeted advertising.',\n cookies: ['marketing'],\n },\n ],\n buttons: [\n { text: 'Accept All', cookies: '*', style: 'primary', action: 'custom' },\n { text: 'Reject Optional', cookies: '!', style: 'secondary', action: 'custom' },\n { text: 'Save My Choices', style: 'text', action: 'submit' },\n ],\n },\n}\n\nexport default profile\n","import type { ResolvedProfile } from '../types'\n\nconst profile: ResolvedProfile = {\n id: 0,\n version: 1,\n defaultLocale: 'en',\n complianceGroup: 'notice-only',\n gpcMode: 'ignore',\n cookies: [\n { id: 'necessary', legalBasis: 'mandatory' },\n { id: 'analytics', legalBasis: 'legitimate_interest', expiry: 365 },\n { id: 'functional', legalBasis: 'legitimate_interest', expiry: 365 },\n ],\n mainBanner: {\n position: 'bottom',\n heading: 'Cookie Notice',\n htmlText:\n 'This website uses cookies and similar technologies to ensure it works correctly and to ' +\n 'improve your experience. By continuing to browse this site, you acknowledge the use of ' +\n 'these technologies. For more information, please read our ' +\n '<a href=\"/privacy-policy\">Privacy Policy</a> and <a href=\"/cookie-policy\">Cookie Policy</a>.',\n buttons: [\n { text: 'Got It', style: 'primary', action: 'submit' },\n { text: 'Learn More', style: 'text', action: 'manage' },\n ],\n },\n gpcBanner: {\n position: 'bottom',\n heading: 'Cookie Notice',\n htmlText:\n 'This site uses cookies necessary for its operation and for improving your experience. ' +\n 'No consent is required in this jurisdiction. ' +\n 'See our <a href=\"/privacy-policy\">Privacy Policy</a> for details.',\n buttons: [\n { text: 'Acknowledge', style: 'primary', action: 'submit' },\n ],\n },\n preferenceModal: {\n position: 'right',\n heading: 'Cookie Information',\n subheading: 'This notice explains how and why we use cookies on this website.',\n htmlText:\n 'This website uses cookies to function correctly and to improve your experience. ' +\n 'In the jurisdiction where you are located, explicit consent for cookies may not be required. ' +\n 'However, we believe in transparency and provide this information so you understand how we ' +\n 'use cookies. Some cookies are strictly necessary for the site to work.',\n showClose: true,\n overlayOpacity: 40,\n categories: [\n {\n id: 'cat-necessary',\n heading: 'Strictly Necessary',\n htmlText:\n 'These cookies are required for the website to function correctly. Without these cookies, ' +\n 'some parts of the website will not work as intended. They cannot be disabled.',\n mandatory: true,\n cookies: ['necessary'],\n },\n {\n id: 'cat-functional',\n heading: 'Functional and Analytics',\n htmlText:\n 'These cookies help us deliver a better experience by remembering your preferences ' +\n 'and measuring how the site is used. They are used under a legitimate interest basis ' +\n 'and do not require your consent in this jurisdiction.',\n type: 'legitimate_interest',\n legitimateInterest: {\n enabled: true,\n description:\n 'We use functional and analytics cookies under a legitimate interest basis to ' +\n 'improve our service and to understand how users interact with our website. ' +\n 'This data is anonymised and does not identify you personally.',\n },\n cookies: ['functional', 'analytics'],\n },\n ],\n buttons: [\n { text: 'Acknowledge', style: 'primary', action: 'submit' },\n { text: 'Close', style: 'text', action: 'close' },\n ],\n },\n}\n\nexport default profile\n","import type { ResolvedProfile } from '../types'\n\nconst profile: ResolvedProfile = {\n id: 0,\n version: 1,\n defaultLocale: 'en',\n complianceGroup: 'opt-in',\n gpcMode: 'honor',\n cookies: [\n { id: 'necessary', legalBasis: 'mandatory' },\n { id: 'analytics', legalBasis: 'consent', listenGpc: true, expiry: 395 },\n { id: 'marketing', legalBasis: 'consent', listenGpc: true, expiry: 395 },\n { id: 'functional', legalBasis: 'consent', expiry: 395 },\n ],\n mainBanner: {\n position: 'bottom',\n heading: 'We use cookies',\n htmlText:\n 'We and our partners use cookies and similar technologies to provide you with a better browsing experience, ' +\n 'to analyse site traffic, and to personalise content and advertisements. ' +\n 'Please give your consent to our use of cookies as described in our ' +\n '<a href=\"/privacy-policy\">Privacy Policy</a>. ' +\n 'You may withdraw your consent at any time.',\n buttons: [\n { text: 'Accept All', cookies: '*', style: 'primary', action: 'custom' },\n { text: 'Reject All', cookies: '!', style: 'secondary', action: 'custom' },\n { text: 'Manage Preferences', style: 'text', action: 'manage' },\n ],\n },\n gpcBanner: {\n position: 'bottom',\n heading: 'Your Privacy Preference Has Been Detected',\n htmlText:\n 'Your browser or device has sent a Global Privacy Control (GPC) signal indicating you prefer ' +\n 'to limit the sale or sharing of your personal data. As required under applicable law, ' +\n 'we have applied your preference. You can review or update your choices at any time.',\n buttons: [\n { text: 'Manage Preferences', style: 'primary', action: 'manage' },\n { text: 'Continue', style: 'secondary', action: 'submit' },\n ],\n },\n preferenceModal: {\n position: 'right',\n heading: 'Privacy Preferences',\n subheading: 'Manage your cookie preferences below. Strictly necessary cookies cannot be disabled.',\n htmlText:\n 'We use different types of cookies to optimise your experience on our website. ' +\n 'You can choose which categories to allow. Your choices are saved for the duration indicated ' +\n 'for each category and can be changed at any time.',\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 and cannot be disabled. ' +\n 'They are set in response to actions you take such as logging in, filling in forms, or ' +\n 'setting privacy preferences. They do not store personally identifiable information.',\n mandatory: true,\n cookies: ['necessary'],\n },\n {\n id: 'cat-functional',\n heading: 'Functional',\n htmlText:\n 'These cookies enable enhanced functionality and personalisation, such as remembering ' +\n 'your language preference or region. They may be set by us or by third-party providers ' +\n 'whose services we have added to our pages.',\n cookies: ['functional'],\n },\n {\n id: 'cat-analytics',\n heading: 'Analytics',\n htmlText:\n 'These cookies help us understand how visitors interact with our site by collecting and ' +\n 'reporting information anonymously. This data helps us improve our content and navigation. ' +\n 'No personally identifiable information is collected.',\n cookies: ['analytics'],\n },\n {\n id: 'cat-marketing',\n heading: 'Marketing',\n htmlText:\n 'These cookies track your online activity to help advertisers deliver more relevant ' +\n 'advertising to you, or to limit how many times you see an advertisement. They may be ' +\n 'shared with other organisations or advertisers. Declining these cookies will not reduce ' +\n 'the number of advertisements you see.',\n cookies: ['marketing'],\n },\n ],\n buttons: [\n { text: 'Accept All', cookies: '*', style: 'primary', action: 'custom' },\n { text: 'Reject All', cookies: '!', style: 'secondary', action: 'custom' },\n { text: 'Save My Choices', style: 'text', action: 'submit' },\n ],\n },\n}\n\nexport default profile\n","import type { ResolvedProfile } from '../types'\n\nconst profile: ResolvedProfile = {\n id: 0,\n version: 1,\n defaultLocale: 'en',\n complianceGroup: 'opt-in-brazil',\n gpcMode: 'honor',\n cookies: [\n { id: 'necessary', legalBasis: 'mandatory' },\n { id: 'analytics', legalBasis: 'consent', listenGpc: true, expiry: 365 },\n { id: 'marketing', legalBasis: 'consent', listenGpc: true, expiry: 365 },\n { id: 'functional', legalBasis: 'consent', expiry: 365 },\n ],\n mainBanner: {\n position: 'bottom',\n heading: 'Your Privacy Matters — LGPD Notice',\n htmlText:\n 'In accordance with the Lei Geral de Proteção de Dados (LGPD — Brazilian General Data ' +\n 'Protection Law), we request your consent before collecting and processing personal data ' +\n 'beyond what is strictly necessary. As a data subject (titular), you have the right to ' +\n 'access, correct, delete, and withdraw consent at any time. ' +\n 'Read our <a href=\"/privacy-policy\">Privacy Policy</a> for full details.',\n buttons: [\n { text: 'Accept All', cookies: '*', style: 'primary', action: 'custom' },\n { text: 'Reject All', cookies: '!', style: 'secondary', action: 'custom' },\n { text: 'Manage Preferences', style: 'text', action: 'manage' },\n ],\n },\n gpcBanner: {\n position: 'bottom',\n heading: 'Your Privacy Signal Was Detected',\n htmlText:\n 'A Global Privacy Control (GPC) signal was detected from your browser. We have applied ' +\n 'your preference in accordance with applicable data protection law. ' +\n 'You may manage your choices at any time.',\n buttons: [\n { text: 'Manage Preferences', style: 'primary', action: 'manage' },\n { text: 'Continue', style: 'secondary', action: 'submit' },\n ],\n },\n preferenceModal: {\n position: 'right',\n heading: 'Privacy Preferences — LGPD',\n subheading:\n 'Under the LGPD, you have the right to give, manage, or withdraw consent for each purpose ' +\n 'of personal data processing. You may update your choices at any time.',\n htmlText:\n 'As a titular (data subject) under the Lei Geral de Proteção de Dados (LGPD), you have the ' +\n 'right to: confirm the existence of processing, access your data, correct incomplete or ' +\n 'inaccurate data, anonymise or delete unnecessary data, and withdraw consent. ' +\n 'Use the controls below to set your preferences.',\n showClose: true,\n overlayOpacity: 50,\n categories: [\n {\n id: 'cat-necessary',\n heading: 'Strictly Necessary',\n htmlText:\n 'Processing under this purpose is indispensable for the operation of our services. ' +\n 'This is based on the legal bases of contract performance and legal obligation under ' +\n 'Article 7 of the LGPD.',\n mandatory: true,\n cookies: ['necessary'],\n },\n {\n id: 'cat-functional',\n heading: 'Functional',\n htmlText:\n 'These cookies allow us to remember your preferences and provide a personalised experience. ' +\n 'Your consent enables this functionality.',\n cookies: ['functional'],\n },\n {\n id: 'cat-analytics',\n heading: 'Analytics',\n htmlText:\n 'With your consent, we collect statistical data about your use of our services to ' +\n 'improve our offering. Data is anonymised where possible and is not used to identify you.',\n cookies: ['analytics'],\n },\n {\n id: 'cat-marketing',\n heading: 'Marketing',\n htmlText:\n 'With your consent, we and our partners process your personal data to deliver ' +\n 'personalised advertising content. You may withdraw this consent at any time, ' +\n 'and withdrawal will not affect the lawfulness of prior processing.',\n cookies: ['marketing'],\n },\n ],\n buttons: [\n { text: 'Accept All', cookies: '*', style: 'primary', action: 'custom' },\n { text: 'Reject All', cookies: '!', style: 'secondary', action: 'custom' },\n { text: 'Save My Choices', style: 'text', action: 'submit' },\n ],\n },\n}\n\nexport default profile\n","import type { ResolvedProfile } from '../types'\n\nconst profile: ResolvedProfile = {\n id: 0,\n version: 1,\n defaultLocale: 'en',\n complianceGroup: 'opt-in-china',\n gpcMode: 'ignore',\n cookies: [\n { id: 'necessary', legalBasis: 'mandatory' },\n { id: 'analytics', legalBasis: 'consent', expiry: 365 },\n { id: 'marketing', legalBasis: 'consent', expiry: 365 },\n { id: 'functional', legalBasis: 'consent', expiry: 365 },\n ],\n mainBanner: {\n position: 'bottom',\n heading: 'Personal Information Processing Notice',\n htmlText:\n 'In accordance with the Personal Information Protection Law of the People\\'s Republic of ' +\n 'China (PIPL), we hereby inform you of the types of personal information we collect, ' +\n 'the purposes for which it is used, and your rights as a personal information subject. ' +\n 'Your consent is required before we may process your personal information beyond what is ' +\n 'strictly necessary. Please read our <a href=\"/privacy-policy\">Privacy Policy</a> carefully.',\n buttons: [\n { text: 'Agree to All', cookies: '*', style: 'primary', action: 'custom' },\n { text: 'Reject Non-Necessary', cookies: '!', style: 'secondary', action: 'custom' },\n { text: 'Manage Preferences', style: 'text', action: 'manage' },\n ],\n },\n gpcBanner: {\n position: 'bottom',\n heading: 'Privacy Notice',\n htmlText:\n 'We have detected a privacy preference signal. We have applied your preferences for ' +\n 'the processing of your personal information. You may review and update your choices at any time.',\n buttons: [\n { text: 'Manage Preferences', style: 'primary', action: 'manage' },\n { text: 'Continue', style: 'secondary', action: 'submit' },\n ],\n },\n preferenceModal: {\n position: 'right',\n heading: 'Personal Information Processing Preferences',\n subheading:\n 'Under the PIPL, you have the right to know, decide, access, copy, correct, delete, and ' +\n 'withdraw consent for the processing of your personal information. Please select your preferences.',\n htmlText:\n 'We process personal information lawfully, legitimately, and necessarily in accordance ' +\n 'with the PIPL. Your personal information is stored and processed within the territory of ' +\n 'China. Cross-border transfer of your data, if any, is handled in accordance with Chapter III ' +\n 'of the PIPL. You may withdraw your consent at any time by updating these settings.',\n showClose: true,\n overlayOpacity: 50,\n categories: [\n {\n id: 'cat-necessary',\n heading: 'Strictly Necessary',\n htmlText:\n 'Processing under this purpose is required for the normal functioning of our services ' +\n 'as requested by you. This is processed under the legitimate basis of contract ' +\n 'performance and legal obligation under the PIPL.',\n mandatory: true,\n cookies: ['necessary'],\n },\n {\n id: 'cat-functional',\n heading: 'Functional',\n htmlText:\n 'These cookies allow us to remember your preferences and settings to provide you with ' +\n 'an enhanced experience. Your consent allows us to personalise our service for you.',\n cookies: ['functional'],\n },\n {\n id: 'cat-analytics',\n heading: 'Statistical Analysis',\n htmlText:\n 'With your consent, we collect usage data to analyse how our services are used, ' +\n 'enabling us to improve performance and user experience. Data is anonymised where possible.',\n cookies: ['analytics'],\n },\n {\n id: 'cat-marketing',\n heading: 'Marketing and Commercial Purposes',\n htmlText:\n 'With your consent, we use your personal information to deliver personalised ' +\n 'commercial information and advertisements. You may withdraw this consent at any time.',\n cookies: ['marketing'],\n },\n ],\n buttons: [\n { text: 'Agree and Continue', cookies: '*', style: 'primary', action: 'custom' },\n { text: 'Only Necessary', cookies: '!', style: 'secondary', action: 'custom' },\n { text: 'Save My Choices', style: 'text', action: 'submit' },\n ],\n },\n}\n\nexport default profile\n","import type { ResolvedProfile } from '../types'\n\nconst profile: ResolvedProfile = {\n id: 0,\n version: 1,\n defaultLocale: 'en',\n complianceGroup: 'opt-in-dpdpa',\n gpcMode: 'honor',\n cookies: [\n { id: 'necessary', legalBasis: 'mandatory' },\n { id: 'analytics', legalBasis: 'consent', listenGpc: true, expiry: 365 },\n { id: 'marketing', legalBasis: 'consent', listenGpc: true, expiry: 365 },\n { id: 'functional', legalBasis: 'consent', expiry: 365 },\n ],\n mainBanner: {\n position: 'bottom',\n heading: 'We Need Your Consent',\n htmlText:\n 'In accordance with the Digital Personal Data Protection Act, 2023 (DPDPA) of India, ' +\n 'we require your explicit consent before collecting and processing your personal data. ' +\n 'You have the right to withdraw consent at any time and to access, correct, or erase your data. ' +\n 'Please review our <a href=\"/privacy-policy\">Privacy Policy</a> for details.',\n buttons: [\n { text: 'Accept All', cookies: '*', style: 'primary', action: 'custom' },\n { text: 'Reject All', cookies: '!', style: 'secondary', action: 'custom' },\n { text: 'Manage Preferences', style: 'text', action: 'manage' },\n ],\n },\n gpcBanner: {\n position: 'bottom',\n heading: 'Privacy Preference Detected',\n htmlText:\n 'A privacy preference signal was detected from your browser. We have applied your opt-out ' +\n 'preference in accordance with applicable privacy regulations. You may review and update ' +\n 'your consent choices at any time.',\n buttons: [\n { text: 'Manage Preferences', style: 'primary', action: 'manage' },\n { text: 'Continue', style: 'secondary', action: 'submit' },\n ],\n },\n preferenceModal: {\n position: 'right',\n heading: 'Consent Preferences — DPDPA',\n subheading:\n 'Under the Digital Personal Data Protection Act (DPDPA), you have the right to give, ' +\n 'withdraw, or manage your consent for each purpose of data processing. Please select ' +\n 'your preferences below.',\n htmlText:\n 'As a Data Principal under the DPDPA, you have the right to access, correct, erase, and ' +\n 'withdraw consent for your personal data. This notice is provided by the Data Fiduciary ' +\n 'as required under Section 5 of the DPDPA. You can update your preferences at any time.',\n showClose: true,\n overlayOpacity: 50,\n categories: [\n {\n id: 'cat-necessary',\n heading: 'Necessary Processing',\n htmlText:\n 'Processing your personal data under this purpose is necessary for the delivery of ' +\n 'the service you have requested. This processing is exempt from consent requirements ' +\n 'under Section 7(a) of the DPDPA.',\n mandatory: true,\n cookies: ['necessary'],\n },\n {\n id: 'cat-functional',\n heading: 'Functional Enhancements',\n htmlText:\n 'We process limited personal data to remember your preferences and settings, ' +\n 'improving your experience on return visits. Your consent enables this personalisation.',\n cookies: ['functional'],\n },\n {\n id: 'cat-analytics',\n heading: 'Analytics and Improvement',\n htmlText:\n 'With your consent, we collect data about how you use our services in order to ' +\n 'improve them. The data is anonymised where possible and is not shared with third parties ' +\n 'for commercial purposes.',\n cookies: ['analytics'],\n },\n {\n id: 'cat-marketing',\n heading: 'Marketing and Personalised Advertising',\n htmlText:\n 'With your consent, we and our advertising partners process your personal data to ' +\n 'deliver advertisements tailored to your interests. You may withdraw this consent ' +\n 'at any time without affecting the lawfulness of prior processing.',\n cookies: ['marketing'],\n },\n ],\n buttons: [\n { text: 'Give Consent to All', cookies: '*', style: 'primary', action: 'custom' },\n { text: 'Withdraw All Consent', cookies: '!', style: 'secondary', action: 'custom' },\n { text: 'Save My Choices', style: 'text', action: 'submit' },\n ],\n },\n}\n\nexport default profile\n","import type { ResolvedProfile } from '../types'\n\nconst profile: ResolvedProfile = {\n id: 0,\n version: 1,\n defaultLocale: 'en',\n complianceGroup: 'opt-out',\n gpcMode: 'honor',\n cookies: [\n { id: 'necessary', legalBasis: 'mandatory' },\n { id: 'analytics', legalBasis: 'consent', listenGpc: true, expiry: 365 },\n { id: 'marketing', legalBasis: 'consent', listenGpc: true, expiry: 365, cpraCategory: 'sale' },\n { id: 'functional', legalBasis: 'consent', expiry: 365 },\n ],\n mainBanner: {\n position: 'bottom',\n heading: 'Your Privacy Choices',\n htmlText:\n 'We use cookies and similar technologies on this site. Some help us operate the site, ' +\n 'while others are used for analytics and advertising purposes. ' +\n 'You may opt out of the sale or sharing of your personal information at any time. ' +\n 'See our <a href=\"/privacy-policy\">Privacy Policy</a> for more information.',\n buttons: [\n { text: 'Accept All', cookies: '*', style: 'primary', action: 'custom' },\n { text: 'Do Not Sell or Share My Personal Information', cookies: '!', style: 'secondary', action: 'custom' },\n { text: 'Manage Preferences', style: 'text', action: 'manage' },\n ],\n },\n gpcBanner: {\n position: 'bottom',\n heading: 'Your Opt-Out Preference Has Been Applied',\n htmlText:\n 'We detected a Global Privacy Control (GPC) signal from your browser. ' +\n 'In compliance with applicable privacy law, we have opted you out of the sale ' +\n 'and sharing of your personal information. You can review or update your preferences below.',\n buttons: [\n { text: 'Manage Preferences', style: 'primary', action: 'manage' },\n { text: 'Continue', style: 'secondary', action: 'submit' },\n ],\n },\n preferenceModal: {\n position: 'right',\n heading: 'Privacy Preferences',\n subheading:\n 'You have the right to opt out of the sale and sharing of your personal information. ' +\n 'Toggle the categories below to set your preferences.',\n htmlText:\n 'We use different types of cookies and tracking technologies. Strictly necessary cookies ' +\n 'are always active. For all other categories, you can choose to opt out at any time.',\n showClose: true,\n overlayOpacity: 50,\n categories: [\n {\n id: 'cat-necessary',\n heading: 'Strictly Necessary',\n htmlText:\n 'These cookies are required for the website to operate. They include cookies that ' +\n 'enable you to log in, use shopping carts, and access secure areas of the website.',\n mandatory: true,\n cookies: ['necessary'],\n },\n {\n id: 'cat-functional',\n heading: 'Functional',\n htmlText:\n 'These cookies allow the website to remember choices you make (such as your user name, ' +\n 'language, or the region you are in) and provide enhanced, more personal features.',\n cookies: ['functional'],\n },\n {\n id: 'cat-analytics',\n heading: 'Analytics',\n htmlText:\n 'These cookies collect information about how visitors use our website, such as which ' +\n 'pages they visit most often. This data helps us improve the site and your experience.',\n cookies: ['analytics'],\n },\n {\n id: 'cat-marketing',\n heading: 'Sale / Sharing of Personal Information',\n htmlText:\n 'These cookies and trackers are used to deliver targeted advertising to you, and may ' +\n 'constitute a \"sale\" or \"sharing\" of personal information under the California Consumer ' +\n 'Privacy Act (CCPA). You have the right to opt out of this activity.',\n cookies: ['marketing'],\n },\n ],\n buttons: [\n { text: 'Accept All', cookies: '*', style: 'primary', action: 'custom' },\n { text: 'Opt Out of Sale/Sharing', cookies: '!', style: 'secondary', action: 'custom' },\n { text: 'Save My Choices', style: 'text', action: 'submit' },\n ],\n },\n}\n\nexport default profile\n","import type { ResolvedProfile } from '../types'\n\nconst profile: ResolvedProfile = {\n id: 0,\n version: 1,\n defaultLocale: 'en',\n complianceGroup: 'opt-out-strict',\n gpcMode: 'strict',\n cookies: [\n { id: 'necessary', legalBasis: 'mandatory' },\n { id: 'analytics', legalBasis: 'consent', listenGpc: true, expiry: 365 },\n {\n id: 'marketing',\n legalBasis: 'consent',\n listenGpc: true,\n expiry: 365,\n cpraCategory: 'sale',\n },\n {\n id: 'sensitive',\n legalBasis: 'consent',\n listenGpc: true,\n expiry: 365,\n cpraCategory: 'sensitive',\n },\n { id: 'functional', legalBasis: 'consent', expiry: 365 },\n ],\n mainBanner: {\n position: 'bottom',\n heading: 'Your Privacy Rights (CPRA)',\n htmlText:\n 'Under the California Privacy Rights Act (CPRA), you have the right to opt out of the ' +\n 'sale, sharing, and use of your sensitive personal information. ' +\n 'We also honor Global Privacy Control (GPC) signals automatically. ' +\n 'See our <a href=\"/privacy-policy\">Privacy Policy</a> and ' +\n '<a href=\"/do-not-sell\">Do Not Sell or Share My Personal Information</a> page.',\n buttons: [\n { text: 'Accept All', cookies: '*', style: 'primary', action: 'custom' },\n { text: 'Opt Out of Sale, Sharing & Sensitive Use', cookies: '!', style: 'secondary', action: 'custom' },\n { text: 'Manage Preferences', style: 'text', action: 'manage' },\n ],\n },\n gpcBanner: {\n position: 'bottom',\n heading: 'GPC Signal Detected — Opt-Out Applied',\n htmlText:\n 'A Global Privacy Control (GPC) opt-out signal was detected. As required by CPRA, ' +\n 'we have automatically applied your opt-out of the sale and sharing of your personal ' +\n 'information and the use of your sensitive personal information. ' +\n 'Your preference has been saved.',\n buttons: [\n { text: 'Manage Preferences', style: 'primary', action: 'manage' },\n { text: 'Acknowledge', style: 'secondary', action: 'submit' },\n ],\n },\n preferenceModal: {\n position: 'right',\n heading: 'Privacy Preferences (CPRA)',\n subheading:\n 'Manage your privacy choices. You may opt out of the sale, sharing, and sensitive use ' +\n 'of your personal information at any time.',\n htmlText:\n 'The California Privacy Rights Act (CPRA) provides you with additional rights over your ' +\n 'personal information. GPC signals are honored automatically. You can update your choices below.',\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 and are always active. ' +\n 'They are exempt from CPRA opt-out requirements.',\n mandatory: true,\n cookies: ['necessary'],\n },\n {\n id: 'cat-functional',\n heading: 'Functional',\n htmlText:\n 'These cookies personalise your experience (e.g. language, region preferences). ' +\n 'They do not involve the sale or sharing of personal information.',\n cookies: ['functional'],\n },\n {\n id: 'cat-analytics',\n heading: 'Analytics',\n htmlText:\n 'These cookies measure site performance and visitor behaviour. Data is used internally ' +\n 'to improve our services. Opting out stops this collection.',\n cookies: ['analytics'],\n },\n {\n id: 'cat-marketing',\n heading: 'Sale and Sharing of Personal Information',\n htmlText:\n 'These technologies may constitute a \"sale\" or \"sharing\" of your personal information ' +\n 'under CPRA, including cross-context behavioural advertising. ' +\n 'You have the right to opt out.',\n cookies: ['marketing'],\n },\n {\n id: 'cat-sensitive',\n heading: 'Use of Sensitive Personal Information',\n htmlText:\n 'Some technologies may use sensitive personal information as defined by CPRA ' +\n '(e.g. precise geolocation, health data). You have the right to limit such use.',\n cookies: ['sensitive'],\n },\n ],\n buttons: [\n { text: 'Accept All', cookies: '*', style: 'primary', action: 'custom' },\n { text: 'Opt Out of All Non-Necessary', cookies: '!', style: 'secondary', action: 'custom' },\n { text: 'Save My Choices', style: 'text', action: 'submit' },\n ],\n },\n}\n\nexport default profile\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 hidePoweredBy = false,\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 const pb = document.createElement('a')\n pb.href = 'https://consenti.dev'\n pb.className = 'consenti-banner__powered-by'\n pb.textContent = 'Powered by Consenti'\n pb.setAttribute('target', '_blank')\n pb.setAttribute('rel', 'noopener noreferrer')\n if (hidePoweredBy) {\n pb.classList.add('consenti-d-none')\n }\n banner.appendChild(pb)\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 /** Screen width (px) below which the modal goes full-screen. 0 = disabled. */\n private mobileBreakpoint = 576\n /** Saved body overflow value — restored when modal closes. */\n private savedBodyOverflow: string | undefined = undefined\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 hidePoweredBy = false,\n ): void {\n this.mobileBreakpoint = config.mobileFullScreenBreakpoint ?? 576\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 const body = document.createElement('div')\n body.className = 'consenti-modal__body'\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 if (config.subheading) {\n const sub = document.createElement('p')\n sub.className = 'consenti-modal__subheading'\n sub.textContent = config.subheading\n header.appendChild(sub)\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.htmlText) {\n const desc = document.createElement('div')\n desc.className = 'consenti-modal__text'\n desc.innerHTML = config.htmlText\n body.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 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 body.appendChild(this.buildReceiptOption())\n }\n\n if (dpdpaConfig) {\n const notice = document.createElement('div')\n notice.className = 'consenti-modal__dpdpa-notice'\n const esc = (s: string) => s.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/\"/g, '"').replace(/'/g, ''')\n const safeEmail = dpdpaConfig.grievanceEmail.includes('@') && !dpdpaConfig.grievanceEmail.toLowerCase().startsWith('javascript:')\n ? dpdpaConfig.grievanceEmail : ''\n notice.innerHTML = [\n `<strong>Data Fiduciary:</strong> ${esc(dpdpaConfig.dataFiduciary)}.`,\n dpdpaConfig.purposeDescription ? ` <strong>Purpose:</strong> ${esc(dpdpaConfig.purposeDescription)}.` : '',\n ` You may withdraw consent at any time or contact our Grievance Officer at`,\n safeEmail ? ` <a href=\"mailto:${esc(safeEmail)}\">${esc(safeEmail)}</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 const pb = document.createElement('a')\n pb.href = 'https://consenti.dev'\n pb.className = 'consenti-modal__powered-by'\n pb.textContent = 'Powered by Consenti'\n pb.setAttribute('target', '_blank')\n pb.setAttribute('rel', 'noopener noreferrer')\n if (hidePoweredBy) {\n pb.classList.add('consenti-d-none')\n }\n body.appendChild(pb)\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 checkIcon = document.createElement('span')\n checkIcon.className = 'consenti-category__toggle-icon consenti-category__toggle-icon--check'\n checkIcon.setAttribute('aria-hidden', 'true')\n checkIcon.innerHTML = '<svg viewBox=\"0 0 10 10\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M1.5 5L4 7.5L8.5 2.5\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/></svg>'\n toggle.appendChild(checkIcon)\n\n const crossIcon = document.createElement('span')\n crossIcon.className = 'consenti-category__toggle-icon consenti-category__toggle-icon--cross'\n crossIcon.setAttribute('aria-hidden', 'true')\n crossIcon.innerHTML = '<svg viewBox=\"0 0 10 10\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M2 2L8 8M8 2L2 8\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\"/></svg>'\n toggle.appendChild(crossIcon)\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 const isFullscreen = this.mobileBreakpoint > 0 && window.innerWidth <= this.mobileBreakpoint\n if (isFullscreen) {\n this.el.classList.add('consenti-modal--fullscreen')\n } else {\n this.el.classList.remove('consenti-modal--fullscreen')\n }\n document.body.appendChild(this.el)\n // Lock page scroll when the modal has an overlay or is in mobile full-screen mode.\n // Skip when passthrough and not full-screen — the user can still scroll the page.\n if (!this.passthrough || isFullscreen) {\n this.savedBodyOverflow = document.body.style.overflow\n document.body.style.overflow = 'hidden'\n }\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 if (this.savedBodyOverflow !== undefined) {\n document.body.style.overflow = this.savedBodyOverflow\n this.savedBodyOverflow = undefined\n }\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 if (this.savedBodyOverflow !== undefined) {\n document.body.style.overflow = this.savedBodyOverflow\n this.savedBodyOverflow = undefined\n }\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 * compliance: { type: 'opt-in' },\n * })\n * await widget.ready\n * console.log(widget.hasConsent())\n * ```\n */\n\nimport type {\n ConsentiConfig,\n ConsentValue,\n ConsentAction,\n ConsentEvent,\n ResolvedProfile,\n ConsentiPlugin,\n ConsentiWidgetAPI,\n ConsentiMessage,\n ConsentiEventName,\n ThemeConfig,\n DeepPartial,\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'\nimport { deepMerge } from '../utils/locale'\nimport { logger, initConsole } from '../utils/console'\n\nexport class ConsentiSetup implements ConsentiWidgetAPI {\n private initialized = false\n private initializing = false\n private profile: ResolvedProfile | null = null\n private resolvedBase: 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 /** Maps original handler functions to their DOM-wrapped listeners for on/off bookkeeping. */\n private _eventHandlers = new Map<\n (data: ConsentEvent) => void,\n Array<{ name: string; wrapped: EventListener }>\n >()\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 initConsole(config.core.console ?? ['error'])\n\n if (config.api?.enabled && config.core.storage === 'localStorage') {\n logger.warn(\n '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) => logger.warn('Init error:', err))\n }\n }\n\n private profileId(): number {\n return this.profile?.id ?? 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 const resolvedProfile = await resolveProfile(this.config)\n this.resolvedBase = resolvedProfile\n\n // Apply widget-level config overrides on top of the resolved profile\n if (this.config.darkMode !== undefined) resolvedProfile.darkMode = this.config.darkMode\n\n const profile = this.config.profileOverride\n ? deepMerge<ResolvedProfile>(resolvedProfile, this.config.profileOverride)\n : resolvedProfile\n this.profile = profile\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.consentStore.setProfileCookies(profile.cookies)\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 = profile.cookieSigningKey ?? this.config.core.cookieSigningKey\n\n const existing =\n 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 — widget config overrides profile JSON when explicitly set\n const profileGpcMode = profile.gpcMode\n const gpcMode: boolean | 'strict' =\n this.config.core.autoHonorGPC !== undefined\n ? this.config.core.autoHonorGPC\n : profileGpcMode === 'strict' ? 'strict'\n : profileGpcMode === 'honor' ? true\n : false\n if (gpcMode && detectGPC()) {\n const gpcConsent = applyGPCToConsent(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 = profile.cookieSigningKey ?? this.config.core.cookieSigningKey\n if (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 // Derive opt-out mode from profile complianceGroup (replaces old core.regulation field)\n const compGroup = profile.complianceGroup ?? ''\n const isOptOut = compGroup === 'opt-out'\n const isOptOutStrict = compGroup === 'opt-out-strict'\n\n // CCPA opt-out: silently write all-granted on first visit; no banner.\n if (isOptOut && !hasExisting) {\n const defaultConsent: ConsentValue = {}\n for (const cookie of profile.cookies) {\n defaultConsent[cookie.id] = 'granted'\n }\n const ccpaSigKey = profile.cookieSigningKey ?? this.config.core.cookieSigningKey\n if (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 (isOptOutStrict && !hasExisting) {\n const gpcActive = this.config.core.autoHonorGPC && detectGPC()\n const defaultConsent: ConsentValue = {}\n for (const cookie of profile.cookies) {\n if (cookie.mandatory) { defaultConsent[cookie.id] = 'granted'; continue }\n if (cookie.cpraCategory === 'sensitive') {\n defaultConsent[cookie.id] = 'denied'\n } else if (gpcActive && (cookie.cpraCategory === 'sale' || cookie.cpraCategory === 'sharing')) {\n defaultConsent[cookie.id] = 'denied'\n } else {\n defaultConsent[cookie.id] = 'granted'\n }\n }\n const sigKey = profile.cookieSigningKey ?? this.config.core.cookieSigningKey\n if (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 logger.warn('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 logger.warn('Plugin hook failed:', err)\n }\n }\n }\n\n private _buildGrantAllConsent(onlyMandatory = false): ConsentValue {\n const consent: ConsentValue = {}\n for (const cookie of this.profile?.cookies ?? []) {\n consent[cookie.id] = onlyMandatory && !cookie.mandatory ? 'denied' : 'granted'\n }\n return consent\n }\n\n private _buildDenyAllConsent(includingMandatory = false): ConsentValue {\n const consent: ConsentValue = {}\n for (const cookie of this.profile?.cookies ?? []) {\n consent[cookie.id] = !includingMandatory && cookie.mandatory ? 'granted' : 'denied'\n }\n return consent\n }\n\n private buildHandlers(context: 'banner' | 'modal'): ButtonClickHandler {\n return {\n onGrantAll: () => {\n void this.submitConsent(this._buildGrantAllConsent())\n if (context === 'banner') { this.banner?.hide(); this._bannerVariant = null }\n },\n onDenyAll: () => {\n void this.submitConsent(this._buildDenyAllConsent())\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 compGroup = this.profile.complianceGroup ?? ''\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 (compGroup === 'opt-out-strict') {\n const fallback = cookie.cpraCategory === 'sensitive' ? 'denied' : 'granted'\n fullConsent[cookie.id] = consent[cookie.id] ?? fallback\n } else {\n const defaultDenied = compGroup !== 'opt-out' && compGroup !== 'notice-only'\n fullConsent[cookie.id] = consent[cookie.id] ?? (defaultDenied ? 'denied' : 'granted')\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 profileUuid: this.profile?.profileUuid,\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 logger.warn('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 (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 undefined,\n locales,\n activeLocale,\n (locale) => this.switchLocale(locale),\n this.config.hidePoweredBy ?? false,\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 dpdpaCfg = this.profile.complianceGroup === 'opt-in-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 !(this.config.hidePoweredBy ?? false),\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 logger.warn('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) => logger.warn('Locale switch error:', err))\n }\n\n /**\n * Returns `true` if the named cookie's consent is `'granted'`.\n * When `requestValue` is `true`, returns the raw `ConsentStatus` string instead of a boolean,\n * or `false` if the cookie is not in the consent map.\n */\n isCookieGranted(cookieId: string, requestValue = false): boolean | 'granted' | 'denied' | 'objected' {\n if (!isClient()) return false\n const status = this.getConsent()?.[cookieId]\n if (requestValue) return status ?? false\n return status === 'granted'\n }\n\n /**\n * Checks consent for all cookies belonging to the given category.\n *\n * With `requestValue = false` (default): returns `true` only when every cookie in the\n * category is `'granted'`, `false` otherwise.\n *\n * With `requestValue = true`: returns an array of one-key records — one per cookie in the\n * category — e.g. `[{ analytics: 'granted' }, { pixel: 'denied' }]`.\n *\n * Returns `false` / `[]` if the category is not found or the widget is not yet initialized.\n */\n isCategoryGranted(categoryId: string, requestValue = false): boolean | { [cookieId: string]: 'granted' | 'denied' | 'objected' }[] {\n if (!isClient() || !this.profile) return requestValue ? [] : false\n const category = this.profile.preferenceModal.categories.find((c) => c.id === categoryId)\n if (!category) return requestValue ? [] : false\n\n const consent = this.getConsent()\n\n if (requestValue) {\n return category.cookies.map((cookieId) => ({\n [cookieId]: (consent?.[cookieId] ?? 'denied') as 'granted' | 'denied' | 'objected',\n }))\n }\n\n return category.cookies.every((cookieId) => consent?.[cookieId] === 'granted')\n }\n\n /**\n * Programmatically accepts cookies and hides the banner.\n *\n * @param onlyMandatory - When `true`, only mandatory cookies are granted and all others\n * are set to `'denied'`. Defaults to `false` (grant everything).\n */\n async grantAll(onlyMandatory = false): Promise<void> {\n if (!isClient() || !this.profile) return\n await this.submitConsent(this._buildGrantAllConsent(onlyMandatory))\n }\n\n /**\n * Programmatically denies cookies and hides the banner.\n *\n * @param includingMandatory - When `true`, mandatory cookies are also denied. Defaults to\n * `false` (mandatory cookies remain `'granted'`). Passing `true`\n * logs a warning — ensure this is intentional.\n */\n async denyAll(includingMandatory = false): Promise<void> {\n if (!isClient() || !this.profile) return\n if (includingMandatory) {\n logger.warn('denyAll called with includingMandatory=true — mandatory cookies will be denied. Ensure this is intentional.')\n }\n await this.submitConsent(this._buildDenyAllConsent(includingMandatory))\n }\n\n /**\n * Subscribes to a Consenti widget event. The `consenti:` prefix is optional —\n * both `'consentSubmitted'` and `'consenti:consentSubmitted'` are accepted.\n *\n * The same handler reference must be passed to `off()` to unsubscribe.\n *\n * @example\n * ```ts\n * const handler = (data) => console.log(data)\n * widget.on('consentSubmitted', handler)\n * // later:\n * widget.off('consentSubmitted', handler)\n * ```\n */\n on(event: ConsentiEventName, handler: (data: ConsentEvent) => void): void {\n if (!isClient()) return\n const name = event.startsWith('consenti:') ? event : `consenti:${event}`\n const wrapped: EventListener = (e) => handler((e as CustomEvent<ConsentEvent>).detail)\n const existing = this._eventHandlers.get(handler) ?? []\n existing.push({ name, wrapped })\n this._eventHandlers.set(handler, existing)\n window.addEventListener(name, wrapped)\n }\n\n /**\n * Removes a previously registered event handler. The same function reference passed to\n * `on()` must be used here.\n */\n off(event: ConsentiEventName, handler: (data: ConsentEvent) => void): void {\n if (!isClient()) return\n const name = event.startsWith('consenti:') ? event : `consenti:${event}`\n const entries = this._eventHandlers.get(handler)\n if (!entries) return\n const idx = entries.findIndex((e) => e.name === name)\n if (idx === -1) return\n const entry = entries[idx]\n if (entry) window.removeEventListener(name, entry.wrapped)\n entries.splice(idx, 1)\n if (entries.length === 0) this._eventHandlers.delete(handler)\n }\n\n /**\n * Returns the current version of the package and the active profile/consent versions.\n * Useful for debugging and support diagnostics.\n */\n version(): { package: string; profileVersion: number | null; consentVersion: number } {\n return {\n package: __CONSENTI_VERSION__,\n profileVersion: this.profile?.version ?? null,\n consentVersion: 1,\n }\n }\n\n /**\n * Toggles or sets dark mode at runtime without re-initialising the widget.\n * When `enable` is omitted, the current dark mode state is toggled.\n */\n setDarkMode(enable?: boolean): void {\n if (!isClient()) return\n this.config.darkMode = enable === undefined ? !this.config.darkMode : enable\n this.applyDarkMode()\n const isDark = this.config.darkMode ?? false\n document.getElementById('consenti-modal')?.classList.toggle('consenti-root--dark', isDark)\n }\n\n /**\n * Applies partial theme token overrides at runtime, merging them into the current theme.\n * CSS custom properties on the root element are updated immediately.\n */\n setTheme(theme: Partial<ThemeConfig>): void {\n if (!isClient()) return\n this.config.core.theme = Object.assign({}, this.config.core.theme, theme)\n this.applyTheme()\n }\n\n /**\n * Deep-merges a partial config into the current widget config.\n * Theme and dark mode side-effects are re-applied immediately.\n * Changes to locale or profile require an explicit `switchLocale()` or `init()` call.\n */\n setConfig(config: DeepPartial<ConsentiConfig>): void {\n this.config = deepMerge<ConsentiConfig>(this.config, config)\n if (isClient()) {\n this.applyTheme()\n this.applyDarkMode()\n }\n }\n\n /**\n * Merges a partial profile override into the current profile and re-renders any visible UI.\n * Does not make a network request — the resolved base profile from the last `init()` is reused.\n *\n * No-op before `init()` completes (while `resolvedBase` is null).\n */\n setProfile(override: DeepPartial<ResolvedProfile>): void {\n if (!isClient() || !this.resolvedBase) return\n\n this.config.profileOverride = this.config.profileOverride\n ? deepMerge<DeepPartial<ResolvedProfile>>(this.config.profileOverride, override)\n : override\n\n this.profile = deepMerge<ResolvedProfile>(this.resolvedBase, this.config.profileOverride!)\n\n const modalWasOpen = this.modal?.isOpen() ?? false\n if (modalWasOpen) this.modal?.close()\n\n if (this._bannerVariant !== null) {\n const wasGpc = this._bannerVariant === 'gpc'\n this._bannerVariant = null\n this.showBanner(wasGpc)\n }\n\n if (modalWasOpen) this.showModal()\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 if (isClient()) {\n for (const entries of this._eventHandlers.values()) {\n for (const { name, wrapped } of entries) {\n window.removeEventListener(name, wrapped)\n }\n }\n this._eventHandlers.clear()\n }\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'\nimport { logger } from './console'\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 /**\n * Inline JavaScript to inject when consent is granted (e.g. a GTM snippet).\n * WARNING: this string is assigned directly to `script.innerHTML` — only\n * pass a static, trusted literal. Never pass user-supplied or remote content.\n */\n unsafeInnerHTML?: 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 * When `true` (default), the script is automatically removed when consent is revoked and\n * re-injected when consent is re-granted. Set to `false` to evaluate consent only once at\n * construction time — no event listener is attached and the script is never auto-removed.\n */\n bind?: boolean\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 if (options.bind !== false) {\n window.addEventListener('consenti:consentSubmitted', this.handler)\n }\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) {\n // Reject javascript: URLs — only allow http(s)/protocol-relative/relative paths.\n const src = this.options.src.trim()\n if (/^javascript:/i.test(src)) {\n logger.warn('ConsentScript: rejected src with javascript: protocol')\n return\n }\n script.src = src\n }\n // unsafeInnerHTML is an intentional escape hatch for inline scripts (e.g. GTM snippet).\n // The content is the caller's responsibility — only pass trusted, static strings.\n if (this.options.unsafeInnerHTML) script.innerHTML = this.options.unsafeInnerHTML\n for (const [key, value] of Object.entries(this.options.attributes ?? {})) {\n // Strip inline event handler attributes (on*) — they bypass CSP and are never\n // a legitimate need here since the caller can use onLoad/onRevoke callbacks.\n if (/^on/i.test(key)) {\n logger.warn(`ConsentScript: rejected attribute \"${key}\" (inline event handlers are not allowed)`)\n continue\n }\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 if (this.options.bind !== false) {\n window.removeEventListener('consenti:consentSubmitted', this.handler)\n }\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":["hmac_exports","__export","signValue","verifyValue","importHmacKey","rawKey","keyData","value","key","cryptoKey","data","signature","b","init_hmac","__esmMin","http_exports","__export","httpRequest","url","options","authToken","headers","res","init_http","__esmMin","src_exports","__export","ConsentScript","ConsentiPlugin","ConsentiProfile","ConsentiSetup","CookieTrigger","CONSENTI_CSS","isClient","safeWindow","safeNavigator","isClient","_levels","initConsole","levels","logger","args","ConsentiChannel","isClient","onMessage","event","logger","message","generateReceipt","params","receipt","downloadReceipt","json","blob","url","anchor","getCookie","name","isClient","match","escapeRegExp","setCookie","value","opts","secure","parts","deleteCookie","str","ConsentStorage","mode","isClient","logger","key","getCookie","value","opts","setCookie","deleteCookie","resolveConsentMaxAge","cookies","c","min","d","COOKIE_NAME_PREFIX","buildConsentCookieName","userId","profileId","buildCookieValue","consent","epochSeconds","pairs","id","status","parseConsentCookie","value","parts","tsPart","timestamp","epochStr","epoch","consentPart","sigPart","pair","colonIdx","result","ConsentStore","_ConsentStore","storageMode","cookieDomains","ConsentStorage","raw","parsed","signingKey","verifyValue","valueToVerify","sig","logger","signature","opts","signValue","rawValue","finalValue","prefix","suffix","all","name","inner","domain","EventBus","profileId","gtmConfig","name","detail","isClient","event","gtm","win","safeWindow","layerName","layer","adConsentDenied","init_http","resolveLocale","translations","requested","defaultLocale","base","deepMerge","lang","override","result","key","overrideVal","baseVal","getBrowserGeoHints","COMPLIANCE_GROUP_IDS","EMBEDDED_COMPLIANCE_MAP","COUNTRY_CODES","acc","c","d","LOCALES","l","profile","general_privacy_consent_default","profile","notice_only_default","profile","opt_in_default","profile","opt_in_brazil_default","profile","opt_in_china_default","profile","opt_in_dpdpa_default","profile","opt_out_default","profile","opt_out_strict_default","PREBUILT_PROFILE_MAP","general_privacy_consent_default","notice_only_default","opt_in_default","opt_in_brazil_default","opt_in_china_default","opt_in_dpdpa_default","opt_out_default","opt_out_strict_default","profileRegistry","registerProfile","id","config","DEFAULT_PROFILE","mapPublicProfileToResolved","resp","gpcMode","CACHE_KEY_PREFIX","CACHE_TTL_MS","getCachedResolution","tenantId","locale","cacheEnabled","key","raw","cached","setCachedResolution","data","isDomainAllowed","profile","trustDomain","origins","current","loadPrebuiltProfile","complianceGroup","PREBUILT_PROFILE_MAP","isPrebuiltAllowed","config","setting","resolveGroupFromHints","tz","lang","parts","tag","countryCode","entry","EMBEDDED_COMPLIANCE_MAP","tzParts","city","effectiveLocale","l","effectiveTenantId","isKnownComplianceGroup","value","COMPLIANCE_GROUP_IDS","fetchProfileJson","url","authToken","httpRequest","scenario1A","getBrowserGeoHints","scenario1B","scenario2A","base","json","logger","resolveUrl","filePath","version","resolved","scenario2B","group","scenarioLocal","typeValue","local","profileRegistry","p","resolveLocale","resolveProfile","complianceType","apiEnabled","detectGPC","nav","safeNavigator","applyGPCToConsent","cookies","currentConsent","mode","result","cookie","buildButton","btn","handlers","el","cookies","createOverlay","opacity","onClick","el","Banner","config","handlers","privacyPolicyUrl","locales","activeLocale","onLocaleSwitch","hidePoweredBy","banner","textContainer","linksContainer","headingTag","heading","text","link","linkButtons","b","btn","anchor","actionButtons","btns","buildButton","hasClose","hasLocaleSwitcher","controls","buildLocaleSwitcher","close","createOverlay","pb","onSwitch","wrapper","list","locale","item","localeBtn","e","FOCUSABLE_SELECTORS","trapFocus","container","getFocusable","el","handleKeydown","e","els","first","last","handleEscape","Modal","config","initialConsent","handlers","allowReceipt","dpdpaConfig","locales","activeLocale","onLocaleSwitch","hidePoweredBy","position","wrapper","overlayOpacity","overlayClickHandler","overlay","createOverlay","container","header","body","headingTag","heading","sub","hasClose","hasLocaleSwitcher","headerControls","buildModalLocaleSwitcher","close","desc","linkButtons","b","linksContainer","btn","anchor","categories","cat","catEl","notice","esc","s","safeEmail","btns","buildButton","pb","isLI","isMandatory","defaultOn","toggle","checkIcon","crossIcon","knob","next","text","li","label","checkbox","triggerEl","isFullscreen","trapFocus","consent","on","cookieId","onSwitch","list","locale","item","localeBtn","e","ROOT_ID","Renderer","rootEl","isClient","found","root","css","style","message","announcer","ConsentiSetup","config","ConsentiChannel","resolve","isClient","initConsole","logger","err","storageMode","userId","ConsentStore","resolvedProfile","resolveProfile","profile","deepMerge","EventBus","Renderer","CONSENTI_CSS","Banner","Modal","msg","signingKey","hasExisting","profileGpcMode","gpcMode","detectGPC","gpcConsent","applyGPCToConsent","gpcSigningKey","compGroup","isOptOut","isOptOutStrict","defaultConsent","cookie","ccpaSigKey","gpcActive","sigKey","isDark","root","theme","map","prop","value","mult","baseSize","headingSize","plugin","invoke","onlyMandatory","consent","includingMandatory","context","p","cookieIds","fullConsent","hadExistingConsent","nonMandatory","c","allGranted","allDenied","gcmKeys","result","key","raw","safeDefault","gpcDerived","consentId","pageUrl","fallback","defaultDenied","consentAction","apiResponse","base","httpRequest","receiptParams","receipt","generateReceipt","downloadReceipt","useGpcVariant","variant","bannerConfig","handlers","locales","activeLocale","bannerEl","locale","overlay","triggerEl","existing","dpdpaCfg","callback","visitorId","cookieId","requestValue","status","categoryId","category","event","handler","name","wrapped","e","entries","idx","entry","enable","override","modalWasOpen","wasGpc","ConsentiPlugin","nextLocalId","ConsentiProfile","config","registerProfile","ConsentScript","options","isClient","granted","script","src","logger","key","value","CookieTrigger","options","action","isClient","found","btn"]}
|