@consenti/ui 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +325 -117
- package/dist/angular.d.cts +233 -34
- package/dist/angular.d.ts +233 -34
- package/dist/angular.js +1 -73
- package/dist/angular.js.map +1 -1
- package/dist/angular.mjs +1 -52
- package/dist/angular.mjs.map +1 -1
- package/dist/chunk-K2UN3O67.mjs +2 -0
- package/dist/{chunk-ARALKXMB.mjs.map → chunk-K2UN3O67.mjs.map} +1 -1
- package/dist/hmac-UKMRMKN6.mjs +2 -0
- package/dist/{hmac-N6H7F2K2.mjs.map → hmac-UKMRMKN6.mjs.map} +1 -1
- package/dist/http-UU2NIW6M.mjs +2 -0
- package/dist/index.css +1 -1
- package/dist/index.d.ts +217 -25
- package/dist/index.mjs +1 -2220
- package/dist/index.mjs.map +1 -1
- package/dist/index.umd.js +1 -2304
- package/dist/index.umd.js.map +1 -1
- package/dist/react.d.cts +233 -29
- package/dist/react.d.ts +233 -29
- package/dist/react.js +1 -60
- package/dist/react.js.map +1 -1
- package/dist/react.mjs +1 -39
- package/dist/react.mjs.map +1 -1
- package/dist/testing.js +1 -94
- package/dist/testing.js.map +1 -1
- package/dist/testing.mjs +1 -73
- package/dist/testing.mjs.map +1 -1
- package/dist/vue.d.cts +244 -53
- package/dist/vue.d.ts +244 -53
- package/dist/vue.js +1 -72
- package/dist/vue.js.map +1 -1
- package/dist/vue.mjs +1 -51
- package/dist/vue.mjs.map +1 -1
- package/package.json +13 -5
- package/dist/chunk-ARALKXMB.mjs +0 -20
- package/dist/hmac-N6H7F2K2.mjs +0 -26
- package/dist/http-VHOGZ6ZJ.mjs +0 -7
- /package/dist/{http-VHOGZ6ZJ.mjs.map → http-UU2NIW6M.mjs.map} +0 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/utils/http.ts"],"sourcesContent":["/**\n * Minimal `fetch` wrapper used for all Consenti API calls.\n *\n * Adds `Content-Type: application/json` and, when `authToken` is provided,\n * an `Authorization: Bearer <token>` header. Throws a descriptive `Error` on\n * non-2xx responses so callers can catch and fall back gracefully.\n *\n * This module is tree-shaken out of bundles that do not use API mode.\n */\n\n/**\n * Sends an HTTP request and returns the parsed JSON response body.\n *\n * @param url - The request URL.\n * @param options - Standard `RequestInit` options (method, body, headers, etc.).\n * @param authToken - Optional Bearer token. Added as `Authorization: Bearer <token>` when present.\n * @returns Parsed JSON body typed as `T`.\n * @throws `Error` with HTTP status and URL on non-2xx responses.\n */\nexport async function httpRequest<T>(\n url: string,\n options: RequestInit = {},\n authToken?: string,\n): Promise<T> {\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n ...(options.headers as Record<string, string> | undefined),\n }\n if (authToken) {\n headers['Authorization'] = `Bearer ${authToken}`\n }\n const res = await fetch(url, { ...options, headers })\n if (!res.ok) {\n throw new Error(`[Consenti] HTTP ${res.status} ${res.statusText} — ${url}`)\n }\n return res.json() as Promise<T>\n}\n"],"mappings":"
|
|
1
|
+
{"version":3,"sources":["../src/utils/http.ts"],"sourcesContent":["/**\n * Minimal `fetch` wrapper used for all Consenti API calls.\n *\n * Adds `Content-Type: application/json` and, when `authToken` is provided,\n * an `Authorization: Bearer <token>` header. Throws a descriptive `Error` on\n * non-2xx responses so callers can catch and fall back gracefully.\n *\n * This module is tree-shaken out of bundles that do not use API mode.\n */\n\n/**\n * Sends an HTTP request and returns the parsed JSON response body.\n *\n * @param url - The request URL.\n * @param options - Standard `RequestInit` options (method, body, headers, etc.).\n * @param authToken - Optional Bearer token. Added as `Authorization: Bearer <token>` when present.\n * @returns Parsed JSON body typed as `T`.\n * @throws `Error` with HTTP status and URL on non-2xx responses.\n */\nexport async function httpRequest<T>(\n url: string,\n options: RequestInit = {},\n authToken?: string,\n): Promise<T> {\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n ...(options.headers as Record<string, string> | undefined),\n }\n if (authToken) {\n headers['Authorization'] = `Bearer ${authToken}`\n }\n const res = await fetch(url, { ...options, headers })\n if (!res.ok) {\n throw new Error(`[Consenti] HTTP ${res.status} ${res.statusText} — ${url}`)\n }\n return res.json() as Promise<T>\n}\n"],"mappings":"AAmBA,eAAsBA,EACpBC,EACAC,EAAuB,CAAC,EACxBC,EACY,CACZ,IAAMC,EAAkC,CACtC,eAAgB,mBAChB,GAAIF,EAAQ,OACd,EACIC,IACFC,EAAQ,cAAmB,UAAUD,CAAS,IAEhD,IAAME,EAAM,MAAM,MAAMJ,EAAK,CAAE,GAAGC,EAAS,QAAAE,CAAQ,CAAC,EACpD,GAAI,CAACC,EAAI,GACP,MAAM,IAAI,MAAM,mBAAmBA,EAAI,MAAM,IAAIA,EAAI,UAAU,WAAMJ,CAAG,EAAE,EAE5E,OAAOI,EAAI,KAAK,CAClB","names":["httpRequest","url","options","authToken","headers","res"]}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
async function i(t){let e=new TextEncoder().encode(t);return crypto.subtle.importKey("raw",e,{name:"HMAC",hash:"SHA-256"},!1,["sign","verify"])}async function s(t,e){let n=await i(e),r=new TextEncoder().encode(t),o=await crypto.subtle.sign("HMAC",n,r);return Array.from(new Uint8Array(o)).map(a=>a.toString(16).padStart(2,"0")).join("")}async function c(t,e,n){return await s(t,n)===e}export{s as signValue,c as verifyValue};
|
|
2
|
+
//# sourceMappingURL=hmac-UKMRMKN6.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/utils/hmac.ts"],"sourcesContent":["/**\n * HMAC-SHA256 signing utilities using the native Web Crypto API.\n *\n * Used to sign consent cookies so that tampering (e.g. a user manually editing\n * their cookie to fake a broader consent) is detectable on the next page load.\n * The signing key is sourced from the API profile response or from\n * `core.cookieSigningKey` in the widget config.\n *\n * All operations are async because `crypto.subtle` is Promise-based.\n * This module is tree-shaken out of bundles that do not use `core.signCookies`.\n */\n\n/**\n * Imports a raw UTF-8 string key as a non-extractable `CryptoKey` for HMAC-SHA256.\n * The key is not extractable — it can only be used for sign/verify operations.\n */\nasync function importHmacKey(rawKey: string): Promise<CryptoKey> {\n const keyData = new TextEncoder().encode(rawKey)\n return crypto.subtle.importKey(\n 'raw',\n keyData,\n { name: 'HMAC', hash: 'SHA-256' },\n false,\n ['sign', 'verify'],\n )\n}\n\n/**\n * Signs a string value with HMAC-SHA256 and returns the lowercase hex-encoded signature.\n *\n * @param value - The plaintext string to sign (the raw cookie value without `::sig:` segment).\n * @param key - The signing key as a UTF-8 string.\n * @returns Lowercase hex string, e.g. `\"a3f9c2e1b8d0...\"`\n */\nexport async function signValue(value: string, key: string): Promise<string> {\n const cryptoKey = await importHmacKey(key)\n const data = new TextEncoder().encode(value)\n const signature = await crypto.subtle.sign('HMAC', cryptoKey, data)\n return Array.from(new Uint8Array(signature))\n .map((b) => b.toString(16).padStart(2, '0'))\n .join('')\n}\n\n/**\n * Verifies a HMAC-SHA256 signature by re-computing the expected signature and comparing.\n *\n * Note: comparison is not constant-time, but this runs entirely client-side against\n * data the user already controls, so timing attacks offer no practical advantage.\n *\n * @param value - The original plaintext string that was signed.\n * @param signature - The hex-encoded signature to verify.\n * @param key - The signing key as a UTF-8 string.\n * @returns `true` if the signature matches, `false` otherwise.\n */\nexport async function verifyValue(value: string, signature: string, key: string): Promise<boolean> {\n const expected = await signValue(value, key)\n return expected === signature\n}\n"],"mappings":"
|
|
1
|
+
{"version":3,"sources":["../src/utils/hmac.ts"],"sourcesContent":["/**\n * HMAC-SHA256 signing utilities using the native Web Crypto API.\n *\n * Used to sign consent cookies so that tampering (e.g. a user manually editing\n * their cookie to fake a broader consent) is detectable on the next page load.\n * The signing key is sourced from the API profile response or from\n * `core.cookieSigningKey` in the widget config.\n *\n * All operations are async because `crypto.subtle` is Promise-based.\n * This module is tree-shaken out of bundles that do not use `core.signCookies`.\n */\n\n/**\n * Imports a raw UTF-8 string key as a non-extractable `CryptoKey` for HMAC-SHA256.\n * The key is not extractable — it can only be used for sign/verify operations.\n */\nasync function importHmacKey(rawKey: string): Promise<CryptoKey> {\n const keyData = new TextEncoder().encode(rawKey)\n return crypto.subtle.importKey(\n 'raw',\n keyData,\n { name: 'HMAC', hash: 'SHA-256' },\n false,\n ['sign', 'verify'],\n )\n}\n\n/**\n * Signs a string value with HMAC-SHA256 and returns the lowercase hex-encoded signature.\n *\n * @param value - The plaintext string to sign (the raw cookie value without `::sig:` segment).\n * @param key - The signing key as a UTF-8 string.\n * @returns Lowercase hex string, e.g. `\"a3f9c2e1b8d0...\"`\n */\nexport async function signValue(value: string, key: string): Promise<string> {\n const cryptoKey = await importHmacKey(key)\n const data = new TextEncoder().encode(value)\n const signature = await crypto.subtle.sign('HMAC', cryptoKey, data)\n return Array.from(new Uint8Array(signature))\n .map((b) => b.toString(16).padStart(2, '0'))\n .join('')\n}\n\n/**\n * Verifies a HMAC-SHA256 signature by re-computing the expected signature and comparing.\n *\n * Note: comparison is not constant-time, but this runs entirely client-side against\n * data the user already controls, so timing attacks offer no practical advantage.\n *\n * @param value - The original plaintext string that was signed.\n * @param signature - The hex-encoded signature to verify.\n * @param key - The signing key as a UTF-8 string.\n * @returns `true` if the signature matches, `false` otherwise.\n */\nexport async function verifyValue(value: string, signature: string, key: string): Promise<boolean> {\n const expected = await signValue(value, key)\n return expected === signature\n}\n"],"mappings":"AAgBA,eAAeA,EAAcC,EAAoC,CAC/D,IAAMC,EAAU,IAAI,YAAY,EAAE,OAAOD,CAAM,EAC/C,OAAO,OAAO,OAAO,UACnB,MACAC,EACA,CAAE,KAAM,OAAQ,KAAM,SAAU,EAChC,GACA,CAAC,OAAQ,QAAQ,CACnB,CACF,CASA,eAAsBC,EAAUC,EAAeC,EAA8B,CAC3E,IAAMC,EAAY,MAAMN,EAAcK,CAAG,EACnCE,EAAO,IAAI,YAAY,EAAE,OAAOH,CAAK,EACrCI,EAAY,MAAM,OAAO,OAAO,KAAK,OAAQF,EAAWC,CAAI,EAClE,OAAO,MAAM,KAAK,IAAI,WAAWC,CAAS,CAAC,EACxC,IAAKC,GAAMA,EAAE,SAAS,EAAE,EAAE,SAAS,EAAG,GAAG,CAAC,EAC1C,KAAK,EAAE,CACZ,CAaA,eAAsBC,EAAYN,EAAeI,EAAmBH,EAA+B,CAEjG,OADiB,MAAMF,EAAUC,EAAOC,CAAG,IACvBG,CACtB","names":["importHmacKey","rawKey","keyData","signValue","value","key","cryptoKey","data","signature","b","verifyValue"]}
|
package/dist/index.css
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
:root{--consenti-color-bg: #ffffff;--consenti-color-text: #1a2e4a;--consenti-color-primary: #1565c0;--consenti-color-primary-text: #ffffff;--consenti-color-secondary: #f0f4f8;--consenti-color-secondary-text: #1a2e4a;--consenti-color-border: #dbe4ee;--consenti-color-overlay: rgba(26, 52, 96, 0.55);--consenti-font-family: system-ui, -apple-system, sans-serif;--consenti-font-size-base: 14px;--consenti-font-size-heading: 16px;--consenti-font-weight-heading: 600;--consenti-line-height: 1.5;--consenti-spacing-sm: 8px;--consenti-spacing-md: 16px;--consenti-spacing-lg: 24px;--consenti-border-radius: 8px;--consenti-border-radius-btn: 4px;--consenti-shadow: 0 4px 24px rgba(21, 101, 192, 0.14);--consenti-color-accent: #d32f2f;--consenti-color-accent-text: #ffffff;--consenti-toggle-bg-on: #43a047;--consenti-toggle-bg-off: #c8d6e8;--consenti-toggle-knob: #ffffff;--consenti-toggle-width: 44px;--consenti-toggle-height: 24px;--consenti-z-banner: 9999;--consenti-z-overlay: 9998;--consenti-z-modal: 10000}.consenti-root--dark{--consenti-color-bg: #1e2535;--consenti-color-text: #e2e8f0;--consenti-color-primary: #4a9eff;--consenti-color-primary-text: #ffffff;--consenti-color-secondary: #2a3447;--consenti-color-secondary-text: #cbd5e1;--consenti-color-border: #374151;--consenti-color-overlay: rgba(0, 0, 0, 0.65);--consenti-shadow: 0 4px 24px rgba(0, 0, 0, 0.4);--consenti-toggle-bg-off: #4b5563}.consenti-overlay{position:fixed;inset:0;background:var(--consenti-color-overlay);z-index:var(--consenti-z-overlay)}.consenti-btn{display:inline-flex;align-items:center;justify-content:center;padding:var(--consenti-spacing-sm) var(--consenti-spacing-md);border:none;border-radius:var(--consenti-border-radius-btn);font-family:var(--consenti-font-family);font-size:var(--consenti-font-size-base);font-weight:500;line-height:1;cursor:pointer;text-decoration:none;transition:opacity .15s ease}.consenti-btn:focus-visible{outline:2px solid var(--consenti-color-primary);outline-offset:2px}.consenti-btn:hover{opacity:.9}.consenti-btn--primary{background:var(--consenti-color-primary);color:var(--consenti-color-primary-text)}.consenti-btn--secondary{background:var(--consenti-color-secondary);color:var(--consenti-color-secondary-text)}.consenti-btn--text{background:rgba(0,0,0,0);color:var(--consenti-color-primary);padding-left:0;padding-right:0;text-decoration:underline}.consenti-btn--accent{background:var(--consenti-color-accent);color:var(--consenti-color-accent-text)}.consenti-btn--close{background:rgba(0,0,0,0);color:var(--consenti-color-text);font-size:20px;line-height:1;padding:4px 8px;border-radius:50%}.consenti-btn--close:hover{background:var(--consenti-color-secondary);opacity:1}.consenti-banner{position:fixed;left:0;right:0;z-index:var(--consenti-z-banner);background:var(--consenti-color-bg);color:var(--consenti-color-text);font-family:var(--consenti-font-family);font-size:var(--consenti-font-size-base);line-height:var(--consenti-line-height);box-shadow:var(--consenti-shadow);padding:var(--consenti-spacing-lg);display:flex;flex-wrap:wrap;align-items:center;gap:var(--consenti-spacing-md)}.consenti-banner--top{top:0;border-bottom:1px solid var(--consenti-color-border)}@media(prefers-reduced-motion: no-preference){.consenti-banner--top{animation:consenti-slide-down .2s ease-out}}.consenti-banner--bottom{bottom:0;border-top:1px solid var(--consenti-color-border)}@media(prefers-reduced-motion: no-preference){.consenti-banner--bottom{animation:consenti-slide-up .2s ease-out}}.consenti-banner--middle{top:50%;transform:translateY(-50%);left:50%;right:auto;transform:translate(-50%, -50%);width:min(600px,90vw);border-radius:var(--consenti-border-radius)}.consenti-banner--left-bottom{bottom:var(--consenti-spacing-lg);left:var(--consenti-spacing-lg);right:auto;width:min(400px,90vw);border-radius:var(--consenti-border-radius)}.consenti-banner--right-bottom{bottom:var(--consenti-spacing-lg);right:var(--consenti-spacing-lg);left:auto;width:min(400px,90vw);border-radius:var(--consenti-border-radius)}.consenti-banner__text-container{flex:1;min-width:200px}.consenti-banner__heading{font-size:var(--consenti-font-size-heading);font-weight:var(--consenti-font-weight-heading);margin:0 0 var(--consenti-spacing-sm);width:100%}.consenti-banner__text{margin:0}.consenti-banner__links{display:flex;flex-wrap:wrap;gap:var(--consenti-spacing-sm);margin-top:var(--consenti-spacing-sm)}.consenti-banner__link{font-size:.85em;text-decoration:underline;text-underline-offset:2px}.consenti-banner__controls{position:absolute;top:var(--consenti-spacing-sm);right:var(--consenti-spacing-sm);display:flex;align-items:center;gap:4px}.consenti-banner__buttons{display:flex;flex-wrap:wrap;gap:var(--consenti-spacing-sm);align-items:center}.consenti-banner--top .consenti-banner__buttons,.consenti-banner--bottom .consenti-banner__buttons{margin-left:2vw}@keyframes consenti-slide-up{from{transform:translateY(100%)}to{transform:translateY(0)}}@keyframes consenti-slide-down{from{transform:translateY(-100%)}to{transform:translateY(0)}}.consenti-modal{position:fixed;inset:0;z-index:var(--consenti-z-modal);display:flex;font-family:var(--consenti-font-family);font-size:var(--consenti-font-size-base);color:var(--consenti-color-text);line-height:var(--consenti-line-height)}.consenti-modal--passthrough{pointer-events:none}.consenti-modal--passthrough .consenti-modal__container{pointer-events:auto}.consenti-modal--center{align-items:center;justify-content:center}.consenti-modal--left{align-items:stretch;justify-content:flex-start}.consenti-modal--left .consenti-modal__container{border-radius:0 var(--consenti-border-radius) var(--consenti-border-radius) 0;height:100%;max-height:100vh}.consenti-modal--right{align-items:stretch;justify-content:flex-end}.consenti-modal--right .consenti-modal__container{border-radius:0;height:100%;max-height:100vh}.consenti-modal__overlay{position:absolute;inset:0;background:var(--consenti-color-overlay)}.consenti-modal__container{position:relative;z-index:1;background:var(--consenti-color-bg);border-radius:var(--consenti-border-radius);box-shadow:var(--consenti-shadow);width:min(560px,90vw);max-height:85vh;display:flex;flex-direction:column;overflow:hidden}@media(prefers-reduced-motion: no-preference){.consenti-modal__container{animation:consenti-fade-in .15s ease-out}}.consenti-modal__header{display:flex;align-items:center;justify-content:space-between;padding:var(--consenti-spacing-md) var(--consenti-spacing-lg);border-bottom:1px solid var(--consenti-color-border);flex-shrink:0}.consenti-modal__heading{font-size:var(--consenti-font-size-heading);font-weight:var(--consenti-font-weight-heading);margin:0;flex:1}.consenti-modal__header-controls{display:flex;align-items:center;gap:4px;flex-shrink:0}.consenti-modal__close{flex-shrink:0}.consenti-modal__links{display:flex;flex-wrap:wrap;gap:var(--consenti-spacing-sm);padding:var(--consenti-spacing-sm) var(--consenti-spacing-lg)}.consenti-modal__link{font-size:.85em;text-decoration:underline;text-underline-offset:2px}.consenti-modal__subheading{padding:var(--consenti-spacing-sm) var(--consenti-spacing-lg) 0;margin:0;color:#6b7280;font-size:13px}.consenti-modal__text{padding:var(--consenti-spacing-sm) var(--consenti-spacing-lg)}.consenti-modal__body{overflow-y:auto;flex:1;padding:0 var(--consenti-spacing-lg)}.consenti-modal__categories{display:flex;flex-direction:column;gap:var(--consenti-spacing-md);padding:var(--consenti-spacing-md) 0}.consenti-modal__buttons{display:flex;flex-wrap:wrap;gap:var(--consenti-spacing-sm);padding:var(--consenti-spacing-md) var(--consenti-spacing-lg);border-top:1px solid var(--consenti-color-border);flex-shrink:0}.consenti-modal__receipt-option{margin:0 var(--consenti-spacing-lg);padding-top:var(--consenti-spacing-md);border-top:1px solid var(--consenti-color-border);flex-shrink:0}.consenti-modal__receipt-label{display:flex;align-items:center;gap:var(--consenti-spacing-sm);font-size:var(--consenti-font-size-base);cursor:pointer}.consenti-modal__receipt-checkbox{width:16px;height:16px;flex-shrink:0;cursor:pointer}.consenti-modal__receipt-desc{margin:4px 0 0 24px;font-size:12px;color:#6b7280}@keyframes consenti-fade-in{from{opacity:0;transform:scale(0.97)}to{opacity:1;transform:scale(1)}}.consenti-category{padding:var(--consenti-spacing-md) 0;border-bottom:1px solid var(--consenti-color-border)}.consenti-category:last-child{border-bottom:none}.consenti-category--mandatory .consenti-category__toggle{opacity:.6;cursor:default}.consenti-category__header{display:flex;align-items:center;justify-content:space-between;gap:var(--consenti-spacing-md);margin-bottom:var(--consenti-spacing-sm)}.consenti-category__heading{font-size:var(--consenti-font-size-heading);font-weight:var(--consenti-font-weight-heading);margin:0}.consenti-category__text{color:#4b5563;font-size:13px;margin:0}.consenti-category__li-desc{margin:var(--consenti-spacing-sm) 0 0;font-size:12px;color:#6b7280;font-style:italic}.consenti-category__toggle{flex-shrink:0;position:relative;width:var(--consenti-toggle-width);height:var(--consenti-toggle-height);border-radius:calc(var(--consenti-toggle-height)/2);background:var(--consenti-toggle-bg-off);border:none;cursor:pointer;transition:background .2s ease;padding:0}.consenti-category__toggle:focus-visible{outline:2px solid var(--consenti-color-primary);outline-offset:2px}.consenti-category__toggle[aria-checked=true]{background:var(--consenti-toggle-bg-on)}.consenti-category__toggle--mandatory{cursor:default}.consenti-category__toggle-knob{position:absolute;top:2px;left:2px;width:calc(var(--consenti-toggle-height) - 4px);height:calc(var(--consenti-toggle-height) - 4px);border-radius:50%;background:var(--consenti-toggle-knob);transition:transform .2s ease;display:block}[aria-checked=true] .consenti-category__toggle-knob{transform:translateX(calc(var(--consenti-toggle-width) - var(--consenti-toggle-height)))}.consenti-locale-switcher{position:relative;display:inline-flex}.consenti-locale-switcher__btn{font-size:1rem;line-height:1;cursor:pointer}.consenti-locale-switcher__list{position:absolute;top:calc(100% + 4px);right:0;z-index:calc(var(--consenti-z-modal) + 1);background:var(--consenti-color-bg);border:1px solid var(--consenti-color-border);border-radius:var(--consenti-border-radius);box-shadow:var(--consenti-shadow);list-style:none;margin:0;padding:4px 0;min-width:80px}.consenti-locale-switcher__list li{display:block}.consenti-locale-switcher__option{display:block;width:100%;padding:6px 12px;text-align:left;font-size:var(--consenti-font-size-base);color:var(--consenti-color-text);background:none;border:none;cursor:pointer}.consenti-locale-switcher__option:hover{background:var(--consenti-color-secondary)}.consenti-locale-switcher__option--active{font-weight:600;color:var(--consenti-color-primary)}.consenti-locale-switcher--modal .consenti-locale-switcher__list{right:0}
|
|
1
|
+
#consenti-root{position:relative}.consenti-d-none{display:none}:root{--consenti-color-bg: #ffffff;--consenti-color-text: #1a2e4a;--consenti-color-primary: #1565c0;--consenti-color-primary-text: #ffffff;--consenti-color-secondary: #f0f4f8;--consenti-color-secondary-text: #1a2e4a;--consenti-color-border: #dbe4ee;--consenti-color-secondary-border: #1a2e4a;--consenti-color-overlay: rgba(26, 52, 96, 0.55);--consenti-color-accent: #d32f2f;--consenti-color-accent-text: #ffffff;--consenti-font-family: system-ui, -apple-system, sans-serif;--consenti-font-size-base: 14px;--consenti-font-size-heading: 16px;--consenti-font-weight-heading: 600;--consenti-line-height: 1.5;--consenti-spacing-sm: 8px;--consenti-spacing-md: 16px;--consenti-spacing-lg: 24px;--consenti-border-radius: 8px;--consenti-border-radius-btn: 0;--consenti-shadow: 0 4px 24px rgba(21, 101, 192, 0.14);--consenti-toggle-bg-on: #43a047;--consenti-toggle-bg-off: #9ca3af;--consenti-toggle-knob: #ffffff;--consenti-toggle-width: 52px;--consenti-toggle-height: 28px;--consenti-z-banner: 9999;--consenti-z-overlay: 9998;--consenti-z-modal: 10000}.consenti-root--dark{--consenti-color-bg: #1e2535;--consenti-color-text: #e2e8f0;--consenti-color-primary: #4a9eff;--consenti-color-primary-text: #ffffff;--consenti-color-secondary: #2a3447;--consenti-color-secondary-text: #cbd5e1;--consenti-color-border: #374151;--consenti-color-overlay: rgba(0, 0, 0, 0.65);--consenti-shadow: 0 4px 24px rgba(0, 0, 0, 0.4);--consenti-toggle-bg-off: #6b7280}.consenti-overlay{position:fixed;inset:0;background:var(--consenti-color-overlay);z-index:var(--consenti-z-overlay)}.consenti-btn{display:inline-flex;align-items:center;justify-content:center;padding:var(--consenti-spacing-sm) var(--consenti-spacing-md);border:none;border-radius:var(--consenti-border-radius-btn);font-family:var(--consenti-font-family);font-size:var(--consenti-font-size-base);font-weight:500;line-height:1;cursor:pointer;text-decoration:none;transition:opacity .15s ease}.consenti-btn:focus-visible{outline:2px solid var(--consenti-color-primary);outline-offset:2px}.consenti-btn:hover{opacity:.9}.consenti-btn--primary{background:var(--consenti-color-primary);color:var(--consenti-color-primary-text)}.consenti-btn--secondary{background:var(--consenti-color-secondary);color:var(--consenti-color-primary);border:1px solid var(--consenti-color-primary)}.consenti-btn--secondary:hover{background:var(--consenti-color-primary);color:var(--consenti-color-secondary);border:1px solid var(--consenti-color-secondary)}.consenti-btn--text{background:rgba(0,0,0,0);color:var(--consenti-color-primary);padding-left:0;padding-right:0;text-decoration:underline;text-underline-offset:4px}.consenti-btn--accent{background:var(--consenti-color-accent);color:var(--consenti-color-accent-text)}.consenti-btn--close{background:rgba(0,0,0,0);color:var(--consenti-color-text);font-size:20px;line-height:1;padding:4px 8px;border-radius:50%}.consenti-btn--close:hover{background:var(--consenti-color-secondary);opacity:1}.consenti-banner{position:fixed;left:0;right:0;z-index:var(--consenti-z-banner);background:var(--consenti-color-bg);color:var(--consenti-color-text);font-family:var(--consenti-font-family);font-size:var(--consenti-font-size-base);line-height:var(--consenti-line-height);box-shadow:var(--consenti-shadow);padding:var(--consenti-spacing-lg);display:flex;flex-wrap:wrap;align-items:center;gap:var(--consenti-spacing-md)}.consenti-banner--top{top:0;border-bottom:1px solid var(--consenti-color-border)}@media(prefers-reduced-motion: no-preference){.consenti-banner--top{animation:consenti-slide-down .2s ease-out}}.consenti-banner--bottom{bottom:0;border-top:1px solid var(--consenti-color-border)}@media(prefers-reduced-motion: no-preference){.consenti-banner--bottom{animation:consenti-slide-up .2s ease-out}}.consenti-banner--middle{top:50%;transform:translateY(-50%);left:50%;right:auto;transform:translate(-50%, -50%);width:min(600px,90vw);border-radius:var(--consenti-border-radius)}.consenti-banner--left-bottom{bottom:var(--consenti-spacing-lg);left:var(--consenti-spacing-lg);right:auto;width:min(400px,90vw);border-radius:var(--consenti-border-radius)}.consenti-banner--right-bottom{bottom:var(--consenti-spacing-lg);right:var(--consenti-spacing-lg);left:auto;width:min(400px,90vw);border-radius:var(--consenti-border-radius)}.consenti-banner__text-container{flex:1;min-width:500px}.consenti-banner__heading{font-size:var(--consenti-font-size-heading);font-weight:var(--consenti-font-weight-heading);margin:0 0 var(--consenti-spacing-sm);width:100%}.consenti-banner__text{margin:0}.consenti-banner__links{display:flex;flex-wrap:wrap;gap:var(--consenti-spacing-sm);margin-top:var(--consenti-spacing-sm)}.consenti-banner__link{font-size:.85em}.consenti-banner__controls{position:absolute;top:var(--consenti-spacing-sm);right:var(--consenti-spacing-sm);display:flex;align-items:center;gap:4px}.consenti-banner__buttons{display:flex;flex-wrap:wrap;gap:var(--consenti-spacing-sm);align-items:center;margin-block:var(--consenti-spacing-md)}.consenti-banner__powered-by{position:absolute;right:10px;bottom:2px;opacity:.2;color:var(--consenti-color-primary)}@media(max-width: 576px){.consenti-banner{padding:var(--consenti-spacing-md);gap:var(--consenti-spacing-sm)}.consenti-banner--left-bottom,.consenti-banner--right-bottom{left:0;right:0;bottom:0;width:100%;border-radius:0;border-top:1px solid var(--consenti-color-border)}.consenti-banner--middle{width:95vw}.consenti-banner__buttons{width:100%;flex-direction:row;align-items:stretch;margin-bottom:var(--consenti-spacing-md)}.consenti-banner__text-container{min-width:0;width:100%}}@keyframes consenti-slide-up{from{transform:translateY(100%)}to{transform:translateY(0)}}@keyframes consenti-slide-down{from{transform:translateY(-100%)}to{transform:translateY(0)}}.consenti-modal{position:fixed;inset:0;z-index:var(--consenti-z-modal);display:flex;font-family:var(--consenti-font-family);font-size:var(--consenti-font-size-base);color:var(--consenti-color-text);line-height:var(--consenti-line-height)}.consenti-modal--passthrough{pointer-events:none}.consenti-modal--passthrough .consenti-modal__container{pointer-events:auto}.consenti-modal--center{align-items:center;justify-content:center}.consenti-modal--left{align-items:stretch;justify-content:flex-start}.consenti-modal--left .consenti-modal__container{border-radius:0 var(--consenti-border-radius) var(--consenti-border-radius) 0;height:100%;max-height:100vh}.consenti-modal--right{align-items:stretch;justify-content:flex-end}.consenti-modal--right .consenti-modal__container{border-radius:0;height:100%;max-height:100vh}.consenti-modal--fullscreen{align-items:flex-start;justify-content:flex-start}.consenti-modal--fullscreen .consenti-modal__container{width:100vw;max-width:100vw;height:100dvh;max-height:100dvh;border-radius:0;margin:0}.consenti-modal__overlay{position:absolute;inset:0;background:var(--consenti-color-overlay)}.consenti-modal__container{position:relative;z-index:1;background:var(--consenti-color-bg);border-radius:var(--consenti-border-radius);box-shadow:var(--consenti-shadow);width:min(560px,90vw);max-height:85vh;display:flex;flex-direction:column;overflow:hidden}@media(prefers-reduced-motion: no-preference){.consenti-modal__container{animation:consenti-fade-in .15s ease-out}}.consenti-modal__header{display:flex;justify-content:space-between;padding:var(--consenti-spacing-md) var(--consenti-spacing-lg);border-bottom:1px solid var(--consenti-color-border);flex-shrink:0;position:relative;flex-direction:column}.consenti-modal__heading{font-size:var(--consenti-font-size-heading);font-weight:var(--consenti-font-weight-heading);margin:0;flex:1}.consenti-modal__header-controls{display:flex;align-items:center;gap:4px;flex-shrink:0;position:absolute;right:15px;top:10px}.consenti-modal__close{flex-shrink:0}.consenti-modal__links{display:flex;flex-wrap:wrap;gap:var(--consenti-spacing-sm);padding:var(--consenti-spacing-sm) var(--consenti-spacing-lg)}.consenti-modal__link{font-size:.85em;text-decoration:underline;text-underline-offset:2px}.consenti-modal__subheading{padding-top:var(--consenti-spacing-sm);margin:0;color:#6b7280;font-size:13px}.consenti-modal__text{padding:var(--consenti-spacing-sm) 0}.consenti-modal__body{overflow-y:auto;flex:1;padding:var(--consenti-spacing-sm) var(--consenti-spacing-lg);position:relative}.consenti-modal__categories{display:flex;flex-direction:column;gap:var(--consenti-spacing-md);padding:var(--consenti-spacing-md) 0}.consenti-modal__buttons{display:flex;flex-wrap:wrap;gap:var(--consenti-spacing-sm);padding:var(--consenti-spacing-md);border-top:1px solid var(--consenti-color-border);flex-shrink:0}.consenti-modal__receipt-option{padding-top:var(--consenti-spacing-md);border-top:1px solid var(--consenti-color-border);flex-shrink:0}.consenti-modal__receipt-label{display:flex;align-items:center;gap:var(--consenti-spacing-sm);font-size:var(--consenti-font-size-base);cursor:pointer}.consenti-modal__receipt-checkbox{width:16px;height:16px;flex-shrink:0;cursor:pointer}.consenti-modal__receipt-desc{margin:4px 0 0 24px;font-size:12px;color:#6b7280}.consenti-modal__powered-by{opacity:.5;color:var(--consenti-color-primary)}@keyframes consenti-fade-in{from{opacity:0;transform:scale(0.97)}to{opacity:1;transform:scale(1)}}.consenti-category{padding:var(--consenti-spacing-md) 0;border-bottom:1px solid var(--consenti-color-border)}.consenti-category:last-child{border-bottom:none}.consenti-category--mandatory .consenti-category__toggle{opacity:.6;cursor:default}.consenti-category__header{display:flex;align-items:center;justify-content:space-between;gap:var(--consenti-spacing-md);margin-bottom:var(--consenti-spacing-sm)}.consenti-category__heading{font-size:var(--consenti-font-size-heading);font-weight:var(--consenti-font-weight-heading);margin:0}.consenti-category__text{color:#949dab;font-size:13px;margin:0}.consenti-category__li-desc{margin:var(--consenti-spacing-sm) 0 0;font-size:12px;color:#6b7280;font-style:italic}.consenti-category__toggle{flex-shrink:0;position:relative;width:var(--consenti-toggle-width);height:var(--consenti-toggle-height);border-radius:calc(var(--consenti-toggle-height)/2);background:var(--consenti-toggle-bg-off);border:none;cursor:pointer;transition:background .2s ease;padding:0}.consenti-category__toggle:focus-visible{outline:2px solid var(--consenti-color-primary);outline-offset:2px}.consenti-category__toggle[aria-checked=true]{background:var(--consenti-toggle-bg-on)}.consenti-category__toggle--mandatory{cursor:default}.consenti-category__toggle-icon{position:absolute;top:50%;transform:translateY(-50%);width:10px;height:10px;display:flex;align-items:center;justify-content:center;pointer-events:none;transition:opacity .15s ease;color:hsla(0,0%,100%,.9)}.consenti-category__toggle-icon svg{width:10px;height:10px;overflow:visible}.consenti-category__toggle-icon--check{left:8px;opacity:0}[aria-checked=true] .consenti-category__toggle-icon--check{opacity:1}.consenti-category__toggle-icon--cross{right:8px;opacity:1}[aria-checked=true] .consenti-category__toggle-icon--cross{opacity:0}.consenti-category__toggle-knob{position:absolute;top:2px;left:2px;width:calc(var(--consenti-toggle-height) - 4px);height:calc(var(--consenti-toggle-height) - 4px);border-radius:50%;background:var(--consenti-toggle-knob);transition:transform .2s ease;display:block}[aria-checked=true] .consenti-category__toggle-knob{transform:translateX(calc(var(--consenti-toggle-width) - var(--consenti-toggle-height)))}.consenti-locale-switcher{position:relative;display:inline-flex}.consenti-locale-switcher__btn{font-size:1rem;line-height:1;cursor:pointer}.consenti-locale-switcher__list{position:absolute;top:calc(100% + 4px);right:0;z-index:calc(var(--consenti-z-modal) + 1);background:var(--consenti-color-bg);border:1px solid var(--consenti-color-border);border-radius:var(--consenti-border-radius);box-shadow:var(--consenti-shadow);list-style:none;margin:0;padding:4px 0;min-width:80px}.consenti-locale-switcher__list li{display:block}.consenti-locale-switcher__option{display:block;width:100%;padding:6px 12px;text-align:left;font-size:var(--consenti-font-size-base);color:var(--consenti-color-text);background:none;border:none;cursor:pointer}.consenti-locale-switcher__option:hover{background:var(--consenti-color-secondary)}.consenti-locale-switcher__option--active{font-weight:600;color:var(--consenti-color-primary)}.consenti-locale-switcher--modal .consenti-locale-switcher__list{right:0}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,14 +1,54 @@
|
|
|
1
|
+
type NonEmptyArray<T> = [T, ...T[]];
|
|
2
|
+
type DeepPartial<T> = T extends Function ? T : T extends Array<infer InferredArrayType> ? Array<DeepPartial<InferredArrayType>> : T extends ReadonlyArray<infer InferredReadonlyArrayType> ? ReadonlyArray<DeepPartial<InferredReadonlyArrayType>> : T extends object ? {
|
|
3
|
+
[Key in keyof T]?: DeepPartial<T[Key]>;
|
|
4
|
+
} : T;
|
|
5
|
+
|
|
6
|
+
declare const COMPLIANCE_GROUP_IDS: readonly ["opt-in", "opt-out", "opt-out-strict", "opt-in-dpdpa", "opt-in-china", "opt-in-brazil", "general-privacy-consent", "notice-only"];
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Compliance-related literal types, derived from the regulation/group ID data
|
|
10
|
+
* that lives in @consenti/utils (packages/utils/src/compliance.ts).
|
|
11
|
+
*
|
|
12
|
+
* This import is type-only and fully erased at compile time — @consenti/types
|
|
13
|
+
* has no runtime dependency on @consenti/utils. The actual data (COMPLIANCE_GROUPS,
|
|
14
|
+
* EMBEDDED_COMPLIANCE_MAP, COMPLIANCE_VALIDATION_RULES, GPC_OPTIONS, etc.) lives in
|
|
15
|
+
* @consenti/utils; import it from there.
|
|
16
|
+
*
|
|
17
|
+
* HOW TO ADD A NEW REGULATION OR COMPLIANCE GROUP: see the header comment in
|
|
18
|
+
* packages/utils/src/compliance.ts — these types auto-update from that data.
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
type ComplianceGroupId = typeof COMPLIANCE_GROUP_IDS[number];
|
|
22
|
+
|
|
23
|
+
interface ComplianceMapData {
|
|
24
|
+
version: string;
|
|
25
|
+
countries: Record<string, CountryComplianceEntry>;
|
|
26
|
+
}
|
|
27
|
+
interface CountryComplianceEntry {
|
|
28
|
+
complianceGroup: ComplianceGroupId;
|
|
29
|
+
default: ComplianceGroupId;
|
|
30
|
+
description: string;
|
|
31
|
+
overriddenRegions?: Record<string, {
|
|
32
|
+
complianceGroup: ComplianceGroupId;
|
|
33
|
+
description: string;
|
|
34
|
+
}>;
|
|
35
|
+
}
|
|
36
|
+
|
|
1
37
|
type ConsentStatus = 'granted' | 'denied' | 'objected';
|
|
2
38
|
type ConsentAction = 'accept_all' | 'reject_all' | 'custom' | 'update';
|
|
3
39
|
type ConsentValue = Record<string, ConsentStatus>;
|
|
4
40
|
interface Cookie {
|
|
5
41
|
id: string;
|
|
6
|
-
|
|
42
|
+
legalBasis?: 'mandatory' | 'consent' | 'legitimate_interest';
|
|
43
|
+
/** @deprecated use legalBasis: 'mandatory' */
|
|
7
44
|
mandatory?: boolean;
|
|
45
|
+
/** @deprecated use legalBasis */
|
|
46
|
+
type?: 'consent' | 'legitimate_interest';
|
|
8
47
|
listenGpc?: boolean;
|
|
9
48
|
expiry?: number;
|
|
10
49
|
tcfVendorId?: number;
|
|
11
50
|
tcfPurposes?: number[];
|
|
51
|
+
tcfSpecialFeatures?: number[];
|
|
12
52
|
cpraCategory?: 'sale' | 'sharing' | 'sensitive';
|
|
13
53
|
}
|
|
14
54
|
type ButtonStyle = 'primary' | 'secondary' | 'text' | 'accent';
|
|
@@ -19,6 +59,7 @@ interface Button {
|
|
|
19
59
|
action: ButtonAction;
|
|
20
60
|
cookies?: string[] | '*' | '!';
|
|
21
61
|
url?: string;
|
|
62
|
+
type?: string;
|
|
22
63
|
}
|
|
23
64
|
interface MainBanner {
|
|
24
65
|
position: 'top' | 'bottom' | 'middle' | 'left-bottom' | 'right-bottom';
|
|
@@ -44,18 +85,13 @@ interface Category {
|
|
|
44
85
|
};
|
|
45
86
|
cookies: string[];
|
|
46
87
|
}
|
|
47
|
-
interface PreferenceModal {
|
|
88
|
+
interface PreferenceModal extends Omit<MainBanner, "position"> {
|
|
48
89
|
position?: 'left' | 'right' | 'center';
|
|
49
|
-
overlayOpacity?: number;
|
|
50
|
-
showClose?: boolean;
|
|
51
|
-
showLocaleSwitcher?: boolean;
|
|
52
|
-
heading?: string;
|
|
53
|
-
headingTag?: string;
|
|
54
90
|
subheading?: string;
|
|
55
|
-
htmlText?: string;
|
|
56
|
-
buttons: Button[];
|
|
57
91
|
categories: Category[];
|
|
58
92
|
persistent?: boolean;
|
|
93
|
+
/** Screen width in px below which the modal expands to full screen. Default: 576. Set to 0 to disable. */
|
|
94
|
+
mobileFullScreenBreakpoint?: number;
|
|
59
95
|
}
|
|
60
96
|
interface LocaleTranslations {
|
|
61
97
|
mainBanner: MainBanner;
|
|
@@ -72,25 +108,25 @@ interface DpdpaConfig {
|
|
|
72
108
|
}
|
|
73
109
|
interface LocaleTextContent {
|
|
74
110
|
mainBanner: {
|
|
75
|
-
heading
|
|
111
|
+
heading?: string;
|
|
76
112
|
htmlText: string;
|
|
77
|
-
buttonLabels
|
|
113
|
+
buttonLabels?: string[];
|
|
78
114
|
};
|
|
79
115
|
gpcBanner: {
|
|
80
|
-
heading
|
|
116
|
+
heading?: string;
|
|
81
117
|
htmlText: string;
|
|
82
|
-
buttonLabels
|
|
118
|
+
buttonLabels?: string[];
|
|
83
119
|
};
|
|
84
120
|
preferenceModal: {
|
|
85
121
|
heading: string;
|
|
86
122
|
subheading?: string;
|
|
87
123
|
htmlText?: string;
|
|
88
|
-
buttonLabels
|
|
89
|
-
categories:
|
|
124
|
+
buttonLabels?: string[];
|
|
125
|
+
categories: {
|
|
90
126
|
id: string;
|
|
91
127
|
heading: string;
|
|
92
128
|
htmlText: string;
|
|
93
|
-
}
|
|
129
|
+
}[];
|
|
94
130
|
};
|
|
95
131
|
}
|
|
96
132
|
interface ProfileConfig {
|
|
@@ -106,9 +142,19 @@ interface ProfileConfig {
|
|
|
106
142
|
cookieTemplateId?: string;
|
|
107
143
|
uiTemplateId?: string;
|
|
108
144
|
localeContents?: Record<string, LocaleTextContent>;
|
|
145
|
+
complianceGroup?: string;
|
|
146
|
+
/** @deprecated use gpcMode */
|
|
147
|
+
gpcCompliance?: 'strict' | 'honor' | 'ignore';
|
|
148
|
+
gpcMode?: 'ignore' | 'honor' | 'strict';
|
|
149
|
+
isActive?: boolean;
|
|
150
|
+
hidePoweredBy?: boolean;
|
|
151
|
+
allowReceipt?: boolean;
|
|
152
|
+
/** Per-compliance extra config (e.g. DPDPA data fiduciary name). */
|
|
153
|
+
complianceConfig?: Record<string, string>;
|
|
109
154
|
}
|
|
110
155
|
interface ResolvedProfile {
|
|
111
156
|
id: number;
|
|
157
|
+
profileUuid?: string;
|
|
112
158
|
version: number;
|
|
113
159
|
defaultLocale: string;
|
|
114
160
|
locales?: string[];
|
|
@@ -119,6 +165,26 @@ interface ResolvedProfile {
|
|
|
119
165
|
cookieSigningKey?: string;
|
|
120
166
|
dpdpa?: DpdpaConfig;
|
|
121
167
|
darkMode?: boolean;
|
|
168
|
+
gpcMode?: 'ignore' | 'honor' | 'strict';
|
|
169
|
+
complianceGroup?: string;
|
|
170
|
+
complianceConfig?: Record<string, string>;
|
|
171
|
+
}
|
|
172
|
+
type WidgetCountryResolverFn = () => Promise<{
|
|
173
|
+
country: string | null;
|
|
174
|
+
region: string | null;
|
|
175
|
+
confidence: number;
|
|
176
|
+
}>;
|
|
177
|
+
interface AgeGateWidgetConfig {
|
|
178
|
+
enabled: boolean;
|
|
179
|
+
minimumAge: number;
|
|
180
|
+
requireParentalConsent?: boolean;
|
|
181
|
+
}
|
|
182
|
+
interface ComplianceWidgetConfig {
|
|
183
|
+
type?: 'auto' | ComplianceGroupId | string;
|
|
184
|
+
geoDataProvider?: 'default' | WidgetCountryResolverFn;
|
|
185
|
+
autoComplianceMap?: 'default' | ComplianceMapData | 'auto';
|
|
186
|
+
complianceMapUrl?: string;
|
|
187
|
+
ageGate?: AgeGateWidgetConfig;
|
|
122
188
|
}
|
|
123
189
|
interface GtmConfig {
|
|
124
190
|
containerId?: string;
|
|
@@ -134,6 +200,9 @@ interface ApiConfig {
|
|
|
134
200
|
enabled?: boolean;
|
|
135
201
|
baseUrl?: string;
|
|
136
202
|
authToken?: string;
|
|
203
|
+
tenantId?: string;
|
|
204
|
+
complianceGroup?: string;
|
|
205
|
+
trustDomain?: boolean;
|
|
137
206
|
}
|
|
138
207
|
interface ThemeConfig {
|
|
139
208
|
bgColor?: string;
|
|
@@ -155,35 +224,52 @@ interface ThemeConfig {
|
|
|
155
224
|
accentTextColor?: string;
|
|
156
225
|
}
|
|
157
226
|
interface CoreConfig {
|
|
158
|
-
|
|
227
|
+
tenantId?: string;
|
|
159
228
|
autoHonorGPC?: boolean | 'strict';
|
|
160
229
|
locale?: string;
|
|
161
230
|
disableCssTemplate?: boolean;
|
|
162
|
-
signCookies?: boolean;
|
|
163
231
|
cookieSigningKey?: string;
|
|
164
232
|
allowReceipt?: boolean;
|
|
165
233
|
cookieDomains?: string;
|
|
166
234
|
storage?: 'cookie' | 'localStorage';
|
|
167
235
|
theme?: ThemeConfig;
|
|
168
|
-
privacyPolicyUrl?: string;
|
|
169
|
-
regulation?: 'gdpr' | 'ccpa' | 'cpra' | 'dpdpa' | 'uk-gdpr' | 'lgpd' | 'pipeda' | 'popia' | 'pdpa-th' | 'appi' | 'kvkk';
|
|
170
236
|
userId?: string;
|
|
237
|
+
usePrebuiltProfiles?: 'all' | NonEmptyArray<ComplianceGroupId>;
|
|
238
|
+
cacheResolvedProfiles?: boolean;
|
|
239
|
+
console?: Array<'info' | 'log' | 'warning' | 'error'>;
|
|
171
240
|
}
|
|
172
241
|
interface ConsentiConfig {
|
|
173
242
|
core: CoreConfig;
|
|
243
|
+
compliance?: ComplianceWidgetConfig;
|
|
174
244
|
rootEl?: string | HTMLElement;
|
|
175
245
|
darkMode?: boolean;
|
|
176
246
|
autoInit?: boolean;
|
|
177
247
|
api?: ApiConfig;
|
|
178
248
|
utils?: UtilsConfig;
|
|
179
249
|
plugins?: ConsentiPlugin$1[];
|
|
180
|
-
profileOverride?:
|
|
250
|
+
profileOverride?: DeepPartial<ResolvedProfile>;
|
|
251
|
+
/** When true, suppresses the "Powered by Consenti" footer link in the banner and modal. */
|
|
252
|
+
hidePoweredBy?: boolean;
|
|
181
253
|
}
|
|
254
|
+
type ConsentiEventName = 'bannerInitialized' | 'consenti:bannerInitialized' | 'bannerVisibility' | 'consenti:bannerVisibility' | 'modalVisibility' | 'consenti:modalVisibility' | 'consentBeingSubmitted' | 'consenti:consentBeingSubmitted' | 'consentSubmitted' | 'consenti:consentSubmitted';
|
|
182
255
|
interface ConsentiWidgetAPI {
|
|
183
256
|
hasConsent(): boolean;
|
|
184
257
|
getConsent(): ConsentValue | null;
|
|
185
258
|
getConsentDate(): Date | false;
|
|
186
259
|
getGTMConsent(): Record<string, string> | null;
|
|
260
|
+
isCookieGranted(cookieId: string, requestValue?: boolean): boolean | ConsentStatus;
|
|
261
|
+
isCategoryGranted(categoryId: string, requestValue?: boolean): boolean | {
|
|
262
|
+
[cookieId: string]: ConsentStatus;
|
|
263
|
+
}[];
|
|
264
|
+
grantAll(onlyMandatory?: boolean): Promise<void>;
|
|
265
|
+
denyAll(includingMandatory?: boolean): Promise<void>;
|
|
266
|
+
on(event: ConsentiEventName, handler: (data: ConsentEvent) => void): void;
|
|
267
|
+
off(event: ConsentiEventName, handler: (data: ConsentEvent) => void): void;
|
|
268
|
+
version(): {
|
|
269
|
+
package: string;
|
|
270
|
+
profileVersion: number | null;
|
|
271
|
+
consentVersion: number;
|
|
272
|
+
};
|
|
187
273
|
bannerVisibility(): 'main' | 'gpc' | false;
|
|
188
274
|
modalVisibility(): 'preference' | false;
|
|
189
275
|
getProfile(): ResolvedProfile | null;
|
|
@@ -198,6 +284,10 @@ interface ConsentiWidgetAPI {
|
|
|
198
284
|
getBannerElement(): HTMLElement | null;
|
|
199
285
|
getModalElement(): HTMLElement | null;
|
|
200
286
|
switchLocale(locale: string): void;
|
|
287
|
+
setDarkMode(enable?: boolean): void;
|
|
288
|
+
setTheme(theme: Partial<ThemeConfig>): void;
|
|
289
|
+
setConfig(config: DeepPartial<ConsentiConfig>): void;
|
|
290
|
+
setProfile(override: DeepPartial<ResolvedProfile>): void;
|
|
201
291
|
init(): Promise<void>;
|
|
202
292
|
onReady(callback: () => void): void;
|
|
203
293
|
destroy(): void;
|
|
@@ -277,7 +367,7 @@ type ConsentiMessage = {
|
|
|
277
367
|
* @example
|
|
278
368
|
* ```ts
|
|
279
369
|
* const widget = new ConsentiSetup({
|
|
280
|
-
*
|
|
370
|
+
* compliance: { type: 'opt-in' },
|
|
281
371
|
* })
|
|
282
372
|
* await widget.ready
|
|
283
373
|
* console.log(widget.hasConsent())
|
|
@@ -289,6 +379,7 @@ declare class ConsentiSetup implements ConsentiWidgetAPI {
|
|
|
289
379
|
private initialized;
|
|
290
380
|
private initializing;
|
|
291
381
|
private profile;
|
|
382
|
+
private resolvedBase;
|
|
292
383
|
private consentStore;
|
|
293
384
|
private eventBus;
|
|
294
385
|
private banner;
|
|
@@ -302,6 +393,8 @@ declare class ConsentiSetup implements ConsentiWidgetAPI {
|
|
|
302
393
|
private lastSignature;
|
|
303
394
|
/** Tracks which banner variant is currently visible, or null when hidden. */
|
|
304
395
|
private _bannerVariant;
|
|
396
|
+
/** Maps original handler functions to their DOM-wrapped listeners for on/off bookkeeping. */
|
|
397
|
+
private _eventHandlers;
|
|
305
398
|
/**
|
|
306
399
|
* Creates and initialises the consent widget.
|
|
307
400
|
*
|
|
@@ -334,6 +427,8 @@ declare class ConsentiSetup implements ConsentiWidgetAPI {
|
|
|
334
427
|
* @param invoke - Function that calls the desired hook method on a single plugin.
|
|
335
428
|
*/
|
|
336
429
|
private callPluginHook;
|
|
430
|
+
private _buildGrantAllConsent;
|
|
431
|
+
private _buildDenyAllConsent;
|
|
337
432
|
private buildHandlers;
|
|
338
433
|
/**
|
|
339
434
|
* Classifies a consent submission as `accept_all`, `reject_all`, `custom`, or `update`.
|
|
@@ -482,6 +577,93 @@ declare class ConsentiSetup implements ConsentiWidgetAPI {
|
|
|
482
577
|
* @param locale - BCP 47 locale code to switch to (e.g. `'fr'`, `'de-AT'`).
|
|
483
578
|
*/
|
|
484
579
|
switchLocale(locale: string): void;
|
|
580
|
+
/**
|
|
581
|
+
* Returns `true` if the named cookie's consent is `'granted'`.
|
|
582
|
+
* When `requestValue` is `true`, returns the raw `ConsentStatus` string instead of a boolean,
|
|
583
|
+
* or `false` if the cookie is not in the consent map.
|
|
584
|
+
*/
|
|
585
|
+
isCookieGranted(cookieId: string, requestValue?: boolean): boolean | 'granted' | 'denied' | 'objected';
|
|
586
|
+
/**
|
|
587
|
+
* Checks consent for all cookies belonging to the given category.
|
|
588
|
+
*
|
|
589
|
+
* With `requestValue = false` (default): returns `true` only when every cookie in the
|
|
590
|
+
* category is `'granted'`, `false` otherwise.
|
|
591
|
+
*
|
|
592
|
+
* With `requestValue = true`: returns an array of one-key records — one per cookie in the
|
|
593
|
+
* category — e.g. `[{ analytics: 'granted' }, { pixel: 'denied' }]`.
|
|
594
|
+
*
|
|
595
|
+
* Returns `false` / `[]` if the category is not found or the widget is not yet initialized.
|
|
596
|
+
*/
|
|
597
|
+
isCategoryGranted(categoryId: string, requestValue?: boolean): boolean | {
|
|
598
|
+
[cookieId: string]: 'granted' | 'denied' | 'objected';
|
|
599
|
+
}[];
|
|
600
|
+
/**
|
|
601
|
+
* Programmatically accepts cookies and hides the banner.
|
|
602
|
+
*
|
|
603
|
+
* @param onlyMandatory - When `true`, only mandatory cookies are granted and all others
|
|
604
|
+
* are set to `'denied'`. Defaults to `false` (grant everything).
|
|
605
|
+
*/
|
|
606
|
+
grantAll(onlyMandatory?: boolean): Promise<void>;
|
|
607
|
+
/**
|
|
608
|
+
* Programmatically denies cookies and hides the banner.
|
|
609
|
+
*
|
|
610
|
+
* @param includingMandatory - When `true`, mandatory cookies are also denied. Defaults to
|
|
611
|
+
* `false` (mandatory cookies remain `'granted'`). Passing `true`
|
|
612
|
+
* logs a warning — ensure this is intentional.
|
|
613
|
+
*/
|
|
614
|
+
denyAll(includingMandatory?: boolean): Promise<void>;
|
|
615
|
+
/**
|
|
616
|
+
* Subscribes to a Consenti widget event. The `consenti:` prefix is optional —
|
|
617
|
+
* both `'consentSubmitted'` and `'consenti:consentSubmitted'` are accepted.
|
|
618
|
+
*
|
|
619
|
+
* The same handler reference must be passed to `off()` to unsubscribe.
|
|
620
|
+
*
|
|
621
|
+
* @example
|
|
622
|
+
* ```ts
|
|
623
|
+
* const handler = (data) => console.log(data)
|
|
624
|
+
* widget.on('consentSubmitted', handler)
|
|
625
|
+
* // later:
|
|
626
|
+
* widget.off('consentSubmitted', handler)
|
|
627
|
+
* ```
|
|
628
|
+
*/
|
|
629
|
+
on(event: ConsentiEventName, handler: (data: ConsentEvent) => void): void;
|
|
630
|
+
/**
|
|
631
|
+
* Removes a previously registered event handler. The same function reference passed to
|
|
632
|
+
* `on()` must be used here.
|
|
633
|
+
*/
|
|
634
|
+
off(event: ConsentiEventName, handler: (data: ConsentEvent) => void): void;
|
|
635
|
+
/**
|
|
636
|
+
* Returns the current version of the package and the active profile/consent versions.
|
|
637
|
+
* Useful for debugging and support diagnostics.
|
|
638
|
+
*/
|
|
639
|
+
version(): {
|
|
640
|
+
package: string;
|
|
641
|
+
profileVersion: number | null;
|
|
642
|
+
consentVersion: number;
|
|
643
|
+
};
|
|
644
|
+
/**
|
|
645
|
+
* Toggles or sets dark mode at runtime without re-initialising the widget.
|
|
646
|
+
* When `enable` is omitted, the current dark mode state is toggled.
|
|
647
|
+
*/
|
|
648
|
+
setDarkMode(enable?: boolean): void;
|
|
649
|
+
/**
|
|
650
|
+
* Applies partial theme token overrides at runtime, merging them into the current theme.
|
|
651
|
+
* CSS custom properties on the root element are updated immediately.
|
|
652
|
+
*/
|
|
653
|
+
setTheme(theme: Partial<ThemeConfig>): void;
|
|
654
|
+
/**
|
|
655
|
+
* Deep-merges a partial config into the current widget config.
|
|
656
|
+
* Theme and dark mode side-effects are re-applied immediately.
|
|
657
|
+
* Changes to locale or profile require an explicit `switchLocale()` or `init()` call.
|
|
658
|
+
*/
|
|
659
|
+
setConfig(config: DeepPartial<ConsentiConfig>): void;
|
|
660
|
+
/**
|
|
661
|
+
* Merges a partial profile override into the current profile and re-renders any visible UI.
|
|
662
|
+
* Does not make a network request — the resolved base profile from the last `init()` is reused.
|
|
663
|
+
*
|
|
664
|
+
* No-op before `init()` completes (while `resolvedBase` is null).
|
|
665
|
+
*/
|
|
666
|
+
setProfile(override: DeepPartial<ResolvedProfile>): void;
|
|
485
667
|
/**
|
|
486
668
|
* Destroys the widget — removes all DOM elements, disconnects the BroadcastChannel,
|
|
487
669
|
* and calls `destroy()` on all registered plugins.
|
|
@@ -655,8 +837,12 @@ interface ConsentScriptOptions {
|
|
|
655
837
|
widget: ConsentiSetup;
|
|
656
838
|
/** Remote script `src` URL to inject when consent is granted. */
|
|
657
839
|
src?: string;
|
|
658
|
-
/**
|
|
659
|
-
|
|
840
|
+
/**
|
|
841
|
+
* Inline JavaScript to inject when consent is granted (e.g. a GTM snippet).
|
|
842
|
+
* WARNING: this string is assigned directly to `script.innerHTML` — only
|
|
843
|
+
* pass a static, trusted literal. Never pass user-supplied or remote content.
|
|
844
|
+
*/
|
|
845
|
+
unsafeInnerHTML?: string;
|
|
660
846
|
/**
|
|
661
847
|
* Additional HTML attributes applied to the injected `<script>` element.
|
|
662
848
|
* Set a key to an empty string for boolean attributes (e.g. `{ async: '', defer: '' }`).
|
|
@@ -666,6 +852,12 @@ interface ConsentScriptOptions {
|
|
|
666
852
|
onLoad?: () => void;
|
|
667
853
|
/** Called after the `<script>` element has been removed from the DOM. */
|
|
668
854
|
onRevoke?: () => void;
|
|
855
|
+
/**
|
|
856
|
+
* When `true` (default), the script is automatically removed when consent is revoked and
|
|
857
|
+
* re-injected when consent is re-granted. Set to `false` to evaluate consent only once at
|
|
858
|
+
* construction time — no event listener is attached and the script is never auto-removed.
|
|
859
|
+
*/
|
|
860
|
+
bind?: boolean;
|
|
669
861
|
}
|
|
670
862
|
/**
|
|
671
863
|
* Conditionally injects and removes a `<script>` element based on a specific
|
|
@@ -767,4 +959,4 @@ declare class CookieTrigger {
|
|
|
767
959
|
destroy(): void;
|
|
768
960
|
}
|
|
769
961
|
|
|
770
|
-
export { type ApiConfig, type Button, type ButtonAction, type ButtonStyle, type Category, type ConsentAction, type ConsentEvent, type ConsentReceipt, type ConsentRecord, ConsentScript, type ConsentScriptOptions, type ConsentStatus, type ConsentValue, type ConsentiConfig, type ConsentiMessage, ConsentiPlugin, ConsentiProfile, ConsentiSetup, type ConsentiWidgetAPI, type Cookie, type CookieOptions, CookieTrigger, type CookieTriggerOptions, type CoreConfig, type GpcBanner, type GtmConfig, type LocaleTranslations, type MainBanner, type ParsedConsent, type PreferenceModal, type ProfileConfig, type ProfileTranslations, type ResolvedProfile, type ThemeConfig, type UtilsConfig };
|
|
962
|
+
export { type ApiConfig, type Button, type ButtonAction, type ButtonStyle, type Category, type ConsentAction, type ConsentEvent, type ConsentReceipt, type ConsentRecord, ConsentScript, type ConsentScriptOptions, type ConsentStatus, type ConsentValue, type ConsentiConfig, type ConsentiEventName, type ConsentiMessage, ConsentiPlugin, ConsentiProfile, ConsentiSetup, type ConsentiWidgetAPI, type Cookie, type CookieOptions, CookieTrigger, type CookieTriggerOptions, type CoreConfig, type GpcBanner, type GtmConfig, type LocaleTranslations, type MainBanner, type ParsedConsent, type PreferenceModal, type ProfileConfig, type ProfileTranslations, type ResolvedProfile, type ThemeConfig, type UtilsConfig };
|