@cookieyes/core 0.7.0 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{categories-DMBKo4Eb.d.ts → categories-BRwmnMyp.d.ts} +7 -0
- package/dist/index.cjs +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/integrations.d.ts +1 -1
- package/dist/network-blocker.d.ts +1 -1
- package/package.json +1 -1
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","sources":["../src/cookie.ts","../src/categories.ts","../src/config.ts","../src/deprecations.ts","../src/events.ts","../src/google-consent-mode.ts","../src/translations/en.ts","../src/i18n.ts","../src/integrations-lazy.ts","../src/language.ts","../src/scripts.ts","../src/stop-handlers.ts","../src/sync.ts","../src/manager.ts","../src/region.ts","../src/runtime.ts","../src/server-consent.ts"],"sourcesContent":["import type { ResolvedCategories } from \"./categories.js\";\nimport type { ConsentSnapshot, Regulation } from \"./types.js\";\n\nconst COOKIE_NAME = \"cookieyes-consent\";\n\n// Meta keys carry consent metadata; every other `key:value` pair in the cookie\n// is a category id → \"yes\"/\"no\", so custom taxonomies serialize without a\n// fixed schema. Exported so category validation can reject ids that would\n// collide with these (see resolveCategories) — a single source of truth.\nexport const COOKIE_META_KEYS = new Set([\n \"consentid\",\n \"consent\",\n \"action\",\n \"tax\",\n \"lastRenewedDate\",\n]);\n\nexport type RawCookieFields = {\n consentid?: string;\n consent?: string;\n action?: string;\n /** Taxonomy signature stored with the consent (see ResolvedCategories.taxonomyHash). */\n tax?: string;\n lastRenewedDate?: string;\n /** Every non-meta pair: category id → \"yes\" | \"no\". */\n categories: Record<string, string>;\n};\n\nexport function parseCookie(raw: string): RawCookieFields {\n const fields: RawCookieFields = { categories: {} };\n for (const pair of raw.split(\",\")) {\n const colonIdx = pair.indexOf(\":\");\n if (colonIdx === -1) continue;\n const key = pair.slice(0, colonIdx).trim();\n const value = pair.slice(colonIdx + 1).trim();\n if (COOKIE_META_KEYS.has(key)) {\n (fields as Record<string, unknown>)[key] = value;\n } else if (key.length > 0) {\n fields.categories[key] = value;\n }\n }\n return fields;\n}\n\nexport function serializeCookie(snapshot: ConsentSnapshot): string {\n const parts: string[] = [\n `consentid:${snapshot.consentId}`,\n `consent:${snapshot.hasActed ? \"yes\" : \"no\"}`,\n `action:${snapshot.hasActed ? \"yes\" : \"no\"}`,\n ];\n if (snapshot.taxonomyHash) parts.push(`tax:${snapshot.taxonomyHash}`);\n for (const [id, granted] of Object.entries(snapshot.categories)) {\n parts.push(`${id}:${granted ? \"yes\" : \"no\"}`);\n }\n parts.push(`lastRenewedDate:${snapshot.lastRenewed ?? Date.now()}`);\n return parts.join(\",\");\n}\n\n/**\n * Find and parse the consent cookie inside a `name=value; name2=value2` string.\n *\n * Shared by the browser (`document.cookie`) and the server (a request's `Cookie`\n * header) — the two formats are identical, and one implementation means the\n * server can never disagree with the client about what a visitor's cookie says.\n * Returns `null` when the cookie is absent or its value cannot be decoded.\n */\nexport function parseCookieHeader(header: string): RawCookieFields | null {\n for (const cookie of header.split(\";\")) {\n const trimmed = cookie.trim();\n const eqIdx = trimmed.indexOf(\"=\");\n if (eqIdx === -1) continue;\n const name = trimmed.slice(0, eqIdx).trim();\n if (name !== COOKIE_NAME) continue;\n const value = trimmed.slice(eqIdx + 1).trim();\n try {\n return parseCookie(decodeURIComponent(value));\n } catch {\n // A malformed percent-encoding is a corrupt cookie, not a crash: treat it\n // as no stored consent so the visitor is asked again.\n return null;\n }\n }\n return null;\n}\n\nexport function readConsentCookie(): RawCookieFields | null {\n if (typeof document === \"undefined\") return null;\n return parseCookieHeader(document.cookie);\n}\n\nexport function writeConsentCookie(snapshot: ConsentSnapshot): void {\n if (typeof document === \"undefined\") return;\n const value = encodeURIComponent(serializeCookie(snapshot));\n const maxAge = 365 * 24 * 60 * 60; // 1 year\n document.cookie = `${COOKIE_NAME}=${value}; max-age=${maxAge}; path=/; SameSite=Lax`;\n}\n\nexport function clearConsentCookie(): void {\n if (typeof document === \"undefined\") return;\n document.cookie = `${COOKIE_NAME}=; max-age=0; path=/`;\n}\n\nexport function generateConsentId(): string {\n const array = new Uint8Array(32);\n if (typeof crypto !== \"undefined\" && crypto.getRandomValues) {\n crypto.getRandomValues(array);\n } else {\n for (let i = 0; i < array.length; i++) {\n array[i] = Math.floor(Math.random() * 256);\n }\n }\n return btoa(String.fromCharCode(...array))\n .replace(/\\+/g, \"-\")\n .replace(/\\//g, \"_\")\n .replace(/=/g, \"\")\n .slice(0, 44);\n}\n\n/**\n * Build a snapshot from stored cookie fields, against the *resolved* taxonomy.\n * Required categories are always granted; everything else reflects the stored\n * \"yes\"/\"no\" (absent → not granted). Carries the stored taxonomy hash so the\n * caller can detect a taxonomy change.\n */\nexport function rawFieldsToSnapshot(\n fields: RawCookieFields,\n regulation: Regulation,\n resolved: ResolvedCategories,\n): ConsentSnapshot {\n const categories: Record<string, boolean> = {};\n for (const id of resolved.ids) {\n categories[id] = resolved.requiredIds.has(id) ? true : fields.categories[id] === \"yes\";\n }\n return {\n consentId: fields.consentid ?? generateConsentId(),\n hasActed: fields.action === \"yes\",\n categories,\n regulation,\n lastRenewed: fields.lastRenewedDate ? Number(fields.lastRenewedDate) : undefined,\n taxonomyHash: fields.tax,\n };\n}\n\nexport function defaultSnapshot(\n consentId: string,\n regulation: Regulation,\n resolved: ResolvedCategories,\n): ConsentSnapshot {\n const isOptOut = regulation === \"CCPA\";\n const categories: Record<string, boolean> = {};\n for (const id of resolved.ids) {\n // CCPA is opt-out: everything implicitly on until the visitor opts out.\n // Otherwise only the required category(ies) start on.\n categories[id] = resolved.requiredIds.has(id) ? true : isOptOut;\n }\n return {\n consentId,\n hasActed: false,\n categories,\n regulation,\n taxonomyHash: resolved.taxonomyHash,\n };\n}\n","import { COOKIE_META_KEYS } from \"./cookie.js\";\nimport type { ConsentCategory } from \"./types.js\";\n\n/**\n * Google Consent Mode v2 storage/signal types. A category can declare which of\n * these it represents via {@link CategoryDef.gcm}; the SDK then broadcasts them\n * (see google-consent-mode.ts). `security_storage` is always granted and is\n * handled by the broadcast itself, so it never needs to be mapped.\n */\nexport type GoogleConsentSignal =\n | \"ad_storage\"\n | \"ad_user_data\"\n | \"ad_personalization\"\n | \"analytics_storage\"\n | \"functionality_storage\"\n | \"personalization_storage\"\n | \"security_storage\";\n\n/**\n * A single consent category. `id` is the stable key stored in the cookie and\n * used everywhere (banner, preferences, read APIs, integrations). Exactly one\n * category should be marked `required` — the always-on, non-optional one (like\n * the default \"necessary\") — flagged explicitly here, never inferred from a\n * name, so it survives full renaming.\n */\nexport type CategoryDef = {\n id: ConsentCategory;\n /** The always-on, non-optional category. At least one is required. */\n required?: boolean | undefined;\n /** Display label. Falls back to the translation for built-in ids. */\n label?: string | undefined;\n /** Display description. Falls back to the translation for built-in ids. */\n description?: string | undefined;\n /** Google Consent Mode signals this category governs (see {@link GoogleConsentSignal}). */\n gcm?: GoogleConsentSignal[] | undefined;\n};\n\n/**\n * The built-in five, used verbatim when a customer configures nothing. GCM\n * mapping mirrors production's `_ckySetGoogleConsentMode` (analytics →\n * analytics_storage, advertisement → the ad_* signals, functional →\n * functionality/personalization; performance maps to nothing; security_storage\n * is always granted by the broadcast).\n */\nexport const DEFAULT_CATEGORIES: CategoryDef[] = [\n { id: \"necessary\", required: true },\n { id: \"functional\", gcm: [\"functionality_storage\", \"personalization_storage\"] },\n { id: \"analytics\", gcm: [\"analytics_storage\"] },\n { id: \"performance\" },\n { id: \"advertisement\", gcm: [\"ad_storage\", \"ad_user_data\", \"ad_personalization\"] },\n];\n\nexport type ResolvedCategories = {\n /** Ordered category definitions actually in effect. */\n list: CategoryDef[];\n /** Ordered ids (fast access). */\n ids: ConsentCategory[];\n /** Ids marked `required` (always granted, never toggleable). */\n requiredIds: Set<ConsentCategory>;\n /** Stable signature of this taxonomy; a change here re-requests consent. */\n taxonomyHash: string;\n /** True when the built-in five are in effect (configured or fallback). */\n isDefault: boolean;\n};\n\n/** Small, stable non-crypto hash → short base36 string, for the cookie stamp. */\nfunction hashString(input: string): string {\n let h = 2166136261;\n for (let i = 0; i < input.length; i++) {\n h ^= input.charCodeAt(i);\n h = Math.imul(h, 16777619);\n }\n return (h >>> 0).toString(36);\n}\n\nfunction build(list: CategoryDef[], isDefault: boolean): ResolvedCategories {\n const ids = list.map((c) => c.id);\n const requiredIds = new Set(list.filter((c) => c.required).map((c) => c.id));\n // Signature includes id + required flag + gcm, so any meaningful change to\n // the taxonomy invalidates prior consent (see the manager's load check).\n const signature = list\n .map((c) => `${c.id}:${c.required ? 1 : 0}:${(c.gcm ?? []).join(\"+\")}`)\n .join(\"|\");\n return { list, ids, requiredIds, taxonomyHash: hashString(signature), isDefault };\n}\n\n/**\n * Validate a custom category config. Returns a human-readable reason if it's\n * invalid, or `null` if it's good. Order matters: the first failing rule wins.\n */\nfunction validationError(defs: CategoryDef[]): string | null {\n const ids = defs.map((d) => d.id);\n if (ids.some((id) => typeof id !== \"string\" || id.length === 0)) {\n return \"every category needs a non-empty string id\";\n }\n // `,` and `:` are the cookie's field/key delimiters — an id containing either\n // would corrupt persistence silently on the round-trip.\n if (ids.some((id) => id.includes(\",\") || id.includes(\":\"))) {\n return \"category ids must not contain ',' or ':'\";\n }\n if (new Set(ids).size !== ids.length) {\n return \"category ids must be unique\";\n }\n // Ids that would collide with the cookie's reserved metadata keys — a category\n // named e.g. \"consent\" or \"tax\" would corrupt persistence silently.\n if (ids.some((id) => COOKIE_META_KEYS.has(id))) {\n return `category ids must not be one of the reserved keys: ${[...COOKIE_META_KEYS].join(\", \")}`;\n }\n if (!defs.some((d) => d.required === true)) {\n return \"at least one category must be marked { required: true }\";\n }\n return null;\n}\n\n/**\n * Resolve the category list from config. Returns the built-in five when nothing\n * is configured. On an invalid custom config (empty, duplicate/reserved ids, or\n * no `required` category) it warns and falls back to the built-in five, rather\n * than leaving the visitor a broken/empty or unprotected setup.\n */\nexport function resolveCategories(defs?: CategoryDef[]): ResolvedCategories {\n if (!defs || defs.length === 0) return build(DEFAULT_CATEGORIES, true);\n\n const error = validationError(defs);\n if (error) {\n if (typeof console !== \"undefined\") {\n // eslint-disable-next-line no-console\n console.warn(\n `[cookieyes] Invalid categories config (${error}). Falling back to the ` +\n \"default five (necessary, functional, analytics, performance, advertisement).\",\n );\n }\n return build(DEFAULT_CATEGORIES, true);\n }\n\n return build(defs, false);\n}\n","import type { CategoryDef } from \"./categories.js\";\nimport type { Integration } from \"./integrations.js\";\nimport type { NetworkBlockerConfig } from \"./network-blocker.js\";\nimport type { BuiltInIntegration, StopHandler } from \"./stop-handlers.js\";\nimport type {\n ColorScheme,\n ConsentBackend,\n ConsentRuntimeMode,\n ConsentSnapshot,\n CookieYesConfig,\n I18nConfig,\n RegionConfig,\n Regulation,\n ThemeConfig,\n} from \"./types.js\";\n\n/**\n * The canonical config with every deprecated alias already collapsed into its\n * top-level key. Both `@cookieyes/core` and `@cookieyes/react` consume the\n * output of {@link _normalizeConfig}, so alias resolution lives in exactly one\n * place and the two packages can never drift.\n *\n * @internal\n */\nexport type _NormalizedConfig = {\n mode: ConsentRuntimeMode;\n regulation?: Regulation | undefined;\n region?: RegionConfig | undefined;\n colorScheme?: ColorScheme | undefined;\n theme?: ThemeConfig | undefined;\n i18n?: I18nConfig | undefined;\n categories?: CategoryDef[] | undefined;\n networkBlocker?: NetworkBlockerConfig | undefined;\n reloadOnRevoke?: boolean | undefined;\n googleConsentMatch?: \"all\" | \"any\" | undefined;\n integrations?: Integration[] | undefined;\n /** @deprecated Renamed from `integrations`; use `integrations` with a `@cookieyes/scripts` preset. */\n builtInIntegrations?: BuiltInIntegration[] | undefined;\n customStopHandlers?: StopHandler[] | undefined;\n onConsentReady?: ((state: ConsentSnapshot) => void) | undefined;\n onConsentUpdate?: ((state: ConsentSnapshot) => void) | undefined;\n apiUrl?: string | undefined;\n apiKey?: string | undefined;\n backend?: ConsentBackend | undefined;\n};\n\nfunction warn(message: string): void {\n if (typeof console !== \"undefined\") {\n // eslint-disable-next-line no-console\n console.warn(message);\n }\n}\n\n/**\n * Resolve a public {@link CookieYesConfig} into its canonical internal form.\n *\n * Deprecated aliases map silently to their canonical key when used alone; when\n * an alias and its canonical key are both present, the canonical key wins and\n * exactly one warning is logged for that collision.\n *\n * - `overrides.regulation` → `regulation`\n * - `backendURL` → `apiUrl`\n *\n * @internal\n */\nexport function _normalizeConfig(config: CookieYesConfig): _NormalizedConfig {\n const normalized: _NormalizedConfig = { mode: config.mode };\n\n // ── regulation (top-level) ⇐ deprecated overrides.regulation ──────────────\n const nestedRegulation = config.overrides?.regulation;\n if (config.regulation !== undefined) {\n normalized.regulation = config.regulation;\n if (nestedRegulation !== undefined) {\n warn(\n \"[CookieYes] Received both `regulation` and the deprecated \" +\n \"`overrides.regulation`. Using the top-level `regulation` and ignoring \" +\n \"`overrides`. Drop the `overrides` object — it is deprecated and will be \" +\n \"removed after three release cycles.\",\n );\n }\n } else if (nestedRegulation !== undefined) {\n normalized.regulation = nestedRegulation;\n }\n\n if (config.region !== undefined) normalized.region = config.region;\n if (config.colorScheme !== undefined) normalized.colorScheme = config.colorScheme;\n if (config.theme !== undefined) normalized.theme = config.theme;\n if (config.i18n !== undefined) normalized.i18n = config.i18n;\n if (config.categories !== undefined) normalized.categories = config.categories;\n if (config.networkBlocker !== undefined) normalized.networkBlocker = config.networkBlocker;\n if (config.reloadOnRevoke !== undefined) normalized.reloadOnRevoke = config.reloadOnRevoke;\n if (config.googleConsentMatch !== undefined)\n normalized.googleConsentMatch = config.googleConsentMatch;\n if (config.integrations !== undefined) normalized.integrations = config.integrations;\n if (config.builtInIntegrations !== undefined)\n normalized.builtInIntegrations = config.builtInIntegrations;\n if (config.customStopHandlers !== undefined)\n normalized.customStopHandlers = config.customStopHandlers;\n if (config.onConsentReady !== undefined) normalized.onConsentReady = config.onConsentReady;\n if (config.onConsentUpdate !== undefined) normalized.onConsentUpdate = config.onConsentUpdate;\n\n // ── backend keys (self-hosted only) ⇐ deprecated backendURL ───────────────\n if (config.mode === \"self-hosted\") {\n if (config.apiKey !== undefined) normalized.apiKey = config.apiKey;\n if (config.backend !== undefined) normalized.backend = config.backend;\n\n if (config.apiUrl !== undefined) {\n normalized.apiUrl = config.apiUrl;\n if (config.backendURL !== undefined) {\n warn(\n \"[CookieYes] Received both `apiUrl` and the deprecated `backendURL`. \" +\n \"Using `apiUrl` and ignoring `backendURL`. Rename `backendURL` to \" +\n \"`apiUrl` — the alias is deprecated and will be removed after three \" +\n \"release cycles.\",\n );\n }\n } else if (config.backendURL !== undefined) {\n normalized.apiUrl = config.backendURL;\n }\n }\n\n return normalized;\n}\n","/**\n * Declared locally rather than pulled in from `@types/node`.\n *\n * The guard below needs `process.env.NODE_ENV` to survive into the published\n * output as that exact literal, so a consumer's bundler can replace it. That\n * rules out any defensive form — `globalThis.process?.env?.NODE_ENV`, a\n * `typeof` check — because none of them are the pattern bundlers match.\n *\n * Referencing Node's global types instead (`/// <reference types=\"node\" />`)\n * would work for the compiler but risks that reference reaching the emitted\n * `.d.ts`, which would make every consumer of this package need `@types/node`\n * to typecheck. This declaration is module-scoped and ambient, so it types the\n * one expression that needs it and reaches nothing else.\n */\ndeclare const process: { env: { NODE_ENV?: string } };\n\n/*\n * Every warning in this file opens with `if (process.env.NODE_ENV ===\n * \"production\") return;`.\n *\n * Deprecation warnings exist for whoever is writing the integration. They are\n * of no use to a visitor, who cannot act on one and never opens the console, so\n * shipping the text to them is pure weight — measured at 224 bytes of gzip in\n * core and 175 in the interface layer (see tools/size/README.md).\n *\n * The check is written as this exact literal, at the top of each function and\n * not hoisted into a shared constant, because that is the form bundlers\n * recognise and replace. Once `process.env.NODE_ENV` becomes `\"production\"` the\n * condition is constant, the early return is unconditional, and the rest of the\n * function — the strings included — is dead code the minifier removes.\n *\n * Two forms that look equivalent and are not, both tried and measured:\n *\n * - Hoisting it into `const DEV = ...` shared by the file: the constant folds,\n * but nothing else does, and the guarded bodies survive.\n * - Guarding with `typeof process === \"undefined\" || process.env.NODE_ENV !==\n * \"production\"` to stay safe where `process` is absent: `typeof process` is\n * not statically replaced, so the whole expression stops folding. Measured:\n * core grew by 10 bytes and every warning string stayed in the bundle.\n *\n * The consequence of the literal form is that these functions need `process` to\n * exist, which means a bundler (or Node). That is already true of every\n * supported install path — the package advertises only `import`/`require`\n * conditions, no browser-global build — and it is the same trade React makes\n * for the same reason.\n */\n\nlet warnedOfflineMode = false;\n\n/**\n * One-time-per-page-load console warning for `mode: \"offline\"`.\n * Both @cookieyes/core and @cookieyes/react call this so the wording and the\n * \"once\" behavior stay identical no matter which package reads the setting.\n *\n * No-op in a production bundle; see the note at the top of this file.\n */\nexport function _warnOfflineModeDeprecated(): void {\n if (process.env.NODE_ENV === \"production\") return;\n if (warnedOfflineMode) return;\n warnedOfflineMode = true;\n if (typeof console === \"undefined\") return;\n // eslint-disable-next-line no-console\n console.warn(\n '[cookieyes] mode: \"offline\" has been renamed to \"cookie-only\". Both do exactly ' +\n 'the same thing, but \"offline\" is deprecated and will be removed after three release cycles. ' +\n \"See https://github.com/cookieyes/cookieyes/blob/main/apps/web/content/docs/migration.mdx for the full migration guide. \" +\n 'Update to .mode(\"cookie-only\") (or { mode: \"cookie-only\" }).',\n );\n}\n\n/** @internal test-only — resets the one-time warning guard between test cases. */\nexport function _resetOfflineModeWarning(): void {\n warnedOfflineMode = false;\n}\n\nlet warnedBuiltInIntegrations = false;\n\n/**\n * One-time-per-page-load console warning for the deprecated `builtInIntegrations`\n * config field (formerly `integrations`). Both packages call this so the wording\n * and the \"once\" behavior stay identical.\n *\n * No-op in a production bundle; see the note at the top of this file.\n */\nexport function _warnBuiltInIntegrationsDeprecated(): void {\n if (process.env.NODE_ENV === \"production\") return;\n if (warnedBuiltInIntegrations) return;\n warnedBuiltInIntegrations = true;\n if (typeof console === \"undefined\") return;\n // eslint-disable-next-line no-console\n console.warn(\n \"[cookieyes] `builtInIntegrations` (formerly the `integrations` field) is deprecated \" +\n \"and will be removed after three release cycles. Use the `integrations` field with a \" +\n \"preset from `@cookieyes/scripts` instead. See \" +\n \"https://github.com/cookieyes/cookieyes/blob/main/apps/web/content/docs/migration.mdx for the full migration guide.\",\n );\n}\n\n/** @internal test-only — resets the one-time warning guard between test cases. */\nexport function _resetBuiltInIntegrationsWarning(): void {\n warnedBuiltInIntegrations = false;\n}\n","import type {\n ConsentCategory,\n ConsentEventListener,\n ConsentEventOptions,\n ConsentEventPayload,\n ConsentEventType,\n} from \"./types.js\";\n\nexport type ConsentEmitter = {\n /**\n * Listen for consent events. `\"save\"` fires on every saved decision (even an\n * unchanged re-confirm); `\"change\"` fires only when a category actually\n * differs. The listener fires once immediately with the current state\n * (`isInitial: true`) so a late listener isn't blind to earlier choices.\n * Pass `{ category }` to only be called when that one category changes.\n * Returns an unsubscribe function.\n */\n on: (\n type: ConsentEventType,\n listener: ConsentEventListener,\n options?: ConsentEventOptions,\n ) => () => void;\n /** Feed in the committed categories after a save; the emitter fans out events. */\n push: (categories: Record<string, boolean>) => void;\n};\n\ntype Registration = { listener: ConsentEventListener; category?: ConsentCategory };\n\n/**\n * The consent event fan-out, shared by the core and React runtimes so both\n * behave identically. `getCommitted` returns the consent currently in effect,\n * used for the immediate replay a new listener receives.\n */\nexport function createConsentEmitter(getCommitted: () => Record<string, boolean>): ConsentEmitter {\n const listeners: Record<ConsentEventType, Set<Registration>> = {\n save: new Set(),\n change: new Set(),\n };\n // Committed categories at the last push — the baseline for diffing changes.\n let last: Record<string, boolean> = { ...getCommitted() };\n\n function deliver(reg: Registration, type: ConsentEventType, payload: ConsentEventPayload): void {\n try {\n reg.listener(payload);\n } catch (err) {\n // One listener throwing must never stop the others (Story 5).\n if (typeof console !== \"undefined\") {\n console.error(\n `[cookieyes] a consent \"${type}\" listener threw; others are unaffected:`,\n err,\n );\n }\n }\n }\n\n function emit(type: ConsentEventType, payload: ConsentEventPayload): void {\n // Copy first so a listener that unsubscribes (or subscribes) while firing\n // can't corrupt the loop.\n for (const reg of [...listeners[type]]) {\n if (reg.category && !payload.changedCategories.includes(reg.category)) continue;\n deliver(reg, type, payload);\n }\n }\n\n return {\n on(type, listener, options) {\n const reg: Registration = options?.category\n ? { listener, category: options.category }\n : { listener };\n listeners[type].add(reg);\n // Replay current state immediately (Story 4). isInitial marks it as the\n // replay, not a live action; changedCategories is empty because nothing\n // changed — the current values live in `categories`.\n deliver(reg, type, {\n categories: { ...getCommitted() },\n changedCategories: [],\n isInitial: true,\n });\n return () => {\n listeners[type].delete(reg);\n };\n },\n\n push(categories) {\n const next = { ...categories };\n const changedCategories: ConsentCategory[] = [];\n for (const id of Object.keys(next) as ConsentCategory[]) {\n if (last[id] !== next[id]) changedCategories.push(id);\n }\n last = next;\n // \"save\" always fires; \"change\" only when something genuinely differed.\n emit(\"save\", { categories: next, changedCategories, isInitial: false });\n if (changedCategories.length > 0) {\n emit(\"change\", { categories: next, changedCategories, isInitial: false });\n }\n },\n };\n}\n","import type { GoogleConsentSignal, ResolvedCategories } from \"./categories.js\";\n\nfunction warn(message: string): void {\n if (typeof console !== \"undefined\") console.warn(`[cookieyes] ${message}`);\n}\n\n/**\n * The full set of Google Consent Mode v2 signals. We always broadcast all\n * seven so any Google tag (GA4, Ads, GTM-managed tags) sees a complete picture,\n * rather than only the ones a customer happened to map.\n */\nconst ALL_SIGNALS: GoogleConsentSignal[] = [\n \"ad_storage\",\n \"ad_user_data\",\n \"ad_personalization\",\n \"analytics_storage\",\n \"functionality_storage\",\n \"personalization_storage\",\n \"security_storage\",\n];\n\ntype GcmValue = \"granted\" | \"denied\";\n\ntype WindowWithDataLayer = Window & {\n dataLayer?: unknown[];\n};\n\n/**\n * True when a Google `dataLayer` is present on the page. That's our single\n * trigger: if a dataLayer exists, some Google service is (or will be) listening,\n * so we broadcast. No dataLayer → no-op.\n */\nfunction hasDataLayer(): boolean {\n if (typeof window === \"undefined\") return false;\n return Array.isArray((window as WindowWithDataLayer).dataLayer);\n}\n\n/**\n * Warn when more than one category maps to the same Consent Mode signal. Google\n * has a single on/off per signal, so this mapping is lossy — `googleConsentMatch`\n * decides which way it fails (`\"any\"` grants if either is granted, `\"all\"`\n * requires both). Surfacing it lets the customer choose deliberately instead of\n * discovering it in an audit. The built-in five don't overlap, so this is quiet\n * unless a custom taxonomy creates it.\n */\nexport function warnOverlappingGcm(resolved: ResolvedCategories): void {\n const perSignal = new Map<GoogleConsentSignal, Set<string>>();\n for (const def of resolved.list) {\n for (const signal of def.gcm ?? []) {\n let ids = perSignal.get(signal);\n if (!ids) {\n ids = new Set();\n perSignal.set(signal, ids);\n }\n ids.add(def.id);\n }\n }\n for (const [signal, idSet] of perSignal) {\n // >1 distinct category → a genuine overlap (a category mapping a signal twice isn't one).\n if (idSet.size <= 1) continue;\n const ids = [...idSet].map((id) => JSON.stringify(id)).join(\", \");\n warn(\n `categories ${ids} all map to the Google signal \"${signal}\", which is a single ` +\n 'on/off — this mapping is lossy. Set `googleConsentMatch: \"all\"` to grant it only ' +\n 'when all are granted, or \"any\" (default) to grant it when any is.',\n );\n }\n}\n\n/**\n * Compute the granted/denied value for every GCM signal from the current\n * category consent, using each category's `gcm` mapping.\n *\n * - When several categories map to the same signal, `match` decides: `\"any\"`\n * (default) grants it if *any* mapping category is granted; `\"all\"` requires\n * *every* mapping category to be granted. For the built-in five (one category\n * per signal) the two are identical — `match` only matters for custom overlaps.\n * - `security_storage` is always `granted` (strictly necessary, not consentable).\n * - A signal that no category maps to stays `denied`.\n */\nexport function computeGoogleConsent(\n resolved: ResolvedCategories,\n categories: Record<string, boolean>,\n match: \"all\" | \"any\" = \"any\",\n): Record<GoogleConsentSignal, GcmValue> {\n const result = {} as Record<GoogleConsentSignal, GcmValue>;\n for (const signal of ALL_SIGNALS) {\n if (signal === \"security_storage\") {\n result[signal] = \"granted\"; // never gated on consent\n continue;\n }\n const mappers = resolved.list.filter((def) => def.gcm?.includes(signal));\n if (mappers.length === 0) {\n result[signal] = \"denied\"; // nothing maps to it\n continue;\n }\n const granted =\n match === \"all\"\n ? mappers.every((def) => categories[def.id] === true)\n : mappers.some((def) => categories[def.id] === true);\n result[signal] = granted ? \"granted\" : \"denied\";\n }\n return result;\n}\n\n/**\n * Push a Consent Mode `update` for all seven signals onto the dataLayer, if one\n * is present. Safe to call on load and on every consent change; a no-op when no\n * Google service is on the page.\n *\n * The customer is responsible for the Consent Mode *default* (the gtag snippet\n * that must run before their Google tags, typically denying everything). This\n * function owns the *update* that reflects the visitor's actual choice.\n */\nexport function broadcastGoogleConsent(\n resolved: ResolvedCategories,\n categories: Record<string, boolean>,\n match: \"all\" | \"any\" = \"any\",\n): void {\n if (!hasDataLayer()) return;\n\n const consent = computeGoogleConsent(resolved, categories, match);\n const dataLayer = (window as WindowWithDataLayer).dataLayer;\n if (!dataLayer) return;\n\n // Emit the command in gtag's exact wire format: `gtag()` is defined as\n // `function gtag(){ dataLayer.push(arguments); }`, so Google's Consent Mode\n // reads back an *arguments object* — not a plain array. We reproduce that\n // shape here (rather than pushing an array) so every GTM/gtag version\n // recognises the `consent` command reliably, without needing a global\n // `gtag()` to already exist on the page. Empty params keep `arguments` legal;\n // the variable's type makes the 3-arg call typecheck.\n const gtag: (...args: unknown[]) => void = function () {\n // biome-ignore lint/complexity/noArguments: gtag's wire format IS the arguments object — this is the canonical Google snippet.\n dataLayer.push(arguments);\n };\n gtag(\"consent\", \"update\", consent);\n}\n","import type { TranslationMap } from \"../types.js\";\n\nexport const en: TranslationMap = {\n bannerTitle: \"We value your privacy\",\n bannerDescription:\n \"We use cookies to enhance your browsing experience, serve personalised ads or content, and analyse our traffic. By clicking \\u201cAccept All\\u201d, you consent to our use of cookies.\",\n acceptAll: \"Accept All\",\n rejectAll: \"Reject All\",\n managePreferences: \"Customise\",\n savePreferences: \"Save My Preferences\",\n doNotSell: \"Do Not Sell or Share My Personal Information\",\n ccpaDescription:\n \"This website or its third-party tools process personal data. You can opt out of the sale of your personal information by clicking on the \\u201cDo Not Sell or Share My Personal Information\\u201d button.\",\n accept: \"Accept\",\n poweredBy: \"Powered by CookieYes\",\n opensInNewTab: \"opens in new tab\",\n preferencesTitle: \"Customise Consent Preferences\",\n preferencesIntro:\n \"We use cookies to help you navigate efficiently and perform certain functions. You will find detailed information about all cookies under each consent category below.\",\n alwaysActive: \"Always Active\",\n preferencesDialogLabel: \"Cookie preferences\",\n optOutDialogLabel: \"Opt-out preferences\",\n recallButtonLabel: \"Consent Preferences\",\n categories: {\n necessary: {\n label: \"Necessary\",\n description:\n \"Necessary cookies are required to enable the basic features of this site, such as providing secure log-in or adjusting your consent preferences. These cookies do not store any personally identifiable data.\",\n },\n functional: {\n label: \"Functional\",\n description:\n \"Functional cookies help perform certain functionalities like sharing the content of the website on social media platforms, collecting feedback, and other third-party features.\",\n },\n analytics: {\n label: \"Analytics\",\n description:\n \"Analytical cookies are used to understand how visitors interact with the website. These cookies help provide information on metrics such as the number of visitors, bounce rate, traffic source, etc.\",\n },\n performance: {\n label: \"Performance\",\n description:\n \"Performance cookies are used to understand and analyse the key performance indexes of the website which helps in delivering a better user experience for the visitors.\",\n },\n advertisement: {\n label: \"Advertisement\",\n description:\n \"Advertisement cookies are used to provide visitors with customised advertisements based on the pages you visited previously and to analyse the effectiveness of the ad campaigns.\",\n },\n },\n optOut: {\n title: \"Opt-out Preferences\",\n description:\n 'We use third-party cookies that help us analyse how you use this website, store your preferences, and provide the content and advertisements that are relevant to you. However, you can opt out of these cookies by checking \"Do Not Sell or Share My Personal Information\" and clicking the \"Save My Preferences\" button. Once you opt out, you can opt in again at any time by unchecking \"Do Not Sell or Share My Personal Information\" and clicking the \"Save My Preferences\" button.',\n cancel: \"Cancel\",\n successText: \"Your opt-out preference has been honored.\",\n successCountdown: \"Banner closes automatically in {seconds} seconds...\",\n },\n bannerCloseLabel: \"Close\",\n preferencesCloseLabel: \"Close preferences\",\n optOutCloseLabel: \"Close\",\n gatedFrame: {\n placeholder: \"This content requires {category} cookies to be enabled.\",\n action: \"Manage Preferences\",\n },\n reloadNotice: {\n message:\n \"Some tracking on this page can only be fully stopped by reloading. Reload to apply your change, or dismiss to keep browsing.\",\n reloadButton: \"Reload page\",\n dismissButton: \"Dismiss\",\n },\n};\n","import { en } from \"./translations/en.js\";\nimport type { I18nConfig, PartialTranslations, TextDirection, TranslationMap } from \"./types.js\";\n\nexport { en as defaultTranslations };\n\n// Languages written right-to-left, by primary subtag. Everything else is ltr.\nconst RTL = new Set([\"ar\", \"he\", \"fa\", \"ur\", \"ps\", \"sd\", \"yi\", \"dv\"]);\n\n/** The base subtag of a language tag, lowercased: \"en-GB\" → \"en\". */\nexport function primaryOf(tag: string): string {\n return tag.split(\"-\")[0]?.toLowerCase() ?? \"\";\n}\n\n/** Reading direction for a language tag, e.g. \"ar\" or \"ar-EG\" → \"rtl\". */\nexport function getTextDirection(tag: string): TextDirection {\n return RTL.has(primaryOf(tag)) ? \"rtl\" : \"ltr\";\n}\n\n/** Deep-merge a (possibly partial) override onto a complete base map. */\nexport function mergeTranslations(\n base: TranslationMap,\n override?: PartialTranslations,\n): TranslationMap {\n if (!override) return base;\n const out: Record<string, unknown> = { ...base };\n for (const [key, value] of Object.entries(override)) {\n if (value == null) continue;\n const baseVal = (base as Record<string, unknown>)[key];\n const bothObjects =\n typeof value === \"object\" &&\n !Array.isArray(value) &&\n typeof baseVal === \"object\" &&\n baseVal != null;\n out[key] = bothObjects\n ? mergeTranslations(baseVal as TranslationMap, value as PartialTranslations)\n : value;\n }\n return out as TranslationMap;\n}\n\n/**\n * The language to start in, resolved in order: explicit `locale`, then the\n * browser's language, then English. Only returns one we actually have text for\n * (others can be brought in later via `loadLanguage`).\n */\nexport function pickLanguage(i18n?: I18nConfig): string {\n const messages = i18n?.messages ?? {};\n const candidates: string[] = [];\n if (i18n?.locale) candidates.push(i18n.locale);\n if (\n (i18n?.detectBrowserLanguage ?? true) &&\n typeof navigator !== \"undefined\" &&\n navigator.language\n ) {\n candidates.push(navigator.language);\n }\n for (const tag of candidates) {\n if (messages[tag]) return tag;\n const primary = primaryOf(tag);\n if (primary && messages[primary]) return primary;\n }\n return \"en\";\n}\n\n/** Full translations for the resolved starting language, English filling any gaps. */\nexport function resolveTranslations(i18n?: I18nConfig): TranslationMap {\n const messages = i18n?.messages ?? {};\n const tag = pickLanguage(i18n);\n return mergeTranslations(en, messages[tag] ?? messages[primaryOf(tag)]);\n}\n","import type { Integration, IntegrationHost, IntegrationRunner } from \"./integrations.js\";\n\n/**\n * Load the integration runner on demand.\n *\n * The runner is the largest single subsystem in `@cookieyes/core` — 1.2 KB of\n * gzip, measured — and it does nothing at all unless `integrations` is\n * configured, which most consumers never do. `integrations.ts` is a separate\n * build entry (see `sdk/core/rollup.config.mjs`) so that this stays a real\n * `import()` in the published output rather than being flattened back into the\n * main chunk, and a bundler can therefore keep it out of the initial download.\n *\n * This indirection exists so that `@cookieyes/react` does not need a static\n * import of `runIntegrations` to do the same thing. A dynamic\n * `import(\"@cookieyes/core\")` from the adapter would pull the whole barrel and\n * defeat the split; a static import of *this* function costs a few bytes and\n * leaves the heavy module behind one `import()` that only core knows about.\n *\n * @internal — consumed by framework adapters, not part of the public API.\n */\nexport async function _loadIntegrations(): Promise<{\n runIntegrations: (list: Integration[], host: IntegrationHost) => IntegrationRunner;\n warnOverlappingVendors: (ids: string[], vendors: string[]) => void;\n warnUnknownCategories: (list: Integration[], known: string[]) => void;\n}> {\n const module = await import(\"./integrations.js\");\n return {\n runIntegrations: module.runIntegrations,\n warnOverlappingVendors: module.warnOverlappingVendors,\n warnUnknownCategories: module.warnUnknownCategories,\n };\n}\n","import {\n defaultTranslations,\n getTextDirection,\n mergeTranslations,\n pickLanguage,\n primaryOf,\n} from \"./i18n.js\";\nimport type {\n CategoryText,\n I18nConfig,\n LanguageInfo,\n PartialTranslations,\n TranslationMap,\n} from \"./types.js\";\n\nexport type LanguageController = {\n /** Text for the active language (English fills any gaps). */\n getTranslations: () => TranslationMap;\n getLanguageInfo: () => LanguageInfo;\n /** Switch language live; loads via `i18n.loadLanguage` if not already present. */\n setLanguage: (tag: string) => Promise<void>;\n /**\n * The customer's own text for a category in the *active* language, if they\n * provided it — kept separate from the English defaults so a translation can\n * win over a category's config label without the English default masking it.\n */\n getCategoryText: (id: string) => Partial<CategoryText> | undefined;\n};\n\n/**\n * Owns the active language: which one is showing, its (English-filled) text,\n * and switching to another — loading it on demand when a loader is provided.\n * `onChange` runs after every switch so the UI can re-render.\n *\n * Framework-agnostic: used by both the core and React runtimes, so they behave\n * identically.\n */\nexport function createLanguageController(\n i18n: I18nConfig | undefined,\n onChange: () => void,\n): LanguageController {\n const messages: Record<string, PartialTranslations> = { ...i18n?.messages };\n const loadLanguage = i18n?.loadLanguage;\n const warned = new Set<string>();\n\n let language = pickLanguage(i18n);\n let translations = build(language);\n let info = buildInfo();\n\n function messagesFor(tag: string): PartialTranslations | undefined {\n return messages[tag] ?? messages[primaryOf(tag)];\n }\n // English is always available — it's the base every language merges onto,\n // so switching to it never needs a loader even when `messages` has no \"en\".\n function isAvailable(tag: string): boolean {\n return primaryOf(tag) === \"en\" || messagesFor(tag) !== undefined;\n }\n function build(tag: string): TranslationMap {\n return mergeTranslations(defaultTranslations, messagesFor(tag));\n }\n function buildInfo(): LanguageInfo {\n return {\n language,\n direction: getTextDirection(language),\n languages: Array.from(new Set([\"en\", ...Object.keys(messages)])),\n };\n }\n function apply(tag: string): void {\n language = tag;\n translations = build(tag);\n info = buildInfo();\n onChange();\n }\n function warnMissing(tag: string, err?: unknown): void {\n if (warned.has(tag) || typeof console === \"undefined\") return;\n warned.add(tag);\n // eslint-disable-next-line no-console\n console.warn(\n `[cookieyes] no translations for language \"${tag}\"; staying on \"${language}\". ` +\n \"Add it to i18n.messages or provide i18n.loadLanguage.\",\n err ?? \"\",\n );\n }\n function setLanguage(tag: string): Promise<void> {\n // Already have it (or it's English) → switch immediately.\n if (isAvailable(tag)) {\n apply(tag);\n return Promise.resolve();\n }\n // Otherwise ask the loader (keeps showing the current language until it lands).\n if (loadLanguage) {\n return Promise.resolve()\n .then(() => loadLanguage(tag))\n .then((loaded) => {\n messages[tag] = loaded;\n apply(tag);\n })\n .catch((err) => warnMissing(tag, err));\n }\n warnMissing(tag);\n return Promise.resolve();\n }\n function getCategoryText(id: string): Partial<CategoryText> | undefined {\n return messagesFor(language)?.categories?.[id];\n }\n\n // An explicit starting language that isn't bundled but has a loader: fetch it\n // now, so we honour the request (English shows until it lands). Browser-only —\n // never fire a load during server rendering.\n if (loadLanguage && i18n?.locale && !isAvailable(i18n.locale) && typeof window !== \"undefined\") {\n void setLanguage(i18n.locale);\n }\n\n return {\n getTranslations: () => translations,\n getLanguageInfo: () => info,\n setLanguage,\n getCategoryText,\n };\n}\n","import type { ScriptEntry } from \"./types.js\";\n\nconst registry = new Map<string, ScriptEntry>();\nconst injected = new Map<string, HTMLScriptElement>();\n\nexport function registerScript(entry: ScriptEntry): void {\n registry.set(entry.id, entry);\n}\n\n/**\n * Inject each registered script whose category is granted. Pass the *committed*\n * consent so an unsaved toggle never loads a script. Once injected, a script\n * stays — revoking doesn't unload it (that can't undo what already ran); the\n * block takes effect on the next page load.\n */\nexport function applyScripts(categories: Record<string, boolean>): void {\n if (typeof document === \"undefined\") return;\n\n for (const [id, entry] of registry) {\n if (categories[entry.category] !== true) continue;\n if (injected.has(id)) continue;\n injectScript(id, entry);\n }\n}\n\n/**\n * @internal Test-only — empty the script registry and forget what was injected.\n * Mirrors {@link _clearStopHandlers}. When a `document` is present the injected\n * `<script>` elements are removed from it too, so one test can never leave a\n * gated script behind for the next one. Safe to call with nothing registered.\n */\nexport function _clearScriptRegistry(): void {\n if (typeof document !== \"undefined\") {\n for (const el of injected.values()) el.remove();\n }\n registry.clear();\n injected.clear();\n}\n\nfunction injectScript(id: string, entry: ScriptEntry): void {\n const existing = document.getElementById(id);\n if (existing) return;\n\n const el = document.createElement(\"script\");\n el.id = id;\n el.src = entry.src;\n el.async = true;\n if (entry.onLoad) {\n el.addEventListener(\"load\", entry.onLoad, { once: true });\n }\n document.head.appendChild(el);\n injected.set(id, el);\n}\n","import type { ConsentCategory } from \"./types.js\";\n\n/**\n * A tool that can be stopped (and optionally resumed) at runtime when consent\n * for its category changes — no page reload needed. `stop()` is called when the\n * category is revoked; `resume()` (if provided) when it's re-granted.\n *\n * If `stop()` throws, that tool is treated as \"couldn't be stopped cleanly\" and\n * falls back to the reload notice for that one tool — it never breaks the page.\n */\nexport type StopHandler = {\n id: string;\n category: ConsentCategory;\n stop: () => void;\n resume?: (() => void) | undefined;\n};\n\n/**\n * A tool with no known clean runtime stop — revoking its category can only be\n * fully applied by reloading the page. Registering one means \"if this category\n * is revoked, show the visitor the reload notice.\"\n */\nexport type ReloadOnlyHandler = {\n id: string;\n category: ConsentCategory;\n needsReload: true;\n};\n\nexport type AnyStopHandler = StopHandler | ReloadOnlyHandler;\n\nfunction isReloadOnly(h: AnyStopHandler): h is ReloadOnlyHandler {\n return \"needsReload\" in h && h.needsReload === true;\n}\n\n/**\n * Built-in, first-party integrations. Each maps to either a clean stop-handler\n * or a reload-only marker (see the audit in the README).\n *\n * Note: Google Analytics 4 and Google Tag Manager are **not** listed here.\n * They're governed by Google Consent Mode v2, which the SDK broadcasts\n * automatically whenever a `dataLayer` is present (see google-consent-mode.ts)\n * — on load and on every consent change, derived from each category's `gcm`\n * mapping. So you don't register them as integrations; just add the standard\n * Consent Mode default snippet and the SDK owns the updates.\n *\n * VERIFIED clean-stop vendors (documented, stable runtime opt-out):\n * - `meta` — `fbq('consent','revoke'|'grant')`, Meta's official consent API.\n *\n * The rest have no confident, documented runtime stop, so they're modelled as\n * reload-only (Story 1's honest answer). Upgrading any of them to a clean-stop\n * later is a one-line change here once a real API is confirmed.\n */\nexport type BuiltInIntegration =\n | { vendor: \"meta\"; category?: ConsentCategory | undefined }\n | { vendor: \"tiktok\"; category?: ConsentCategory | undefined }\n | { vendor: \"linkedin\"; category?: ConsentCategory | undefined }\n | { vendor: \"hotjar\"; category?: ConsentCategory | undefined }\n | { vendor: \"segment\"; category?: ConsentCategory | undefined };\n\ntype WindowWithVendors = Window &\n typeof globalThis & {\n fbq?: (...args: unknown[]) => void;\n };\n\nexport function resolveBuiltInIntegration(cfg: BuiltInIntegration): AnyStopHandler {\n switch (cfg.vendor) {\n case \"meta\":\n return {\n id: \"meta\",\n category: cfg.category ?? \"advertisement\",\n stop: () => (window as WindowWithVendors).fbq?.(\"consent\", \"revoke\"),\n resume: () => (window as WindowWithVendors).fbq?.(\"consent\", \"grant\"),\n };\n // No confident documented runtime stop — reload-only (see README audit).\n case \"tiktok\":\n return { id: \"tiktok\", category: cfg.category ?? \"advertisement\", needsReload: true };\n case \"linkedin\":\n return { id: \"linkedin\", category: cfg.category ?? \"advertisement\", needsReload: true };\n case \"hotjar\":\n return { id: \"hotjar\", category: cfg.category ?? \"analytics\", needsReload: true };\n case \"segment\":\n return { id: \"segment\", category: cfg.category ?? \"analytics\", needsReload: true };\n }\n}\n\n// --- Registry (module-level, mirrors scripts.ts) ---\n\nconst handlers = new Map<string, AnyStopHandler>();\n// Tracks which clean-stop handlers are currently in the \"stopped\" state, so we\n// only fire stop()/resume() on an actual transition, not on every save.\nconst stopped = new Set<string>();\n// Tracks reload-only handlers whose category is currently granted (so the tool\n// is presumed to be running). A reload notice is raised only when one of these\n// transitions granted → denied — i.e. a genuine withdrawal of something that\n// could be active — not on a standing/first-time reject where it never ran.\nconst reloadActive = new Set<string>();\n\nexport function registerStopHandler(handler: AnyStopHandler): void {\n handlers.set(handler.id, handler);\n}\n\n/** Test-only: reset registry + transition state between cases. */\nexport function _clearStopHandlers(): void {\n handlers.clear();\n stopped.clear();\n reloadActive.clear();\n}\n\nexport type StopHandlerResult = {\n /** Ids of reload-only tools just revoked (were granted, now denied). */\n reloadRequiredBy: string[];\n};\n\n/**\n * Reconcile every registered handler against the current consent state:\n * - clean handler denied → run `stop()` (once), or flag reload if it throws\n * - clean handler granted → run `resume()` (once) for anything previously stopped\n * - reload-only handler → flag reload only on a granted → denied transition\n *\n * Never throws: a failing `stop()` is downgraded to a reload requirement for\n * that one tool, so a broken handler can't break the page.\n */\nexport function applyStopHandlers(categories: Record<string, boolean>): StopHandlerResult {\n const reloadRequiredBy: string[] = [];\n\n for (const handler of handlers.values()) {\n const denied = categories[handler.category] !== true;\n\n if (isReloadOnly(handler)) {\n if (denied) {\n // Only a genuine revoke (was granted/active, now denied) warrants the\n // notice — a first-time or standing reject never had it running.\n if (reloadActive.has(handler.id)) {\n reloadRequiredBy.push(handler.id);\n reloadActive.delete(handler.id);\n }\n } else {\n reloadActive.add(handler.id);\n }\n continue;\n }\n\n if (denied) {\n if (!stopped.has(handler.id)) {\n try {\n handler.stop();\n stopped.add(handler.id);\n } catch {\n // Couldn't stop cleanly → fall back to the reload notice for this one.\n reloadRequiredBy.push(handler.id);\n }\n }\n } else if (stopped.has(handler.id)) {\n stopped.delete(handler.id);\n try {\n handler.resume?.();\n } catch {\n // A failed resume is non-fatal; the next accept re-attempts nothing\n // worse than the tool staying stopped until reload.\n }\n }\n }\n\n return { reloadRequiredBy };\n}\n\n/**\n * Load-time initialization: reflect the *full* stored consent state in both\n * directions, once, so tools start in the right mode from first paint.\n *\n * This is distinct from {@link applyStopHandlers} (which only fires on\n * transitions): a returning visitor who previously *granted* a category must\n * get a `resume()` at load — e.g. Consent Mode `update: granted` — otherwise\n * they stay stuck in the page's deny-by-default state despite having consented.\n * Denied categories get `stop()`. It never raises a reload notice (a fresh load\n * needs no \"reload to apply\"), but it *does* seed reload-only tools' active\n * state from the stored consent, so a later live revoke is correctly detected.\n */\nexport function initStopHandlers(categories: Record<string, boolean>): void {\n for (const handler of handlers.values()) {\n const denied = categories[handler.category] !== true;\n\n if (isReloadOnly(handler)) {\n // Seed active state (granted = presumed running) so a later granted →\n // denied revoke raises the notice; never raise it at load itself.\n if (denied) reloadActive.delete(handler.id);\n else reloadActive.add(handler.id);\n continue;\n }\n\n try {\n if (denied) {\n handler.stop();\n stopped.add(handler.id);\n } else {\n // Granted at load → reflect stored consent (e.g. Consent Mode grant).\n stopped.delete(handler.id);\n handler.resume?.();\n }\n } catch {\n // Never let a vendor handler break page load; state simply stays as-is.\n }\n }\n}\n","import type { ConsentPayload, ConsentSnapshot } from \"./types.js\";\n\nexport function buildConsentPayload(snapshot: ConsentSnapshot, region?: string): ConsentPayload {\n const payload: ConsentPayload = {\n consentId: snapshot.consentId,\n categories: snapshot.categories,\n regulation: snapshot.regulation,\n domain: typeof window !== \"undefined\" ? window.location.hostname : \"unknown\",\n };\n if (region) payload.region = region;\n return payload;\n}\n\nexport async function pushConsent(\n apiUrl: string,\n apiKey: string | undefined,\n snapshot: ConsentSnapshot,\n region?: string,\n): Promise<void> {\n const payload = buildConsentPayload(snapshot, region);\n\n const headers: Record<string, string> = {\n \"Content-Type\": \"application/json\",\n };\n if (apiKey) headers.Authorization = `Bearer ${apiKey}`;\n\n try {\n await fetch(apiUrl, {\n method: \"POST\",\n headers,\n body: JSON.stringify(payload),\n keepalive: true,\n });\n } catch {\n // Backend sync is best-effort — never fail the consent flow\n }\n}\n","import { resolveCategories } from \"./categories.js\";\nimport {\n clearConsentCookie,\n defaultSnapshot,\n generateConsentId,\n rawFieldsToSnapshot,\n readConsentCookie,\n writeConsentCookie,\n} from \"./cookie.js\";\nimport { broadcastGoogleConsent, warnOverlappingGcm } from \"./google-consent-mode.js\";\nimport { applyScripts, registerScript } from \"./scripts.js\";\nimport {\n applyStopHandlers,\n initStopHandlers,\n registerStopHandler,\n resolveBuiltInIntegration,\n} from \"./stop-handlers.js\";\nimport { buildConsentPayload, pushConsent } from \"./sync.js\";\nimport type {\n ConsentCategory,\n ConsentConfig,\n ConsentManager,\n ConsentSnapshot,\n ReloadNoticeState,\n ScriptEntry,\n} from \"./types.js\";\n\nexport function createConsentManager(config: ConsentConfig): ConsentManager {\n const listeners = new Set<(state: ConsentSnapshot) => void>();\n\n // Resolve the category taxonomy (built-in five, or the customer's, or a\n // validated fallback to the five). Everything below is driven by this.\n const resolved = resolveCategories(config.categories);\n\n // How to combine multiple categories mapping to the same Google signal.\n const gcmMatch = config.googleConsentMatch ?? \"any\";\n // If the taxonomy has a lossy overlap and the customer hasn't chosen a mode, warn.\n if (config.googleConsentMatch === undefined) warnOverlappingGcm(resolved);\n\n let state: ConsentSnapshot;\n let isPreferencesOpen = false;\n let lastPersistedCategories: Record<string, boolean>;\n // Consent actually in effect — changes only on a real decision (accept /\n // reject / save / reset / load), never on a dialog toggle. Gating reads this;\n // `state.categories` stays live to drive the dialog checkboxes.\n let committedCategories: Record<string, boolean>;\n\n /** Build a category map over the resolved ids; required ids are always granted. */\n function buildCategories(\n grantNonRequired: (id: ConsentCategory) => boolean,\n ): Record<string, boolean> {\n const cats: Record<string, boolean> = {};\n for (const id of resolved.ids) {\n cats[id] = resolved.requiredIds.has(id) ? true : grantNonRequired(id);\n }\n return cats;\n }\n\n // Reload-notice state: `reasons` is the set of handler ids that currently\n // can't be stopped cleanly; `dismissed` suppresses the notice until a\n // genuinely different set of reasons appears (so it doesn't keep popping up).\n let reloadReasons: string[] = [];\n let reloadDismissed = false;\n\n // Register stop-handlers: built-in integrations + the customer's own.\n for (const integration of config.integrations ?? []) {\n registerStopHandler(resolveBuiltInIntegration(integration));\n }\n for (const handler of config.customStopHandlers ?? []) {\n registerStopHandler(handler);\n }\n\n // --- Synchronous initialisation from cookie ---\n const rawFields = readConsentCookie();\n const savedRegulation = config.regulation ?? \"DEFAULT\";\n\n // Decide whether stored consent is still valid for the current taxonomy:\n // - tax stamp matches → reuse it.\n // - legacy cookie (no stamp) on the default taxonomy → reuse it (upgrade-safe:\n // never resets existing users who were on the built-in five).\n // - otherwise (taxonomy changed) → re-request from scratch.\n const storedTax = rawFields?.tax;\n const taxMatches = storedTax === resolved.taxonomyHash;\n const legacyCookie = storedTax === undefined;\n const storedConsentValid =\n rawFields != null && (taxMatches || (legacyCookie && resolved.isDefault));\n\n if (rawFields != null && storedConsentValid) {\n state = rawFieldsToSnapshot(rawFields, savedRegulation, resolved);\n } else {\n const consentId = rawFields?.consentid ?? generateConsentId();\n state = defaultSnapshot(consentId, savedRegulation, resolved);\n // Taxonomy changed under an existing visitor → drop the stale cookie so we\n // genuinely re-request rather than leaving a mismatched record behind.\n if (rawFields != null) clearConsentCookie();\n\n // CCPA is an opt-out model: consent is implicit from page load.\n // Write the cookie immediately so all-category values (yes) are available\n // to third-party scripts before the user has explicitly acted.\n if (state.regulation === \"CCPA\") {\n writeConsentCookie(state);\n }\n }\n // The state's taxonomy is always the resolved one — stamp it so cookies\n // written from here on carry the current signature (upgrades legacy cookies).\n state = { ...state, taxonomyHash: resolved.taxonomyHash };\n\n // GPC \"do not sell\": until the visitor explicitly acts, an incoming CCPA\n // opt-out signal starts them opted out — non-required categories off — so no\n // gated script or embed runs before they choose. An explicit choice\n // (hasActed) always wins over the signal. `gpcOptOut` is only set for CCPA,\n // where the cookie is written at load, so re-write it to carry the opt-out.\n if (config.gpcOptOut && !state.hasActed) {\n state = { ...state, categories: buildCategories(() => false) };\n writeConsentCookie(state);\n }\n lastPersistedCategories = { ...state.categories };\n committedCategories = { ...state.categories };\n\n // Fire onConsentReady synchronously on next tick.\n Promise.resolve().then(() => config.onConsentReady?.(state));\n\n function notify(): void {\n const snap = snapshot();\n for (const fn of listeners) fn(snap);\n // Scripts are applied from committed consent (below), not here — so a dialog\n // toggle, which calls notify, never loads a gated script before save.\n }\n\n /** Apply script gating against the committed consent, not the working toggles. */\n function applyCommittedScripts(): void {\n applyScripts(committedCategories);\n }\n\n function snapshot(): ConsentSnapshot {\n return {\n consentId: state.consentId,\n hasActed: state.hasActed,\n categories: { ...state.categories },\n regulation: state.regulation,\n lastRenewed: state.lastRenewed,\n taxonomyHash: state.taxonomyHash,\n };\n }\n\n /** Returns whether the reasons actually changed, so callers know to re-notify. */\n function setReloadReasons(reasons: string[]): boolean {\n const changed =\n reasons.length !== reloadReasons.length || reasons.some((r, i) => r !== reloadReasons[i]);\n if (changed) {\n reloadReasons = reasons;\n // A genuinely new set of blocked tools → allow the notice to show again.\n reloadDismissed = false;\n }\n return changed;\n }\n\n /**\n * Commit a decision, then run its side effects.\n *\n * Order matters, and not for the reason you might expect. Reordering work\n * inside one synchronous task cannot make the browser paint sooner — it can't\n * paint mid-task — so this is not a latency optimisation (measured: ~11ms\n * click-to-response either way). What it buys is that the visible response no\n * longer depends on third-party code succeeding.\n *\n * Previously every side effect ran *before* `notify()`: gated-script injection,\n * integration stop handlers, and the Google Consent Mode broadcast. Two of\n * those can throw for reasons outside our control — `injectScript` touches the\n * DOM, and `broadcastGoogleConsent` calls `dataLayer.push`, which GTM replaces\n * with its own function that runs customer-authored templates. A throw there\n * meant `notify()` never ran: the cookie said \"accepted\" but the banner stayed\n * on screen until reload. The visitor's click appeared to do nothing.\n *\n * So: commit the decision and tell the UI first, then run each side effect in\n * isolation, so no single failure can strand the banner or block the others.\n */\n function persist(): void {\n state = {\n ...state,\n hasActed: true,\n lastRenewed: Date.now(),\n };\n // Durability first: the decision must survive even if everything below fails.\n writeConsentCookie(state);\n\n // Detect \"revoke\" — any category that was previously consented but now isn't.\n // Computed before `lastPersistedCategories` is overwritten below.\n let didRevoke = false;\n for (const id of resolved.ids) {\n if (lastPersistedCategories[id] && !state.categories[id]) {\n didRevoke = true;\n break;\n }\n }\n lastPersistedCategories = { ...state.categories };\n // This is a real decision → commit it.\n committedCategories = { ...state.categories };\n\n // The visible response: the banner closes from here. Everything after this\n // point is a side effect that must not be able to prevent it.\n notify();\n config.onConsentUpdate?.(state);\n\n // Best-effort: swallow both sync throws and async rejections so a\n // broken/missing backend never breaks the consent UX.\n if (config.backend) {\n try {\n Promise.resolve(config.backend.persist(buildConsentPayload(state, config.region))).catch(\n () => undefined,\n );\n } catch {\n // sync throw from .persist itself\n }\n } else if (config.apiUrl) {\n void pushConsent(config.apiUrl, config.apiKey, state, config.region);\n }\n\n // Apply script gating from the committed consent. Isolated: a DOM failure\n // here must not stop the integrations below from being told about the change.\n try {\n applyCommittedScripts();\n } catch {\n // Injection is best-effort; consent is already committed and broadcast.\n }\n\n // Stop (or resume) integrations to match the new consent state — without a\n // reload. Anything with no clean runtime stop comes back in reloadRequiredBy\n // and surfaces the reload notice instead of silently continuing to track.\n let reasonsChanged = false;\n try {\n const { reloadRequiredBy } = applyStopHandlers(committedCategories);\n reasonsChanged = setReloadReasons(reloadRequiredBy);\n } catch {\n // Individual handlers already fail safe; this guards the loop itself.\n }\n\n // Broadcast Google Consent Mode signals for the new state (no-op unless a\n // dataLayer is present). Derived from the category → GCM-signal mapping.\n // Still in the same task as the click, so tags see the update immediately.\n try {\n broadcastGoogleConsent(resolved, committedCategories, gcmMatch);\n } catch {\n // A hostile or broken `dataLayer.push` (GTM replaces it) must not strand\n // the banner — the case this whole ordering exists to prevent.\n }\n\n // The reload notice is derived above, i.e. after the notify() that closed the\n // banner, so it needs its own notification to reach the UI.\n if (reasonsChanged) notify();\n\n // Legacy opt-in hard reload (off by default). The stop-handlers above are\n // the safe path; this remains only for customers who explicitly want it.\n // pushConsent uses keepalive: true so it survives the navigation.\n if (didRevoke && config.reloadOnRevoke && typeof window !== \"undefined\") {\n window.location.reload();\n }\n }\n\n const manager: ConsentManager = {\n get consentId() {\n return state.consentId;\n },\n get hasActed() {\n return state.hasActed;\n },\n get categories() {\n return { ...state.categories };\n },\n get committedCategories() {\n return { ...committedCategories };\n },\n get regulation() {\n return state.regulation;\n },\n get lastRenewed() {\n return state.lastRenewed;\n },\n get taxonomyHash() {\n return state.taxonomyHash;\n },\n get isPreferencesOpen() {\n return isPreferencesOpen;\n },\n\n acceptAll() {\n state = { ...state, categories: buildCategories(() => true) };\n isPreferencesOpen = false;\n persist();\n },\n\n rejectAll() {\n state = { ...state, categories: buildCategories(() => false) };\n isPreferencesOpen = false;\n persist();\n },\n\n acceptSelected(categories: ConsentCategory[]) {\n state = { ...state, categories: buildCategories((id) => categories.includes(id)) };\n isPreferencesOpen = false;\n persist();\n },\n\n updateCategory(category: ConsentCategory, value: boolean) {\n // Required categories are always on and can't be toggled off.\n if (resolved.requiredIds.has(category)) return;\n // Ignore ids that aren't part of the configured taxonomy.\n if (!resolved.ids.includes(category)) return;\n state = {\n ...state,\n categories: { ...state.categories, [category]: value },\n };\n notify();\n },\n\n savePreferences() {\n isPreferencesOpen = false;\n persist();\n },\n\n resetConsent() {\n clearConsentCookie();\n const consentId = generateConsentId();\n state = defaultSnapshot(consentId, state.regulation, resolved);\n committedCategories = { ...state.categories };\n lastPersistedCategories = { ...state.categories };\n isPreferencesOpen = false;\n // Realign clean-stop flags with the reset state; clear any reload notice\n // (a reset re-prompts, so a stale \"reload to apply\" message is wrong).\n applyStopHandlers(committedCategories);\n broadcastGoogleConsent(resolved, committedCategories, gcmMatch);\n reloadReasons = [];\n reloadDismissed = false;\n notify();\n },\n\n showPreferences() {\n isPreferencesOpen = true;\n notify();\n },\n\n hidePreferences() {\n isPreferencesOpen = false;\n notify();\n },\n\n subscribe(listener: (state: ConsentSnapshot) => void) {\n listeners.add(listener);\n return () => listeners.delete(listener);\n },\n\n registerScript(entry: ScriptEntry) {\n registerScript(entry);\n applyCommittedScripts();\n },\n\n get reloadNotice(): ReloadNoticeState {\n return {\n required: reloadReasons.length > 0 && !reloadDismissed,\n reasons: [...reloadReasons],\n };\n },\n\n dismissReloadNotice() {\n if (reloadDismissed) return;\n reloadDismissed = true;\n notify();\n },\n };\n\n // Apply scripts for any that were already registered before manager created\n applyCommittedScripts();\n\n // Reflect the full stored consent at load — in both directions — so tools\n // start in the right mode from first paint. In particular a returning\n // visitor who previously *granted* a category gets a resume (e.g. Consent\n // Mode `update: granted`), instead of being stuck in the page's\n // deny-by-default state. No reload notice at load (that's only for live\n // revokes). Live changes after this go through applyStopHandlers.\n // Both of the following are best-effort and individually isolated, for the\n // same reason as in persist() — but the stakes at load are higher. These run\n // inside createConsentManager, so an uncaught throw propagates out of\n // initCookieYes and the SDK never mounts: no banner, no consent prompt at\n // all. `dataLayer.push` is the realistic culprit (GTM replaces it with a\n // function that runs customer-authored templates), and a broken third-party\n // tag must not be able to take the consent banner down with it.\n try {\n initStopHandlers(state.categories);\n } catch {\n // Integrations start in the page's deny-by-default state; consent is intact.\n }\n\n // Broadcast the initial Consent Mode state on load (no-op unless a Google\n // dataLayer is present), so Google tags see the returning visitor's choice\n // — or the deny-by-default for a first-time visitor — from first paint.\n try {\n broadcastGoogleConsent(resolved, state.categories, gcmMatch);\n } catch {\n // Google tags keep whatever default the page set; the banner still works.\n }\n\n return manager;\n}\n","import type { RegionConfig, RegionDecision, Regulation } from \"./types.js\";\n\n/** Anything with a header getter — a `Headers` object, Next's `headers()`, etc. */\nexport type HeaderSource = { get(name: string): string | null | undefined };\n\n// Location headers hosting providers add automatically, tried in order.\nconst GEO_HEADERS: ReadonlyArray<{ country: string; region?: string }> = [\n // Vercel — country + region give e.g. \"US-CA\".\n { country: \"x-vercel-ip-country\", region: \"x-vercel-ip-country-region\" },\n // Cloudflare — country only by default (a Worker/rule can add a region header).\n { country: \"cf-ipcountry\" },\n];\n\n/**\n * Read the visitor's region from request headers on the server (Next.js, or any\n * framework). Pass the request's headers and get back a region like \"US-CA\" or\n * \"DE\" (or undefined). By default it reads the well-known Vercel/Cloudflare\n * headers; pass `{ header }` to read your own instead. Hand the result to\n * `region.detect` in your client config.\n */\nexport function regionFromHeaders(\n headers: HeaderSource,\n options?: { header?: string },\n): string | undefined {\n if (options?.header) {\n return headers.get(options.header) || undefined;\n }\n for (const { country, region } of GEO_HEADERS) {\n const countryCode = headers.get(country);\n if (!countryCode) continue;\n const regionCode = region ? headers.get(region) : undefined;\n return regionCode ? `${countryCode}-${regionCode}` : countryCode;\n }\n return undefined;\n}\n\n/** True when the browser is sending the GPC \"do not sell/share\" signal. */\nexport function readGpc(): boolean {\n return (\n typeof navigator !== \"undefined\" &&\n (navigator as { globalPrivacyControl?: boolean }).globalPrivacyControl === true\n );\n}\n\n// Match the full region first (\"US-CA\"), then its country part (\"US\").\nfunction mapRegion(\n map: Record<string, Regulation> | undefined,\n region: string,\n): Regulation | undefined {\n if (!map) return undefined;\n return map[region] ?? map[region.split(\"-\")[0] ?? \"\"];\n}\n\n/**\n * Decide which regulation applies from the visitor's region alone. A manual\n * regulation always wins; otherwise the detected region is mapped to a\n * regulation, and anything unknown falls back to the strictest — never to the\n * lightest, so a required banner is never skipped.\n *\n * GPC is deliberately *not* considered here: it never changes which banner\n * shows (that is geo only), it only opts a CCPA visitor out client-side. Server\n * and client therefore resolve the same regulation, with no hydration mismatch.\n */\nexport function resolveRegion(config: RegionConfig, manual?: Regulation): RegionDecision {\n const strictest = config.strictest ?? \"GDPR\";\n\n // A manual regulation always wins.\n if (manual) {\n if (config.detect && typeof console !== \"undefined\") {\n // eslint-disable-next-line no-console\n console.warn(\n \"[cookieyes] `regulation` is set manually, so region detection is ignored. \" +\n \"Remove one of them to clear the conflict.\",\n );\n }\n return { region: undefined, regulation: manual, source: \"manual\", confidence: \"high\" };\n }\n\n // Detect the region and map it. Unknown/unmapped → strictest.\n const region = config.detect?.();\n const mapped = region ? mapRegion(config.map, region) : undefined;\n const regulation: Regulation = mapped ?? strictest;\n const source: RegionDecision[\"source\"] = mapped ? \"detected\" : \"strictest\";\n const confidence: RegionDecision[\"confidence\"] = mapped ? \"high\" : \"low\";\n\n return { region, regulation, source, confidence };\n}\n\n/**\n * @internal Dev aid for `region.debug`: print how the regulation was decided,\n * plus whether GPC started the visitor opted out. Shared by both runtimes.\n */\nexport function _logRegionDecision(decision: RegionDecision, gpcOptOut: boolean): void {\n if (typeof console === \"undefined\") return;\n // eslint-disable-next-line no-console\n console.info(\"[cookieyes] region detection\", {\n region: decision.region,\n regulation: decision.regulation,\n source: decision.source,\n confidence: decision.confidence,\n gpcOptOut,\n });\n}\n","import { resolveCategories } from \"./categories.js\";\nimport { _normalizeConfig } from \"./config.js\";\nimport { _warnBuiltInIntegrationsDeprecated, _warnOfflineModeDeprecated } from \"./deprecations.js\";\nimport { type ConsentEmitter, createConsentEmitter } from \"./events.js\";\nimport type { IntegrationRunner } from \"./integrations.js\";\nimport { _loadIntegrations } from \"./integrations-lazy.js\";\nimport { createLanguageController } from \"./language.js\";\nimport { createConsentManager } from \"./manager.js\";\nimport {\n _installRegisteredNetworkBlocker,\n _uninstallRegisteredNetworkBlocker,\n} from \"./network-blocker-slot.js\";\nimport { _logRegionDecision, readGpc, resolveRegion } from \"./region.js\";\nimport type {\n ActiveUI,\n ConsentCategory,\n ConsentChangePayload,\n ConsentConfig,\n ConsentRuntime,\n ConsentStore,\n ConsentStoreState,\n CookieYesConfig,\n RegionConfig,\n RegionDecision,\n Regulation,\n} from \"./types.js\";\n\n/** True when a CCPA visitor's browser sends GPC and we're set to honour it. */\nfunction wantsGpcOptOut(regulation: Regulation, region: RegionConfig | undefined): boolean {\n return regulation === \"CCPA\" && (region?.honorGpc ?? true) && readGpc();\n}\n\nfunction splitCategories(categories: Record<string, boolean>): ConsentChangePayload {\n const allowed: ConsentCategory[] = [];\n const denied: ConsentCategory[] = [];\n for (const cat of Object.keys(categories) as ConsentCategory[]) {\n if (categories[cat]) allowed.push(cat);\n else denied.push(cat);\n }\n return { allowedCategories: allowed, deniedCategories: denied };\n}\n\nlet _runtime: ConsentRuntime | null = null;\nlet _integrationRunner: IntegrationRunner | null = null;\n/**\n * Bumped by every runtime creation and by every reset, so a chunk that arrives\n * after the runtime it was requested for has gone away can tell and do nothing.\n * Without it, a `resetConsentRuntime()` between the `import()` and its\n * resolution would be followed by the old runner installing itself against a\n * manager that is no longer current.\n */\nlet _integrationGeneration = 0;\n\nexport function getOrCreateConsentRuntime(config: CookieYesConfig): ConsentRuntime {\n if (_runtime) return _runtime;\n\n // `\"offline\"` is a deprecated alias for `\"cookie-only\"` — same behavior, one\n // warning per page load. Checked on the raw config before normalization.\n if (config.mode === \"offline\") _warnOfflineModeDeprecated();\n\n // Collapse deprecated aliases (`overrides.regulation` → `regulation`,\n // `backendURL` → `apiUrl`) into the one canonical shape both packages share.\n const options = _normalizeConfig(config);\n const changeListeners = new Set<(payload: ConsentChangePayload) => void>();\n const userOnConsentUpdate = options.onConsentUpdate;\n // Assigned right after the manager exists; only ever read from within\n // onConsentUpdate, which can't fire until the visitor acts (post-init).\n let emitter: ConsentEmitter;\n\n // Resolve which regulation applies (geo-detection if configured, else the\n // manual/default). Drives the banner and is recorded on the consent payload.\n const regionDecision: RegionDecision = options.region\n ? resolveRegion(options.region, options.regulation)\n : {\n region: undefined,\n regulation: options.regulation ?? \"DEFAULT\",\n source: \"manual\",\n confidence: \"high\",\n };\n\n const cfg: ConsentConfig = {};\n if (options.mode === \"self-hosted\") {\n if (options.backend) cfg.backend = options.backend;\n else if (options.apiUrl) cfg.apiUrl = options.apiUrl;\n }\n if (options.apiKey) cfg.apiKey = options.apiKey;\n cfg.regulation = regionDecision.regulation;\n if (regionDecision.region) cfg.region = regionDecision.region;\n // Honour the browser's GPC \"do not sell\" signal on a CCPA banner: start the\n // visitor opted out. GPC never changes the regulation (that's geo only).\n const gpcOptOut = wantsGpcOptOut(regionDecision.regulation, options.region);\n if (gpcOptOut) cfg.gpcOptOut = true;\n if (options.region?.debug) _logRegionDecision(regionDecision, gpcOptOut);\n if (options.colorScheme) cfg.colorScheme = options.colorScheme;\n if (options.theme) cfg.theme = options.theme;\n if (options.reloadOnRevoke) cfg.reloadOnRevoke = options.reloadOnRevoke;\n if (options.googleConsentMatch) cfg.googleConsentMatch = options.googleConsentMatch;\n if (options.builtInIntegrations && options.builtInIntegrations.length > 0) {\n _warnBuiltInIntegrationsDeprecated();\n cfg.integrations = options.builtInIntegrations;\n }\n if (options.customStopHandlers) cfg.customStopHandlers = options.customStopHandlers;\n if (options.categories) cfg.categories = options.categories;\n if (options.onConsentReady) cfg.onConsentReady = options.onConsentReady;\n\n cfg.onConsentUpdate = (snap) => {\n userOnConsentUpdate?.(snap);\n emitter.push(snap.categories);\n const payload = splitCategories(snap.categories);\n for (const fn of changeListeners) fn(payload);\n };\n\n const manager = createConsentManager(cfg);\n emitter = createConsentEmitter(() => manager.committedCategories);\n // Same resolution the manager uses internally — exposed so a custom UI can\n // iterate the taxonomy actually in effect (custom list or built-in five).\n const resolved = resolveCategories(options.categories);\n\n // One listener set drives `consentStore.subscribe`, fed by both consent\n // changes and language switches, so a custom UI re-renders on either.\n const stateListeners = new Set<(state: ConsentStoreState) => void>();\n function notifyState(): void {\n const state = buildState();\n for (const fn of stateListeners) fn(state);\n }\n manager.subscribe(notifyState);\n const language = createLanguageController(options.i18n, notifyState);\n\n function activeUI(): ActiveUI {\n if (manager.isPreferencesOpen) return \"dialog\";\n if (!manager.hasActed) return \"banner\";\n return null;\n }\n\n function buildState(): ConsentStoreState {\n // `consents`/`categories` are live (drive checkboxes); `committedConsents`\n // and `has()` are the consent in effect (gate scripts/embeds on those).\n const categories = manager.categories;\n return {\n consentId: manager.consentId,\n hasActed: manager.hasActed,\n categories,\n consents: categories,\n committedConsents: manager.committedCategories,\n regulation: manager.regulation,\n lastRenewed: manager.lastRenewed,\n taxonomyHash: manager.taxonomyHash,\n activeUI: activeUI(),\n has: (category) => manager.committedCategories[category] === true,\n saveConsents: async (target) => {\n if (target === \"all\") manager.acceptAll();\n else if (target === \"necessary\") manager.rejectAll();\n else manager.acceptSelected(target);\n },\n setConsent: (category, value) => manager.updateCategory(category, value),\n subscribeToConsentChanges: (listener) => {\n changeListeners.add(listener);\n return () => {\n changeListeners.delete(listener);\n };\n },\n };\n }\n\n const consentStore: ConsentStore = {\n subscribe: (listener) => {\n stateListeners.add(listener);\n return () => {\n stateListeners.delete(listener);\n };\n },\n getState: buildState,\n on: (type, listener, opts) => emitter.on(type, listener, opts),\n get translations() {\n return language.getTranslations();\n },\n getLanguageInfo: language.getLanguageInfo,\n setLanguage: language.setLanguage,\n getCategoryText: language.getCategoryText,\n categories: resolved,\n getRegion: () => regionDecision,\n };\n\n // Installed through the slot rather than imported directly, so the blocker\n // ships only to customers who register it. Still eager and synchronous: a\n // registered blocker patches the browser's networking here, exactly as\n // before. See network-blocker-slot.ts.\n if (options.networkBlocker && options.networkBlocker.rules.length > 0) {\n _installRegisteredNetworkBlocker(\n options.networkBlocker,\n (cat) => manager.committedCategories[cat] === true,\n );\n }\n\n // Run the configured script integrations (Segment, Google, Meta, …) against\n // the committed consent. Reconciles on every consent change; torn down on reset.\n let integrationsReady: Promise<void> = Promise.resolve();\n if (options.integrations && options.integrations.length > 0) {\n const configured = options.integrations;\n const builtIn = options.builtInIntegrations ?? [];\n const generation = ++_integrationGeneration;\n // Loaded on demand: see `_loadIntegrations`. The setup of each integration\n // is therefore deferred by one chunk fetch. Integrations exist to load\n // third-party tags, which are asynchronous anyway, and consent gating is\n // unaffected — nothing loads that would not have loaded. It is still a\n // deferral, not a deletion, which is why `total` in the size report does\n // not fall even though `initial` does.\n integrationsReady = _loadIntegrations().then((m) => {\n if (generation !== _integrationGeneration) return;\n m.warnOverlappingVendors(\n configured.map((i) => i.id),\n builtIn.map((b) => b.vendor),\n );\n m.warnUnknownCategories(configured, resolved.ids);\n _integrationRunner = m.runIntegrations(configured, {\n granted: (category) => manager.committedCategories[category] === true,\n subscribe: (fn) => manager.subscribe(() => fn()),\n region: regionDecision,\n });\n });\n }\n\n _runtime = {\n consentManager: manager,\n consentStore,\n getIntegrations: () => _integrationRunner?.list() ?? [],\n integrationsReady,\n };\n return _runtime;\n}\n\n/**\n * Canonical setup entry point. Alias of {@link getOrCreateConsentRuntime} that\n * accepts the same {@link CookieYesConfig} and returns the same process-wide\n * singleton — provided so documentation can use one setup name (`initCookieYes`)\n * across every package.\n */\nexport function initCookieYes(config: CookieYesConfig): ConsentRuntime {\n return getOrCreateConsentRuntime(config);\n}\n\nexport function resetConsentRuntime(): void {\n // Un-patch `fetch`/XHR/`sendBeacon` before dropping the runtime. The blocker is a\n // module-level singleton: leaving it installed would keep the *old* manager's\n // committed-consent closure deciding what is allowed, and because a second\n // `installNetworkBlocker()` is a no-op while one is active, the next\n // `initCookieYes()` would silently never apply its own rules. Idempotent — a no-op\n // when nothing was installed.\n _uninstallRegisteredNetworkBlocker();\n // Invalidate any in-flight integration load before dropping the runner, so a\n // chunk still on its way cannot install itself after the reset.\n _integrationGeneration++;\n _integrationRunner?.stop();\n _integrationRunner = null;\n _runtime = null;\n}\n","import { type CategoryDef, resolveCategories } from \"./categories.js\";\nimport { parseCookieHeader, rawFieldsToSnapshot } from \"./cookie.js\";\nimport type { ConsentSnapshot, Regulation } from \"./types.js\";\n\n/**\n * The subset of your consent config that affects reading a stored decision.\n * `CookieYesConfig` satisfies this structurally, so you can pass the same object\n * you give `initCookieYes`.\n */\nexport type ServerConsentOptions = {\n regulation?: Regulation | undefined;\n categories?: CategoryDef[] | undefined;\n};\n\n/**\n * Read a visitor's already-made consent decision from a request's `Cookie`\n * header, on the server, with no `document` and no browser APIs.\n *\n * Use it to keep the banner out of the HTML entirely for a returning visitor.\n * Without it the server has no idea whether the visitor has chosen, so it sends\n * banner markup to everyone and the client removes it after hydration — the\n * banner visibly appears and then vanishes, which reads as a bug.\n *\n * Returns `null` whenever the banner *should* be shown:\n * - no consent cookie (a first-time visitor),\n * - a cookie that records no decision yet (`action:no`, e.g. a CCPA visitor who\n * has an implicit-consent cookie but has not acted),\n * - a corrupt cookie,\n * - a cookie written against a **different category taxonomy**, which the client\n * also treats as stale and re-requests. The one exception mirrors the client\n * exactly: a legacy cookie with no taxonomy stamp is still honoured when the\n * built-in five categories are in effect, so existing visitors are not\n * re-prompted by an upgrade.\n *\n * Otherwise returns the stored snapshot, ready to hand to `CookieYesProvider`'s\n * `initialConsent`.\n *\n * ```ts\n * // Any SSR framework — pass the request's Cookie header:\n * const initialConsent = readServerConsent(request.headers.get(\"cookie\") ?? \"\", config);\n * ```\n *\n * In Next.js App Router, prefer `getServerConsent(config)` from\n * `@cookieyes/nextjs`, which reads `cookies()` for you.\n *\n * **Never** put the result on `initCookieYes` or the runtime: the runtime is a\n * module-level singleton shared across concurrent requests, so per-visitor state\n * there would leak between them. It belongs in the component tree.\n */\nexport function readServerConsent(\n cookieHeader: string,\n options: ServerConsentOptions = {},\n): ConsentSnapshot | null {\n if (typeof cookieHeader !== \"string\" || cookieHeader.length === 0) return null;\n\n const fields = parseCookieHeader(cookieHeader);\n if (fields == null) return null;\n\n const resolved = resolveCategories(options.categories);\n\n // Mirrors createConsentManager's stored-consent validity rule exactly. If the\n // two ever disagree, the server and client reach different conclusions about\n // the same visitor and the banner flashes — the bug this function prevents.\n const storedTax = fields.tax;\n const taxMatches = storedTax === resolved.taxonomyHash;\n const legacyCookie = storedTax === undefined;\n if (!taxMatches && !(legacyCookie && resolved.isDefault)) return null;\n\n const snapshot = rawFieldsToSnapshot(fields, options.regulation ?? \"DEFAULT\", resolved);\n\n // No explicit decision yet → the banner is supposed to show.\n if (!snapshot.hasActed) return null;\n\n // Stamp the current taxonomy, as the client does after reading the cookie, so\n // the server and hydration snapshots are identical.\n return { ...snapshot, taxonomyHash: resolved.taxonomyHash };\n}\n"],"names":["COOKIE_NAME","COOKIE_META_KEYS","Set","parseCookie","raw","fields","categories","pair","split","colonIdx","indexOf","key","slice","trim","value","has","length","serializeCookie","snapshot","parts","consentId","hasActed","taxonomyHash","push","id","granted","Object","entries","lastRenewed","Date","now","join","parseCookieHeader","header","cookie","trimmed","eqIdx","decodeURIComponent","writeConsentCookie","document","encodeURIComponent","clearConsentCookie","generateConsentId","array","Uint8Array","crypto","getRandomValues","i","Math","floor","random","btoa","String","fromCharCode","replace","rawFieldsToSnapshot","regulation","resolved","ids","requiredIds","consentid","action","lastRenewedDate","Number","tax","defaultSnapshot","isOptOut","DEFAULT_CATEGORIES","required","gcm","hashString","input","h","charCodeAt","imul","toString","build","list","isDefault","map","c","filter","signature","resolveCategories","defs","error","d","some","includes","size","validationError","console","warn","message","_normalizeConfig","config","normalized","mode","nestedRegulation","overrides","region","colorScheme","theme","i18n","networkBlocker","reloadOnRevoke","googleConsentMatch","integrations","builtInIntegrations","customStopHandlers","onConsentReady","onConsentUpdate","apiKey","backend","apiUrl","backendURL","warnedOfflineMode","_warnOfflineModeDeprecated","process","env","NODE_ENV","warnedBuiltInIntegrations","_warnBuiltInIntegrationsDeprecated","createConsentEmitter","getCommitted","listeners","save","change","last","deliver","reg","type","payload","listener","err","emit","category","changedCategories","on","options","add","isInitial","delete","next","keys","ALL_SIGNALS","computeGoogleConsent","match","result","signal","mappers","def","every","broadcastGoogleConsent","window","Array","isArray","dataLayer","consent","arguments","gtag","en","bannerTitle","bannerDescription","acceptAll","rejectAll","managePreferences","savePreferences","doNotSell","ccpaDescription","accept","poweredBy","opensInNewTab","preferencesTitle","preferencesIntro","alwaysActive","preferencesDialogLabel","optOutDialogLabel","recallButtonLabel","necessary","label","description","functional","analytics","performance","advertisement","optOut","title","cancel","successText","successCountdown","bannerCloseLabel","preferencesCloseLabel","optOutCloseLabel","gatedFrame","placeholder","reloadNotice","reloadButton","dismissButton","RTL","primaryOf","tag","toLowerCase","getTextDirection","mergeTranslations","base","override","out","baseVal","bothObjects","pickLanguage","messages","candidates","locale","detectBrowserLanguage","navigator","language","primary","async","_loadIntegrations","module","Promise","resolve","then","require","runIntegrations","warnOverlappingVendors","warnUnknownCategories","createLanguageController","onChange","loadLanguage","warned","translations","info","buildInfo","messagesFor","isAvailable","defaultTranslations","direction","languages","from","apply","warnMissing","setLanguage","loaded","catch","getTranslations","getLanguageInfo","getCategoryText","registry","Map","injected","injectScript","entry","getElementById","el","createElement","src","onLoad","addEventListener","once","head","appendChild","set","isReloadOnly","needsReload","resolveBuiltInIntegration","cfg","vendor","stop","fbq","resume","handlers","stopped","reloadActive","registerStopHandler","handler","applyStopHandlers","reloadRequiredBy","values","denied","buildConsentPayload","domain","location","hostname","createConsentManager","gcmMatch","state","perSignal","get","idSet","JSON","stringify","warnOverlappingGcm","lastPersistedCategories","committedCategories","isPreferencesOpen","buildCategories","grantNonRequired","cats","reloadReasons","reloadDismissed","integration","rawFields","savedRegulation","storedTax","taxMatches","storedConsentValid","notify","snap","fn","applyCommittedScripts","applyScripts","persist","didRevoke","headers","Authorization","fetch","method","body","keepalive","pushConsent","reasonsChanged","reasons","changed","r","setReloadReasons","reload","gpcOptOut","manager","acceptSelected","updateCategory","resetConsent","showPreferences","hidePreferences","subscribe","registerScript","dismissReloadNotice","initStopHandlers","GEO_HEADERS","country","readGpc","globalPrivacyControl","resolveRegion","manual","strictest","detect","source","confidence","mapped","mapRegion","_logRegionDecision","decision","_runtime","_integrationRunner","_integrationGeneration","getOrCreateConsentRuntime","changeListeners","userOnConsentUpdate","emitter","regionDecision","honorGpc","debug","allowed","cat","allowedCategories","deniedCategories","splitCategories","stateListeners","notifyState","buildState","consents","committedConsents","activeUI","saveConsents","target","setConsent","subscribeToConsentChanges","consentStore","getState","opts","getRegion","rules","_installRegisteredNetworkBlocker","integrationsReady","configured","builtIn","generation","m","b","consentManager","getIntegrations","remove","clear","cookieHeader","countryCode","regionCode","_uninstallRegisteredNetworkBlocker"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.cjs","sources":["../src/cookie.ts","../src/categories.ts","../src/config.ts","../src/deprecations.ts","../src/events.ts","../src/google-consent-mode.ts","../src/translations/en.ts","../src/i18n.ts","../src/integrations-lazy.ts","../src/language.ts","../src/scripts.ts","../src/stop-handlers.ts","../src/sync.ts","../src/manager.ts","../src/region.ts","../src/runtime.ts","../src/server-consent.ts"],"sourcesContent":["import type { ResolvedCategories } from \"./categories.js\";\nimport type { ConsentSnapshot, Regulation } from \"./types.js\";\n\nconst COOKIE_NAME = \"cookieyes-consent\";\n\n// Meta keys carry consent metadata; every other `key:value` pair in the cookie\n// is a category id → \"yes\"/\"no\", so custom taxonomies serialize without a\n// fixed schema. Exported so category validation can reject ids that would\n// collide with these (see resolveCategories) — a single source of truth.\nexport const COOKIE_META_KEYS = new Set([\n \"consentid\",\n \"consent\",\n \"action\",\n \"tax\",\n \"lastRenewedDate\",\n]);\n\nexport type RawCookieFields = {\n consentid?: string;\n consent?: string;\n action?: string;\n /** Taxonomy signature stored with the consent (see ResolvedCategories.taxonomyHash). */\n tax?: string;\n lastRenewedDate?: string;\n /** Every non-meta pair: category id → \"yes\" | \"no\". */\n categories: Record<string, string>;\n};\n\nexport function parseCookie(raw: string): RawCookieFields {\n const fields: RawCookieFields = { categories: {} };\n for (const pair of raw.split(\",\")) {\n const colonIdx = pair.indexOf(\":\");\n if (colonIdx === -1) continue;\n const key = pair.slice(0, colonIdx).trim();\n const value = pair.slice(colonIdx + 1).trim();\n if (COOKIE_META_KEYS.has(key)) {\n (fields as Record<string, unknown>)[key] = value;\n } else if (key.length > 0) {\n fields.categories[key] = value;\n }\n }\n return fields;\n}\n\nexport function serializeCookie(snapshot: ConsentSnapshot): string {\n const parts: string[] = [\n `consentid:${snapshot.consentId}`,\n `consent:${snapshot.hasActed ? \"yes\" : \"no\"}`,\n `action:${snapshot.hasActed ? \"yes\" : \"no\"}`,\n ];\n if (snapshot.taxonomyHash) parts.push(`tax:${snapshot.taxonomyHash}`);\n for (const [id, granted] of Object.entries(snapshot.categories)) {\n parts.push(`${id}:${granted ? \"yes\" : \"no\"}`);\n }\n parts.push(`lastRenewedDate:${snapshot.lastRenewed ?? Date.now()}`);\n return parts.join(\",\");\n}\n\n/**\n * Find and parse the consent cookie inside a `name=value; name2=value2` string.\n *\n * Shared by the browser (`document.cookie`) and the server (a request's `Cookie`\n * header) — the two formats are identical, and one implementation means the\n * server can never disagree with the client about what a visitor's cookie says.\n * Returns `null` when the cookie is absent or its value cannot be decoded.\n */\nexport function parseCookieHeader(header: string): RawCookieFields | null {\n for (const cookie of header.split(\";\")) {\n const trimmed = cookie.trim();\n const eqIdx = trimmed.indexOf(\"=\");\n if (eqIdx === -1) continue;\n const name = trimmed.slice(0, eqIdx).trim();\n if (name !== COOKIE_NAME) continue;\n const value = trimmed.slice(eqIdx + 1).trim();\n try {\n return parseCookie(decodeURIComponent(value));\n } catch {\n // A malformed percent-encoding is a corrupt cookie, not a crash: treat it\n // as no stored consent so the visitor is asked again.\n return null;\n }\n }\n return null;\n}\n\nexport function readConsentCookie(): RawCookieFields | null {\n if (typeof document === \"undefined\") return null;\n return parseCookieHeader(document.cookie);\n}\n\nexport function writeConsentCookie(snapshot: ConsentSnapshot): void {\n if (typeof document === \"undefined\") return;\n const value = encodeURIComponent(serializeCookie(snapshot));\n const maxAge = 365 * 24 * 60 * 60; // 1 year\n document.cookie = `${COOKIE_NAME}=${value}; max-age=${maxAge}; path=/; SameSite=Lax`;\n}\n\nexport function clearConsentCookie(): void {\n if (typeof document === \"undefined\") return;\n document.cookie = `${COOKIE_NAME}=; max-age=0; path=/`;\n}\n\nexport function generateConsentId(): string {\n const array = new Uint8Array(32);\n if (typeof crypto !== \"undefined\" && crypto.getRandomValues) {\n crypto.getRandomValues(array);\n } else {\n for (let i = 0; i < array.length; i++) {\n array[i] = Math.floor(Math.random() * 256);\n }\n }\n return btoa(String.fromCharCode(...array))\n .replace(/\\+/g, \"-\")\n .replace(/\\//g, \"_\")\n .replace(/=/g, \"\")\n .slice(0, 44);\n}\n\n/**\n * Build a snapshot from stored cookie fields, against the *resolved* taxonomy.\n * Required categories are always granted; everything else reflects the stored\n * \"yes\"/\"no\" (absent → not granted). Carries the stored taxonomy hash so the\n * caller can detect a taxonomy change.\n */\nexport function rawFieldsToSnapshot(\n fields: RawCookieFields,\n regulation: Regulation,\n resolved: ResolvedCategories,\n): ConsentSnapshot {\n const categories: Record<string, boolean> = {};\n for (const id of resolved.ids) {\n categories[id] = resolved.requiredIds.has(id) ? true : fields.categories[id] === \"yes\";\n }\n return {\n consentId: fields.consentid ?? generateConsentId(),\n hasActed: fields.action === \"yes\",\n categories,\n regulation,\n lastRenewed: fields.lastRenewedDate ? Number(fields.lastRenewedDate) : undefined,\n taxonomyHash: fields.tax,\n };\n}\n\nexport function defaultSnapshot(\n consentId: string,\n regulation: Regulation,\n resolved: ResolvedCategories,\n): ConsentSnapshot {\n const isOptOut = regulation === \"CCPA\";\n const categories: Record<string, boolean> = {};\n for (const id of resolved.ids) {\n // CCPA is opt-out: everything implicitly on until the visitor opts out.\n // Otherwise only the required category(ies) start on.\n categories[id] = resolved.requiredIds.has(id) ? true : isOptOut;\n }\n return {\n consentId,\n hasActed: false,\n categories,\n regulation,\n taxonomyHash: resolved.taxonomyHash,\n };\n}\n","import { COOKIE_META_KEYS } from \"./cookie.js\";\nimport type { ConsentCategory } from \"./types.js\";\n\n/**\n * Google Consent Mode v2 storage/signal types. A category can declare which of\n * these it represents via {@link CategoryDef.gcm}; the SDK then broadcasts them\n * (see google-consent-mode.ts). `security_storage` is always granted and is\n * handled by the broadcast itself, so it never needs to be mapped.\n */\nexport type GoogleConsentSignal =\n | \"ad_storage\"\n | \"ad_user_data\"\n | \"ad_personalization\"\n | \"analytics_storage\"\n | \"functionality_storage\"\n | \"personalization_storage\"\n | \"security_storage\";\n\n/**\n * A single consent category. `id` is the stable key stored in the cookie and\n * used everywhere (banner, preferences, read APIs, integrations). Exactly one\n * category should be marked `required` — the always-on, non-optional one (like\n * the default \"necessary\") — flagged explicitly here, never inferred from a\n * name, so it survives full renaming.\n */\nexport type CategoryDef = {\n id: ConsentCategory;\n /** The always-on, non-optional category. At least one is required. */\n required?: boolean | undefined;\n /** Display label. Falls back to the translation for built-in ids. */\n label?: string | undefined;\n /** Display description. Falls back to the translation for built-in ids. */\n description?: string | undefined;\n /** Google Consent Mode signals this category governs (see {@link GoogleConsentSignal}). */\n gcm?: GoogleConsentSignal[] | undefined;\n};\n\n/**\n * The built-in five, used verbatim when a customer configures nothing. GCM\n * mapping mirrors production's `_ckySetGoogleConsentMode` (analytics →\n * analytics_storage, advertisement → the ad_* signals, functional →\n * functionality/personalization; performance maps to nothing; security_storage\n * is always granted by the broadcast).\n */\nexport const DEFAULT_CATEGORIES: CategoryDef[] = [\n { id: \"necessary\", required: true },\n { id: \"functional\", gcm: [\"functionality_storage\", \"personalization_storage\"] },\n { id: \"analytics\", gcm: [\"analytics_storage\"] },\n { id: \"performance\" },\n { id: \"advertisement\", gcm: [\"ad_storage\", \"ad_user_data\", \"ad_personalization\"] },\n];\n\nexport type ResolvedCategories = {\n /** Ordered category definitions actually in effect. */\n list: CategoryDef[];\n /** Ordered ids (fast access). */\n ids: ConsentCategory[];\n /** Ids marked `required` (always granted, never toggleable). */\n requiredIds: Set<ConsentCategory>;\n /** Stable signature of this taxonomy; a change here re-requests consent. */\n taxonomyHash: string;\n /** True when the built-in five are in effect (configured or fallback). */\n isDefault: boolean;\n};\n\n/** Small, stable non-crypto hash → short base36 string, for the cookie stamp. */\nfunction hashString(input: string): string {\n let h = 2166136261;\n for (let i = 0; i < input.length; i++) {\n h ^= input.charCodeAt(i);\n h = Math.imul(h, 16777619);\n }\n return (h >>> 0).toString(36);\n}\n\nfunction build(list: CategoryDef[], isDefault: boolean): ResolvedCategories {\n const ids = list.map((c) => c.id);\n const requiredIds = new Set(list.filter((c) => c.required).map((c) => c.id));\n // Signature includes id + required flag + gcm, so any meaningful change to\n // the taxonomy invalidates prior consent (see the manager's load check).\n const signature = list\n .map((c) => `${c.id}:${c.required ? 1 : 0}:${(c.gcm ?? []).join(\"+\")}`)\n .join(\"|\");\n return { list, ids, requiredIds, taxonomyHash: hashString(signature), isDefault };\n}\n\n/**\n * Validate a custom category config. Returns a human-readable reason if it's\n * invalid, or `null` if it's good. Order matters: the first failing rule wins.\n */\nfunction validationError(defs: CategoryDef[]): string | null {\n const ids = defs.map((d) => d.id);\n if (ids.some((id) => typeof id !== \"string\" || id.length === 0)) {\n return \"every category needs a non-empty string id\";\n }\n // `,` and `:` are the cookie's field/key delimiters — an id containing either\n // would corrupt persistence silently on the round-trip.\n if (ids.some((id) => id.includes(\",\") || id.includes(\":\"))) {\n return \"category ids must not contain ',' or ':'\";\n }\n if (new Set(ids).size !== ids.length) {\n return \"category ids must be unique\";\n }\n // Ids that would collide with the cookie's reserved metadata keys — a category\n // named e.g. \"consent\" or \"tax\" would corrupt persistence silently.\n if (ids.some((id) => COOKIE_META_KEYS.has(id))) {\n return `category ids must not be one of the reserved keys: ${[...COOKIE_META_KEYS].join(\", \")}`;\n }\n if (!defs.some((d) => d.required === true)) {\n return \"at least one category must be marked { required: true }\";\n }\n return null;\n}\n\n/**\n * Resolve the category list from config. Returns the built-in five when nothing\n * is configured. On an invalid custom config (empty, duplicate/reserved ids, or\n * no `required` category) it warns and falls back to the built-in five, rather\n * than leaving the visitor a broken/empty or unprotected setup.\n */\nexport function resolveCategories(defs?: CategoryDef[]): ResolvedCategories {\n if (!defs || defs.length === 0) return build(DEFAULT_CATEGORIES, true);\n\n const error = validationError(defs);\n if (error) {\n if (typeof console !== \"undefined\") {\n // eslint-disable-next-line no-console\n console.warn(\n `[cookieyes] Invalid categories config (${error}). Falling back to the ` +\n \"default five (necessary, functional, analytics, performance, advertisement).\",\n );\n }\n return build(DEFAULT_CATEGORIES, true);\n }\n\n return build(defs, false);\n}\n","import type { CategoryDef } from \"./categories.js\";\nimport type { Integration } from \"./integrations.js\";\nimport type { NetworkBlockerConfig } from \"./network-blocker.js\";\nimport type { BuiltInIntegration, StopHandler } from \"./stop-handlers.js\";\nimport type {\n ColorScheme,\n ConsentBackend,\n ConsentRuntimeMode,\n ConsentSnapshot,\n CookieYesConfig,\n I18nConfig,\n RegionConfig,\n Regulation,\n ThemeConfig,\n} from \"./types.js\";\n\n/**\n * The canonical config with every deprecated alias already collapsed into its\n * top-level key. Both `@cookieyes/core` and `@cookieyes/react` consume the\n * output of {@link _normalizeConfig}, so alias resolution lives in exactly one\n * place and the two packages can never drift.\n *\n * @internal\n */\nexport type _NormalizedConfig = {\n mode: ConsentRuntimeMode;\n regulation?: Regulation | undefined;\n region?: RegionConfig | undefined;\n colorScheme?: ColorScheme | undefined;\n theme?: ThemeConfig | undefined;\n i18n?: I18nConfig | undefined;\n categories?: CategoryDef[] | undefined;\n networkBlocker?: NetworkBlockerConfig | undefined;\n reloadOnRevoke?: boolean | undefined;\n googleConsentMatch?: \"all\" | \"any\" | undefined;\n integrations?: Integration[] | undefined;\n /** @deprecated Renamed from `integrations`; use `integrations` with a `@cookieyes/scripts` preset. */\n builtInIntegrations?: BuiltInIntegration[] | undefined;\n customStopHandlers?: StopHandler[] | undefined;\n onConsentReady?: ((state: ConsentSnapshot) => void) | undefined;\n onConsentUpdate?: ((state: ConsentSnapshot) => void) | undefined;\n apiUrl?: string | undefined;\n apiKey?: string | undefined;\n backend?: ConsentBackend | undefined;\n};\n\nfunction warn(message: string): void {\n if (typeof console !== \"undefined\") {\n // eslint-disable-next-line no-console\n console.warn(message);\n }\n}\n\n/**\n * Resolve a public {@link CookieYesConfig} into its canonical internal form.\n *\n * Deprecated aliases map silently to their canonical key when used alone; when\n * an alias and its canonical key are both present, the canonical key wins and\n * exactly one warning is logged for that collision.\n *\n * - `overrides.regulation` → `regulation`\n * - `backendURL` → `apiUrl`\n *\n * @internal\n */\nexport function _normalizeConfig(config: CookieYesConfig): _NormalizedConfig {\n const normalized: _NormalizedConfig = { mode: config.mode };\n\n // ── regulation (top-level) ⇐ deprecated overrides.regulation ──────────────\n const nestedRegulation = config.overrides?.regulation;\n if (config.regulation !== undefined) {\n normalized.regulation = config.regulation;\n if (nestedRegulation !== undefined) {\n warn(\n \"[CookieYes] Received both `regulation` and the deprecated \" +\n \"`overrides.regulation`. Using the top-level `regulation` and ignoring \" +\n \"`overrides`. Drop the `overrides` object — it is deprecated and will be \" +\n \"removed after three release cycles.\",\n );\n }\n } else if (nestedRegulation !== undefined) {\n normalized.regulation = nestedRegulation;\n }\n\n if (config.region !== undefined) normalized.region = config.region;\n if (config.colorScheme !== undefined) normalized.colorScheme = config.colorScheme;\n if (config.theme !== undefined) normalized.theme = config.theme;\n if (config.i18n !== undefined) normalized.i18n = config.i18n;\n if (config.categories !== undefined) normalized.categories = config.categories;\n if (config.networkBlocker !== undefined) normalized.networkBlocker = config.networkBlocker;\n if (config.reloadOnRevoke !== undefined) normalized.reloadOnRevoke = config.reloadOnRevoke;\n if (config.googleConsentMatch !== undefined)\n normalized.googleConsentMatch = config.googleConsentMatch;\n if (config.integrations !== undefined) normalized.integrations = config.integrations;\n if (config.builtInIntegrations !== undefined)\n normalized.builtInIntegrations = config.builtInIntegrations;\n if (config.customStopHandlers !== undefined)\n normalized.customStopHandlers = config.customStopHandlers;\n if (config.onConsentReady !== undefined) normalized.onConsentReady = config.onConsentReady;\n if (config.onConsentUpdate !== undefined) normalized.onConsentUpdate = config.onConsentUpdate;\n\n // ── backend keys (self-hosted only) ⇐ deprecated backendURL ───────────────\n if (config.mode === \"self-hosted\") {\n if (config.apiKey !== undefined) normalized.apiKey = config.apiKey;\n if (config.backend !== undefined) normalized.backend = config.backend;\n\n if (config.apiUrl !== undefined) {\n normalized.apiUrl = config.apiUrl;\n if (config.backendURL !== undefined) {\n warn(\n \"[CookieYes] Received both `apiUrl` and the deprecated `backendURL`. \" +\n \"Using `apiUrl` and ignoring `backendURL`. Rename `backendURL` to \" +\n \"`apiUrl` — the alias is deprecated and will be removed after three \" +\n \"release cycles.\",\n );\n }\n } else if (config.backendURL !== undefined) {\n normalized.apiUrl = config.backendURL;\n }\n }\n\n return normalized;\n}\n","/**\n * Declared locally rather than pulled in from `@types/node`.\n *\n * The guard below needs `process.env.NODE_ENV` to survive into the published\n * output as that exact literal, so a consumer's bundler can replace it. That\n * rules out any defensive form — `globalThis.process?.env?.NODE_ENV`, a\n * `typeof` check — because none of them are the pattern bundlers match.\n *\n * Referencing Node's global types instead (`/// <reference types=\"node\" />`)\n * would work for the compiler but risks that reference reaching the emitted\n * `.d.ts`, which would make every consumer of this package need `@types/node`\n * to typecheck. This declaration is module-scoped and ambient, so it types the\n * one expression that needs it and reaches nothing else.\n */\ndeclare const process: { env: { NODE_ENV?: string } };\n\n/*\n * Every warning in this file opens with `if (process.env.NODE_ENV ===\n * \"production\") return;`.\n *\n * Deprecation warnings exist for whoever is writing the integration. They are\n * of no use to a visitor, who cannot act on one and never opens the console, so\n * shipping the text to them is pure weight — measured at 224 bytes of gzip in\n * core and 175 in the interface layer (see tools/size/README.md).\n *\n * The check is written as this exact literal, at the top of each function and\n * not hoisted into a shared constant, because that is the form bundlers\n * recognise and replace. Once `process.env.NODE_ENV` becomes `\"production\"` the\n * condition is constant, the early return is unconditional, and the rest of the\n * function — the strings included — is dead code the minifier removes.\n *\n * Two forms that look equivalent and are not, both tried and measured:\n *\n * - Hoisting it into `const DEV = ...` shared by the file: the constant folds,\n * but nothing else does, and the guarded bodies survive.\n * - Guarding with `typeof process === \"undefined\" || process.env.NODE_ENV !==\n * \"production\"` to stay safe where `process` is absent: `typeof process` is\n * not statically replaced, so the whole expression stops folding. Measured:\n * core grew by 10 bytes and every warning string stayed in the bundle.\n *\n * The consequence of the literal form is that these functions need `process` to\n * exist, which means a bundler (or Node). That is already true of every\n * supported install path — the package advertises only `import`/`require`\n * conditions, no browser-global build — and it is the same trade React makes\n * for the same reason.\n */\n\nlet warnedOfflineMode = false;\n\n/**\n * One-time-per-page-load console warning for `mode: \"offline\"`.\n * Both @cookieyes/core and @cookieyes/react call this so the wording and the\n * \"once\" behavior stay identical no matter which package reads the setting.\n *\n * No-op in a production bundle; see the note at the top of this file.\n */\nexport function _warnOfflineModeDeprecated(): void {\n if (process.env.NODE_ENV === \"production\") return;\n if (warnedOfflineMode) return;\n warnedOfflineMode = true;\n if (typeof console === \"undefined\") return;\n // eslint-disable-next-line no-console\n console.warn(\n '[cookieyes] mode: \"offline\" has been renamed to \"cookie-only\". Both do exactly ' +\n 'the same thing, but \"offline\" is deprecated and will be removed after three release cycles. ' +\n \"See https://github.com/cookieyes/cookieyes/blob/main/apps/web/content/docs/migration.mdx for the full migration guide. \" +\n 'Update to .mode(\"cookie-only\") (or { mode: \"cookie-only\" }).',\n );\n}\n\n/** @internal test-only — resets the one-time warning guard between test cases. */\nexport function _resetOfflineModeWarning(): void {\n warnedOfflineMode = false;\n}\n\nlet warnedBuiltInIntegrations = false;\n\n/**\n * One-time-per-page-load console warning for the deprecated `builtInIntegrations`\n * config field (formerly `integrations`). Both packages call this so the wording\n * and the \"once\" behavior stay identical.\n *\n * No-op in a production bundle; see the note at the top of this file.\n */\nexport function _warnBuiltInIntegrationsDeprecated(): void {\n if (process.env.NODE_ENV === \"production\") return;\n if (warnedBuiltInIntegrations) return;\n warnedBuiltInIntegrations = true;\n if (typeof console === \"undefined\") return;\n // eslint-disable-next-line no-console\n console.warn(\n \"[cookieyes] `builtInIntegrations` (formerly the `integrations` field) is deprecated \" +\n \"and will be removed after three release cycles. Use the `integrations` field with a \" +\n \"preset from `@cookieyes/scripts` instead. See \" +\n \"https://github.com/cookieyes/cookieyes/blob/main/apps/web/content/docs/migration.mdx for the full migration guide.\",\n );\n}\n\n/** @internal test-only — resets the one-time warning guard between test cases. */\nexport function _resetBuiltInIntegrationsWarning(): void {\n warnedBuiltInIntegrations = false;\n}\n","import type {\n ConsentCategory,\n ConsentEventListener,\n ConsentEventOptions,\n ConsentEventPayload,\n ConsentEventType,\n} from \"./types.js\";\n\nexport type ConsentEmitter = {\n /**\n * Listen for consent events. `\"save\"` fires on every saved decision (even an\n * unchanged re-confirm); `\"change\"` fires only when a category actually\n * differs. The listener fires once immediately with the current state\n * (`isInitial: true`) so a late listener isn't blind to earlier choices.\n * Pass `{ category }` to only be called when that one category changes.\n * Returns an unsubscribe function.\n */\n on: (\n type: ConsentEventType,\n listener: ConsentEventListener,\n options?: ConsentEventOptions,\n ) => () => void;\n /** Feed in the committed categories after a save; the emitter fans out events. */\n push: (categories: Record<string, boolean>) => void;\n};\n\ntype Registration = { listener: ConsentEventListener; category?: ConsentCategory };\n\n/**\n * The consent event fan-out, shared by the core and React runtimes so both\n * behave identically. `getCommitted` returns the consent currently in effect,\n * used for the immediate replay a new listener receives.\n */\nexport function createConsentEmitter(getCommitted: () => Record<string, boolean>): ConsentEmitter {\n const listeners: Record<ConsentEventType, Set<Registration>> = {\n save: new Set(),\n change: new Set(),\n };\n // Committed categories at the last push — the baseline for diffing changes.\n let last: Record<string, boolean> = { ...getCommitted() };\n\n function deliver(reg: Registration, type: ConsentEventType, payload: ConsentEventPayload): void {\n try {\n reg.listener(payload);\n } catch (err) {\n // One listener throwing must never stop the others (Story 5).\n if (typeof console !== \"undefined\") {\n console.error(\n `[cookieyes] a consent \"${type}\" listener threw; others are unaffected:`,\n err,\n );\n }\n }\n }\n\n function emit(type: ConsentEventType, payload: ConsentEventPayload): void {\n // Copy first so a listener that unsubscribes (or subscribes) while firing\n // can't corrupt the loop.\n for (const reg of [...listeners[type]]) {\n if (reg.category && !payload.changedCategories.includes(reg.category)) continue;\n deliver(reg, type, payload);\n }\n }\n\n return {\n on(type, listener, options) {\n const reg: Registration = options?.category\n ? { listener, category: options.category }\n : { listener };\n listeners[type].add(reg);\n // Replay current state immediately (Story 4). isInitial marks it as the\n // replay, not a live action; changedCategories is empty because nothing\n // changed — the current values live in `categories`.\n deliver(reg, type, {\n categories: { ...getCommitted() },\n changedCategories: [],\n isInitial: true,\n });\n return () => {\n listeners[type].delete(reg);\n };\n },\n\n push(categories) {\n const next = { ...categories };\n const changedCategories: ConsentCategory[] = [];\n for (const id of Object.keys(next) as ConsentCategory[]) {\n if (last[id] !== next[id]) changedCategories.push(id);\n }\n last = next;\n // \"save\" always fires; \"change\" only when something genuinely differed.\n emit(\"save\", { categories: next, changedCategories, isInitial: false });\n if (changedCategories.length > 0) {\n emit(\"change\", { categories: next, changedCategories, isInitial: false });\n }\n },\n };\n}\n","import type { GoogleConsentSignal, ResolvedCategories } from \"./categories.js\";\n\nfunction warn(message: string): void {\n if (typeof console !== \"undefined\") console.warn(`[cookieyes] ${message}`);\n}\n\n/**\n * The full set of Google Consent Mode v2 signals. We always broadcast all\n * seven so any Google tag (GA4, Ads, GTM-managed tags) sees a complete picture,\n * rather than only the ones a customer happened to map.\n */\nconst ALL_SIGNALS: GoogleConsentSignal[] = [\n \"ad_storage\",\n \"ad_user_data\",\n \"ad_personalization\",\n \"analytics_storage\",\n \"functionality_storage\",\n \"personalization_storage\",\n \"security_storage\",\n];\n\ntype GcmValue = \"granted\" | \"denied\";\n\ntype WindowWithDataLayer = Window & {\n dataLayer?: unknown[];\n};\n\n/**\n * True when a Google `dataLayer` is present on the page. That's our single\n * trigger: if a dataLayer exists, some Google service is (or will be) listening,\n * so we broadcast. No dataLayer → no-op.\n */\nfunction hasDataLayer(): boolean {\n if (typeof window === \"undefined\") return false;\n return Array.isArray((window as WindowWithDataLayer).dataLayer);\n}\n\n/**\n * Warn when more than one category maps to the same Consent Mode signal. Google\n * has a single on/off per signal, so this mapping is lossy — `googleConsentMatch`\n * decides which way it fails (`\"any\"` grants if either is granted, `\"all\"`\n * requires both). Surfacing it lets the customer choose deliberately instead of\n * discovering it in an audit. The built-in five don't overlap, so this is quiet\n * unless a custom taxonomy creates it.\n */\nexport function warnOverlappingGcm(resolved: ResolvedCategories): void {\n const perSignal = new Map<GoogleConsentSignal, Set<string>>();\n for (const def of resolved.list) {\n for (const signal of def.gcm ?? []) {\n let ids = perSignal.get(signal);\n if (!ids) {\n ids = new Set();\n perSignal.set(signal, ids);\n }\n ids.add(def.id);\n }\n }\n for (const [signal, idSet] of perSignal) {\n // >1 distinct category → a genuine overlap (a category mapping a signal twice isn't one).\n if (idSet.size <= 1) continue;\n const ids = [...idSet].map((id) => JSON.stringify(id)).join(\", \");\n warn(\n `categories ${ids} all map to the Google signal \"${signal}\", which is a single ` +\n 'on/off — this mapping is lossy. Set `googleConsentMatch: \"all\"` to grant it only ' +\n 'when all are granted, or \"any\" (default) to grant it when any is.',\n );\n }\n}\n\n/**\n * Compute the granted/denied value for every GCM signal from the current\n * category consent, using each category's `gcm` mapping.\n *\n * - When several categories map to the same signal, `match` decides: `\"any\"`\n * (default) grants it if *any* mapping category is granted; `\"all\"` requires\n * *every* mapping category to be granted. For the built-in five (one category\n * per signal) the two are identical — `match` only matters for custom overlaps.\n * - `security_storage` is always `granted` (strictly necessary, not consentable).\n * - A signal that no category maps to stays `denied`.\n */\nexport function computeGoogleConsent(\n resolved: ResolvedCategories,\n categories: Record<string, boolean>,\n match: \"all\" | \"any\" = \"any\",\n): Record<GoogleConsentSignal, GcmValue> {\n const result = {} as Record<GoogleConsentSignal, GcmValue>;\n for (const signal of ALL_SIGNALS) {\n if (signal === \"security_storage\") {\n result[signal] = \"granted\"; // never gated on consent\n continue;\n }\n const mappers = resolved.list.filter((def) => def.gcm?.includes(signal));\n if (mappers.length === 0) {\n result[signal] = \"denied\"; // nothing maps to it\n continue;\n }\n const granted =\n match === \"all\"\n ? mappers.every((def) => categories[def.id] === true)\n : mappers.some((def) => categories[def.id] === true);\n result[signal] = granted ? \"granted\" : \"denied\";\n }\n return result;\n}\n\n/**\n * Push a Consent Mode `update` for all seven signals onto the dataLayer, if one\n * is present. Safe to call on load and on every consent change; a no-op when no\n * Google service is on the page.\n *\n * The customer is responsible for the Consent Mode *default* (the gtag snippet\n * that must run before their Google tags, typically denying everything). This\n * function owns the *update* that reflects the visitor's actual choice.\n */\nexport function broadcastGoogleConsent(\n resolved: ResolvedCategories,\n categories: Record<string, boolean>,\n match: \"all\" | \"any\" = \"any\",\n): void {\n if (!hasDataLayer()) return;\n\n const consent = computeGoogleConsent(resolved, categories, match);\n const dataLayer = (window as WindowWithDataLayer).dataLayer;\n if (!dataLayer) return;\n\n // Emit the command in gtag's exact wire format: `gtag()` is defined as\n // `function gtag(){ dataLayer.push(arguments); }`, so Google's Consent Mode\n // reads back an *arguments object* — not a plain array. We reproduce that\n // shape here (rather than pushing an array) so every GTM/gtag version\n // recognises the `consent` command reliably, without needing a global\n // `gtag()` to already exist on the page. Empty params keep `arguments` legal;\n // the variable's type makes the 3-arg call typecheck.\n const gtag: (...args: unknown[]) => void = function () {\n // biome-ignore lint/complexity/noArguments: gtag's wire format IS the arguments object — this is the canonical Google snippet.\n dataLayer.push(arguments);\n };\n gtag(\"consent\", \"update\", consent);\n}\n","import type { TranslationMap } from \"../types.js\";\n\nexport const en: TranslationMap = {\n bannerTitle: \"We value your privacy\",\n bannerDescription:\n \"We use cookies to enhance your browsing experience, serve personalised ads or content, and analyse our traffic. By clicking \\u201cAccept All\\u201d, you consent to our use of cookies.\",\n acceptAll: \"Accept All\",\n rejectAll: \"Reject All\",\n managePreferences: \"Customise\",\n savePreferences: \"Save My Preferences\",\n doNotSell: \"Do Not Sell or Share My Personal Information\",\n ccpaDescription:\n \"This website or its third-party tools process personal data. You can opt out of the sale of your personal information by clicking on the \\u201cDo Not Sell or Share My Personal Information\\u201d button.\",\n accept: \"Accept\",\n poweredBy: \"Powered by CookieYes\",\n opensInNewTab: \"opens in new tab\",\n preferencesTitle: \"Customise Consent Preferences\",\n preferencesIntro:\n \"We use cookies to help you navigate efficiently and perform certain functions. You will find detailed information about all cookies under each consent category below.\",\n alwaysActive: \"Always Active\",\n preferencesDialogLabel: \"Cookie preferences\",\n optOutDialogLabel: \"Opt-out preferences\",\n recallButtonLabel: \"Consent Preferences\",\n categories: {\n necessary: {\n label: \"Necessary\",\n description:\n \"Necessary cookies are required to enable the basic features of this site, such as providing secure log-in or adjusting your consent preferences. These cookies do not store any personally identifiable data.\",\n },\n functional: {\n label: \"Functional\",\n description:\n \"Functional cookies help perform certain functionalities like sharing the content of the website on social media platforms, collecting feedback, and other third-party features.\",\n },\n analytics: {\n label: \"Analytics\",\n description:\n \"Analytical cookies are used to understand how visitors interact with the website. These cookies help provide information on metrics such as the number of visitors, bounce rate, traffic source, etc.\",\n },\n performance: {\n label: \"Performance\",\n description:\n \"Performance cookies are used to understand and analyse the key performance indexes of the website which helps in delivering a better user experience for the visitors.\",\n },\n advertisement: {\n label: \"Advertisement\",\n description:\n \"Advertisement cookies are used to provide visitors with customised advertisements based on the pages you visited previously and to analyse the effectiveness of the ad campaigns.\",\n },\n },\n optOut: {\n title: \"Opt-out Preferences\",\n description:\n 'We use third-party cookies that help us analyse how you use this website, store your preferences, and provide the content and advertisements that are relevant to you. However, you can opt out of these cookies by checking \"Do Not Sell or Share My Personal Information\" and clicking the \"Save My Preferences\" button. Once you opt out, you can opt in again at any time by unchecking \"Do Not Sell or Share My Personal Information\" and clicking the \"Save My Preferences\" button.',\n cancel: \"Cancel\",\n successText: \"Your opt-out preference has been honored.\",\n successCountdown: \"Banner closes automatically in {seconds} seconds...\",\n },\n bannerCloseLabel: \"Close\",\n preferencesCloseLabel: \"Close preferences\",\n optOutCloseLabel: \"Close\",\n gatedFrame: {\n placeholder: \"This content requires {category} cookies to be enabled.\",\n action: \"Manage Preferences\",\n },\n reloadNotice: {\n message:\n \"Some tracking on this page can only be fully stopped by reloading. Reload to apply your change, or dismiss to keep browsing.\",\n reloadButton: \"Reload page\",\n dismissButton: \"Dismiss\",\n },\n};\n","import { en } from \"./translations/en.js\";\nimport type { I18nConfig, PartialTranslations, TextDirection, TranslationMap } from \"./types.js\";\n\nexport { en as defaultTranslations };\n\n// Languages written right-to-left, by primary subtag. Everything else is ltr.\nconst RTL = new Set([\"ar\", \"he\", \"fa\", \"ur\", \"ps\", \"sd\", \"yi\", \"dv\"]);\n\n/** The base subtag of a language tag, lowercased: \"en-GB\" → \"en\". */\nexport function primaryOf(tag: string): string {\n return tag.split(\"-\")[0]?.toLowerCase() ?? \"\";\n}\n\n/** Reading direction for a language tag, e.g. \"ar\" or \"ar-EG\" → \"rtl\". */\nexport function getTextDirection(tag: string): TextDirection {\n return RTL.has(primaryOf(tag)) ? \"rtl\" : \"ltr\";\n}\n\n/** Deep-merge a (possibly partial) override onto a complete base map. */\nexport function mergeTranslations(\n base: TranslationMap,\n override?: PartialTranslations,\n): TranslationMap {\n if (!override) return base;\n const out: Record<string, unknown> = { ...base };\n for (const [key, value] of Object.entries(override)) {\n if (value == null) continue;\n const baseVal = (base as Record<string, unknown>)[key];\n const bothObjects =\n typeof value === \"object\" &&\n !Array.isArray(value) &&\n typeof baseVal === \"object\" &&\n baseVal != null;\n out[key] = bothObjects\n ? mergeTranslations(baseVal as TranslationMap, value as PartialTranslations)\n : value;\n }\n return out as TranslationMap;\n}\n\n/**\n * The language to start in, resolved in order: explicit `locale`, then the\n * browser's language, then English. Only returns one we actually have text for\n * (others can be brought in later via `loadLanguage`).\n */\nexport function pickLanguage(i18n?: I18nConfig): string {\n const messages = i18n?.messages ?? {};\n const candidates: string[] = [];\n if (i18n?.locale) candidates.push(i18n.locale);\n if (\n (i18n?.detectBrowserLanguage ?? true) &&\n typeof navigator !== \"undefined\" &&\n navigator.language\n ) {\n candidates.push(navigator.language);\n }\n for (const tag of candidates) {\n if (messages[tag]) return tag;\n const primary = primaryOf(tag);\n if (primary && messages[primary]) return primary;\n }\n return \"en\";\n}\n\n/** Full translations for the resolved starting language, English filling any gaps. */\nexport function resolveTranslations(i18n?: I18nConfig): TranslationMap {\n const messages = i18n?.messages ?? {};\n const tag = pickLanguage(i18n);\n return mergeTranslations(en, messages[tag] ?? messages[primaryOf(tag)]);\n}\n","import type { Integration, IntegrationHost, IntegrationRunner } from \"./integrations.js\";\n\n/**\n * Load the integration runner on demand.\n *\n * The runner is the largest single subsystem in `@cookieyes/core` — 1.2 KB of\n * gzip, measured — and it does nothing at all unless `integrations` is\n * configured, which most consumers never do. `integrations.ts` is a separate\n * build entry (see `sdk/core/rollup.config.mjs`) so that this stays a real\n * `import()` in the published output rather than being flattened back into the\n * main chunk, and a bundler can therefore keep it out of the initial download.\n *\n * This indirection exists so that `@cookieyes/react` does not need a static\n * import of `runIntegrations` to do the same thing. A dynamic\n * `import(\"@cookieyes/core\")` from the adapter would pull the whole barrel and\n * defeat the split; a static import of *this* function costs a few bytes and\n * leaves the heavy module behind one `import()` that only core knows about.\n *\n * @internal — consumed by framework adapters, not part of the public API.\n */\nexport async function _loadIntegrations(): Promise<{\n runIntegrations: (list: Integration[], host: IntegrationHost) => IntegrationRunner;\n warnOverlappingVendors: (ids: string[], vendors: string[]) => void;\n warnUnknownCategories: (list: Integration[], known: string[]) => void;\n}> {\n const module = await import(\"./integrations.js\");\n return {\n runIntegrations: module.runIntegrations,\n warnOverlappingVendors: module.warnOverlappingVendors,\n warnUnknownCategories: module.warnUnknownCategories,\n };\n}\n","import {\n defaultTranslations,\n getTextDirection,\n mergeTranslations,\n pickLanguage,\n primaryOf,\n} from \"./i18n.js\";\nimport type {\n CategoryText,\n I18nConfig,\n LanguageInfo,\n PartialTranslations,\n TranslationMap,\n} from \"./types.js\";\n\nexport type LanguageController = {\n /** Text for the active language (English fills any gaps). */\n getTranslations: () => TranslationMap;\n getLanguageInfo: () => LanguageInfo;\n /** Switch language live; loads via `i18n.loadLanguage` if not already present. */\n setLanguage: (tag: string) => Promise<void>;\n /**\n * The customer's own text for a category in the *active* language, if they\n * provided it — kept separate from the English defaults so a translation can\n * win over a category's config label without the English default masking it.\n */\n getCategoryText: (id: string) => Partial<CategoryText> | undefined;\n};\n\n/**\n * Owns the active language: which one is showing, its (English-filled) text,\n * and switching to another — loading it on demand when a loader is provided.\n * `onChange` runs after every switch so the UI can re-render.\n *\n * Framework-agnostic: used by both the core and React runtimes, so they behave\n * identically.\n */\nexport function createLanguageController(\n i18n: I18nConfig | undefined,\n onChange: () => void,\n): LanguageController {\n const messages: Record<string, PartialTranslations> = { ...i18n?.messages };\n const loadLanguage = i18n?.loadLanguage;\n const warned = new Set<string>();\n\n let language = pickLanguage(i18n);\n let translations = build(language);\n let info = buildInfo();\n\n function messagesFor(tag: string): PartialTranslations | undefined {\n return messages[tag] ?? messages[primaryOf(tag)];\n }\n // English is always available — it's the base every language merges onto,\n // so switching to it never needs a loader even when `messages` has no \"en\".\n function isAvailable(tag: string): boolean {\n return primaryOf(tag) === \"en\" || messagesFor(tag) !== undefined;\n }\n function build(tag: string): TranslationMap {\n return mergeTranslations(defaultTranslations, messagesFor(tag));\n }\n function buildInfo(): LanguageInfo {\n return {\n language,\n direction: getTextDirection(language),\n languages: Array.from(new Set([\"en\", ...Object.keys(messages)])),\n };\n }\n function apply(tag: string): void {\n language = tag;\n translations = build(tag);\n info = buildInfo();\n onChange();\n }\n function warnMissing(tag: string, err?: unknown): void {\n if (warned.has(tag) || typeof console === \"undefined\") return;\n warned.add(tag);\n // eslint-disable-next-line no-console\n console.warn(\n `[cookieyes] no translations for language \"${tag}\"; staying on \"${language}\". ` +\n \"Add it to i18n.messages or provide i18n.loadLanguage.\",\n err ?? \"\",\n );\n }\n function setLanguage(tag: string): Promise<void> {\n // Already have it (or it's English) → switch immediately.\n if (isAvailable(tag)) {\n apply(tag);\n return Promise.resolve();\n }\n // Otherwise ask the loader (keeps showing the current language until it lands).\n if (loadLanguage) {\n return Promise.resolve()\n .then(() => loadLanguage(tag))\n .then((loaded) => {\n messages[tag] = loaded;\n apply(tag);\n })\n .catch((err) => warnMissing(tag, err));\n }\n warnMissing(tag);\n return Promise.resolve();\n }\n function getCategoryText(id: string): Partial<CategoryText> | undefined {\n return messagesFor(language)?.categories?.[id];\n }\n\n // An explicit starting language that isn't bundled but has a loader: fetch it\n // now, so we honour the request (English shows until it lands). Browser-only —\n // never fire a load during server rendering.\n if (loadLanguage && i18n?.locale && !isAvailable(i18n.locale) && typeof window !== \"undefined\") {\n void setLanguage(i18n.locale);\n }\n\n return {\n getTranslations: () => translations,\n getLanguageInfo: () => info,\n setLanguage,\n getCategoryText,\n };\n}\n","import type { ScriptEntry } from \"./types.js\";\n\nconst registry = new Map<string, ScriptEntry>();\nconst injected = new Map<string, HTMLScriptElement>();\n\nexport function registerScript(entry: ScriptEntry): void {\n registry.set(entry.id, entry);\n}\n\n/**\n * Inject each registered script whose category is granted. Pass the *committed*\n * consent so an unsaved toggle never loads a script. Once injected, a script\n * stays — revoking doesn't unload it (that can't undo what already ran); the\n * block takes effect on the next page load.\n */\nexport function applyScripts(categories: Record<string, boolean>): void {\n if (typeof document === \"undefined\") return;\n\n for (const [id, entry] of registry) {\n if (categories[entry.category] !== true) continue;\n if (injected.has(id)) continue;\n injectScript(id, entry);\n }\n}\n\n/**\n * @internal Test-only — empty the script registry and forget what was injected.\n * Mirrors {@link _clearStopHandlers}. When a `document` is present the injected\n * `<script>` elements are removed from it too, so one test can never leave a\n * gated script behind for the next one. Safe to call with nothing registered.\n */\nexport function _clearScriptRegistry(): void {\n if (typeof document !== \"undefined\") {\n for (const el of injected.values()) el.remove();\n }\n registry.clear();\n injected.clear();\n}\n\nfunction injectScript(id: string, entry: ScriptEntry): void {\n const existing = document.getElementById(id);\n if (existing) return;\n\n const el = document.createElement(\"script\");\n el.id = id;\n el.src = entry.src;\n el.async = true;\n if (entry.onLoad) {\n el.addEventListener(\"load\", entry.onLoad, { once: true });\n }\n document.head.appendChild(el);\n injected.set(id, el);\n}\n","import type { ConsentCategory } from \"./types.js\";\n\n/**\n * A tool that can be stopped (and optionally resumed) at runtime when consent\n * for its category changes — no page reload needed. `stop()` is called when the\n * category is revoked; `resume()` (if provided) when it's re-granted.\n *\n * If `stop()` throws, that tool is treated as \"couldn't be stopped cleanly\" and\n * falls back to the reload notice for that one tool — it never breaks the page.\n */\nexport type StopHandler = {\n id: string;\n category: ConsentCategory;\n stop: () => void;\n resume?: (() => void) | undefined;\n};\n\n/**\n * A tool with no known clean runtime stop — revoking its category can only be\n * fully applied by reloading the page. Registering one means \"if this category\n * is revoked, show the visitor the reload notice.\"\n */\nexport type ReloadOnlyHandler = {\n id: string;\n category: ConsentCategory;\n needsReload: true;\n};\n\nexport type AnyStopHandler = StopHandler | ReloadOnlyHandler;\n\nfunction isReloadOnly(h: AnyStopHandler): h is ReloadOnlyHandler {\n return \"needsReload\" in h && h.needsReload === true;\n}\n\n/**\n * Built-in, first-party integrations. Each maps to either a clean stop-handler\n * or a reload-only marker (see the audit in the README).\n *\n * Note: Google Analytics 4 and Google Tag Manager are **not** listed here.\n * They're governed by Google Consent Mode v2, which the SDK broadcasts\n * automatically whenever a `dataLayer` is present (see google-consent-mode.ts)\n * — on load and on every consent change, derived from each category's `gcm`\n * mapping. So you don't register them as integrations; just add the standard\n * Consent Mode default snippet and the SDK owns the updates.\n *\n * VERIFIED clean-stop vendors (documented, stable runtime opt-out):\n * - `meta` — `fbq('consent','revoke'|'grant')`, Meta's official consent API.\n *\n * The rest have no confident, documented runtime stop, so they're modelled as\n * reload-only (Story 1's honest answer). Upgrading any of them to a clean-stop\n * later is a one-line change here once a real API is confirmed.\n */\nexport type BuiltInIntegration =\n | { vendor: \"meta\"; category?: ConsentCategory | undefined }\n | { vendor: \"tiktok\"; category?: ConsentCategory | undefined }\n | { vendor: \"linkedin\"; category?: ConsentCategory | undefined }\n | { vendor: \"hotjar\"; category?: ConsentCategory | undefined }\n | { vendor: \"segment\"; category?: ConsentCategory | undefined };\n\ntype WindowWithVendors = Window &\n typeof globalThis & {\n fbq?: (...args: unknown[]) => void;\n };\n\nexport function resolveBuiltInIntegration(cfg: BuiltInIntegration): AnyStopHandler {\n switch (cfg.vendor) {\n case \"meta\":\n return {\n id: \"meta\",\n category: cfg.category ?? \"advertisement\",\n stop: () => (window as WindowWithVendors).fbq?.(\"consent\", \"revoke\"),\n resume: () => (window as WindowWithVendors).fbq?.(\"consent\", \"grant\"),\n };\n // No confident documented runtime stop — reload-only (see README audit).\n case \"tiktok\":\n return { id: \"tiktok\", category: cfg.category ?? \"advertisement\", needsReload: true };\n case \"linkedin\":\n return { id: \"linkedin\", category: cfg.category ?? \"advertisement\", needsReload: true };\n case \"hotjar\":\n return { id: \"hotjar\", category: cfg.category ?? \"analytics\", needsReload: true };\n case \"segment\":\n return { id: \"segment\", category: cfg.category ?? \"analytics\", needsReload: true };\n }\n}\n\n// --- Registry (module-level, mirrors scripts.ts) ---\n\nconst handlers = new Map<string, AnyStopHandler>();\n// Tracks which clean-stop handlers are currently in the \"stopped\" state, so we\n// only fire stop()/resume() on an actual transition, not on every save.\nconst stopped = new Set<string>();\n// Tracks reload-only handlers whose category is currently granted (so the tool\n// is presumed to be running). A reload notice is raised only when one of these\n// transitions granted → denied — i.e. a genuine withdrawal of something that\n// could be active — not on a standing/first-time reject where it never ran.\nconst reloadActive = new Set<string>();\n\nexport function registerStopHandler(handler: AnyStopHandler): void {\n handlers.set(handler.id, handler);\n}\n\n/** Test-only: reset registry + transition state between cases. */\nexport function _clearStopHandlers(): void {\n handlers.clear();\n stopped.clear();\n reloadActive.clear();\n}\n\nexport type StopHandlerResult = {\n /** Ids of reload-only tools just revoked (were granted, now denied). */\n reloadRequiredBy: string[];\n};\n\n/**\n * Reconcile every registered handler against the current consent state:\n * - clean handler denied → run `stop()` (once), or flag reload if it throws\n * - clean handler granted → run `resume()` (once) for anything previously stopped\n * - reload-only handler → flag reload only on a granted → denied transition\n *\n * Never throws: a failing `stop()` is downgraded to a reload requirement for\n * that one tool, so a broken handler can't break the page.\n */\nexport function applyStopHandlers(categories: Record<string, boolean>): StopHandlerResult {\n const reloadRequiredBy: string[] = [];\n\n for (const handler of handlers.values()) {\n const denied = categories[handler.category] !== true;\n\n if (isReloadOnly(handler)) {\n if (denied) {\n // Only a genuine revoke (was granted/active, now denied) warrants the\n // notice — a first-time or standing reject never had it running.\n if (reloadActive.has(handler.id)) {\n reloadRequiredBy.push(handler.id);\n reloadActive.delete(handler.id);\n }\n } else {\n reloadActive.add(handler.id);\n }\n continue;\n }\n\n if (denied) {\n if (!stopped.has(handler.id)) {\n try {\n handler.stop();\n stopped.add(handler.id);\n } catch {\n // Couldn't stop cleanly → fall back to the reload notice for this one.\n reloadRequiredBy.push(handler.id);\n }\n }\n } else if (stopped.has(handler.id)) {\n stopped.delete(handler.id);\n try {\n handler.resume?.();\n } catch {\n // A failed resume is non-fatal; the next accept re-attempts nothing\n // worse than the tool staying stopped until reload.\n }\n }\n }\n\n return { reloadRequiredBy };\n}\n\n/**\n * Load-time initialization: reflect the *full* stored consent state in both\n * directions, once, so tools start in the right mode from first paint.\n *\n * This is distinct from {@link applyStopHandlers} (which only fires on\n * transitions): a returning visitor who previously *granted* a category must\n * get a `resume()` at load — e.g. Consent Mode `update: granted` — otherwise\n * they stay stuck in the page's deny-by-default state despite having consented.\n * Denied categories get `stop()`. It never raises a reload notice (a fresh load\n * needs no \"reload to apply\"), but it *does* seed reload-only tools' active\n * state from the stored consent, so a later live revoke is correctly detected.\n */\nexport function initStopHandlers(categories: Record<string, boolean>): void {\n for (const handler of handlers.values()) {\n const denied = categories[handler.category] !== true;\n\n if (isReloadOnly(handler)) {\n // Seed active state (granted = presumed running) so a later granted →\n // denied revoke raises the notice; never raise it at load itself.\n if (denied) reloadActive.delete(handler.id);\n else reloadActive.add(handler.id);\n continue;\n }\n\n try {\n if (denied) {\n handler.stop();\n stopped.add(handler.id);\n } else {\n // Granted at load → reflect stored consent (e.g. Consent Mode grant).\n stopped.delete(handler.id);\n handler.resume?.();\n }\n } catch {\n // Never let a vendor handler break page load; state simply stays as-is.\n }\n }\n}\n","import type { ConsentPayload, ConsentSnapshot } from \"./types.js\";\n\nexport function buildConsentPayload(snapshot: ConsentSnapshot, region?: string): ConsentPayload {\n const payload: ConsentPayload = {\n consentId: snapshot.consentId,\n categories: snapshot.categories,\n regulation: snapshot.regulation,\n domain: typeof window !== \"undefined\" ? window.location.hostname : \"unknown\",\n };\n if (region) payload.region = region;\n return payload;\n}\n\nexport async function pushConsent(\n apiUrl: string,\n apiKey: string | undefined,\n snapshot: ConsentSnapshot,\n region?: string,\n): Promise<void> {\n const payload = buildConsentPayload(snapshot, region);\n\n const headers: Record<string, string> = {\n \"Content-Type\": \"application/json\",\n };\n if (apiKey) headers.Authorization = `Bearer ${apiKey}`;\n\n try {\n await fetch(apiUrl, {\n method: \"POST\",\n headers,\n body: JSON.stringify(payload),\n keepalive: true,\n });\n } catch {\n // Backend sync is best-effort — never fail the consent flow\n }\n}\n","import { resolveCategories } from \"./categories.js\";\nimport {\n clearConsentCookie,\n defaultSnapshot,\n generateConsentId,\n rawFieldsToSnapshot,\n readConsentCookie,\n writeConsentCookie,\n} from \"./cookie.js\";\nimport { broadcastGoogleConsent, warnOverlappingGcm } from \"./google-consent-mode.js\";\nimport { applyScripts, registerScript } from \"./scripts.js\";\nimport {\n applyStopHandlers,\n initStopHandlers,\n registerStopHandler,\n resolveBuiltInIntegration,\n} from \"./stop-handlers.js\";\nimport { buildConsentPayload, pushConsent } from \"./sync.js\";\nimport type {\n ConsentCategory,\n ConsentConfig,\n ConsentManager,\n ConsentSnapshot,\n ReloadNoticeState,\n ScriptEntry,\n} from \"./types.js\";\n\nexport function createConsentManager(config: ConsentConfig): ConsentManager {\n const listeners = new Set<(state: ConsentSnapshot) => void>();\n\n // Resolve the category taxonomy (built-in five, or the customer's, or a\n // validated fallback to the five). Everything below is driven by this.\n const resolved = resolveCategories(config.categories);\n\n // How to combine multiple categories mapping to the same Google signal.\n const gcmMatch = config.googleConsentMatch ?? \"any\";\n // If the taxonomy has a lossy overlap and the customer hasn't chosen a mode, warn.\n if (config.googleConsentMatch === undefined) warnOverlappingGcm(resolved);\n\n let state: ConsentSnapshot;\n let isPreferencesOpen = false;\n // Banner closed without a decision. In memory only: nothing is saved, so the\n // banner comes back on the next page load.\n let bannerDismissed = false;\n let lastPersistedCategories: Record<string, boolean>;\n // Consent actually in effect — changes only on a real decision (accept /\n // reject / save / reset / load), never on a dialog toggle. Gating reads this;\n // `state.categories` stays live to drive the dialog checkboxes.\n let committedCategories: Record<string, boolean>;\n\n /** Build a category map over the resolved ids; required ids are always granted. */\n function buildCategories(\n grantNonRequired: (id: ConsentCategory) => boolean,\n ): Record<string, boolean> {\n const cats: Record<string, boolean> = {};\n for (const id of resolved.ids) {\n cats[id] = resolved.requiredIds.has(id) ? true : grantNonRequired(id);\n }\n return cats;\n }\n\n // Reload-notice state: `reasons` is the set of handler ids that currently\n // can't be stopped cleanly; `dismissed` suppresses the notice until a\n // genuinely different set of reasons appears (so it doesn't keep popping up).\n let reloadReasons: string[] = [];\n let reloadDismissed = false;\n\n // Register stop-handlers: built-in integrations + the customer's own.\n for (const integration of config.integrations ?? []) {\n registerStopHandler(resolveBuiltInIntegration(integration));\n }\n for (const handler of config.customStopHandlers ?? []) {\n registerStopHandler(handler);\n }\n\n // --- Synchronous initialisation from cookie ---\n const rawFields = readConsentCookie();\n const savedRegulation = config.regulation ?? \"DEFAULT\";\n\n // Decide whether stored consent is still valid for the current taxonomy:\n // - tax stamp matches → reuse it.\n // - legacy cookie (no stamp) on the default taxonomy → reuse it (upgrade-safe:\n // never resets existing users who were on the built-in five).\n // - otherwise (taxonomy changed) → re-request from scratch.\n const storedTax = rawFields?.tax;\n const taxMatches = storedTax === resolved.taxonomyHash;\n const legacyCookie = storedTax === undefined;\n const storedConsentValid =\n rawFields != null && (taxMatches || (legacyCookie && resolved.isDefault));\n\n if (rawFields != null && storedConsentValid) {\n state = rawFieldsToSnapshot(rawFields, savedRegulation, resolved);\n } else {\n const consentId = rawFields?.consentid ?? generateConsentId();\n state = defaultSnapshot(consentId, savedRegulation, resolved);\n // Taxonomy changed under an existing visitor → drop the stale cookie so we\n // genuinely re-request rather than leaving a mismatched record behind.\n if (rawFields != null) clearConsentCookie();\n\n // CCPA is an opt-out model: consent is implicit from page load.\n // Write the cookie immediately so all-category values (yes) are available\n // to third-party scripts before the user has explicitly acted.\n if (state.regulation === \"CCPA\") {\n writeConsentCookie(state);\n }\n }\n // The state's taxonomy is always the resolved one — stamp it so cookies\n // written from here on carry the current signature (upgrades legacy cookies).\n state = { ...state, taxonomyHash: resolved.taxonomyHash };\n\n // GPC \"do not sell\": until the visitor explicitly acts, an incoming CCPA\n // opt-out signal starts them opted out — non-required categories off — so no\n // gated script or embed runs before they choose. An explicit choice\n // (hasActed) always wins over the signal. `gpcOptOut` is only set for CCPA,\n // where the cookie is written at load, so re-write it to carry the opt-out.\n if (config.gpcOptOut && !state.hasActed) {\n state = { ...state, categories: buildCategories(() => false) };\n writeConsentCookie(state);\n }\n lastPersistedCategories = { ...state.categories };\n committedCategories = { ...state.categories };\n\n // Fire onConsentReady synchronously on next tick.\n Promise.resolve().then(() => config.onConsentReady?.(state));\n\n function notify(): void {\n const snap = snapshot();\n for (const fn of listeners) fn(snap);\n // Scripts are applied from committed consent (below), not here — so a dialog\n // toggle, which calls notify, never loads a gated script before save.\n }\n\n /** Apply script gating against the committed consent, not the working toggles. */\n function applyCommittedScripts(): void {\n applyScripts(committedCategories);\n }\n\n function snapshot(): ConsentSnapshot {\n return {\n consentId: state.consentId,\n hasActed: state.hasActed,\n categories: { ...state.categories },\n regulation: state.regulation,\n lastRenewed: state.lastRenewed,\n taxonomyHash: state.taxonomyHash,\n };\n }\n\n /** Returns whether the reasons actually changed, so callers know to re-notify. */\n function setReloadReasons(reasons: string[]): boolean {\n const changed =\n reasons.length !== reloadReasons.length || reasons.some((r, i) => r !== reloadReasons[i]);\n if (changed) {\n reloadReasons = reasons;\n // A genuinely new set of blocked tools → allow the notice to show again.\n reloadDismissed = false;\n }\n return changed;\n }\n\n /**\n * Commit a decision, then run its side effects.\n *\n * Order matters, and not for the reason you might expect. Reordering work\n * inside one synchronous task cannot make the browser paint sooner — it can't\n * paint mid-task — so this is not a latency optimisation (measured: ~11ms\n * click-to-response either way). What it buys is that the visible response no\n * longer depends on third-party code succeeding.\n *\n * Previously every side effect ran *before* `notify()`: gated-script injection,\n * integration stop handlers, and the Google Consent Mode broadcast. Two of\n * those can throw for reasons outside our control — `injectScript` touches the\n * DOM, and `broadcastGoogleConsent` calls `dataLayer.push`, which GTM replaces\n * with its own function that runs customer-authored templates. A throw there\n * meant `notify()` never ran: the cookie said \"accepted\" but the banner stayed\n * on screen until reload. The visitor's click appeared to do nothing.\n *\n * So: commit the decision and tell the UI first, then run each side effect in\n * isolation, so no single failure can strand the banner or block the others.\n */\n function persist(): void {\n state = {\n ...state,\n hasActed: true,\n lastRenewed: Date.now(),\n };\n // Durability first: the decision must survive even if everything below fails.\n writeConsentCookie(state);\n\n // Detect \"revoke\" — any category that was previously consented but now isn't.\n // Computed before `lastPersistedCategories` is overwritten below.\n let didRevoke = false;\n for (const id of resolved.ids) {\n if (lastPersistedCategories[id] && !state.categories[id]) {\n didRevoke = true;\n break;\n }\n }\n lastPersistedCategories = { ...state.categories };\n // This is a real decision → commit it.\n committedCategories = { ...state.categories };\n\n // The visible response: the banner closes from here. Everything after this\n // point is a side effect that must not be able to prevent it.\n notify();\n config.onConsentUpdate?.(state);\n\n // Best-effort: swallow both sync throws and async rejections so a\n // broken/missing backend never breaks the consent UX.\n if (config.backend) {\n try {\n Promise.resolve(config.backend.persist(buildConsentPayload(state, config.region))).catch(\n () => undefined,\n );\n } catch {\n // sync throw from .persist itself\n }\n } else if (config.apiUrl) {\n void pushConsent(config.apiUrl, config.apiKey, state, config.region);\n }\n\n // Apply script gating from the committed consent. Isolated: a DOM failure\n // here must not stop the integrations below from being told about the change.\n try {\n applyCommittedScripts();\n } catch {\n // Injection is best-effort; consent is already committed and broadcast.\n }\n\n // Stop (or resume) integrations to match the new consent state — without a\n // reload. Anything with no clean runtime stop comes back in reloadRequiredBy\n // and surfaces the reload notice instead of silently continuing to track.\n let reasonsChanged = false;\n try {\n const { reloadRequiredBy } = applyStopHandlers(committedCategories);\n reasonsChanged = setReloadReasons(reloadRequiredBy);\n } catch {\n // Individual handlers already fail safe; this guards the loop itself.\n }\n\n // Broadcast Google Consent Mode signals for the new state (no-op unless a\n // dataLayer is present). Derived from the category → GCM-signal mapping.\n // Still in the same task as the click, so tags see the update immediately.\n try {\n broadcastGoogleConsent(resolved, committedCategories, gcmMatch);\n } catch {\n // A hostile or broken `dataLayer.push` (GTM replaces it) must not strand\n // the banner — the case this whole ordering exists to prevent.\n }\n\n // The reload notice is derived above, i.e. after the notify() that closed the\n // banner, so it needs its own notification to reach the UI.\n if (reasonsChanged) notify();\n\n // Legacy opt-in hard reload (off by default). The stop-handlers above are\n // the safe path; this remains only for customers who explicitly want it.\n // pushConsent uses keepalive: true so it survives the navigation.\n if (didRevoke && config.reloadOnRevoke && typeof window !== \"undefined\") {\n window.location.reload();\n }\n }\n\n const manager: ConsentManager = {\n get consentId() {\n return state.consentId;\n },\n get hasActed() {\n return state.hasActed;\n },\n get categories() {\n return { ...state.categories };\n },\n get committedCategories() {\n return { ...committedCategories };\n },\n get regulation() {\n return state.regulation;\n },\n get lastRenewed() {\n return state.lastRenewed;\n },\n get taxonomyHash() {\n return state.taxonomyHash;\n },\n get isPreferencesOpen() {\n return isPreferencesOpen;\n },\n get isBannerDismissed() {\n return bannerDismissed;\n },\n\n acceptAll() {\n state = { ...state, categories: buildCategories(() => true) };\n isPreferencesOpen = false;\n persist();\n },\n\n rejectAll() {\n state = { ...state, categories: buildCategories(() => false) };\n isPreferencesOpen = false;\n persist();\n },\n\n acceptSelected(categories: ConsentCategory[]) {\n state = { ...state, categories: buildCategories((id) => categories.includes(id)) };\n isPreferencesOpen = false;\n persist();\n },\n\n updateCategory(category: ConsentCategory, value: boolean) {\n // Required categories are always on and can't be toggled off.\n if (resolved.requiredIds.has(category)) return;\n // Ignore ids that aren't part of the configured taxonomy.\n if (!resolved.ids.includes(category)) return;\n state = {\n ...state,\n categories: { ...state.categories, [category]: value },\n };\n notify();\n },\n\n savePreferences() {\n isPreferencesOpen = false;\n persist();\n },\n\n resetConsent() {\n clearConsentCookie();\n const consentId = generateConsentId();\n state = defaultSnapshot(consentId, state.regulation, resolved);\n committedCategories = { ...state.categories };\n lastPersistedCategories = { ...state.categories };\n isPreferencesOpen = false;\n bannerDismissed = false;\n // Realign clean-stop flags with the reset state; clear any reload notice\n // (a reset re-prompts, so a stale \"reload to apply\" message is wrong).\n applyStopHandlers(committedCategories);\n broadcastGoogleConsent(resolved, committedCategories, gcmMatch);\n reloadReasons = [];\n reloadDismissed = false;\n notify();\n },\n\n showPreferences() {\n isPreferencesOpen = true;\n notify();\n },\n\n hidePreferences() {\n isPreferencesOpen = false;\n notify();\n },\n\n dismissBanner() {\n if (bannerDismissed) return;\n bannerDismissed = true;\n notify();\n },\n\n subscribe(listener: (state: ConsentSnapshot) => void) {\n listeners.add(listener);\n return () => listeners.delete(listener);\n },\n\n registerScript(entry: ScriptEntry) {\n registerScript(entry);\n applyCommittedScripts();\n },\n\n get reloadNotice(): ReloadNoticeState {\n return {\n required: reloadReasons.length > 0 && !reloadDismissed,\n reasons: [...reloadReasons],\n };\n },\n\n dismissReloadNotice() {\n if (reloadDismissed) return;\n reloadDismissed = true;\n notify();\n },\n };\n\n // Apply scripts for any that were already registered before manager created\n applyCommittedScripts();\n\n // Reflect the full stored consent at load — in both directions — so tools\n // start in the right mode from first paint. In particular a returning\n // visitor who previously *granted* a category gets a resume (e.g. Consent\n // Mode `update: granted`), instead of being stuck in the page's\n // deny-by-default state. No reload notice at load (that's only for live\n // revokes). Live changes after this go through applyStopHandlers.\n // Both of the following are best-effort and individually isolated, for the\n // same reason as in persist() — but the stakes at load are higher. These run\n // inside createConsentManager, so an uncaught throw propagates out of\n // initCookieYes and the SDK never mounts: no banner, no consent prompt at\n // all. `dataLayer.push` is the realistic culprit (GTM replaces it with a\n // function that runs customer-authored templates), and a broken third-party\n // tag must not be able to take the consent banner down with it.\n try {\n initStopHandlers(state.categories);\n } catch {\n // Integrations start in the page's deny-by-default state; consent is intact.\n }\n\n // Broadcast the initial Consent Mode state on load (no-op unless a Google\n // dataLayer is present), so Google tags see the returning visitor's choice\n // — or the deny-by-default for a first-time visitor — from first paint.\n try {\n broadcastGoogleConsent(resolved, state.categories, gcmMatch);\n } catch {\n // Google tags keep whatever default the page set; the banner still works.\n }\n\n return manager;\n}\n","import type { RegionConfig, RegionDecision, Regulation } from \"./types.js\";\n\n/** Anything with a header getter — a `Headers` object, Next's `headers()`, etc. */\nexport type HeaderSource = { get(name: string): string | null | undefined };\n\n// Location headers hosting providers add automatically, tried in order.\nconst GEO_HEADERS: ReadonlyArray<{ country: string; region?: string }> = [\n // Vercel — country + region give e.g. \"US-CA\".\n { country: \"x-vercel-ip-country\", region: \"x-vercel-ip-country-region\" },\n // Cloudflare — country only by default (a Worker/rule can add a region header).\n { country: \"cf-ipcountry\" },\n];\n\n/**\n * Read the visitor's region from request headers on the server (Next.js, or any\n * framework). Pass the request's headers and get back a region like \"US-CA\" or\n * \"DE\" (or undefined). By default it reads the well-known Vercel/Cloudflare\n * headers; pass `{ header }` to read your own instead. Hand the result to\n * `region.detect` in your client config.\n */\nexport function regionFromHeaders(\n headers: HeaderSource,\n options?: { header?: string },\n): string | undefined {\n if (options?.header) {\n return headers.get(options.header) || undefined;\n }\n for (const { country, region } of GEO_HEADERS) {\n const countryCode = headers.get(country);\n if (!countryCode) continue;\n const regionCode = region ? headers.get(region) : undefined;\n return regionCode ? `${countryCode}-${regionCode}` : countryCode;\n }\n return undefined;\n}\n\n/** True when the browser is sending the GPC \"do not sell/share\" signal. */\nexport function readGpc(): boolean {\n return (\n typeof navigator !== \"undefined\" &&\n (navigator as { globalPrivacyControl?: boolean }).globalPrivacyControl === true\n );\n}\n\n// Match the full region first (\"US-CA\"), then its country part (\"US\").\nfunction mapRegion(\n map: Record<string, Regulation> | undefined,\n region: string,\n): Regulation | undefined {\n if (!map) return undefined;\n return map[region] ?? map[region.split(\"-\")[0] ?? \"\"];\n}\n\n/**\n * Decide which regulation applies from the visitor's region alone. A manual\n * regulation always wins; otherwise the detected region is mapped to a\n * regulation, and anything unknown falls back to the strictest — never to the\n * lightest, so a required banner is never skipped.\n *\n * GPC is deliberately *not* considered here: it never changes which banner\n * shows (that is geo only), it only opts a CCPA visitor out client-side. Server\n * and client therefore resolve the same regulation, with no hydration mismatch.\n */\nexport function resolveRegion(config: RegionConfig, manual?: Regulation): RegionDecision {\n const strictest = config.strictest ?? \"GDPR\";\n\n // A manual regulation always wins.\n if (manual) {\n if (config.detect && typeof console !== \"undefined\") {\n // eslint-disable-next-line no-console\n console.warn(\n \"[cookieyes] `regulation` is set manually, so region detection is ignored. \" +\n \"Remove one of them to clear the conflict.\",\n );\n }\n return { region: undefined, regulation: manual, source: \"manual\", confidence: \"high\" };\n }\n\n // Detect the region and map it. Unknown/unmapped → strictest.\n const region = config.detect?.();\n const mapped = region ? mapRegion(config.map, region) : undefined;\n const regulation: Regulation = mapped ?? strictest;\n const source: RegionDecision[\"source\"] = mapped ? \"detected\" : \"strictest\";\n const confidence: RegionDecision[\"confidence\"] = mapped ? \"high\" : \"low\";\n\n return { region, regulation, source, confidence };\n}\n\n/**\n * @internal Dev aid for `region.debug`: print how the regulation was decided,\n * plus whether GPC started the visitor opted out. Shared by both runtimes.\n */\nexport function _logRegionDecision(decision: RegionDecision, gpcOptOut: boolean): void {\n if (typeof console === \"undefined\") return;\n // eslint-disable-next-line no-console\n console.info(\"[cookieyes] region detection\", {\n region: decision.region,\n regulation: decision.regulation,\n source: decision.source,\n confidence: decision.confidence,\n gpcOptOut,\n });\n}\n","import { resolveCategories } from \"./categories.js\";\nimport { _normalizeConfig } from \"./config.js\";\nimport { _warnBuiltInIntegrationsDeprecated, _warnOfflineModeDeprecated } from \"./deprecations.js\";\nimport { type ConsentEmitter, createConsentEmitter } from \"./events.js\";\nimport type { IntegrationRunner } from \"./integrations.js\";\nimport { _loadIntegrations } from \"./integrations-lazy.js\";\nimport { createLanguageController } from \"./language.js\";\nimport { createConsentManager } from \"./manager.js\";\nimport {\n _installRegisteredNetworkBlocker,\n _uninstallRegisteredNetworkBlocker,\n} from \"./network-blocker-slot.js\";\nimport { _logRegionDecision, readGpc, resolveRegion } from \"./region.js\";\nimport type {\n ActiveUI,\n ConsentCategory,\n ConsentChangePayload,\n ConsentConfig,\n ConsentRuntime,\n ConsentStore,\n ConsentStoreState,\n CookieYesConfig,\n RegionConfig,\n RegionDecision,\n Regulation,\n} from \"./types.js\";\n\n/** True when a CCPA visitor's browser sends GPC and we're set to honour it. */\nfunction wantsGpcOptOut(regulation: Regulation, region: RegionConfig | undefined): boolean {\n return regulation === \"CCPA\" && (region?.honorGpc ?? true) && readGpc();\n}\n\nfunction splitCategories(categories: Record<string, boolean>): ConsentChangePayload {\n const allowed: ConsentCategory[] = [];\n const denied: ConsentCategory[] = [];\n for (const cat of Object.keys(categories) as ConsentCategory[]) {\n if (categories[cat]) allowed.push(cat);\n else denied.push(cat);\n }\n return { allowedCategories: allowed, deniedCategories: denied };\n}\n\nlet _runtime: ConsentRuntime | null = null;\nlet _integrationRunner: IntegrationRunner | null = null;\n/**\n * Bumped by every runtime creation and by every reset, so a chunk that arrives\n * after the runtime it was requested for has gone away can tell and do nothing.\n * Without it, a `resetConsentRuntime()` between the `import()` and its\n * resolution would be followed by the old runner installing itself against a\n * manager that is no longer current.\n */\nlet _integrationGeneration = 0;\n\nexport function getOrCreateConsentRuntime(config: CookieYesConfig): ConsentRuntime {\n if (_runtime) return _runtime;\n\n // `\"offline\"` is a deprecated alias for `\"cookie-only\"` — same behavior, one\n // warning per page load. Checked on the raw config before normalization.\n if (config.mode === \"offline\") _warnOfflineModeDeprecated();\n\n // Collapse deprecated aliases (`overrides.regulation` → `regulation`,\n // `backendURL` → `apiUrl`) into the one canonical shape both packages share.\n const options = _normalizeConfig(config);\n const changeListeners = new Set<(payload: ConsentChangePayload) => void>();\n const userOnConsentUpdate = options.onConsentUpdate;\n // Assigned right after the manager exists; only ever read from within\n // onConsentUpdate, which can't fire until the visitor acts (post-init).\n let emitter: ConsentEmitter;\n\n // Resolve which regulation applies (geo-detection if configured, else the\n // manual/default). Drives the banner and is recorded on the consent payload.\n const regionDecision: RegionDecision = options.region\n ? resolveRegion(options.region, options.regulation)\n : {\n region: undefined,\n regulation: options.regulation ?? \"DEFAULT\",\n source: \"manual\",\n confidence: \"high\",\n };\n\n const cfg: ConsentConfig = {};\n if (options.mode === \"self-hosted\") {\n if (options.backend) cfg.backend = options.backend;\n else if (options.apiUrl) cfg.apiUrl = options.apiUrl;\n }\n if (options.apiKey) cfg.apiKey = options.apiKey;\n cfg.regulation = regionDecision.regulation;\n if (regionDecision.region) cfg.region = regionDecision.region;\n // Honour the browser's GPC \"do not sell\" signal on a CCPA banner: start the\n // visitor opted out. GPC never changes the regulation (that's geo only).\n const gpcOptOut = wantsGpcOptOut(regionDecision.regulation, options.region);\n if (gpcOptOut) cfg.gpcOptOut = true;\n if (options.region?.debug) _logRegionDecision(regionDecision, gpcOptOut);\n if (options.colorScheme) cfg.colorScheme = options.colorScheme;\n if (options.theme) cfg.theme = options.theme;\n if (options.reloadOnRevoke) cfg.reloadOnRevoke = options.reloadOnRevoke;\n if (options.googleConsentMatch) cfg.googleConsentMatch = options.googleConsentMatch;\n if (options.builtInIntegrations && options.builtInIntegrations.length > 0) {\n _warnBuiltInIntegrationsDeprecated();\n cfg.integrations = options.builtInIntegrations;\n }\n if (options.customStopHandlers) cfg.customStopHandlers = options.customStopHandlers;\n if (options.categories) cfg.categories = options.categories;\n if (options.onConsentReady) cfg.onConsentReady = options.onConsentReady;\n\n cfg.onConsentUpdate = (snap) => {\n userOnConsentUpdate?.(snap);\n emitter.push(snap.categories);\n const payload = splitCategories(snap.categories);\n for (const fn of changeListeners) fn(payload);\n };\n\n const manager = createConsentManager(cfg);\n emitter = createConsentEmitter(() => manager.committedCategories);\n // Same resolution the manager uses internally — exposed so a custom UI can\n // iterate the taxonomy actually in effect (custom list or built-in five).\n const resolved = resolveCategories(options.categories);\n\n // One listener set drives `consentStore.subscribe`, fed by both consent\n // changes and language switches, so a custom UI re-renders on either.\n const stateListeners = new Set<(state: ConsentStoreState) => void>();\n function notifyState(): void {\n const state = buildState();\n for (const fn of stateListeners) fn(state);\n }\n manager.subscribe(notifyState);\n const language = createLanguageController(options.i18n, notifyState);\n\n function activeUI(): ActiveUI {\n if (manager.isPreferencesOpen) return \"dialog\";\n if (!manager.hasActed && !manager.isBannerDismissed) return \"banner\";\n return null;\n }\n\n function buildState(): ConsentStoreState {\n // `consents`/`categories` are live (drive checkboxes); `committedConsents`\n // and `has()` are the consent in effect (gate scripts/embeds on those).\n const categories = manager.categories;\n return {\n consentId: manager.consentId,\n hasActed: manager.hasActed,\n categories,\n consents: categories,\n committedConsents: manager.committedCategories,\n regulation: manager.regulation,\n lastRenewed: manager.lastRenewed,\n taxonomyHash: manager.taxonomyHash,\n activeUI: activeUI(),\n has: (category) => manager.committedCategories[category] === true,\n saveConsents: async (target) => {\n if (target === \"all\") manager.acceptAll();\n else if (target === \"necessary\") manager.rejectAll();\n else manager.acceptSelected(target);\n },\n setConsent: (category, value) => manager.updateCategory(category, value),\n subscribeToConsentChanges: (listener) => {\n changeListeners.add(listener);\n return () => {\n changeListeners.delete(listener);\n };\n },\n };\n }\n\n const consentStore: ConsentStore = {\n subscribe: (listener) => {\n stateListeners.add(listener);\n return () => {\n stateListeners.delete(listener);\n };\n },\n getState: buildState,\n on: (type, listener, opts) => emitter.on(type, listener, opts),\n get translations() {\n return language.getTranslations();\n },\n getLanguageInfo: language.getLanguageInfo,\n setLanguage: language.setLanguage,\n getCategoryText: language.getCategoryText,\n categories: resolved,\n getRegion: () => regionDecision,\n };\n\n // Installed through the slot rather than imported directly, so the blocker\n // ships only to customers who register it. Still eager and synchronous: a\n // registered blocker patches the browser's networking here, exactly as\n // before. See network-blocker-slot.ts.\n if (options.networkBlocker && options.networkBlocker.rules.length > 0) {\n _installRegisteredNetworkBlocker(\n options.networkBlocker,\n (cat) => manager.committedCategories[cat] === true,\n );\n }\n\n // Run the configured script integrations (Segment, Google, Meta, …) against\n // the committed consent. Reconciles on every consent change; torn down on reset.\n let integrationsReady: Promise<void> = Promise.resolve();\n if (options.integrations && options.integrations.length > 0) {\n const configured = options.integrations;\n const builtIn = options.builtInIntegrations ?? [];\n const generation = ++_integrationGeneration;\n // Loaded on demand: see `_loadIntegrations`. The setup of each integration\n // is therefore deferred by one chunk fetch. Integrations exist to load\n // third-party tags, which are asynchronous anyway, and consent gating is\n // unaffected — nothing loads that would not have loaded. It is still a\n // deferral, not a deletion, which is why `total` in the size report does\n // not fall even though `initial` does.\n integrationsReady = _loadIntegrations().then((m) => {\n if (generation !== _integrationGeneration) return;\n m.warnOverlappingVendors(\n configured.map((i) => i.id),\n builtIn.map((b) => b.vendor),\n );\n m.warnUnknownCategories(configured, resolved.ids);\n _integrationRunner = m.runIntegrations(configured, {\n granted: (category) => manager.committedCategories[category] === true,\n subscribe: (fn) => manager.subscribe(() => fn()),\n region: regionDecision,\n });\n });\n }\n\n _runtime = {\n consentManager: manager,\n consentStore,\n getIntegrations: () => _integrationRunner?.list() ?? [],\n integrationsReady,\n };\n return _runtime;\n}\n\n/**\n * Canonical setup entry point. Alias of {@link getOrCreateConsentRuntime} that\n * accepts the same {@link CookieYesConfig} and returns the same process-wide\n * singleton — provided so documentation can use one setup name (`initCookieYes`)\n * across every package.\n */\nexport function initCookieYes(config: CookieYesConfig): ConsentRuntime {\n return getOrCreateConsentRuntime(config);\n}\n\nexport function resetConsentRuntime(): void {\n // Un-patch `fetch`/XHR/`sendBeacon` before dropping the runtime. The blocker is a\n // module-level singleton: leaving it installed would keep the *old* manager's\n // committed-consent closure deciding what is allowed, and because a second\n // `installNetworkBlocker()` is a no-op while one is active, the next\n // `initCookieYes()` would silently never apply its own rules. Idempotent — a no-op\n // when nothing was installed.\n _uninstallRegisteredNetworkBlocker();\n // Invalidate any in-flight integration load before dropping the runner, so a\n // chunk still on its way cannot install itself after the reset.\n _integrationGeneration++;\n _integrationRunner?.stop();\n _integrationRunner = null;\n _runtime = null;\n}\n","import { type CategoryDef, resolveCategories } from \"./categories.js\";\nimport { parseCookieHeader, rawFieldsToSnapshot } from \"./cookie.js\";\nimport type { ConsentSnapshot, Regulation } from \"./types.js\";\n\n/**\n * The subset of your consent config that affects reading a stored decision.\n * `CookieYesConfig` satisfies this structurally, so you can pass the same object\n * you give `initCookieYes`.\n */\nexport type ServerConsentOptions = {\n regulation?: Regulation | undefined;\n categories?: CategoryDef[] | undefined;\n};\n\n/**\n * Read a visitor's already-made consent decision from a request's `Cookie`\n * header, on the server, with no `document` and no browser APIs.\n *\n * Use it to keep the banner out of the HTML entirely for a returning visitor.\n * Without it the server has no idea whether the visitor has chosen, so it sends\n * banner markup to everyone and the client removes it after hydration — the\n * banner visibly appears and then vanishes, which reads as a bug.\n *\n * Returns `null` whenever the banner *should* be shown:\n * - no consent cookie (a first-time visitor),\n * - a cookie that records no decision yet (`action:no`, e.g. a CCPA visitor who\n * has an implicit-consent cookie but has not acted),\n * - a corrupt cookie,\n * - a cookie written against a **different category taxonomy**, which the client\n * also treats as stale and re-requests. The one exception mirrors the client\n * exactly: a legacy cookie with no taxonomy stamp is still honoured when the\n * built-in five categories are in effect, so existing visitors are not\n * re-prompted by an upgrade.\n *\n * Otherwise returns the stored snapshot, ready to hand to `CookieYesProvider`'s\n * `initialConsent`.\n *\n * ```ts\n * // Any SSR framework — pass the request's Cookie header:\n * const initialConsent = readServerConsent(request.headers.get(\"cookie\") ?? \"\", config);\n * ```\n *\n * In Next.js App Router, prefer `getServerConsent(config)` from\n * `@cookieyes/nextjs`, which reads `cookies()` for you.\n *\n * **Never** put the result on `initCookieYes` or the runtime: the runtime is a\n * module-level singleton shared across concurrent requests, so per-visitor state\n * there would leak between them. It belongs in the component tree.\n */\nexport function readServerConsent(\n cookieHeader: string,\n options: ServerConsentOptions = {},\n): ConsentSnapshot | null {\n if (typeof cookieHeader !== \"string\" || cookieHeader.length === 0) return null;\n\n const fields = parseCookieHeader(cookieHeader);\n if (fields == null) return null;\n\n const resolved = resolveCategories(options.categories);\n\n // Mirrors createConsentManager's stored-consent validity rule exactly. If the\n // two ever disagree, the server and client reach different conclusions about\n // the same visitor and the banner flashes — the bug this function prevents.\n const storedTax = fields.tax;\n const taxMatches = storedTax === resolved.taxonomyHash;\n const legacyCookie = storedTax === undefined;\n if (!taxMatches && !(legacyCookie && resolved.isDefault)) return null;\n\n const snapshot = rawFieldsToSnapshot(fields, options.regulation ?? \"DEFAULT\", resolved);\n\n // No explicit decision yet → the banner is supposed to show.\n if (!snapshot.hasActed) return null;\n\n // Stamp the current taxonomy, as the client does after reading the cookie, so\n // the server and hydration snapshots are identical.\n return { ...snapshot, taxonomyHash: resolved.taxonomyHash };\n}\n"],"names":["COOKIE_NAME","COOKIE_META_KEYS","Set","parseCookie","raw","fields","categories","pair","split","colonIdx","indexOf","key","slice","trim","value","has","length","serializeCookie","snapshot","parts","consentId","hasActed","taxonomyHash","push","id","granted","Object","entries","lastRenewed","Date","now","join","parseCookieHeader","header","cookie","trimmed","eqIdx","decodeURIComponent","writeConsentCookie","document","encodeURIComponent","clearConsentCookie","generateConsentId","array","Uint8Array","crypto","getRandomValues","i","Math","floor","random","btoa","String","fromCharCode","replace","rawFieldsToSnapshot","regulation","resolved","ids","requiredIds","consentid","action","lastRenewedDate","Number","tax","defaultSnapshot","isOptOut","DEFAULT_CATEGORIES","required","gcm","hashString","input","h","charCodeAt","imul","toString","build","list","isDefault","map","c","filter","signature","resolveCategories","defs","error","d","some","includes","size","validationError","console","warn","message","_normalizeConfig","config","normalized","mode","nestedRegulation","overrides","region","colorScheme","theme","i18n","networkBlocker","reloadOnRevoke","googleConsentMatch","integrations","builtInIntegrations","customStopHandlers","onConsentReady","onConsentUpdate","apiKey","backend","apiUrl","backendURL","warnedOfflineMode","_warnOfflineModeDeprecated","process","env","NODE_ENV","warnedBuiltInIntegrations","_warnBuiltInIntegrationsDeprecated","createConsentEmitter","getCommitted","listeners","save","change","last","deliver","reg","type","payload","listener","err","emit","category","changedCategories","on","options","add","isInitial","delete","next","keys","ALL_SIGNALS","computeGoogleConsent","match","result","signal","mappers","def","every","broadcastGoogleConsent","window","Array","isArray","dataLayer","consent","arguments","gtag","en","bannerTitle","bannerDescription","acceptAll","rejectAll","managePreferences","savePreferences","doNotSell","ccpaDescription","accept","poweredBy","opensInNewTab","preferencesTitle","preferencesIntro","alwaysActive","preferencesDialogLabel","optOutDialogLabel","recallButtonLabel","necessary","label","description","functional","analytics","performance","advertisement","optOut","title","cancel","successText","successCountdown","bannerCloseLabel","preferencesCloseLabel","optOutCloseLabel","gatedFrame","placeholder","reloadNotice","reloadButton","dismissButton","RTL","primaryOf","tag","toLowerCase","getTextDirection","mergeTranslations","base","override","out","baseVal","bothObjects","pickLanguage","messages","candidates","locale","detectBrowserLanguage","navigator","language","primary","async","_loadIntegrations","module","Promise","resolve","then","require","runIntegrations","warnOverlappingVendors","warnUnknownCategories","createLanguageController","onChange","loadLanguage","warned","translations","info","buildInfo","messagesFor","isAvailable","defaultTranslations","direction","languages","from","apply","warnMissing","setLanguage","loaded","catch","getTranslations","getLanguageInfo","getCategoryText","registry","Map","injected","injectScript","entry","getElementById","el","createElement","src","onLoad","addEventListener","once","head","appendChild","set","isReloadOnly","needsReload","resolveBuiltInIntegration","cfg","vendor","stop","fbq","resume","handlers","stopped","reloadActive","registerStopHandler","handler","applyStopHandlers","reloadRequiredBy","values","denied","buildConsentPayload","domain","location","hostname","createConsentManager","gcmMatch","state","perSignal","get","idSet","JSON","stringify","warnOverlappingGcm","lastPersistedCategories","committedCategories","isPreferencesOpen","bannerDismissed","buildCategories","grantNonRequired","cats","reloadReasons","reloadDismissed","integration","rawFields","savedRegulation","storedTax","taxMatches","storedConsentValid","notify","snap","fn","applyCommittedScripts","applyScripts","persist","didRevoke","headers","Authorization","fetch","method","body","keepalive","pushConsent","reasonsChanged","reasons","changed","r","setReloadReasons","reload","gpcOptOut","manager","isBannerDismissed","acceptSelected","updateCategory","resetConsent","showPreferences","hidePreferences","dismissBanner","subscribe","registerScript","dismissReloadNotice","initStopHandlers","GEO_HEADERS","country","readGpc","globalPrivacyControl","resolveRegion","manual","strictest","detect","source","confidence","mapped","mapRegion","_logRegionDecision","decision","_runtime","_integrationRunner","_integrationGeneration","getOrCreateConsentRuntime","changeListeners","userOnConsentUpdate","emitter","regionDecision","honorGpc","debug","allowed","cat","allowedCategories","deniedCategories","splitCategories","stateListeners","notifyState","buildState","consents","committedConsents","activeUI","saveConsents","target","setConsent","subscribeToConsentChanges","consentStore","getState","opts","getRegion","rules","_installRegisteredNetworkBlocker","integrationsReady","configured","builtIn","generation","m","b","consentManager","getIntegrations","remove","clear","cookieHeader","countryCode","regionCode","_uninstallRegisteredNetworkBlocker"],"mappings":"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"}
|