@consentera/consent-sdk 2.0.0 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +118 -8
- package/README.md +91 -2
- package/dist/consentera-consent.cjs +183 -16
- package/dist/consentera-consent.cjs.map +1 -1
- package/dist/consentera-consent.min.js +1 -1
- package/dist/consentera-consent.min.js.map +1 -1
- package/dist/consentera-consent.mjs +181 -17
- package/dist/consentera-consent.mjs.map +1 -1
- package/dist/react/index.cjs +177 -15
- package/dist/react/index.cjs.map +1 -1
- package/dist/react/index.mjs +177 -15
- package/dist/react/index.mjs.map +1 -1
- package/dist/types/consent/ConsentManager.d.ts +10 -0
- package/dist/types/consent/ConsentValidator.d.ts +27 -0
- package/dist/types/core/ConsentEraClient.d.ts +1 -1
- package/dist/types/core/errors.d.ts +32 -1
- package/dist/types/core/http.d.ts +9 -0
- package/dist/types/core/version.d.ts +4 -4
- package/dist/types/index.d.ts +2 -2
- package/dist/types/types/consent-lifecycle.d.ts +31 -1
- package/package.json +1 -1
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).Consentera={})}(this,function(e){"use strict";const t={local:new Map,session:new Map},n={session:e=>`consentera_session_${e}`,callback:e=>`consentera_callback_${e}`,pendingSync:"consentera_pending_sync",deviceId:"consentera_device_id"};function s(e){try{if("undefined"==typeof window)return null;const t="local"===e?window.localStorage:window.sessionStorage,n="__consentera_probe__";return t.setItem(n,"1"),t.removeItem(n),t}catch{return null}}function i(e,n){const i=s(e);if(!i)return t[e].get(n)??null;try{return i.getItem(n)}catch{return t[e].get(n)??null}}function r(e,n,i){const r=s(e);if(r)try{return r.setItem(n,i),!0}catch{}return t[e].set(n,i),!1}function o(e,n){const i=s(e);if(i)try{i.removeItem(n)}catch{}t[e].delete(n)}class a{config;key;constructor(e){this.config={type:"cookie",cookieName:"consentera_consent",cookieExpiry:365,cookiePath:"/",secure:"undefined"==typeof location||"https:"===location.protocol,sameSite:"Lax",...e},this.key=this.config.cookieName}save(e){const t=JSON.stringify(e);switch(this.config.type){case"cookie":return this.setCookie(t);case"localStorage":return r("local",this.key,t);case"sessionStorage":return r("session",this.key,t);default:return!1}}get(){let e=null;switch(this.config.type){case"cookie":e=this.getCookie();break;case"localStorage":e=i("local",this.key);break;case"sessionStorage":e=i("session",this.key)}if(!e)return null;try{return JSON.parse(e)}catch{return null}}exists(){return null!==this.get()}clear(){switch(this.config.type){case"cookie":this.deleteCookie();break;case"localStorage":o("local",this.key);break;case"sessionStorage":o("session",this.key)}}setCookie(e){const{cookieExpiry:t,cookiePath:n,cookieDomain:s,secure:i,sameSite:r}=this.config;let o=`${encodeURIComponent(this.key)}=${encodeURIComponent(e)}`;if(t){const e=new Date;e.setTime(e.getTime()+24*t*60*60*1e3),o+=`; expires=${e.toUTCString()}`}n&&(o+=`; path=${n}`),s&&(o+=`; domain=${s}`),i&&(o+="; secure"),r&&(o+=`; samesite=${r}`);if(new Blob([o]).size>4096)return!1;if("undefined"==typeof document)return!1;try{document.cookie=o}catch{return!1}return null!==this.getCookie()}getCookie(){if("undefined"==typeof document)return null;const e=encodeURIComponent(this.key)+"=",t=document.cookie.split(";");for(let n of t)if(n=n.trim(),0===n.indexOf(e))return decodeURIComponent(n.substring(e.length));return null}deleteCookie(){if("undefined"==typeof document)return;const{cookiePath:e,cookieDomain:t}=this.config;let n=`${encodeURIComponent(this.key)}=; expires=Thu, 01 Jan 1970 00:00:00 GMT`;e&&(n+=`; path=${e}`),t&&(n+=`; domain=${t}`),document.cookie=n}}function c(e){const t=document.createElement("div");return t.textContent=e,t.innerHTML}function d(e){try{const t=new URL(e,window.location.origin);if(["http:","https:","mailto:"].includes(t.protocol))return t.href}catch{}return"#"}class l{config;theme;sdk;container=null;isVisible=!1;constructor(e,t,n){this.config=e,this.theme=t,this.sdk=n}show(){this.isVisible||(this.createBanner(),this.isVisible=!0)}hide(){this.isVisible&&this.container&&(this.container.classList.add("consentera-banner--closing"),setTimeout(()=>{this.container?.remove(),this.container=null,this.isVisible=!1},300))}createBanner(){this.container=document.createElement("div"),this.container.id="consentera-consent-banner",this.container.className=`consentera-banner consentera-banner--${this.theme.position||"bottom"}`,this.container.setAttribute("role","dialog"),this.container.setAttribute("aria-modal","true"),this.container.setAttribute("aria-labelledby","consentera-banner-title"),this.injectStyles(),this.container.innerHTML=this.getBannerHTML(),this.attachEventListeners(),document.body.appendChild(this.container),requestAnimationFrame(()=>{this.container?.classList.add("consentera-banner--visible")})}getBannerHTML(){const{banner:e}=this.config;return`\n <div class="consentera-banner__content">\n ${this.theme.showLogo?`\n <div class="consentera-banner__logo">\n ${this.theme.logoUrl?`<img src="${d(this.theme.logoUrl)}" alt="Logo" />`:this.getDefaultLogo()}\n </div>\n `:""}\n\n <div class="consentera-banner__text">\n <h2 id="consentera-banner-title" class="consentera-banner__title">\n ${c(e.title||"We value your privacy")}\n </h2>\n <p class="consentera-banner__description">\n ${c(e.description||"We use cookies to enhance your experience.")}\n </p>\n ${e.showPrivacyPolicy&&e.privacyPolicyUrl?`\n <a href="${d(e.privacyPolicyUrl)}" target="_blank" rel="noopener" class="consentera-banner__link">\n Privacy Policy\n </a>\n `:""}\n </div>\n\n <div class="consentera-banner__actions">\n ${e.showCustomize?`\n <button type="button" class="consentera-btn consentera-btn--secondary" data-action="customize">\n ${c(e.customizeText||"Customize")}\n </button>\n `:""}\n ${e.showRejectAll?`\n <button type="button" class="consentera-btn consentera-btn--secondary" data-action="reject">\n ${c(e.rejectAllText||"Reject All")}\n </button>\n `:""}\n <button type="button" class="consentera-btn consentera-btn--primary" data-action="accept">\n ${c(e.acceptAllText||"Accept All")}\n </button>\n </div>\n </div>\n `}getDefaultLogo(){return`\n <svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">\n <rect width="32" height="32" rx="8" fill="${this.theme.primaryColor||"#2563eb"}"/>\n <path d="M16 6L24 10V16C24 21.52 20.6 26.74 16 28C11.4 26.74 8 21.52 8 16V10L16 6Z"\n stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>\n <path d="M12 16L14.5 18.5L20 13"\n stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>\n </svg>\n `}attachEventListeners(){this.container&&(this.container.addEventListener("click",e=>{const t=e.target.dataset.action;if(t)switch(t){case"accept":this.sdk.acceptAll();break;case"reject":this.sdk.rejectAll();break;case"customize":this.hide(),this.sdk.showPreferences()}}),document.addEventListener("keydown",this.handleKeyDown.bind(this)))}handleKeyDown(e){"Escape"===e.key&&this.isVisible}injectStyles(){if(document.getElementById("consentera-consent-styles"))return;const e=document.createElement("style");e.id="consentera-consent-styles",e.textContent=this.getStyles(),document.head.appendChild(e)}getStyles(){const{primaryColor:e,backgroundColor:t,textColor:n,borderRadius:s,fontFamily:i}=this.theme;return`\n .consentera-banner {\n position: fixed;\n left: 0;\n right: 0;\n z-index: 999999;\n font-family: ${i||'-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif'};\n opacity: 0;\n transform: translateY(100%);\n transition: opacity 0.3s ease, transform 0.3s ease;\n }\n\n .consentera-banner--bottom {\n bottom: 0;\n }\n\n .consentera-banner--top {\n top: 0;\n transform: translateY(-100%);\n }\n\n .consentera-banner--visible {\n opacity: 1;\n transform: translateY(0);\n }\n\n .consentera-banner--closing {\n opacity: 0;\n transform: translateY(100%);\n }\n\n .consentera-banner--top.consentera-banner--closing {\n transform: translateY(-100%);\n }\n\n .consentera-banner__content {\n display: flex;\n align-items: center;\n gap: 24px;\n padding: 20px 24px;\n background: ${t||"#ffffff"};\n color: ${n||"#1f2937"};\n box-shadow: 0 -4px 20px rgba(0, 0, 0, 0.1);\n border-radius: ${s||"0"} ${s||"0"} 0 0;\n }\n\n .consentera-banner--top .consentera-banner__content {\n border-radius: 0 0 ${s||"0"} ${s||"0"};\n box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1);\n }\n\n .consentera-banner__logo {\n flex-shrink: 0;\n }\n\n .consentera-banner__logo img,\n .consentera-banner__logo svg {\n width: 48px;\n height: 48px;\n object-fit: contain;\n }\n\n .consentera-banner__text {\n flex: 1;\n min-width: 0;\n }\n\n .consentera-banner__title {\n margin: 0 0 4px 0;\n font-size: 16px;\n font-weight: 600;\n color: ${n||"#1f2937"};\n }\n\n .consentera-banner__description {\n margin: 0;\n font-size: 14px;\n color: ${n||"#6b7280"};\n line-height: 1.5;\n }\n\n .consentera-banner__link {\n display: inline-block;\n margin-top: 8px;\n font-size: 13px;\n color: ${e||"#2563eb"};\n text-decoration: underline;\n }\n\n .consentera-banner__actions {\n display: flex;\n gap: 12px;\n flex-shrink: 0;\n }\n\n .consentera-btn {\n padding: 10px 20px;\n border: none;\n border-radius: 6px;\n font-size: 14px;\n font-weight: 500;\n cursor: pointer;\n transition: all 0.2s ease;\n white-space: nowrap;\n }\n\n .consentera-btn--primary {\n background: ${e||"#2563eb"};\n color: #ffffff;\n }\n\n .consentera-btn--primary:hover {\n opacity: 0.9;\n }\n\n .consentera-btn--secondary {\n background: transparent;\n color: ${n||"#4b5563"};\n border: 1px solid ${n||"#d1d5db"};\n }\n\n .consentera-btn--secondary:hover {\n background: rgba(0, 0, 0, 0.05);\n }\n\n @media (max-width: 768px) {\n .consentera-banner__content {\n flex-direction: column;\n text-align: center;\n padding: 16px;\n gap: 16px;\n }\n\n .consentera-banner__actions {\n width: 100%;\n flex-direction: column;\n }\n\n .consentera-btn {\n width: 100%;\n }\n }\n\n /* Accessibility */\n .consentera-btn:focus {\n outline: 2px solid ${e||"#2563eb"};\n outline-offset: 2px;\n }\n\n @media (prefers-reduced-motion: reduce) {\n .consentera-banner {\n transition: none;\n }\n }\n `}destroy(){document.removeEventListener("keydown",this.handleKeyDown.bind(this)),this.container?.remove(),this.container=null,this.isVisible=!1}}function u(e){const t=document.createElement("div");return t.textContent=e,t.innerHTML}function h(e){try{const t=new URL(e,window.location.origin);if(["http:","https:","mailto:"].includes(t.protocol))return t.href}catch{}return"#"}class p{config;theme;sdk;container=null;overlay=null;isVisible=!1;preferences;constructor(e,t,n){this.config=e,this.theme=t,this.sdk=n,this.preferences=n.getPreferences()}show(){this.isVisible||(this.preferences=this.sdk.getPreferences(),this.createPreferenceCenter(),this.isVisible=!0)}hide(){this.isVisible&&(this.container?.classList.add("consentera-preferences--closing"),this.overlay?.classList.add("consentera-overlay--closing"),setTimeout(()=>{this.container?.remove(),this.overlay?.remove(),this.container=null,this.overlay=null,this.isVisible=!1},300))}createPreferenceCenter(){this.overlay=document.createElement("div"),this.overlay.className="consentera-overlay",this.overlay.addEventListener("click",()=>this.hide()),this.container=document.createElement("div"),this.container.id="consentera-preference-center",this.container.className="consentera-preferences",this.container.setAttribute("role","dialog"),this.container.setAttribute("aria-modal","true"),this.container.setAttribute("aria-labelledby","consentera-preferences-title"),this.injectStyles(),this.container.innerHTML=this.getPreferenceCenterHTML(),this.attachEventListeners(),document.body.appendChild(this.overlay),document.body.appendChild(this.container),requestAnimationFrame(()=>{this.overlay?.classList.add("consentera-overlay--visible"),this.container?.classList.add("consentera-preferences--visible")}),this.container.querySelector(".consentera-preferences__close")?.focus()}getPreferenceCenterHTML(){const{banner:e,purposes:t}=this.config;return`\n <div class="consentera-preferences__header">\n <h2 id="consentera-preferences-title" class="consentera-preferences__title">\n Cookie Preferences\n </h2>\n <button type="button" class="consentera-preferences__close" data-action="close" aria-label="Close">\n <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">\n <path d="M18 6L6 18M6 6l12 12"/>\n </svg>\n </button>\n </div>\n\n <div class="consentera-preferences__body">\n <p class="consentera-preferences__description">\n We use cookies and similar technologies to personalize content, analyze traffic, and improve your experience.\n You can choose which categories of cookies you allow below.\n </p>\n\n <div class="consentera-preferences__purposes">\n ${t.map(e=>`\n <div class="consentera-purpose">\n <div class="consentera-purpose__header">\n <div class="consentera-purpose__info">\n <h3 class="consentera-purpose__name">${u(e.name)}</h3>\n ${e.required?'<span class="consentera-purpose__required">Always Active</span>':""}\n </div>\n <label class="consentera-toggle">\n <input type="checkbox"\n id="purpose-${e.id}"\n data-purpose="${e.id}"\n ${e.required?"checked disabled":""}\n ${this.preferences[e.id]?"checked":""}\n />\n <span class="consentera-toggle__slider"></span>\n </label>\n </div>\n <p class="consentera-purpose__description">${u(e.description)}</p>\n ${e.cookies&&e.cookies.length>0?`\n <details class="consentera-purpose__cookies">\n <summary>View cookies (${e.cookies.length})</summary>\n <table class="consentera-cookies-table">\n <thead>\n <tr>\n <th>Cookie</th>\n <th>Provider</th>\n <th>Purpose</th>\n <th>Expiry</th>\n </tr>\n </thead>\n <tbody>\n ${e.cookies.map(e=>`\n <tr>\n <td>${u(e.name)}</td>\n <td>${u(e.provider)}</td>\n <td>${u(e.purpose)}</td>\n <td>${u(e.expiry)}</td>\n </tr>\n `).join("")}\n </tbody>\n </table>\n </details>\n `:""}\n </div>\n `).join("")}\n </div>\n </div>\n\n <div class="consentera-preferences__footer">\n <div class="consentera-preferences__links">\n ${e.privacyPolicyUrl?`\n <a href="${h(e.privacyPolicyUrl)}" target="_blank" rel="noopener">Privacy Policy</a>\n `:""}\n ${e.cookiePolicyUrl?`\n <a href="${h(e.cookiePolicyUrl)}" target="_blank" rel="noopener">Cookie Policy</a>\n `:""}\n </div>\n <div class="consentera-preferences__actions">\n <button type="button" class="consentera-btn consentera-btn--secondary" data-action="reject">\n ${e.rejectAllText||"Reject All"}\n </button>\n <button type="button" class="consentera-btn consentera-btn--secondary" data-action="accept">\n ${e.acceptAllText||"Accept All"}\n </button>\n <button type="button" class="consentera-btn consentera-btn--primary" data-action="save">\n ${e.saveText||"Save Preferences"}\n </button>\n </div>\n </div>\n `}attachEventListeners(){this.container&&(this.container.addEventListener("click",e=>{const t=e.target,n=t.dataset.action||t.closest("[data-action]")?.getAttribute("data-action");if(n)switch(n){case"close":this.hide();break;case"accept":this.sdk.acceptAll(),this.hide();break;case"reject":this.sdk.rejectAll(),this.hide();break;case"save":this.savePreferences()}}),this.container.querySelectorAll("input[data-purpose]").forEach(e=>{e.addEventListener("change",e=>{const t=e.target,n=t.dataset.purpose;this.preferences[n]=t.checked})}),document.addEventListener("keydown",this.handleKeyDown.bind(this)))}handleKeyDown(e){"Escape"===e.key&&this.isVisible&&this.hide()}savePreferences(){this.sdk.savePreferences(this.preferences),this.hide()}injectStyles(){if(document.getElementById("consentera-preferences-styles"))return;const e=document.createElement("style");e.id="consentera-preferences-styles",e.textContent=this.getStyles(),document.head.appendChild(e)}getStyles(){const{primaryColor:e,backgroundColor:t,textColor:n,borderRadius:s}=this.theme;return`\n .consentera-overlay {\n position: fixed;\n inset: 0;\n background: rgba(0, 0, 0, 0.5);\n z-index: 999998;\n opacity: 0;\n transition: opacity 0.3s ease;\n }\n\n .consentera-overlay--visible {\n opacity: 1;\n }\n\n .consentera-overlay--closing {\n opacity: 0;\n }\n\n .consentera-preferences {\n position: fixed;\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%) scale(0.95);\n width: 90%;\n max-width: 640px;\n max-height: 85vh;\n background: ${t||"#ffffff"};\n border-radius: ${s||"12px"};\n box-shadow: 0 20px 60px rgba(0, 0, 0, 0.2);\n z-index: 999999;\n display: flex;\n flex-direction: column;\n opacity: 0;\n transition: opacity 0.3s ease, transform 0.3s ease;\n }\n\n .consentera-preferences--visible {\n opacity: 1;\n transform: translate(-50%, -50%) scale(1);\n }\n\n .consentera-preferences--closing {\n opacity: 0;\n transform: translate(-50%, -50%) scale(0.95);\n }\n\n .consentera-preferences__header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 20px 24px;\n border-bottom: 1px solid #e5e7eb;\n }\n\n .consentera-preferences__title {\n margin: 0;\n font-size: 18px;\n font-weight: 600;\n color: ${n||"#1f2937"};\n }\n\n .consentera-preferences__close {\n background: none;\n border: none;\n padding: 4px;\n cursor: pointer;\n color: ${n||"#6b7280"};\n border-radius: 4px;\n }\n\n .consentera-preferences__close:hover {\n background: rgba(0, 0, 0, 0.05);\n }\n\n .consentera-preferences__body {\n flex: 1;\n overflow-y: auto;\n padding: 24px;\n }\n\n .consentera-preferences__description {\n margin: 0 0 24px 0;\n font-size: 14px;\n color: ${n||"#6b7280"};\n line-height: 1.5;\n }\n\n .consentera-preferences__purposes {\n display: flex;\n flex-direction: column;\n gap: 16px;\n }\n\n .consentera-purpose {\n padding: 16px;\n background: #f9fafb;\n border-radius: 8px;\n }\n\n .consentera-purpose__header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n margin-bottom: 8px;\n }\n\n .consentera-purpose__info {\n display: flex;\n align-items: center;\n gap: 8px;\n }\n\n .consentera-purpose__name {\n margin: 0;\n font-size: 15px;\n font-weight: 600;\n color: ${n||"#1f2937"};\n }\n\n .consentera-purpose__required {\n font-size: 11px;\n padding: 2px 6px;\n background: ${e||"#2563eb"};\n color: white;\n border-radius: 4px;\n font-weight: 500;\n }\n\n .consentera-purpose__description {\n margin: 0;\n font-size: 13px;\n color: ${n||"#6b7280"};\n line-height: 1.5;\n }\n\n .consentera-purpose__cookies {\n margin-top: 12px;\n }\n\n .consentera-purpose__cookies summary {\n font-size: 12px;\n color: ${e||"#2563eb"};\n cursor: pointer;\n }\n\n .consentera-cookies-table {\n width: 100%;\n margin-top: 8px;\n font-size: 12px;\n border-collapse: collapse;\n }\n\n .consentera-cookies-table th,\n .consentera-cookies-table td {\n padding: 8px;\n text-align: left;\n border-bottom: 1px solid #e5e7eb;\n }\n\n .consentera-cookies-table th {\n font-weight: 600;\n color: ${n||"#374151"};\n }\n\n .consentera-cookies-table td {\n color: ${n||"#6b7280"};\n }\n\n .consentera-toggle {\n position: relative;\n display: inline-block;\n width: 48px;\n height: 26px;\n }\n\n .consentera-toggle input {\n opacity: 0;\n width: 0;\n height: 0;\n }\n\n .consentera-toggle__slider {\n position: absolute;\n cursor: pointer;\n inset: 0;\n background: #d1d5db;\n border-radius: 26px;\n transition: 0.3s;\n }\n\n .consentera-toggle__slider::before {\n position: absolute;\n content: "";\n height: 20px;\n width: 20px;\n left: 3px;\n bottom: 3px;\n background: white;\n border-radius: 50%;\n transition: 0.3s;\n }\n\n .consentera-toggle input:checked + .consentera-toggle__slider {\n background: ${e||"#2563eb"};\n }\n\n .consentera-toggle input:checked + .consentera-toggle__slider::before {\n transform: translateX(22px);\n }\n\n .consentera-toggle input:disabled + .consentera-toggle__slider {\n opacity: 0.7;\n cursor: not-allowed;\n }\n\n .consentera-preferences__footer {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 16px 24px;\n border-top: 1px solid #e5e7eb;\n gap: 16px;\n }\n\n .consentera-preferences__links {\n display: flex;\n gap: 16px;\n }\n\n .consentera-preferences__links a {\n font-size: 13px;\n color: ${e||"#2563eb"};\n text-decoration: underline;\n }\n\n .consentera-preferences__actions {\n display: flex;\n gap: 8px;\n }\n\n @media (max-width: 640px) {\n .consentera-preferences {\n width: 100%;\n height: 100%;\n max-height: 100%;\n border-radius: 0;\n }\n\n .consentera-preferences__footer {\n flex-direction: column;\n }\n\n .consentera-preferences__actions {\n width: 100%;\n flex-direction: column;\n }\n\n .consentera-btn {\n width: 100%;\n }\n }\n `}destroy(){document.removeEventListener("keydown",this.handleKeyDown.bind(this)),this.container?.remove(),this.overlay?.remove(),this.container=null,this.overlay=null,this.isVisible=!1}}class g{config;tcfData=null;constructor(e){this.config={cmpId:123,cmpVersion:1,publisherCountryCode:"IN",gdprApplies:!0,...e}}async init(){this.createTCFApiStub(),this.tcfData=this.getDefaultTCFData()}createTCFApiStub(){"undefined"!=typeof window&&(window.__tcfapi=(e,t,n,s)=>{"ping"===e?n({gdprApplies:this.config.gdprApplies,cmpLoaded:!0,cmpStatus:"loaded",displayStatus:"visible",apiVersion:"2.2",cmpVersion:this.config.cmpVersion,cmpId:this.config.cmpId,gvlVersion:100,tcfPolicyVersion:4}):"getTCData"===e?n(this.tcfData,!0):"addEventListener"===e?n({...this.tcfData,eventStatus:"tcloaded",listenerId:Math.random()},!0):"removeEventListener"===e&&n(!0)},window.__tcfapi.gdprApplies=this.config.gdprApplies,window.__tcfapi.cmpLoaded=!0)}getDefaultTCFData(){return{cmpId:this.config.cmpId,cmpVersion:this.config.cmpVersion,gdprApplies:this.config.gdprApplies,tcString:"",tcfPolicyVersion:4,isServiceSpecific:!0,useNonStandardStacks:!1,purposeOneTreatment:!1,publisherCC:this.config.publisherCountryCode,vendorConsents:{},vendorLegitimateInterests:{},purposeConsents:{},purposeLegitimateInterests:{},specialFeatureOptins:{},publisherRestrictions:[]}}async updateConsent(e){this.tcfData&&(this.tcfData.purposeConsents={1:e.essential,2:e.advertising,3:e.personalization,4:e.advertising,5:e.personalization,6:e.personalization,7:e.analytics,8:e.analytics,9:e.analytics,10:e.functional},this.tcfData.tcString=this.generateTCString(),this.dispatchTCFEvent("useractioncomplete"))}generateTCString(){const e={version:2,created:Date.now(),lastUpdated:Date.now(),cmpId:this.config.cmpId,cmpVersion:this.config.cmpVersion,purposeConsents:this.tcfData?.purposeConsents||{}};return btoa(JSON.stringify(e))}dispatchTCFEvent(e){if("undefined"==typeof window)return;const t=new CustomEvent("tcfapi",{detail:{...this.tcfData,eventStatus:e}});window.dispatchEvent(t)}getTCFData(){return this.tcfData}getTCString(){return this.tcfData?.tcString||""}destroy(){"undefined"!=typeof window&&delete window.__tcfapi,this.tcfData=null}}class f{config;gppData=null;constructor(e){this.config={sections:["tcfeuv2","uspv1"],...e}}async init(){this.createGPPApiStub(),this.gppData=this.getDefaultGPPData()}createGPPApiStub(){if("undefined"==typeof window)return;const e=[],t="loaded",n="visible";window.__gpp=(s,i,r)=>{switch(s){case"ping":i({gppVersion:"1.1",cmpStatus:t,cmpDisplayStatus:n,supportedAPIs:this.config.sections,cmpId:123,sectionList:this.gppData?.sectionIds||[],applicableSections:this.gppData?.applicableSections||[],gppString:this.gppData?.gppString||""});break;case"getSection":this.gppData&&i({sectionId:r,sectionData:this.getSectionData(r)});break;case"getField":i(this.getFieldValue(r));break;case"hasSection":i(this.gppData?.sectionIds.includes(r)||!1);break;case"addEventListener":e.push({callback:i,listenerId:Math.random()}),i({eventName:"listenerRegistered",listenerId:e.length-1,data:this.gppData?.gppString||"",pingData:{gppVersion:"1.1",cmpStatus:t,cmpDisplayStatus:n}});break;case"removeEventListener":{const t=e.findIndex(e=>e.listenerId===r);t>-1?(e.splice(t,1),i(!0)):i(!1);break}default:i(null)}},window.__gpp.version="1.1"}getDefaultGPPData(){return{gppString:"",sectionIds:[],applicableSections:[]}}getSectionData(e){switch(e){case 2:return{Version:2,Created:Math.floor(Date.now()/100),LastUpdated:Math.floor(Date.now()/100),CmpId:123,CmpVersion:1,ConsentScreen:1,ConsentLanguage:"EN"};case 6:return{Version:1,Notice:"Y",OptOutSale:"N",LspaCovered:"Y"};default:return null}}getFieldValue(e){return null}async updateConsent(e){this.gppData&&(this.gppData.gppString=this.buildGPPString(e),this.gppData.sectionIds=this.determineSections(e),this.gppData.applicableSections=this.gppData.sectionIds,this.dispatchGPPEvent("signalStatus"))}buildGPPString(e){const t=[];return this.config.sections?.includes("tcfeuv2")&&t.push({id:2,purposes:{1:e.essential,2:e.advertising,3:e.personalization,4:e.advertising,7:e.analytics,10:e.functional}}),this.config.sections?.includes("uspv1")&&t.push({id:6,notice:!0,optOut:!e.thirdParty,lspaCovered:!0}),this.encodeGPPString(t)}encodeGPPString(e){const t={version:1,sectionIds:e.map(e=>e.id)};return btoa(JSON.stringify({header:t,sections:e}))}determineSections(e){const t=[];return this.config.sections?.includes("tcfeuv2")&&t.push(2),this.config.sections?.includes("uspv1")&&t.push(6),t}dispatchGPPEvent(e){if("undefined"==typeof window)return;const t=new CustomEvent("gpp",{detail:{eventName:e,data:this.gppData?.gppString||"",pingData:{gppVersion:"1.1",cmpStatus:"loaded",applicableSections:this.gppData?.applicableSections||[]}}});window.dispatchEvent(t)}getGPPData(){return this.gppData}getGPPString(){return this.gppData?.gppString||""}destroy(){"undefined"!=typeof window&&delete window.__gpp,this.gppData=null}}class m{events=new Map;on(e,t){this.events.has(e)||this.events.set(e,new Set),this.events.get(e).add(t)}off(e,t){const n=this.events.get(e);n&&n.delete(t)}emit(e,...t){const n=this.events.get(e);n&&n.forEach(e=>{try{e(...t)}catch(e){}})}once(e,t){const n=(...s)=>{this.off(e,n),t(...s)};this.on(e,n)}removeAllListeners(e){e?this.events.delete(e):this.events.clear()}}class y{level;prefix="[ConsentEra]";constructor(e="info"){this.level=e}shouldLog(e){const t=["debug","info","warn","error"];return t.indexOf(e)>=t.indexOf(this.level)}debug(...e){this.shouldLog("debug")}info(...e){this.shouldLog("info")}warn(...e){this.shouldLog("warn")}error(...e){this.shouldLog("error")}setLevel(e){this.level=e}}function _(){return`consent_${Date.now().toString(36)}_${Math.random().toString(36).substring(2,15)}`}function b(){const e=navigator.userAgent.toLowerCase();let t,n,s="desktop";return/mobile|android|iphone|ipod|blackberry|iemobile|opera mini/i.test(e)?s="mobile":/tablet|ipad|playbook|silk/i.test(e)&&(s="tablet"),/windows/i.test(e)?t="Windows":/macintosh|mac os x/i.test(e)?t="macOS":/linux/i.test(e)?t="Linux":/android/i.test(e)?t="Android":/iphone|ipad|ipod/i.test(e)&&(t="iOS"),/chrome/i.test(e)&&!/edge|edg/i.test(e)?n="Chrome":/firefox/i.test(e)?n="Firefox":/safari/i.test(e)&&!/chrome/i.test(e)?n="Safari":/edge|edg/i.test(e)?n="Edge":/msie|trident/i.test(e)&&(n="IE"),{type:s,os:t,browser:n}}const w={VALIDATION_ERROR:"validation",INVALID_REQUEST_BODY:"validation",INVALID_JSON:"validation",INVALID_UUID:"validation",BAD_REQUEST:"validation",MISSING_REQUIRED_FIELD:"validation",MISSING_TENANT_ID:"validation",INVALID_TENANT_ID:"validation",INVALID_DATE_OF_BIRTH:"validation",INVALID_IDENTIFIER_FORMAT:"validation",DATA_PRINCIPAL_REF_REFUSED:"identity",UNKNOWN_IDENTIFIER_FIELD:"identity",IDENTIFIER_REQUIRED:"identity",IDENTITY_MISMATCH:"identity",GUARDIAN_REQUIRED:"guardian",GUARDIAN_CONSENT_REQUIRED:"guardian",GUARDIAN_TOKEN_REQUIRED:"guardian",GUARDIAN_EVIDENCE_MISSING:"guardian",GUARDIAN_IDENTITY_NOT_VERIFIED:"guardian",GUARDIAN_ADULT_NOT_VERIFIED:"guardian",GUARDIAN_VERIFICATION_METHOD_INVALID:"guardian",AGE_REQUIRED:"guardian",UNAUTHORIZED:"auth",INVALID_CREDENTIALS:"auth",TOKEN_MISSING:"auth",TOKEN_INVALID:"auth",TOKEN_EXPIRED:"auth",TOKEN_MALFORMED:"auth",TOKEN_REVOKED:"auth",SESSION_NOT_FOUND:"auth",SESSION_EXPIRED:"auth",SESSION_REVOKED:"auth",FORBIDDEN:"permission",TENANT_MISMATCH:"permission",TENANT_SUSPENDED:"permission",ACCOUNT_DISABLED:"permission",PRIVILEGE_ESCALATION:"permission",SCHEME_NOT_CONFIGURED:"permission",CSRF_TOKEN_MISSING:"permission",CSRF_TOKEN_INVALID:"permission",NOT_FOUND:"not_found",USER_NOT_FOUND:"not_found",CONFLICT:"conflict",IDEMPOTENCY_KEY_REUSE:"conflict",RATE_LIMIT_EXCEEDED:"rate_limit",INTERNAL_ERROR:"server",INTERNAL_SERVER_ERROR:"server",DATABASE_ERROR:"server"};function v(e,t=Date.now()){if(!e)return;const n=e.trim();if(/^\d+$/.test(n)){const e=Number(n);return Number.isFinite(e)?Math.max(0,1e3*e):void 0}const s=Date.parse(n);return Number.isNaN(s)?void 0:Math.max(0,s-t)}class k extends Error{kind;code;status;requestId;responseBody;retryable;retryAfterMs;constructor(e){super(e.message,void 0===e.cause?void 0:{cause:e.cause}),this.name=new.target.name,this.kind=e.kind,this.code=e.code,this.status=e.status??0,this.requestId=e.requestId,this.responseBody=e.responseBody,this.retryable=e.retryable??!1,this.retryAfterMs=e.retryAfterMs,Object.setPrototypeOf(this,new.target.prototype)}get statusCode(){return this.status}toString(){const e=[this.name,this.code??this.kind];return this.status&&e.push(String(this.status)),this.requestId&&e.push(`req=${this.requestId}`),`${e.join(" ")}: ${this.message}`}}class C extends k{}class E extends k{}class x extends k{}class S extends k{}class I extends k{}class A extends k{}class D extends k{}class T extends k{}class P extends k{}class $ extends k{}class R extends k{}class N extends k{}const L={config:C,auth:E,permission:x,validation:S,identity:I,guardian:A,not_found:D,conflict:T,rate_limit:P,server:$,network:R,timeout:N,cancelled:k,unknown:k};function M(e){const{code:t,message:n}=function(e){if(!e||"object"!=typeof e)return{};const t=e,n=t.error&&"object"==typeof t.error?t.error:t;return{code:"string"==typeof n.code?n.code:void 0,message:"string"==typeof n.message?n.message:void 0}}(e.body),s=t&&w[t]||(401===(i=e.status)?"auth":403===i?"permission":404===i?"not_found":409===i?"conflict":429===i?"rate_limit":i>=500?"server":i>=400?"validation":"unknown");var i;const r=429===e.status||e.status>=500;return o={kind:s,code:t,status:e.status,requestId:e.requestId,responseBody:e.body,retryable:r,retryAfterMs:e.retryAfterMs,message:n||`${e.method} ${e.path} failed: ${e.status}${e.statusText?` ${e.statusText}`:""}`},new L[o.kind](o);var o}function O(e,t){return new R({kind:"network",message:e,status:0,retryable:!0,cause:t})}function U(e,t){return new N({kind:"timeout",message:e,status:0,retryable:!0,cause:t})}function K(e,t){return new k({kind:"cancelled",message:e,status:0,retryable:!1,cause:t})}function q(e,t){return new C({kind:"config",message:e,status:0,code:t,retryable:!1})}const V=k,B="2.0.0",F=`js/${B}`;function j(e){const t="undefined"!=typeof process&&process.versions?.node?`node ${process.versions.node}`:"unknown";return`ConsenteraSDK/${B} (web; ${t})`}const G={attempts:3,baseDelayMs:250,maxDelayMs:4e3};function z(){return"undefined"!=typeof window}function H(e){return!!e&&(e.startsWith("tiq_live_")||e.startsWith("tiq_test_"))}function W(e){return!!e&&e.startsWith("tiq_pub_")}function Y(e){if(z()&&e.apiKey&&!e.unsafeAllowSecretKeyInBrowser)throw q("Consentera: `apiKey` is a SECRET and this SDK is running in a browser, where every visitor can read the bundle. "+e.fix+" "+(H(e.apiKey)?`The key supplied starts with "${e.apiKey.slice(0,9)}" — rotate it, it is now in a bundle. `:"")+"If you are certain this code never reaches a browser (a test harness with a jsdom window, a server-side render that only looks like one), set `unsafeAllowSecretKeyInBrowser: true` and every request will warn.","SECRET_KEY_IN_BROWSER")}function J(){const e="undefined"!=typeof globalThis?globalThis.crypto:void 0;if(e&&"function"==typeof e.randomUUID)return e.randomUUID();if(e&&"function"==typeof e.getRandomValues){const t=e.getRandomValues(new Uint8Array(16));return Array.from(t,e=>e.toString(16).padStart(2,"0")).join("")}return`nc-${Date.now().toString(36)}-${Math.random().toString(36).slice(2,10)}`}function X(e,t){return new Promise((n,s)=>{if(t?.aborted)return void s(K("request cancelled"));const i=setTimeout(()=>{t?.removeEventListener("abort",r),n()},e),r=()=>{clearTimeout(i),s(K("request cancelled"))};t?.addEventListener("abort",r,{once:!0})})}function Q(e,t,n=Math.random){const s=Math.min(t.maxDelayMs,t.baseDelayMs*2**e);return Math.floor(n()*s)}class Z{config;logger;constructor(e,t){this.config=e,this.logger=t}updateConfig(e){this.config={...this.config,...e}}authHeadersFor(e,t){const{apiKey:n,siteKey:s,unsafeAllowSecretKeyInBrowser:i}=this.config,r=z();if(t)return{};if("session"===e)return{};if("public"===e)return s?{"X-API-Key":s}:{};if(r&&!i)throw q("Consentera: this call needs a Data Fiduciary credential, which is a SECRET and must never be in a browser bundle. Set `proxyEndpoint` to your own server route (it holds the tiq_live_/tiq_test_ key and forwards the call), or run this call server-side. A public site key (tiq_pub_) cannot authorise it: the lifecycle roads are server-to-server. A site key opens only the public read roads and the session-scoped render/submit, and only for the origins in its allowed_domains.","SECRET_KEY_IN_BROWSER");if(n)return r&&i&&this.logger.warn("Consentera: SENDING A SECRET KEY FROM A BROWSER because unsafeAllowSecretKeyInBrowser is set. Every visitor to this page can read it. Rotate the key and move to proxyEndpoint."),{"X-API-Key":n};if(W(s))throw q("Consentera: a site key (tiq_pub_) was supplied for a Data Fiduciary lifecycle road. Those roads are server-to-server; a site key opens only the public read roads and the session-scoped render/submit, so this call would be refused 403. Use `proxyEndpoint`, or a secret key server-side.","SITE_KEY_ON_DF_ROAD");throw q("Consentera: no credential for a Data Fiduciary road. Set `proxyEndpoint` (browser) or `apiKey` (server).","NO_CREDENTIAL")}urlFor(e,t){const{proxyEndpoint:n,apiEndpoint:s}=this.config,i=!!n,r=i?n:s,o=this.config.apiPathPrefix??"/api/v1/public";if("/api/v1/public"!==o&&"/api/v1/cookie-consent"!==o)throw q("Consentera: unsupported transport API plane.");let a=i?`${r}${e}`:`${r}${o}${e}`;if(t){const e=new URLSearchParams;for(const[n,s]of Object.entries(t))null!=s&&e.set(n,String(s));const n=e.toString();n&&(a+=`?${n}`)}return{url:a,viaProxy:i}}async request(e,t,n,s={}){const i=s.road??"df",{url:r,viaProxy:o}=this.urlFor(t,s.query),a={...G,...this.config.retry,...s.retry},c=s.timeoutMs??this.config.timeoutMs??1e4,d=this.config.fetchImpl??globalThis.fetch;if("function"!=typeof d)throw q("Consentera: no fetch implementation available in this environment.");const l={Accept:"application/json","X-Consentera-SDK":F,...z()?{}:{"User-Agent":j()},...this.authHeadersFor(i,o)};!o&&"session"!==i&&this.config.tenantId&&(l["X-Tenant-Id"]=this.config.tenantId),void 0!==n&&(l["Content-Type"]="application/json"),"GET"!==e&&(l["Idempotency-Key"]=s.idempotencyKey??J());const u="function"==typeof this.config.customHeaders?this.config.customHeaders():this.config.customHeaders;Object.assign(l,u,s.headers);let h=n;if(void 0!==n&&this.config.beforeSend){const s=this.config.beforeSend({method:e,path:t,body:n});if(null==s)throw q(`Consentera: beforeSend refused ${e} ${t}. Nothing was sent.`,"BEFORE_SEND_REFUSED");h=s}const p=void 0===h?void 0:JSON.stringify(h);let g;for(let n=0;n<a.attempts;n++){if(s.signal?.aborted)throw K("request cancelled before attempt");const i=Date.now();let o=!1;const u=new AbortController,h=setTimeout(()=>{o=!0,u.abort()},c),f=()=>u.abort();s.signal?.addEventListener("abort",f,{once:!0});try{const n=await d(r,{method:e,headers:l,body:p,signal:u.signal}),o=Date.now()-i,a=n.headers?.get?.("X-Request-Id")??void 0;if(n.ok){this.logger.debug(`${e} ${t} -> ${n.status} (${o}ms)`,{requestId:a});const i=e=>s.raw?{status:n.status,body:e,requestId:a,retryAfterMs:v(n.headers?.get?.("Retry-After"))}:e;if(204===n.status)return i(void 0);const r=await n.text();if(!r)return i(void 0);try{return i(JSON.parse(r))}catch{throw M({status:n.status,body:r,requestId:a,method:e,path:t,statusText:"response body is not JSON"})}}const c=await n.text();let h=c;try{h=JSON.parse(c)}catch{}const f=v(n.headers?.get?.("Retry-After"));g=M({status:n.status,statusText:n.statusText,body:h,requestId:a,retryAfterMs:f,method:e,path:t}),this.logger.debug(`${e} ${t} -> ${n.status} ${g.code??""} (${o}ms)`,{requestId:a})}catch(n){if(n instanceof k){if(!n.retryable)throw n;g=n}else if(o)g=U(`${e} ${t} timed out after ${c}ms`,n);else{if(s.signal?.aborted)throw K(`${e} ${t} cancelled`,n);g=O(`${e} ${t}: ${n?.message??"network error"}`,n)}}finally{clearTimeout(h),s.signal?.removeEventListener("abort",f)}if(!g.retryable||n===a.attempts-1)throw g;if(void 0!==a.maxRetryAfterMs&&void 0!==g.retryAfterMs&&g.retryAfterMs>a.maxRetryAfterMs)throw g;const m=g.retryAfterMs??Q(n,a);this.logger.debug(`${e} ${t} retry ${n+1}/${a.attempts-1} in ${m}ms (${g.code??g.kind})`),await X(m,s.signal)}throw g??O(`${e} ${t} failed`)}}class ee extends m{config;storage;banner=null;preferenceCenter=null;tcfManager=null;gppManager=null;logger;initialized=!1;initPromise=null;destroyed=!1;remoteConfig=null;remotePolicyVersion="";ageGateEnabled=!1;ageGatePrompt;pendingIsAdult=null;deviceId;constructor(e){if(super(),Y({apiKey:e.apiKey,unsafeAllowSecretKeyInBrowser:e.unsafeAllowSecretKeyInBrowser,fix:'The cookie-consent plane needs no secret at all — use a public site key (`siteKey: "tiq_pub_…"`), which is what it reads. Keep the tiq_live_/tiq_test_ key on your own server.'}),"string"!=typeof e.apiEndpoint||""===e.apiEndpoint.trim())throw q("Consentera: `apiEndpoint` is required — there is no default server. Set it to your Consentera API origin (the SDK calls `${apiEndpoint}/api/v1/cookie-consent/*`); with the script-tag build, set `data-api-endpoint`.","ENDPOINT_REQUIRED");this.config=this.mergeDefaults(e),this.logger=new y(e.apiKey?"info":"debug"),this.storage=new a(this.config.storage),this.deviceId=this.getOrCreateDeviceId(),this.config.autoShow&&this.init().then(()=>{this.hasConsent()||this.showBanner()}).catch(e=>{this.logger.error("Consentera: auto-init failed; no banner will be shown.",e),this.emit("error",e),this.config.callbacks?.onError?.(e)})}mergeDefaults(e){return{autoShow:!0,language:"en",region:"IN",regulations:["DPDP"],theme:{position:"bottom",type:"banner",primaryColor:"#2563eb",backgroundColor:"#ffffff",textColor:"#1f2937",borderRadius:"8px",showLogo:!0},storage:{type:"cookie",cookieName:"consentera_consent",cookieExpiry:365,cookiePath:"/",sameSite:"Lax"},...e}}getOrCreateDeviceId(){const e="consentera_device_id";try{let t=window.localStorage.getItem(e);return t||(t=_(),window.localStorage.setItem(e,t)),t}catch{return _()}}setAgeDeclaration(e){this.pendingIsAdult=e}isAgeGateEnabled(){return this.ageGateEnabled}getAgeGatePrompt(){return this.ageGatePrompt}init(){return this.initialized?(this.logger.warn("ConsentEra Consent SDK already initialized"),Promise.resolve()):(this.initPromise||(this.initPromise=this.doInit().finally(()=>{this.initPromise=null})),this.initPromise)}async doInit(){try{this.logger.info("Initializing ConsentEra Consent SDK...");const e=await this.fetchConfig();if(this.destroyed)return;if(this.remoteConfig=e,this.config.tcf?.enabled&&(this.tcfManager=new g(this.config.tcf),await this.tcfManager.init()),this.config.gpp?.enabled&&(this.gppManager=new f(this.config.gpp),await this.gppManager.init()),this.destroyed)return;this.banner=new l(this.remoteConfig,this.config.theme,this),this.preferenceCenter=new p(this.remoteConfig,this.config.theme,this),this.initialized=!0,this.emit("ready"),this.config.callbacks?.onReady?.(),this.logger.info("ConsentEra Consent SDK initialized successfully")}catch(e){throw this.logger.error("Failed to initialize SDK:",e),this.config.callbacks?.onError?.(e),e}}authHeaders(){const e={"X-Tenant-Id":this.config.tenantId},t=this.config.siteKey||this.config.apiKey;return t&&(z()&&!this.config.siteKey&&H(this.config.apiKey)&&this.logger.warn("Consentera: SENDING A SECRET KEY FROM A BROWSER because unsafeAllowSecretKeyInBrowser is set. Every visitor to this page can read it. The cookie-consent plane needs only a public site key — rotate this one."),e["X-API-Key"]=t),e}async fetchConfig(){try{const e=await fetch(`${this.config.apiEndpoint}/api/v1/cookie-consent/config`,{headers:{...this.authHeaders(),"Accept-Language":this.config.language||"en"}});if(!e.ok)throw new Error(`Failed to fetch config: ${e.statusText}`);const t=await e.json();return this.remotePolicyVersion=t.policy_version,this.ageGateEnabled=t.age_gate_enabled,this.ageGatePrompt=t.age_gate_prompt,function(e,t){const n=[...e.categories].sort((e,t)=>e.sort_order-t.sort_order).map(e=>({id:e.key,name:e.name,description:e.description,required:e.mandatory,defaultEnabled:e.mandatory,category:te(e.key)})),s=e.banner?.messaging||{},i={title:s.title,description:s.description,acceptAllText:s.accept_text,rejectAllText:s.reject_text,customizeText:s.customize_text,saveText:s.save_text,showRejectAll:e.ui_options?.reject_all??!0,showCustomize:e.ui_options?.customize??!0,showPrivacyPolicy:!0,purposes:n};return{tenantId:e.tenant_id,banner:i,purposes:n,theme:t.theme,regulations:t.regulations||["DPDP"],languages:[e.language_code]}}(t,this.config)}catch(e){throw this.logger.error("Consentera: the tenant consent configuration could not be loaded.",e),e instanceof Error?e:new Error(String(e))}}showBanner(){this.initialized?(this.banner?.show(),this.emit("banner_shown"),this.config.callbacks?.onBannerShown?.()):this.logger.warn("SDK not initialized. Call init() first.")}hideBanner(){this.banner?.hide(),this.emit("banner_closed"),this.config.callbacks?.onBannerClosed?.()}showPreferences(){this.initialized?(this.preferenceCenter?.show(),this.emit("preferences_opened")):this.logger.warn("SDK not initialized. Call init() first.")}hidePreferences(){this.preferenceCenter?.hide()}async acceptAll(){return this.emit("accept_all"),this.saveConsent({essential:!0,functional:!0,analytics:!0,marketing:!0,personalization:!0,advertising:!0,thirdParty:!0})}async rejectAll(){return this.emit("reject_all"),this.saveConsent({essential:!0,functional:!1,analytics:!1,marketing:!1,personalization:!1,advertising:!1,thirdParty:!1})}async savePreferences(e){return this.emit("preferences_saved",e),this.saveConsent(e)}async saveConsent(e){try{const t=this.buildSelections(e),n=(await this.syncConsent(t)).effective_selections??t,s=this.buildConsentData(n);return this.storage.save(s),this.tcfManager&&await this.tcfManager.updateConsent(this.selectionsToPreferences(n)),this.gppManager&&await this.gppManager.updateConsent(this.selectionsToPreferences(n)),this.hideBanner(),this.hidePreferences(),this.emit("consent_given",s),this.config.callbacks?.onConsentGiven?.(s),this.config.callbacks?.onPreferencesUpdated?.(this.selectionsToPreferences(n)),{success:!0,consentId:s.consentId}}catch(e){return this.logger.error("Failed to save consent:",e),{success:!1,error:e.message}}}buildSelections(e){const t={};for(const n of this.remoteConfig?.purposes||[])t[n.id]=n.required||!!e[n.id];return t}selectionsToPreferences(e){const t={essential:!0,functional:!1,analytics:!1,marketing:!1,personalization:!1,advertising:!1,thirdParty:!1};for(const[n,s]of Object.entries(e))t[n]=s;return t}buildConsentData(e){const t=(this.remoteConfig?.purposes||[]).map(t=>({id:t.id,name:t.name,description:t.description,category:t.category,required:t.required,consented:t.required||!!e[t.id],legalBasis:t.required?"legitimate_interest":"consent"}));return{consentId:_(),timestamp:(new Date).toISOString(),version:this.remotePolicyVersion||"1.0",purposes:t,legalBasis:"consent",regulation:this.config.regulations?.[0]||"DPDP",metadata:{userAgent:navigator.userAgent,pageUrl:window.location.href,referrer:document.referrer,device:b()}}}async syncConsent(e){try{const t={device_id:this.deviceId,policy_version:this.remotePolicyVersion,selections:e,affirmative_action:{type:"button",ui_event_id:_(),captured_at:(new Date).toISOString()}};this.ageGateEnabled&&null!==this.pendingIsAdult&&(t.age_declaration={is_adult:this.pendingIsAdult});const n=new Z({apiEndpoint:this.config.apiEndpoint,apiPathPrefix:"/api/v1/cookie-consent",retry:{maxRetryAfterMs:5e3}},this.logger);return await n.request("POST","/preferences",t,{road:"public",headers:this.authHeaders()})}catch(e){throw this.logger.error("Consentera: consent sync failed — the decision was not recorded.",{message:e?.message}),this.emit("sync_failed",e),this.config.callbacks?.onSyncFailed?.(e),e}}getConsent(){return this.storage.get()}hasConsent(){return this.storage.exists()}async withdrawConsent(e){try{const t=this.getConsent();if(!t)return{success:!1,error:"No consent found"};const n={};for(const s of this.remoteConfig?.purposes||[]){if(s.required){n[s.id]=!0;continue}if(!e||e.includes(s.id))n[s.id]=!1;else{const e=t.purposes.find(e=>e.id===s.id)?.consented??!1;n[s.id]=e}}const s=(await this.syncConsent(n)).effective_selections??n,i=this.buildConsentData(s);return this.storage.save(i),this.tcfManager&&await this.tcfManager.updateConsent(this.selectionsToPreferences(s)),this.gppManager&&await this.gppManager.updateConsent(this.selectionsToPreferences(s)),this.emit("consent_withdrawn",e||[]),this.config.callbacks?.onConsentWithdrawn?.(e||[]),{success:!0,consentId:i.consentId}}catch(e){return{success:!1,error:e.message}}}isConsentGiven(e){const t=this.getConsent();if(!t)return!1;const n=t.purposes.find(t=>t.id===e);return n?.consented||!1}getPreferences(){const e=this.getConsent();if(!e)return{essential:!0,functional:!1,analytics:!1,marketing:!1,personalization:!1,advertising:!1,thirdParty:!1};const t={essential:!0};return e.purposes.forEach(e=>{t[e.id]=e.consented}),t}setLanguage(e){this.config.language=e}destroy(){this.destroyed=!0,this.banner?.destroy(),this.preferenceCenter?.destroy(),this.tcfManager?.destroy(),this.gppManager?.destroy(),this.removeAllListeners(),this.initialized=!1}}function te(e){const t=e.toLowerCase();return t.includes("essential")||t.includes("necess")||t.includes("strict")||t.includes("security")?"essential":t.includes("analyt")?"analytics":t.includes("market")||t.includes("advertis")?"marketing":t.includes("function")||t.includes("prefer")?"functional":t.includes("personal")?"personalization":"third_party"}function ne(e){return`/consent/artifacts/${encodeURIComponent(e)}`}async function se(e,t,n={}){const s=await e("GET",ne(t),void 0,{session_id:n.sessionId||void 0},{raw:!0,signal:n.signal,timeoutMs:n.timeoutMs});if(200===s.status&&s.body&&"object"==typeof s.body)return{state:"recorded",artifact:s.body,requestId:s.requestId};if(202===s.status&&s.body&&"object"==typeof s.body){const e=s.body,t="number"==typeof e.retry_after&&e.retry_after>=0?1e3*e.retry_after:void 0;return{state:"pending",pending:e,retryAfterMs:s.retryAfterMs??t??1e3,requestId:s.requestId}}throw M({status:s.status,statusText:"not an artifact read answer (expected 200 or 202 with a JSON body)",body:s.body,requestId:s.requestId,method:"GET",path:ne(t)})}const ie=new Set(["consentera:submitted","consentera:declined"]),re=new Set(["granted","partial","denied"]);function oe(e,t){if(!t.origin||e.origin!==t.origin)return null;if(void 0!==t.source&&e.source!==t.source)return null;const n=e.data;if(!n||"object"!=typeof n||"string"!=typeof n.type||!ie.has(n.type))return null;const s="string"==typeof n.session_id&&n.session_id?n.session_id:void 0,i="string"==typeof n.sessionId&&n.sessionId?n.sessionId:void 0;if(s&&i&&s!==i)return null;const r=s??i;if(!r||r!==t.sessionId)return null;const o="string"==typeof n.redirectUrl&&n.redirectUrl?n.redirectUrl:void 0,a=function(e){if("string"!=typeof e||!e)return null;try{return new URL(e,"https://collect.invalid").searchParams}catch{return null}}(o),c=n.choices&&"object"==typeof n.choices?n.choices:void 0,d=n.type,l="string"==typeof n.status&&re.has(n.status)?n.status:function(e,t){const n=e?.get("status");if(n&&re.has(n))return n;const s=Object.values(t??{}),i=s.filter(Boolean).length;return 0===i?"denied":i<s.length?"partial":"granted"}(a,"consentera:declined"===d?void 0:c);return{outcome:"decided",type:d,session_id:r,artifact_id:"string"==typeof n.artifact_id&&n.artifact_id?n.artifact_id:a?.get("artifact_id")??"",status:l,pending:!0===n.pending||"1"===a?.get("pending"),...o?{redirect_url:o}:{},...c?{choices:c}:{}}}function ae(e,t={}){if("undefined"==typeof window||"undefined"==typeof document)throw new Error("openConsentPopup can only be used in browser environments");const n=function(e,t){if(!e)return null;let n;try{n=new URL(e,t)}catch{return`the session's callback_url (${e}) is not a URL`}return"https:"!==n.protocol&&"http:"!==n.protocol?`the session's callback_url is an app link (${n.protocol}//…). /collect then posts its decision to its own origin, which this page is not, so the popup would never hear it. Use redirectToConsent, or create the session with a callback_url on this page’s origin.`:n.origin!==t?`/collect posts the decision to the callback's origin (${n.origin}), and this page is ${t}. The browser drops a message addressed to another origin, so the popup would never hear it. Create the session with a callback_url on this page’s origin.`:null}(t.callbackUrl,window.location.origin);if(n)return Promise.reject(q(`Consentera: ${n}`,"CALLBACK_ORIGIN_MISMATCH"));let s;try{s=new URL(e.consent_url,window.location.href).origin}catch{return Promise.reject(q("Consentera: consent_url is not a URL.","CONSENT_URL_INVALID"))}const i=t.timeoutMs??6e5,r=t.title??"Your consent choices",o=t.container??document.body;return new Promise((t,n)=>{const a=document.activeElement,c=document.documentElement.style.overflow,d=document.createElement("div");d.dataset.consenteraPopup="true",d.style.cssText="position:fixed;inset:0;z-index:2147483000;display:flex;align-items:center;justify-content:center;padding:12px;background:rgba(15,23,42,.6)";const l=document.createElement("div");l.setAttribute("role","dialog"),l.setAttribute("aria-modal","true"),l.setAttribute("aria-label",r),l.style.cssText="display:flex;flex-direction:column;width:100%;max-width:680px;height:min(780px,100%);background:#fff;border-radius:14px;overflow:hidden;box-shadow:0 24px 64px rgba(0,0,0,.35)";const u=document.createElement("div");u.style.cssText="display:flex;justify-content:flex-end;padding:8px;border-bottom:1px solid #e5e7eb";const h=document.createElement("button");h.type="button",h.textContent="Close",h.setAttribute("aria-label",`Close — ${r}`),h.style.cssText="font:inherit;font-size:14px;padding:8px 14px;min-height:40px;border:1px solid #d1d5db;border-radius:8px;background:#fff;color:#111827;cursor:pointer";const p=document.createElement("iframe");p.title=r,p.src=e.consent_url,p.style.cssText="flex:1;width:100%;border:0;background:#fff",u.append(h),l.append(u,p),d.append(l),o.append(d),document.documentElement.style.overflow="hidden",h.focus();let g=!1;const f=e=>{g||(g=!0,clearTimeout(b),window.removeEventListener("message",y),document.removeEventListener("keydown",_,!0),d.remove(),document.documentElement.style.overflow=c,a&&"function"==typeof a.focus&&a.focus(),e())},m=()=>f(()=>t({outcome:"dismissed",session_id:e.consent_session_id})),y=n=>{const i=oe(n,{origin:s,sessionId:e.consent_session_id,source:p.contentWindow});i&&f(()=>t(i))},_=e=>{"Escape"===e.key&&(e.preventDefault(),m())},b=setTimeout(()=>f(()=>n(new Error(`Consentera: the consent dialog had no decision within ${i}ms. The session is still valid — verify it server-side, or reopen it.`))),i);h.addEventListener("click",m),window.addEventListener("message",y),document.addEventListener("keydown",_,!0)})}class ce{request;config;logger;callbackUrls=new Map;constructor(e,t,n){this.request=e,this.config=t,this.logger=n}async createSession(e,t){const s=e.callback_url||this.config.callbackUrl,i=s?function(){const e="undefined"!=typeof globalThis?globalThis.crypto:void 0;if(!e||"function"!=typeof e.getRandomValues)throw q("Consentera: no crypto.getRandomValues here, so no unguessable callback state can be made. This SDK will not fall back to Math.random for it.","NO_SECURE_RANDOM");return Array.from(e.getRandomValues(new Uint8Array(32)),e=>e.toString(16).padStart(2,"0")).join("")}():void 0,o={ui_mode:e.ui_mode||"redirect",callback_url:s&&i?de(s,i):void 0};e.data_principal&&Object.keys(e.data_principal).length>0&&(o.data_principal=e.data_principal),e.data_principal_id&&(o.data_principal_id=e.data_principal_id),e.age&&e.age.date_of_birth&&(o.age={date_of_birth:e.age.date_of_birth});const a=e.language||this.config.language;a&&(o.language=a),e.session_ref&&(o.session_ref=e.session_ref),e.notice_internal_name&&(o.notice_internal_name=e.notice_internal_name),void 0!==e.notice_version_number&&(o.notice_version_number=e.notice_version_number),e.customer_token&&(o.customer_token=e.customer_token),e.guardian_email&&(o.guardian_email=e.guardian_email),e.guardian_phone&&(o.guardian_phone=e.guardian_phone),e.guardian_relationship&&(o.guardian_relationship=e.guardian_relationship);const c=await this.request("POST","/consent/sessions",o,void 0,{idempotencyKey:t?.idempotencyKey??J(),signal:t?.signal,timeoutMs:t?.timeoutMs});return r("session",n.session(c.consent_session_id),JSON.stringify({session_id:c.consent_session_id,...i?{state:i}:{},notice_hash:c.notice_hash,data_principal_id:c.data_principal_id,created_at:(new Date).toISOString()}))||this.logger.warn('Consentera: browser storage is unavailable, so the callback handshake cannot survive a page navigation. Use ui_mode "popup", or verify the artifact server-side.'),"string"==typeof o.callback_url&&o.callback_url&&this.callbackUrls.set(c.consent_session_id,o.callback_url),this.logger.info("Consent session created",{session_id:c.consent_session_id}),c}redirectToConsent(e){if("undefined"==typeof window)throw new Error("redirectToConsent can only be used in browser environments");window.location.href=e.consent_url}openConsentPopup(e,t){return ae(e,{...t,callbackUrl:t?.callbackUrl??this.callbackUrls.get(e.consent_session_id)??this.config.callbackUrl})}async getArtifact(e,t){return se(this.request,e,t)}}function de(e,t){let n;try{n=new URL(e)}catch{throw q(`Consentera: callback_url must be an absolute URL (the platform refuses anything else); got ${JSON.stringify(e.slice(0,80))}`,"INVALID_CALLBACK_URL")}if(n.searchParams.has("state"))throw q("Consentera: callback_url already carries a `state` parameter. This SDK puts its own callback state there to bind the return to this browser; use a different parameter name for yours.","CALLBACK_STATE_RESERVED");return n.searchParams.set("state",t),n.toString()}function le(e){try{const t=new URL(e);return`${t.origin}${t.pathname}`}catch{return}}function ue(e,t="minimal"){if("none"===t)return;if("undefined"==typeof window)return{user_agent:"Consentera SDK (SSR)",platform:"web"};const n=navigator.userAgent;let s="Unknown",i="",r="Unknown",o="",a="desktop";n.includes("Chrome")&&!n.includes("Edg")?(s="Chrome",i=n.match(/Chrome\/([\d.]+)/)?.[1]||""):n.includes("Safari")&&!n.includes("Chrome")?(s="Safari",i=n.match(/Version\/([\d.]+)/)?.[1]||""):n.includes("Firefox")?(s="Firefox",i=n.match(/Firefox\/([\d.]+)/)?.[1]||""):n.includes("Edg")&&(s="Edge",i=n.match(/Edg\/([\d.]+)/)?.[1]||""),n.includes("Windows")?(r="Windows",o=n.match(/Windows NT ([\d.]+)/)?.[1]||""):n.includes("Mac OS X")?(r="macOS",o=n.match(/Mac OS X ([\d_.]+)/)?.[1]?.replace(/_/g,".")||""):n.includes("Android")?(r="Android",o=n.match(/Android ([\d.]+)/)?.[1]||""):n.includes("iPhone")||n.includes("iPad")?(r="iOS",o=n.match(/OS ([\d_]+)/)?.[1]?.replace(/_/g,".")||""):n.includes("Linux")&&(r="Linux"),/Mobi|Android.*Mobile|iPhone/.test(n)?a="mobile":/iPad|Android(?!.*Mobile)|Tablet/.test(n)&&(a="tablet");const c={platform:"web",device_type:a,browser_name:s,os_name:r,session_id:e};return"minimal"===t?c:{...c,user_agent:n,screen_resolution:`${window.screen.width}x${window.screen.height}`,timezone:Intl.DateTimeFormat().resolvedOptions().timeZone,browser_version:i,os_version:o,page_url:le(window.location.href),referrer:document.referrer?le(document.referrer):void 0}}function he(e,t="button"){return{type:t,ui_event_id:e,captured_at:(new Date).toISOString()}}function pe(e){if("data_principal_id"in e&&e.data_principal_id)return{data_principal_id:e.data_principal_id};const t=e.data_principal_identifiers;if(!t||0===Object.keys(t).length)throw new Error("Consentera: name the Data Principal with data_principal_id, or with data_principal_identifiers carrying the identifier fields of this organisation's integration key. data_principal_ref is refused by the API.");return{data_principal_identifiers:t}}class ge{request;logger;constructor(e,t){this.request=e,this.logger=t}async check(e,t,n){const s={...pe(e),purpose_code:t},i=await this.request("POST","/consent/validate",s,void 0,{signal:n?.signal,timeoutMs:n?.timeoutMs});return this.logger.debug("Consent validated",{namedBy:"data_principal_id"in e?"data_principal_id":"data_principal_identifiers",purpose:t,decision:i.decision}),i}async checkBulk(e,t,n){const s={...pe(e),purpose_codes:t},i=await this.request("POST","/consent/validate/bulk",s,void 0,{signal:n?.signal,timeoutMs:n?.timeoutMs});return this.logger.debug("Bulk consent validated",{namedBy:"data_principal_id"in e?"data_principal_id":"data_principal_identifiers",purposes:t.length,results:i.results?.map(e=>`${e.purpose_code}:${e.decision}`)}),i}async isAllowed(e,t,n){return"ALLOW"===(await this.check(e,t,n)).decision}async areAllowed(e,t){const n=await this.checkBulk(e,t),s=new Map;for(const e of n.results||[])s.set(e.purpose_code??e.purpose_id,"ALLOW"===e.decision);return s}}class fe{request;logger;onChanged;contextMode;constructor(e,t,n){this.request=e,this.logger=t,this.contextMode=n?.contextMode??"minimal"}context(){return ue(void 0,this.contextMode)}mutation(e){return{idempotencyKey:e?.idempotencyKey??J(),signal:e?.signal,timeoutMs:e?.timeoutMs}}emitChanged(e,t,n){try{this.onChanged?.({dataPrincipalId:e,changes:t,source:n})}catch(e){this.logger.warn("consent.changed listener threw",{error:String(e)})}}async getUpdateContext(e,t){if(!/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(e))throw new Error("Consentera: getUpdateContext takes a data_principal_id (a uuid), not an identifier. This road deliberately accepts no identifiers — a query string is written verbatim into access logs, browser history and Referer headers. Resolve the id once with validate.check({ data_principal_identifiers: … }, purpose) and pass the data_principal_id from its response.");const n={data_principal_id:e};return t?.language_code&&(n.language_code=t.language_code),this.request("GET","/consent/update/context",void 0,n)}async update(e,t,n,s="btn_save_preferences",i){const r={data_principal_id:e,notice_version_id:n.noticeVersionId,notice_hash:n.noticeHash,language_code:n.languageCode||"en",updates:t,affirmative_action:he(s),client_context:this.context()};await this.request("POST","/consent/update",r,void 0,this.mutation(i)),this.logger.info("Consent updated",{dataPrincipal:e,updates:t.map(e=>`${e.purpose_id}:${e.new_status}`)}),this.emitChanged(e,t.map(e=>({purpose_id:e.purpose_id,status:e.new_status})),"update")}async grant(e,t,n,s="btn_grant_consent",i){const r=t.map(e=>({purpose_id:e,new_status:"granted"}));return this.update(e,r,n,s,i)}async deny(e,t,n,s="btn_deny_consent",i){const r=t.map(e=>({purpose_id:e,new_status:"denied"}));return this.update(e,r,n,s,i)}async getWithdrawalContext(e){return this.request("GET","/consent/withdrawal/context",void 0,{data_principal_id:e})}async withdraw(e,t,n,s="btn_withdraw_consent",i){const r={data_principal_id:e,purposes:t,reason:n,affirmative_action:he(s),client_context:this.context()};await this.request("POST","/consent/withdraw",r,void 0,this.mutation(i)),this.logger.info("Consent withdrawn",{dataPrincipal:e,purposes:t}),this.emitChanged(e,t.map(e=>({purpose_id:e,status:"withdrawn"})),"withdraw")}async withdrawBulk(e,t,n,s="btn_withdraw_all",i){const r={data_principal_id:e,purposes:t,reason:n,affirmative_action:he(s),client_context:this.context()};await this.request("POST","/consent/withdraw/bulk",r,void 0,this.mutation(i)),this.logger.info("Bulk consent withdrawn",{dataPrincipal:e,purposes:t.length}),this.emitChanged(e,t.map(e=>({purpose_id:e,status:"withdrawn"})),"withdraw_bulk")}async getWithdrawalAnalytics(){return this.request("GET","/consent/withdrawal/analytics")}async getRenewalContext(e){return this.request("GET","/consent/renewal/context",void 0,{data_principal_id:e})}async renew(e,t,n,s="btn_renew_consent",i){const r={data_principal_id:e,purpose_ids:t,notice_hash:n,captured_at:he(s).captured_at,client_context:this.context()},o=await this.request("POST","/consent/renew",r,void 0,{...this.mutation(i),raw:!0});return this.logger.info("Consent renewed",{dataPrincipal:e,purposes:t}),this.emitChanged(e,o.body.renewed_consents.map(e=>({purpose_id:e.purpose_id,status:"granted"})),"renew"),o}async renewBulk(e,t,n,s="btn_renew_all",i){const r={data_principal_id:e,renewals:t.map(e=>({purpose_id:e})),notice_hash:n,captured_at:he(s).captured_at,client_context:this.context()},o=await this.request("POST","/consent/renew/bulk",r,void 0,{...this.mutation(i),raw:!0});return this.logger.info("Bulk consent renewed",{dataPrincipal:e,purposes:o.body.success_count}),o.body.renewed_consents.length>0&&this.emitChanged(e,o.body.renewed_consents.map(e=>({purpose_id:e.purpose_id,status:"granted"})),"renew"),o}}const me=new Set(["granted","partial","denied"]);function ye(e){return e&&me.has(e)?e:"unknown"}class _e{logger;request;options;constructor(e,t,n={}){this.logger=e,this.request=t,this.options=n}readCallbackParams(e){let t;if(e)t="string"==typeof e?new URLSearchParams(e):e;else{if("undefined"==typeof window)throw new Error("readCallbackParams requires searchParams in non-browser environments");t=new URLSearchParams(window.location.search)}return{session_id:t.get("session_id")||t.get("consent_session_id")||"",artifact_id:t.get("artifact_id")||t.get("consent_artifact_id")||void 0,raw_status:t.get("status")||"",claimed_status:ye(t.get("status")),pending:"1"===t.get("pending"),state:t.get("state")||void 0,sig:t.get("sig")||void 0,error:t.get("error")||void 0}}async parseCallback(e){const t=this.readCallbackParams(e),s={session_id:t.session_id,artifact_id:t.artifact_id,claimed_status:t.claimed_status,claimed_pending:t.pending,status:"unverified"};if(t.error)return this.finish({...s,status:"error",error:t.error,reason:"the platform returned an error"});if(!t.session_id)return this.finish({...s,status:"unverified",reason:"the callback names no session"});const r=i("session",n.session(t.session_id));if(!r)return this.finish({...s,reason:"no session was stored in this browser for that id — the session was created elsewhere, the tab was replaced, or the callback was not produced by this flow"});let a;try{a=JSON.parse(r)}catch{return o("session",n.session(t.session_id)),this.finish({...s,reason:"the stored session record could not be parsed"})}if(o("session",n.session(t.session_id)),a.session_id&&a.session_id!==t.session_id)return this.finish({...s,reason:"the stored session id does not match the callback"});if(!a.state)return this.finish({...s,reason:"the stored session has no callback state: it was created without a callback_url, or by an SDK version that bound the callback to the server challengeNonce (which the platform never returns). Read the consent back through your backend instead."});if(!t.state)return this.finish({...s,reason:"the callback carries no state, and the session was created with one"});if(!be(a.state,t.state))return this.finish({...s,reason:"the callback state does not match the stored session"});let c="absent";if(t.sig){if(!this.options.verifySignature)return this.finish({...s,signature:"unverifiable",reason:"the callback is signed and no verifier is configured. The signing key is your callback_signing_secret, which must not be in a browser: set `verifyCallbackSignature` to call your own server, which uses verifyCallbackSignature(secret, …) from this package."});let e=!1;try{e=await this.options.verifySignature({sessionId:t.session_id,artifactId:t.artifact_id??"",status:t.raw_status,sig:t.sig})}catch(e){return this.finish({...s,signature:"unverifiable",reason:`the signature verifier threw: ${String(e)}`})}if(!e)return this.finish({...s,signature:"invalid",reason:"the callback signature did not verify"});c="verified"}return"denied"===t.claimed_status?this.finish({...s,status:"denied",signature:c}):"unknown"===t.claimed_status?this.finish({...s,signature:c,reason:`the callback status ${JSON.stringify(t.raw_status)} is not a status the platform issues (granted | partial | denied). Read the consent back through your backend.`}):t.artifact_id?this.request?this.confirmArtifact(s,t.artifact_id,t.session_id,a.data_principal_id,c):this.finish({...s,signature:c,reason:"no transport available to confirm the artifact. Construct the handler through ConsentEraClient so it can call GET /consent/artifacts/{id}."}):this.finish({...s,signature:c,reason:"the callback claims success but names no artifact"})}async confirmArtifact(e,t,n,s,i){const r=this.options.artifactWaitMs??15e3,o=Date.now()+r;for(;;){let r;try{r=await se(this.request,t,{sessionId:n})}catch(s){const r=s instanceof k?s.toString():String(s);return s instanceof k&&(404===s.status||"not_found"===s.kind)?this.finish({...e,signature:i,reason:`the platform has no artifact ${t} for session ${n}: it was never issued for this session, or will never be written. A forged or mismatched artifact_id reads exactly like this. (${r})`}):this.finish({...e,signature:i,reason:`the artifact could not be confirmed: ${r}`})}if("recorded"===r.state){const n=r.artifact;return n.artifact_id&&n.artifact_id!==t?this.finish({...e,signature:i,reason:"the platform returned a different artifact than the one read"}):s?n.data_principal_id!==s?this.finish({...e,signature:i,reason:"the artifact names a different Data Principal from this browser's session — it is not this consent"}):this.finish({...e,status:"completed",artifact:n,signature:i}):this.finish({...e,signature:i,reason:"the stored session names no data_principal_id, so the artifact cannot be tied to the person this session was created for. Create the session with this SDK version, or confirm the artifact server-side."})}const a=o-Date.now();if(a<=0||r.retryAfterMs>a)return this.finish({...e,status:"pending",signature:i,retryAfterMs:r.retryAfterMs,reason:`the platform says the consent was recorded and its artifact is still being written (${r.pending.reason||"pending"}); read it again in ${r.retryAfterMs}ms. This is NOT a failure and NOT a consent that did not happen.`});await new Promise(e=>setTimeout(e,r.retryAfterMs))}}isCallback(e){const t=new URLSearchParams(e||("undefined"!=typeof window?window.location.search:""));return t.has("session_id")||t.has("consent_session_id")}finish(e){return e.session_id&&r("session",n.callback(e.session_id),JSON.stringify(e)),"completed"===e.status?this.logger.info("Consent callback verified",{session_id:e.session_id,status:e.status}):"pending"===e.status?this.logger.info("Consent callback verified; artifact not readable yet",{session_id:e.session_id,status:e.status}):this.logger.warn("Consent callback NOT verified",{session_id:e.session_id,status:e.status,reason:e.reason}),e}}function be(e,t){if(e.length!==t.length)return!1;let n=0;for(let s=0;s<e.length;s++)n|=e.charCodeAt(s)^t.charCodeAt(s);return 0===n}class we{request;logger;cachedConfig=null;constructor(e,t){this.request=e,this.logger=t}async getConfig(e){if(this.cachedConfig)return this.cachedConfig;const t={};e&&(t.language_code=e);const n=await this.request("GET","/df/config",void 0,t,{road:"public"});return this.cachedConfig=n,this.logger.info("DF config loaded",{tenant:n.tenant_name,purposes:n.purposes?.length}),n}async getPurposes(){const e=await this.request("GET","/df/purposes",void 0,void 0,{road:"public"});return Array.isArray(e)?e:e.purposes||[]}async getNoticePurposes(e,t){const n={};return e&&(n.notice_internal_name=e),t&&(n.language_code=t),this.request("GET","/df/notice/purposes",void 0,n,{road:"public"})}async getNoticeTemplate(e,t,n){const s={notice:e};t&&(s.notice_language=t),n&&(s.template_language=n);const i=await this.request("GET","/df/notice/template",void 0,s,{road:"public"});return this.logger.info("Notice template loaded",{notice:e,language:t}),i.data??i}async getSessionWidgetTemplate(e){return this.request("GET",`/consent/sessions/${encodeURIComponent(e)}/widget-template`,void 0,void 0,{road:"session"})}clearCache(){this.cachedConfig=null}}class ve{request;logger;constructor(e,t,n){this.request=e,this.logger=n}async getPortalToken(e,t){const n={data_principal_ref:e,redirect_path:t},s=await this.request("POST","/df/principal-token",n);return this.logger.info("Principal portal token generated",{dataPrincipal:e,expires_in:s.expires_in}),s}async getPortalUrl(e,t){return(await this.getPortalToken(e,t)).redirect_url}async openPortal(e,t){if("undefined"==typeof window)throw new Error("openPortal can only be used in browser environments");const n=await this.getPortalUrl(e,t);window.open(n,"_blank","noopener,noreferrer"),this.logger.info("Principal portal opened",{dataPrincipal:e})}}const ke=["analytics_storage","ad_storage","ad_user_data","ad_personalization"];function Ce(){const e=globalThis;return"function"==typeof e.gtag?e.gtag:(e.dataLayer=e.dataLayer||[],function(...t){e.dataLayer.push(t)})}class Ee{mapping;state={};constructor(e){this.mapping=e}sendDefault(e=500){const t={wait_for_update:e};for(const e of ke)this.mapping[e]?.length&&(t[e]="denied");Ce()("consent","default",t)}apply(e){this.state={...e},this.push()}onChanged(e){for(const t of e.changes)this.state[t.purpose_id]="granted"===t.status;this.push()}push(){const e={};for(const t of ke){const n=this.mapping[t];n?.length&&(e[t]=n.every(e=>!0===this.state[e])?"granted":"denied")}Ce()("consent","update",e)}}if("undefined"!=typeof window){const e=document.currentScript;if("true"===e?.dataset?.autoInit){const t=e.dataset.siteKey,n=e.dataset.apiKey,s=e.dataset.tenantId,i=e.dataset.apiEndpoint??"";if((t||n)&&s)try{window.ConsentEra=new ee({...t?{siteKey:t}:{apiKey:n},tenantId:s,apiEndpoint:i,autoShow:!0})}catch(e){}}}"undefined"!=typeof window&&(window.Consentera=ee,window.ConsentEraConsent=ee),e.CallbackHandler=_e,e.ConsentBanner=l,e.ConsentEraApiError=V,e.ConsentEraClient=class extends m{config;logger;transport;session;validate;manage;callback;df;principal;consent;constructor(e){if(super(),!e.apiEndpoint&&!e.proxyEndpoint)throw q("Consentera: set `proxyEndpoint` (your own server route — required in a browser for consent lifecycle roads) or `apiEndpoint` (server-side use).","ENDPOINT_REQUIRED");if(!e.tenantId&&!e.proxyEndpoint)throw q("Consentera: tenantId is required with `apiEndpoint`. (Behind `proxyEndpoint` it is optional: your server supplies the tenant.)","TENANT_ID_REQUIRED");Y({apiKey:e.apiKey,unsafeAllowSecretKeyInBrowser:e.unsafeAllowSecretKeyInBrowser,fix:'Move the key to your own server and point the SDK at it with `proxyEndpoint: "/api/consentera"`; the browser then sends no credential at all.'}),this.config=e,this.logger=new y(e.debug?"debug":"info"),this.transport=new Z({tenantId:e.tenantId,apiEndpoint:e.apiEndpoint,proxyEndpoint:e.proxyEndpoint,apiKey:e.apiKey,siteKey:e.siteKey,customHeaders:e.customHeaders,unsafeAllowSecretKeyInBrowser:e.unsafeAllowSecretKeyInBrowser,timeoutMs:e.timeoutMs,retry:e.retry,beforeSend:e.beforeSend,fetchImpl:e.fetchImpl},this.logger);const t=this.request.bind(this);this.session=new ce(t,e,this.logger),this.validate=new ge(t,this.logger),this.manage=new fe(t,this.logger,{contextMode:e.collectContext??"minimal"}),this.manage.onChanged=e=>this.emit("consent.changed",e),this.callback=new _e(this.logger,t,{verifySignature:e.verifyCallbackSignature,artifactWaitMs:e.artifactWaitMs}),this.df=new we(t,this.logger),this.principal=new ve(t,e,this.logger),this.consent={createSession:this.session.createSession.bind(this.session),redirectToConsent:this.session.redirectToConsent.bind(this.session),openConsentPopup:this.session.openConsentPopup.bind(this.session),getArtifact:this.session.getArtifact.bind(this.session),validate:this.validate.check.bind(this.validate),validateBulk:this.validate.checkBulk.bind(this.validate),isAllowed:this.validate.isAllowed.bind(this.validate),areAllowed:this.validate.areAllowed.bind(this.validate),handleCallback:this.callback.parseCallback.bind(this.callback),isCallback:this.callback.isCallback.bind(this.callback),getContext:this.manage.getUpdateContext.bind(this.manage),update:this.manage.update.bind(this.manage),grant:this.manage.grant.bind(this.manage),deny:this.manage.deny.bind(this.manage),getWithdrawalContext:this.manage.getWithdrawalContext.bind(this.manage),withdraw:this.manage.withdraw.bind(this.manage),withdrawBulk:this.manage.withdrawBulk.bind(this.manage),getRenewalContext:this.manage.getRenewalContext.bind(this.manage),renew:this.manage.renew.bind(this.manage),renewBulk:this.manage.renewBulk.bind(this.manage)},this.logger.info("Consentera client initialised",{mode:e.proxyEndpoint?"proxy":"direct",tenantId:e.tenantId})}async request(e,t,n,s,i={}){try{return await this.transport.request(e,t,n,{...i,query:{...s,...i.query}})}catch(e){throw e instanceof k&&this.emit("error",e),e}}static buildClientContext=ue;static buildAffirmativeAction=he;getConfig(){const e={...this.config};return e.apiKey&&(e.apiKey=`${e.apiKey.slice(0,9)}…`),e}},e.ConsentEraConsent=ee,e.ConsentManager=fe,e.ConsentModeBridge=Ee,e.ConsentSession=ce,e.ConsentStorage=a,e.ConsentValidator=ge,e.ConsenteraAuthError=E,e.ConsenteraConfigError=C,e.ConsenteraConflictError=T,e.ConsenteraError=k,e.ConsenteraGuardianError=A,e.ConsenteraIdentityError=I,e.ConsenteraNetworkError=R,e.ConsenteraNotFoundError=D,e.ConsenteraPermissionError=x,e.ConsenteraRateLimitError=P,e.ConsenteraServerError=$,e.ConsenteraTimeoutError=N,e.ConsenteraValidationError=S,e.DEFAULT_RETRY=G,e.DEFAULT_TIMEOUT_MS=1e4,e.DFConfigClient=we,e.GPPManager=f,e.HttpTransport=Z,e.PreferenceCenter=p,e.PrincipalClient=ve,e.SDK_HEADER_VALUE=F,e.SDK_NAME="consent-sdk-js",e.SDK_PLATFORM="web",e.SDK_VERSION=B,e.TCFManager=g,e.buildClientContext=ue,e.claimedCallbackStatus=ye,e.default=ee,e.installConsentModeBridge=function(e,t){const n=new Ee(t);return n.sendDefault(),e.on("consent.changed",e=>n.onChanged(e)),n},e.isBrowser=z,e.isSecretKey=H,e.isSiteKey=W,e.newRequestId=J,e.parseRetryAfterMs=v,e.principalBody=pe,e.readDecisionMessage=oe,e.readStored=i,e.removeStored=o,e.storageAvailable=function(e){return null!==s(e)},e.storageKeys=n,e.verifyCallbackSignature=async function(e,t){if(!e||!t.sig)return!1;const n=function(){const e=globalThis.crypto?.subtle;if(e)return e;try{const e=require("node:crypto");return e?.webcrypto?.subtle}catch{return}}();if(!n)throw q("Consentera: no SubtleCrypto available, so the callback signature cannot be verified here. Verify it on a runtime that has WebCrypto (Node 20+, any modern browser) — but note the signing key is your callback_signing_secret and must not be in a browser.","NO_SUBTLE_CRYPTO");const s=new TextEncoder,i=await n.importKey("raw",s.encode(e),{name:"HMAC",hash:"SHA-256"},!1,["sign"]),r=`${t.sessionId}|${t.artifactId}|${t.status}`,o=await n.sign("HMAC",i,s.encode(r));return be(Array.from(new Uint8Array(o),e=>e.toString(16).padStart(2,"0")).join(""),t.sig.toLowerCase())},e.writeStored=r,Object.defineProperty(e,"__esModule",{value:!0})});
|
|
1
|
+
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).Consentera={})}(this,function(e){"use strict";const t={local:new Map,session:new Map},n={session:e=>`consentera_session_${e}`,callback:e=>`consentera_callback_${e}`,pendingSync:"consentera_pending_sync",deviceId:"consentera_device_id"};function i(e){try{if("undefined"==typeof window)return null;const t="local"===e?window.localStorage:window.sessionStorage,n="__consentera_probe__";return t.setItem(n,"1"),t.removeItem(n),t}catch{return null}}function s(e,n){const s=i(e);if(!s)return t[e].get(n)??null;try{return s.getItem(n)}catch{return t[e].get(n)??null}}function r(e,n,s){const r=i(e);if(r)try{return r.setItem(n,s),!0}catch{}return t[e].set(n,s),!1}function o(e,n){const s=i(e);if(s)try{s.removeItem(n)}catch{}t[e].delete(n)}class a{config;key;constructor(e){this.config={type:"cookie",cookieName:"consentera_consent",cookieExpiry:365,cookiePath:"/",secure:"undefined"==typeof location||"https:"===location.protocol,sameSite:"Lax",...e},this.key=this.config.cookieName}save(e){const t=JSON.stringify(e);switch(this.config.type){case"cookie":return this.setCookie(t);case"localStorage":return r("local",this.key,t);case"sessionStorage":return r("session",this.key,t);default:return!1}}get(){let e=null;switch(this.config.type){case"cookie":e=this.getCookie();break;case"localStorage":e=s("local",this.key);break;case"sessionStorage":e=s("session",this.key)}if(!e)return null;try{return JSON.parse(e)}catch{return null}}exists(){return null!==this.get()}clear(){switch(this.config.type){case"cookie":this.deleteCookie();break;case"localStorage":o("local",this.key);break;case"sessionStorage":o("session",this.key)}}setCookie(e){const{cookieExpiry:t,cookiePath:n,cookieDomain:i,secure:s,sameSite:r}=this.config;let o=`${encodeURIComponent(this.key)}=${encodeURIComponent(e)}`;if(t){const e=new Date;e.setTime(e.getTime()+24*t*60*60*1e3),o+=`; expires=${e.toUTCString()}`}n&&(o+=`; path=${n}`),i&&(o+=`; domain=${i}`),s&&(o+="; secure"),r&&(o+=`; samesite=${r}`);if(new Blob([o]).size>4096)return!1;if("undefined"==typeof document)return!1;try{document.cookie=o}catch{return!1}return null!==this.getCookie()}getCookie(){if("undefined"==typeof document)return null;const e=encodeURIComponent(this.key)+"=",t=document.cookie.split(";");for(let n of t)if(n=n.trim(),0===n.indexOf(e))return decodeURIComponent(n.substring(e.length));return null}deleteCookie(){if("undefined"==typeof document)return;const{cookiePath:e,cookieDomain:t}=this.config;let n=`${encodeURIComponent(this.key)}=; expires=Thu, 01 Jan 1970 00:00:00 GMT`;e&&(n+=`; path=${e}`),t&&(n+=`; domain=${t}`),document.cookie=n}}function c(e){const t=document.createElement("div");return t.textContent=e,t.innerHTML}function d(e){try{const t=new URL(e,window.location.origin);if(["http:","https:","mailto:"].includes(t.protocol))return t.href}catch{}return"#"}class l{config;theme;sdk;container=null;isVisible=!1;constructor(e,t,n){this.config=e,this.theme=t,this.sdk=n}show(){this.isVisible||(this.createBanner(),this.isVisible=!0)}hide(){this.isVisible&&this.container&&(this.container.classList.add("consentera-banner--closing"),setTimeout(()=>{this.container?.remove(),this.container=null,this.isVisible=!1},300))}createBanner(){this.container=document.createElement("div"),this.container.id="consentera-consent-banner",this.container.className=`consentera-banner consentera-banner--${this.theme.position||"bottom"}`,this.container.setAttribute("role","dialog"),this.container.setAttribute("aria-modal","true"),this.container.setAttribute("aria-labelledby","consentera-banner-title"),this.injectStyles(),this.container.innerHTML=this.getBannerHTML(),this.attachEventListeners(),document.body.appendChild(this.container),requestAnimationFrame(()=>{this.container?.classList.add("consentera-banner--visible")})}getBannerHTML(){const{banner:e}=this.config;return`\n <div class="consentera-banner__content">\n ${this.theme.showLogo?`\n <div class="consentera-banner__logo">\n ${this.theme.logoUrl?`<img src="${d(this.theme.logoUrl)}" alt="Logo" />`:this.getDefaultLogo()}\n </div>\n `:""}\n\n <div class="consentera-banner__text">\n <h2 id="consentera-banner-title" class="consentera-banner__title">\n ${c(e.title||"We value your privacy")}\n </h2>\n <p class="consentera-banner__description">\n ${c(e.description||"We use cookies to enhance your experience.")}\n </p>\n ${e.showPrivacyPolicy&&e.privacyPolicyUrl?`\n <a href="${d(e.privacyPolicyUrl)}" target="_blank" rel="noopener" class="consentera-banner__link">\n Privacy Policy\n </a>\n `:""}\n </div>\n\n <div class="consentera-banner__actions">\n ${e.showCustomize?`\n <button type="button" class="consentera-btn consentera-btn--secondary" data-action="customize">\n ${c(e.customizeText||"Customize")}\n </button>\n `:""}\n ${e.showRejectAll?`\n <button type="button" class="consentera-btn consentera-btn--secondary" data-action="reject">\n ${c(e.rejectAllText||"Reject All")}\n </button>\n `:""}\n <button type="button" class="consentera-btn consentera-btn--primary" data-action="accept">\n ${c(e.acceptAllText||"Accept All")}\n </button>\n </div>\n </div>\n `}getDefaultLogo(){return`\n <svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">\n <rect width="32" height="32" rx="8" fill="${this.theme.primaryColor||"#2563eb"}"/>\n <path d="M16 6L24 10V16C24 21.52 20.6 26.74 16 28C11.4 26.74 8 21.52 8 16V10L16 6Z"\n stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>\n <path d="M12 16L14.5 18.5L20 13"\n stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>\n </svg>\n `}attachEventListeners(){this.container&&(this.container.addEventListener("click",e=>{const t=e.target.dataset.action;if(t)switch(t){case"accept":this.sdk.acceptAll();break;case"reject":this.sdk.rejectAll();break;case"customize":this.hide(),this.sdk.showPreferences()}}),document.addEventListener("keydown",this.handleKeyDown.bind(this)))}handleKeyDown(e){"Escape"===e.key&&this.isVisible}injectStyles(){if(document.getElementById("consentera-consent-styles"))return;const e=document.createElement("style");e.id="consentera-consent-styles",e.textContent=this.getStyles(),document.head.appendChild(e)}getStyles(){const{primaryColor:e,backgroundColor:t,textColor:n,borderRadius:i,fontFamily:s}=this.theme;return`\n .consentera-banner {\n position: fixed;\n left: 0;\n right: 0;\n z-index: 999999;\n font-family: ${s||'-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif'};\n opacity: 0;\n transform: translateY(100%);\n transition: opacity 0.3s ease, transform 0.3s ease;\n }\n\n .consentera-banner--bottom {\n bottom: 0;\n }\n\n .consentera-banner--top {\n top: 0;\n transform: translateY(-100%);\n }\n\n .consentera-banner--visible {\n opacity: 1;\n transform: translateY(0);\n }\n\n .consentera-banner--closing {\n opacity: 0;\n transform: translateY(100%);\n }\n\n .consentera-banner--top.consentera-banner--closing {\n transform: translateY(-100%);\n }\n\n .consentera-banner__content {\n display: flex;\n align-items: center;\n gap: 24px;\n padding: 20px 24px;\n background: ${t||"#ffffff"};\n color: ${n||"#1f2937"};\n box-shadow: 0 -4px 20px rgba(0, 0, 0, 0.1);\n border-radius: ${i||"0"} ${i||"0"} 0 0;\n }\n\n .consentera-banner--top .consentera-banner__content {\n border-radius: 0 0 ${i||"0"} ${i||"0"};\n box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1);\n }\n\n .consentera-banner__logo {\n flex-shrink: 0;\n }\n\n .consentera-banner__logo img,\n .consentera-banner__logo svg {\n width: 48px;\n height: 48px;\n object-fit: contain;\n }\n\n .consentera-banner__text {\n flex: 1;\n min-width: 0;\n }\n\n .consentera-banner__title {\n margin: 0 0 4px 0;\n font-size: 16px;\n font-weight: 600;\n color: ${n||"#1f2937"};\n }\n\n .consentera-banner__description {\n margin: 0;\n font-size: 14px;\n color: ${n||"#6b7280"};\n line-height: 1.5;\n }\n\n .consentera-banner__link {\n display: inline-block;\n margin-top: 8px;\n font-size: 13px;\n color: ${e||"#2563eb"};\n text-decoration: underline;\n }\n\n .consentera-banner__actions {\n display: flex;\n gap: 12px;\n flex-shrink: 0;\n }\n\n .consentera-btn {\n padding: 10px 20px;\n border: none;\n border-radius: 6px;\n font-size: 14px;\n font-weight: 500;\n cursor: pointer;\n transition: all 0.2s ease;\n white-space: nowrap;\n }\n\n .consentera-btn--primary {\n background: ${e||"#2563eb"};\n color: #ffffff;\n }\n\n .consentera-btn--primary:hover {\n opacity: 0.9;\n }\n\n .consentera-btn--secondary {\n background: transparent;\n color: ${n||"#4b5563"};\n border: 1px solid ${n||"#d1d5db"};\n }\n\n .consentera-btn--secondary:hover {\n background: rgba(0, 0, 0, 0.05);\n }\n\n @media (max-width: 768px) {\n .consentera-banner__content {\n flex-direction: column;\n text-align: center;\n padding: 16px;\n gap: 16px;\n }\n\n .consentera-banner__actions {\n width: 100%;\n flex-direction: column;\n }\n\n .consentera-btn {\n width: 100%;\n }\n }\n\n /* Accessibility */\n .consentera-btn:focus {\n outline: 2px solid ${e||"#2563eb"};\n outline-offset: 2px;\n }\n\n @media (prefers-reduced-motion: reduce) {\n .consentera-banner {\n transition: none;\n }\n }\n `}destroy(){document.removeEventListener("keydown",this.handleKeyDown.bind(this)),this.container?.remove(),this.container=null,this.isVisible=!1}}function u(e){const t=document.createElement("div");return t.textContent=e,t.innerHTML}function p(e){try{const t=new URL(e,window.location.origin);if(["http:","https:","mailto:"].includes(t.protocol))return t.href}catch{}return"#"}class h{config;theme;sdk;container=null;overlay=null;isVisible=!1;preferences;constructor(e,t,n){this.config=e,this.theme=t,this.sdk=n,this.preferences=n.getPreferences()}show(){this.isVisible||(this.preferences=this.sdk.getPreferences(),this.createPreferenceCenter(),this.isVisible=!0)}hide(){this.isVisible&&(this.container?.classList.add("consentera-preferences--closing"),this.overlay?.classList.add("consentera-overlay--closing"),setTimeout(()=>{this.container?.remove(),this.overlay?.remove(),this.container=null,this.overlay=null,this.isVisible=!1},300))}createPreferenceCenter(){this.overlay=document.createElement("div"),this.overlay.className="consentera-overlay",this.overlay.addEventListener("click",()=>this.hide()),this.container=document.createElement("div"),this.container.id="consentera-preference-center",this.container.className="consentera-preferences",this.container.setAttribute("role","dialog"),this.container.setAttribute("aria-modal","true"),this.container.setAttribute("aria-labelledby","consentera-preferences-title"),this.injectStyles(),this.container.innerHTML=this.getPreferenceCenterHTML(),this.attachEventListeners(),document.body.appendChild(this.overlay),document.body.appendChild(this.container),requestAnimationFrame(()=>{this.overlay?.classList.add("consentera-overlay--visible"),this.container?.classList.add("consentera-preferences--visible")}),this.container.querySelector(".consentera-preferences__close")?.focus()}getPreferenceCenterHTML(){const{banner:e,purposes:t}=this.config;return`\n <div class="consentera-preferences__header">\n <h2 id="consentera-preferences-title" class="consentera-preferences__title">\n Cookie Preferences\n </h2>\n <button type="button" class="consentera-preferences__close" data-action="close" aria-label="Close">\n <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">\n <path d="M18 6L6 18M6 6l12 12"/>\n </svg>\n </button>\n </div>\n\n <div class="consentera-preferences__body">\n <p class="consentera-preferences__description">\n We use cookies and similar technologies to personalize content, analyze traffic, and improve your experience.\n You can choose which categories of cookies you allow below.\n </p>\n\n <div class="consentera-preferences__purposes">\n ${t.map(e=>`\n <div class="consentera-purpose">\n <div class="consentera-purpose__header">\n <div class="consentera-purpose__info">\n <h3 class="consentera-purpose__name">${u(e.name)}</h3>\n ${e.required?'<span class="consentera-purpose__required">Always Active</span>':""}\n </div>\n <label class="consentera-toggle">\n <input type="checkbox"\n id="purpose-${e.id}"\n data-purpose="${e.id}"\n ${e.required?"checked disabled":""}\n ${this.preferences[e.id]?"checked":""}\n />\n <span class="consentera-toggle__slider"></span>\n </label>\n </div>\n <p class="consentera-purpose__description">${u(e.description)}</p>\n ${e.cookies&&e.cookies.length>0?`\n <details class="consentera-purpose__cookies">\n <summary>View cookies (${e.cookies.length})</summary>\n <table class="consentera-cookies-table">\n <thead>\n <tr>\n <th>Cookie</th>\n <th>Provider</th>\n <th>Purpose</th>\n <th>Expiry</th>\n </tr>\n </thead>\n <tbody>\n ${e.cookies.map(e=>`\n <tr>\n <td>${u(e.name)}</td>\n <td>${u(e.provider)}</td>\n <td>${u(e.purpose)}</td>\n <td>${u(e.expiry)}</td>\n </tr>\n `).join("")}\n </tbody>\n </table>\n </details>\n `:""}\n </div>\n `).join("")}\n </div>\n </div>\n\n <div class="consentera-preferences__footer">\n <div class="consentera-preferences__links">\n ${e.privacyPolicyUrl?`\n <a href="${p(e.privacyPolicyUrl)}" target="_blank" rel="noopener">Privacy Policy</a>\n `:""}\n ${e.cookiePolicyUrl?`\n <a href="${p(e.cookiePolicyUrl)}" target="_blank" rel="noopener">Cookie Policy</a>\n `:""}\n </div>\n <div class="consentera-preferences__actions">\n <button type="button" class="consentera-btn consentera-btn--secondary" data-action="reject">\n ${e.rejectAllText||"Reject All"}\n </button>\n <button type="button" class="consentera-btn consentera-btn--secondary" data-action="accept">\n ${e.acceptAllText||"Accept All"}\n </button>\n <button type="button" class="consentera-btn consentera-btn--primary" data-action="save">\n ${e.saveText||"Save Preferences"}\n </button>\n </div>\n </div>\n `}attachEventListeners(){this.container&&(this.container.addEventListener("click",e=>{const t=e.target,n=t.dataset.action||t.closest("[data-action]")?.getAttribute("data-action");if(n)switch(n){case"close":this.hide();break;case"accept":this.sdk.acceptAll(),this.hide();break;case"reject":this.sdk.rejectAll(),this.hide();break;case"save":this.savePreferences()}}),this.container.querySelectorAll("input[data-purpose]").forEach(e=>{e.addEventListener("change",e=>{const t=e.target,n=t.dataset.purpose;this.preferences[n]=t.checked})}),document.addEventListener("keydown",this.handleKeyDown.bind(this)))}handleKeyDown(e){"Escape"===e.key&&this.isVisible&&this.hide()}savePreferences(){this.sdk.savePreferences(this.preferences),this.hide()}injectStyles(){if(document.getElementById("consentera-preferences-styles"))return;const e=document.createElement("style");e.id="consentera-preferences-styles",e.textContent=this.getStyles(),document.head.appendChild(e)}getStyles(){const{primaryColor:e,backgroundColor:t,textColor:n,borderRadius:i}=this.theme;return`\n .consentera-overlay {\n position: fixed;\n inset: 0;\n background: rgba(0, 0, 0, 0.5);\n z-index: 999998;\n opacity: 0;\n transition: opacity 0.3s ease;\n }\n\n .consentera-overlay--visible {\n opacity: 1;\n }\n\n .consentera-overlay--closing {\n opacity: 0;\n }\n\n .consentera-preferences {\n position: fixed;\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%) scale(0.95);\n width: 90%;\n max-width: 640px;\n max-height: 85vh;\n background: ${t||"#ffffff"};\n border-radius: ${i||"12px"};\n box-shadow: 0 20px 60px rgba(0, 0, 0, 0.2);\n z-index: 999999;\n display: flex;\n flex-direction: column;\n opacity: 0;\n transition: opacity 0.3s ease, transform 0.3s ease;\n }\n\n .consentera-preferences--visible {\n opacity: 1;\n transform: translate(-50%, -50%) scale(1);\n }\n\n .consentera-preferences--closing {\n opacity: 0;\n transform: translate(-50%, -50%) scale(0.95);\n }\n\n .consentera-preferences__header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 20px 24px;\n border-bottom: 1px solid #e5e7eb;\n }\n\n .consentera-preferences__title {\n margin: 0;\n font-size: 18px;\n font-weight: 600;\n color: ${n||"#1f2937"};\n }\n\n .consentera-preferences__close {\n background: none;\n border: none;\n padding: 4px;\n cursor: pointer;\n color: ${n||"#6b7280"};\n border-radius: 4px;\n }\n\n .consentera-preferences__close:hover {\n background: rgba(0, 0, 0, 0.05);\n }\n\n .consentera-preferences__body {\n flex: 1;\n overflow-y: auto;\n padding: 24px;\n }\n\n .consentera-preferences__description {\n margin: 0 0 24px 0;\n font-size: 14px;\n color: ${n||"#6b7280"};\n line-height: 1.5;\n }\n\n .consentera-preferences__purposes {\n display: flex;\n flex-direction: column;\n gap: 16px;\n }\n\n .consentera-purpose {\n padding: 16px;\n background: #f9fafb;\n border-radius: 8px;\n }\n\n .consentera-purpose__header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n margin-bottom: 8px;\n }\n\n .consentera-purpose__info {\n display: flex;\n align-items: center;\n gap: 8px;\n }\n\n .consentera-purpose__name {\n margin: 0;\n font-size: 15px;\n font-weight: 600;\n color: ${n||"#1f2937"};\n }\n\n .consentera-purpose__required {\n font-size: 11px;\n padding: 2px 6px;\n background: ${e||"#2563eb"};\n color: white;\n border-radius: 4px;\n font-weight: 500;\n }\n\n .consentera-purpose__description {\n margin: 0;\n font-size: 13px;\n color: ${n||"#6b7280"};\n line-height: 1.5;\n }\n\n .consentera-purpose__cookies {\n margin-top: 12px;\n }\n\n .consentera-purpose__cookies summary {\n font-size: 12px;\n color: ${e||"#2563eb"};\n cursor: pointer;\n }\n\n .consentera-cookies-table {\n width: 100%;\n margin-top: 8px;\n font-size: 12px;\n border-collapse: collapse;\n }\n\n .consentera-cookies-table th,\n .consentera-cookies-table td {\n padding: 8px;\n text-align: left;\n border-bottom: 1px solid #e5e7eb;\n }\n\n .consentera-cookies-table th {\n font-weight: 600;\n color: ${n||"#374151"};\n }\n\n .consentera-cookies-table td {\n color: ${n||"#6b7280"};\n }\n\n .consentera-toggle {\n position: relative;\n display: inline-block;\n width: 48px;\n height: 26px;\n }\n\n .consentera-toggle input {\n opacity: 0;\n width: 0;\n height: 0;\n }\n\n .consentera-toggle__slider {\n position: absolute;\n cursor: pointer;\n inset: 0;\n background: #d1d5db;\n border-radius: 26px;\n transition: 0.3s;\n }\n\n .consentera-toggle__slider::before {\n position: absolute;\n content: "";\n height: 20px;\n width: 20px;\n left: 3px;\n bottom: 3px;\n background: white;\n border-radius: 50%;\n transition: 0.3s;\n }\n\n .consentera-toggle input:checked + .consentera-toggle__slider {\n background: ${e||"#2563eb"};\n }\n\n .consentera-toggle input:checked + .consentera-toggle__slider::before {\n transform: translateX(22px);\n }\n\n .consentera-toggle input:disabled + .consentera-toggle__slider {\n opacity: 0.7;\n cursor: not-allowed;\n }\n\n .consentera-preferences__footer {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 16px 24px;\n border-top: 1px solid #e5e7eb;\n gap: 16px;\n }\n\n .consentera-preferences__links {\n display: flex;\n gap: 16px;\n }\n\n .consentera-preferences__links a {\n font-size: 13px;\n color: ${e||"#2563eb"};\n text-decoration: underline;\n }\n\n .consentera-preferences__actions {\n display: flex;\n gap: 8px;\n }\n\n @media (max-width: 640px) {\n .consentera-preferences {\n width: 100%;\n height: 100%;\n max-height: 100%;\n border-radius: 0;\n }\n\n .consentera-preferences__footer {\n flex-direction: column;\n }\n\n .consentera-preferences__actions {\n width: 100%;\n flex-direction: column;\n }\n\n .consentera-btn {\n width: 100%;\n }\n }\n `}destroy(){document.removeEventListener("keydown",this.handleKeyDown.bind(this)),this.container?.remove(),this.overlay?.remove(),this.container=null,this.overlay=null,this.isVisible=!1}}class g{config;tcfData=null;constructor(e){this.config={cmpId:123,cmpVersion:1,publisherCountryCode:"IN",gdprApplies:!0,...e}}async init(){this.createTCFApiStub(),this.tcfData=this.getDefaultTCFData()}createTCFApiStub(){"undefined"!=typeof window&&(window.__tcfapi=(e,t,n,i)=>{"ping"===e?n({gdprApplies:this.config.gdprApplies,cmpLoaded:!0,cmpStatus:"loaded",displayStatus:"visible",apiVersion:"2.2",cmpVersion:this.config.cmpVersion,cmpId:this.config.cmpId,gvlVersion:100,tcfPolicyVersion:4}):"getTCData"===e?n(this.tcfData,!0):"addEventListener"===e?n({...this.tcfData,eventStatus:"tcloaded",listenerId:Math.random()},!0):"removeEventListener"===e&&n(!0)},window.__tcfapi.gdprApplies=this.config.gdprApplies,window.__tcfapi.cmpLoaded=!0)}getDefaultTCFData(){return{cmpId:this.config.cmpId,cmpVersion:this.config.cmpVersion,gdprApplies:this.config.gdprApplies,tcString:"",tcfPolicyVersion:4,isServiceSpecific:!0,useNonStandardStacks:!1,purposeOneTreatment:!1,publisherCC:this.config.publisherCountryCode,vendorConsents:{},vendorLegitimateInterests:{},purposeConsents:{},purposeLegitimateInterests:{},specialFeatureOptins:{},publisherRestrictions:[]}}async updateConsent(e){this.tcfData&&(this.tcfData.purposeConsents={1:e.essential,2:e.advertising,3:e.personalization,4:e.advertising,5:e.personalization,6:e.personalization,7:e.analytics,8:e.analytics,9:e.analytics,10:e.functional},this.tcfData.tcString=this.generateTCString(),this.dispatchTCFEvent("useractioncomplete"))}generateTCString(){const e={version:2,created:Date.now(),lastUpdated:Date.now(),cmpId:this.config.cmpId,cmpVersion:this.config.cmpVersion,purposeConsents:this.tcfData?.purposeConsents||{}};return btoa(JSON.stringify(e))}dispatchTCFEvent(e){if("undefined"==typeof window)return;const t=new CustomEvent("tcfapi",{detail:{...this.tcfData,eventStatus:e}});window.dispatchEvent(t)}getTCFData(){return this.tcfData}getTCString(){return this.tcfData?.tcString||""}destroy(){"undefined"!=typeof window&&delete window.__tcfapi,this.tcfData=null}}class f{config;gppData=null;constructor(e){this.config={sections:["tcfeuv2","uspv1"],...e}}async init(){this.createGPPApiStub(),this.gppData=this.getDefaultGPPData()}createGPPApiStub(){if("undefined"==typeof window)return;const e=[],t="loaded",n="visible";window.__gpp=(i,s,r)=>{switch(i){case"ping":s({gppVersion:"1.1",cmpStatus:t,cmpDisplayStatus:n,supportedAPIs:this.config.sections,cmpId:123,sectionList:this.gppData?.sectionIds||[],applicableSections:this.gppData?.applicableSections||[],gppString:this.gppData?.gppString||""});break;case"getSection":this.gppData&&s({sectionId:r,sectionData:this.getSectionData(r)});break;case"getField":s(this.getFieldValue(r));break;case"hasSection":s(this.gppData?.sectionIds.includes(r)||!1);break;case"addEventListener":e.push({callback:s,listenerId:Math.random()}),s({eventName:"listenerRegistered",listenerId:e.length-1,data:this.gppData?.gppString||"",pingData:{gppVersion:"1.1",cmpStatus:t,cmpDisplayStatus:n}});break;case"removeEventListener":{const t=e.findIndex(e=>e.listenerId===r);t>-1?(e.splice(t,1),s(!0)):s(!1);break}default:s(null)}},window.__gpp.version="1.1"}getDefaultGPPData(){return{gppString:"",sectionIds:[],applicableSections:[]}}getSectionData(e){switch(e){case 2:return{Version:2,Created:Math.floor(Date.now()/100),LastUpdated:Math.floor(Date.now()/100),CmpId:123,CmpVersion:1,ConsentScreen:1,ConsentLanguage:"EN"};case 6:return{Version:1,Notice:"Y",OptOutSale:"N",LspaCovered:"Y"};default:return null}}getFieldValue(e){return null}async updateConsent(e){this.gppData&&(this.gppData.gppString=this.buildGPPString(e),this.gppData.sectionIds=this.determineSections(e),this.gppData.applicableSections=this.gppData.sectionIds,this.dispatchGPPEvent("signalStatus"))}buildGPPString(e){const t=[];return this.config.sections?.includes("tcfeuv2")&&t.push({id:2,purposes:{1:e.essential,2:e.advertising,3:e.personalization,4:e.advertising,7:e.analytics,10:e.functional}}),this.config.sections?.includes("uspv1")&&t.push({id:6,notice:!0,optOut:!e.thirdParty,lspaCovered:!0}),this.encodeGPPString(t)}encodeGPPString(e){const t={version:1,sectionIds:e.map(e=>e.id)};return btoa(JSON.stringify({header:t,sections:e}))}determineSections(e){const t=[];return this.config.sections?.includes("tcfeuv2")&&t.push(2),this.config.sections?.includes("uspv1")&&t.push(6),t}dispatchGPPEvent(e){if("undefined"==typeof window)return;const t=new CustomEvent("gpp",{detail:{eventName:e,data:this.gppData?.gppString||"",pingData:{gppVersion:"1.1",cmpStatus:"loaded",applicableSections:this.gppData?.applicableSections||[]}}});window.dispatchEvent(t)}getGPPData(){return this.gppData}getGPPString(){return this.gppData?.gppString||""}destroy(){"undefined"!=typeof window&&delete window.__gpp,this.gppData=null}}class y{events=new Map;on(e,t){this.events.has(e)||this.events.set(e,new Set),this.events.get(e).add(t)}off(e,t){const n=this.events.get(e);n&&n.delete(t)}emit(e,...t){const n=this.events.get(e);n&&n.forEach(e=>{try{e(...t)}catch(e){}})}once(e,t){const n=(...i)=>{this.off(e,n),t(...i)};this.on(e,n)}removeAllListeners(e){e?this.events.delete(e):this.events.clear()}}class _{level;prefix="[ConsentEra]";constructor(e="info"){this.level=e}shouldLog(e){const t=["debug","info","warn","error"];return t.indexOf(e)>=t.indexOf(this.level)}debug(...e){this.shouldLog("debug")}info(...e){this.shouldLog("info")}warn(...e){this.shouldLog("warn")}error(...e){this.shouldLog("error")}setLevel(e){this.level=e}}function m(){return`consent_${Date.now().toString(36)}_${Math.random().toString(36).substring(2,15)}`}function b(){const e=navigator.userAgent.toLowerCase();let t,n,i="desktop";return/mobile|android|iphone|ipod|blackberry|iemobile|opera mini/i.test(e)?i="mobile":/tablet|ipad|playbook|silk/i.test(e)&&(i="tablet"),/windows/i.test(e)?t="Windows":/macintosh|mac os x/i.test(e)?t="macOS":/linux/i.test(e)?t="Linux":/android/i.test(e)?t="Android":/iphone|ipad|ipod/i.test(e)&&(t="iOS"),/chrome/i.test(e)&&!/edge|edg/i.test(e)?n="Chrome":/firefox/i.test(e)?n="Firefox":/safari/i.test(e)&&!/chrome/i.test(e)?n="Safari":/edge|edg/i.test(e)?n="Edge":/msie|trident/i.test(e)&&(n="IE"),{type:i,os:t,browser:n}}const w={VALIDATION_ERROR:"validation",INVALID_REQUEST_BODY:"validation",INVALID_JSON:"validation",INVALID_UUID:"validation",BAD_REQUEST:"validation",MISSING_REQUIRED_FIELD:"validation",MISSING_TENANT_ID:"validation",INVALID_TENANT_ID:"validation",INVALID_DATE_OF_BIRTH:"validation",INVALID_IDENTIFIER_FORMAT:"validation",DATA_PRINCIPAL_REF_REFUSED:"identity",UNKNOWN_IDENTIFIER_FIELD:"identity",IDENTIFIER_REQUIRED:"identity",IDENTITY_MISMATCH:"identity",GUARDIAN_REQUIRED:"guardian",GUARDIAN_CONSENT_REQUIRED:"guardian",GUARDIAN_TOKEN_REQUIRED:"guardian",GUARDIAN_EVIDENCE_MISSING:"guardian",GUARDIAN_IDENTITY_NOT_VERIFIED:"guardian",GUARDIAN_ADULT_NOT_VERIFIED:"guardian",GUARDIAN_VERIFICATION_METHOD_INVALID:"guardian",AGE_REQUIRED:"guardian",UNAUTHORIZED:"auth",INVALID_CREDENTIALS:"auth",TOKEN_MISSING:"auth",TOKEN_INVALID:"auth",TOKEN_EXPIRED:"auth",TOKEN_MALFORMED:"auth",TOKEN_REVOKED:"auth",SESSION_NOT_FOUND:"auth",SESSION_EXPIRED:"auth",SESSION_REVOKED:"auth",FORBIDDEN:"permission",TENANT_MISMATCH:"permission",TENANT_SUSPENDED:"permission",ACCOUNT_DISABLED:"permission",PRIVILEGE_ESCALATION:"permission",SCHEME_NOT_CONFIGURED:"permission",CSRF_TOKEN_MISSING:"permission",CSRF_TOKEN_INVALID:"permission",NOT_FOUND:"not_found",USER_NOT_FOUND:"not_found",CONFLICT:"conflict",IDEMPOTENCY_KEY_REUSE:"conflict",RATE_LIMIT_EXCEEDED:"rate_limit",PLAN_LIMIT_REACHED:"plan_limit",ARTEFACT_NOT_ACCEPTED_PLAN_LIMIT:"plan_limit",INTERNAL_ERROR:"server",INTERNAL_SERVER_ERROR:"server",DATABASE_ERROR:"server"};function v(e,t=Date.now()){if(!e)return;const n=e.trim();if(/^\d+$/.test(n)){const e=Number(n);return Number.isFinite(e)?Math.max(0,1e3*e):void 0}const i=Date.parse(n);return Number.isNaN(i)?void 0:Math.max(0,i-t)}class C extends Error{kind;code;status;requestId;responseBody;retryable;retryAfterMs;constructor(e){super(e.message,void 0===e.cause?void 0:{cause:e.cause}),this.name=new.target.name,this.kind=e.kind,this.code=e.code,this.status=e.status??0,this.requestId=e.requestId,this.responseBody=e.responseBody,this.retryable=e.retryable??!1,this.retryAfterMs=e.retryAfterMs,Object.setPrototypeOf(this,new.target.prototype)}get statusCode(){return this.status}toString(){const e=[this.name,this.code??this.kind];return this.status&&e.push(String(this.status)),this.requestId&&e.push(`req=${this.requestId}`),`${e.join(" ")}: ${this.message}`}}class k extends C{}class E extends C{}class x extends C{}class S extends C{}class I extends C{}class A extends C{}class D extends C{}class T extends C{}class P extends C{}class R extends C{}class $ extends C{}class N extends C{}class L extends C{reasonCode;limitKey;details;recorded=!1;constructor(e){super({...e,retryable:!1});const t=e.details;this.details=t,this.reasonCode="string"==typeof t?.reason_code?t.reason_code:void 0,this.limitKey="string"==typeof t?.limit_key?t.limit_key:void 0}}const M={config:k,auth:E,permission:x,validation:S,identity:I,guardian:A,not_found:D,conflict:T,rate_limit:P,plan_limit:L,server:R,network:$,timeout:N,cancelled:C,unknown:C};function O(e){const{code:t,message:n,details:i}=function(e){if(!e||"object"!=typeof e)return{};const t=e,n=t.error&&"object"==typeof t.error?t.error:t;return{code:"string"==typeof n.code?n.code:void 0,message:"string"==typeof n.message?n.message:void 0,details:n.details&&"object"==typeof n.details&&!Array.isArray(n.details)?n.details:void 0}}(e.body),s=t&&w[t]||(401===(r=e.status)?"auth":403===r?"permission":404===r?"not_found":409===r?"conflict":429===r?"rate_limit":r>=500?"server":r>=400?"validation":"unknown");var r;const o="plan_limit"!==s&&(429===e.status||e.status>=500);return a={kind:s,code:t,details:i,status:e.status,requestId:e.requestId,responseBody:e.body,retryable:o,retryAfterMs:e.retryAfterMs,message:n||`${e.method} ${e.path} failed: ${e.status}${e.statusText?` ${e.statusText}`:""}`},new M[a.kind](a);var a}function U(e,t){return new $({kind:"network",message:e,status:0,retryable:!0,cause:t})}function K(e,t){return new N({kind:"timeout",message:e,status:0,retryable:!0,cause:t})}function q(e,t){return new C({kind:"cancelled",message:e,status:0,retryable:!1,cause:t})}function V(e,t){return new k({kind:"config",message:e,status:0,code:t,retryable:!1})}const B=C,F="2.1.0",j=`js/${F}`;function G(e){const t="undefined"!=typeof process&&process.versions?.node?`node ${process.versions.node}`:"unknown";return`ConsenteraSDK/${F} (web; ${t})`}const z={attempts:3,baseDelayMs:250,maxDelayMs:4e3};function H(){return"undefined"!=typeof window}function W(e){return!!e&&(e.startsWith("tiq_live_")||e.startsWith("tiq_test_"))}function Y(e){return!!e&&e.startsWith("tiq_pub_")}function J(e){if(H()&&e.apiKey&&!e.unsafeAllowSecretKeyInBrowser)throw V("Consentera: `apiKey` is a SECRET and this SDK is running in a browser, where every visitor can read the bundle. "+e.fix+" "+(W(e.apiKey)?`The key supplied starts with "${e.apiKey.slice(0,9)}" — rotate it, it is now in a bundle. `:"")+"If you are certain this code never reaches a browser (a test harness with a jsdom window, a server-side render that only looks like one), set `unsafeAllowSecretKeyInBrowser: true` and every request will warn.","SECRET_KEY_IN_BROWSER")}function X(){const e="undefined"!=typeof globalThis?globalThis.crypto:void 0;if(e&&"function"==typeof e.randomUUID)return e.randomUUID();if(e&&"function"==typeof e.getRandomValues){const t=e.getRandomValues(new Uint8Array(16));return Array.from(t,e=>e.toString(16).padStart(2,"0")).join("")}return`nc-${Date.now().toString(36)}-${Math.random().toString(36).slice(2,10)}`}function Q(e,t){return new Promise((n,i)=>{if(t?.aborted)return void i(q("request cancelled"));const s=setTimeout(()=>{t?.removeEventListener("abort",r),n()},e),r=()=>{clearTimeout(s),i(q("request cancelled"))};t?.addEventListener("abort",r,{once:!0})})}function Z(e,t,n=Math.random){const i=Math.min(t.maxDelayMs,t.baseDelayMs*2**e);return Math.floor(n()*i)}function ee(e){return"string"==typeof e?e.trim().replace(/\/+$/,""):e}function te(e){const t={...e};return void 0!==t.apiEndpoint&&(t.apiEndpoint=ee(t.apiEndpoint)),void 0!==t.proxyEndpoint&&(t.proxyEndpoint=ee(t.proxyEndpoint)),t}class ne{config;logger;constructor(e,t){this.config=te(e),this.logger=t}updateConfig(e){this.config={...this.config,...te(e)}}authHeadersFor(e,t){const{apiKey:n,siteKey:i,unsafeAllowSecretKeyInBrowser:s}=this.config,r=H();if(t)return{};if("session"===e)return{};if("public"===e)return i?{"X-API-Key":i}:{};if(r&&!s)throw V("Consentera: this call needs a Data Fiduciary credential, which is a SECRET and must never be in a browser bundle. Set `proxyEndpoint` to your own server route (it holds the tiq_live_/tiq_test_ key and forwards the call), or run this call server-side. A public site key (tiq_pub_) cannot authorise it: the lifecycle roads are server-to-server. A site key opens only the public read roads and the session-scoped render/submit, and only for the origins in its allowed_domains.","SECRET_KEY_IN_BROWSER");if(n)return r&&s&&this.logger.warn("Consentera: SENDING A SECRET KEY FROM A BROWSER because unsafeAllowSecretKeyInBrowser is set. Every visitor to this page can read it. Rotate the key and move to proxyEndpoint."),{"X-API-Key":n};if(Y(i))throw V("Consentera: a site key (tiq_pub_) was supplied for a Data Fiduciary lifecycle road. Those roads are server-to-server; a site key opens only the public read roads and the session-scoped render/submit, so this call would be refused 403. Use `proxyEndpoint`, or a secret key server-side.","SITE_KEY_ON_DF_ROAD");throw V("Consentera: no credential for a Data Fiduciary road. Set `proxyEndpoint` (browser) or `apiKey` (server).","NO_CREDENTIAL")}urlFor(e,t){const{proxyEndpoint:n,apiEndpoint:i}=this.config,s=!!n,r=s?n:i,o=this.config.apiPathPrefix??"/api/v1/public";if("/api/v1/public"!==o&&"/api/v1/cookie-consent"!==o)throw V("Consentera: unsupported transport API plane.");let a=s?`${r}${e}`:`${r}${o}${e}`;if(t){const e=new URLSearchParams;for(const[n,i]of Object.entries(t))null!=i&&e.set(n,String(i));const n=e.toString();n&&(a+=`?${n}`)}return{url:a,viaProxy:s}}async request(e,t,n,i={}){const s=i.road??"df",{url:r,viaProxy:o}=this.urlFor(t,i.query),a={...z,...this.config.retry,...i.retry},c=i.timeoutMs??this.config.timeoutMs??1e4,d=this.config.fetchImpl??globalThis.fetch;if("function"!=typeof d)throw V("Consentera: no fetch implementation available in this environment.");const l={Accept:"application/json","X-Consentera-SDK":j,...H()?{}:{"User-Agent":G()},...this.authHeadersFor(s,o)};!o&&"session"!==s&&this.config.tenantId&&(l["X-Tenant-Id"]=this.config.tenantId),void 0!==n&&(l["Content-Type"]="application/json"),"GET"!==e&&(l["Idempotency-Key"]=i.idempotencyKey??X());const u="function"==typeof this.config.customHeaders?this.config.customHeaders():this.config.customHeaders;Object.assign(l,u,i.headers);let p=n;if(void 0!==n&&this.config.beforeSend){const i=this.config.beforeSend({method:e,path:t,body:n});if(null==i)throw V(`Consentera: beforeSend refused ${e} ${t}. Nothing was sent.`,"BEFORE_SEND_REFUSED");p=i}const h=void 0===p?void 0:JSON.stringify(p);let g;for(let n=0;n<a.attempts;n++){if(i.signal?.aborted)throw q("request cancelled before attempt");const s=Date.now();let o=!1;const u=new AbortController,p=setTimeout(()=>{o=!0,u.abort()},c),f=()=>u.abort();i.signal?.addEventListener("abort",f,{once:!0});try{const n=await d(r,{method:e,headers:l,body:h,signal:u.signal}),o=Date.now()-s,a=n.headers?.get?.("X-Request-Id")??void 0;if(n.ok){this.logger.debug(`${e} ${t} -> ${n.status} (${o}ms)`,{requestId:a});const s=e=>i.raw?{status:n.status,body:e,requestId:a,retryAfterMs:v(n.headers?.get?.("Retry-After"))}:e;if(204===n.status)return s(void 0);const r=await n.text();if(!r)return s(void 0);try{return s(JSON.parse(r))}catch{throw O({status:n.status,body:r,requestId:a,method:e,path:t,statusText:"response body is not JSON"})}}const c=await n.text();let p=c;try{p=JSON.parse(c)}catch{}const f=v(n.headers?.get?.("Retry-After"));g=O({status:n.status,statusText:n.statusText,body:p,requestId:a,retryAfterMs:f,method:e,path:t}),this.logger.debug(`${e} ${t} -> ${n.status} ${g.code??""} (${o}ms)`,{requestId:a})}catch(n){if(n instanceof C){if(!n.retryable)throw n;g=n}else if(o)g=K(`${e} ${t} timed out after ${c}ms`,n);else{if(i.signal?.aborted)throw q(`${e} ${t} cancelled`,n);g=U(`${e} ${t}: ${n?.message??"network error"}`,n)}}finally{clearTimeout(p),i.signal?.removeEventListener("abort",f)}if(!g.retryable||n===a.attempts-1)throw g;if(void 0!==a.maxRetryAfterMs&&void 0!==g.retryAfterMs&&g.retryAfterMs>a.maxRetryAfterMs)throw g;const y=g.retryAfterMs??Z(n,a);this.logger.debug(`${e} ${t} retry ${n+1}/${a.attempts-1} in ${y}ms (${g.code??g.kind})`),await Q(y,i.signal)}throw g??U(`${e} ${t} failed`)}}class ie extends y{config;storage;banner=null;preferenceCenter=null;tcfManager=null;gppManager=null;logger;initialized=!1;initPromise=null;destroyed=!1;remoteConfig=null;remotePolicyVersion="";ageGateEnabled=!1;ageGatePrompt;pendingIsAdult=null;deviceId;constructor(e){if(super(),J({apiKey:e.apiKey,unsafeAllowSecretKeyInBrowser:e.unsafeAllowSecretKeyInBrowser,fix:'The cookie-consent plane needs no secret at all — use a public site key (`siteKey: "tiq_pub_…"`), which is what it reads. Keep the tiq_live_/tiq_test_ key on your own server.'}),"string"!=typeof e.apiEndpoint||""===e.apiEndpoint.trim())throw V("Consentera: `apiEndpoint` is required — there is no default server. Set it to your Consentera API origin (the SDK calls `${apiEndpoint}/api/v1/cookie-consent/*`); with the script-tag build, set `data-api-endpoint`.","ENDPOINT_REQUIRED");this.config=this.mergeDefaults({...e,apiEndpoint:ee(e.apiEndpoint)}),this.logger=new _(e.apiKey?"info":"debug"),this.storage=new a(this.config.storage),this.deviceId=this.getOrCreateDeviceId(),this.config.autoShow&&this.init().then(()=>{this.hasConsent()||this.showBanner()}).catch(e=>{this.logger.error("Consentera: auto-init failed; no banner will be shown.",e),this.emit("error",e),this.config.callbacks?.onError?.(e)})}mergeDefaults(e){return{autoShow:!0,language:"en",region:"IN",regulations:["DPDP"],theme:{position:"bottom",type:"banner",primaryColor:"#2563eb",backgroundColor:"#ffffff",textColor:"#1f2937",borderRadius:"8px",showLogo:!0},storage:{type:"cookie",cookieName:"consentera_consent",cookieExpiry:365,cookiePath:"/",sameSite:"Lax"},...e}}getOrCreateDeviceId(){const e="consentera_device_id";try{let t=window.localStorage.getItem(e);return t||(t=m(),window.localStorage.setItem(e,t)),t}catch{return m()}}setAgeDeclaration(e){this.pendingIsAdult=e}isAgeGateEnabled(){return this.ageGateEnabled}getAgeGatePrompt(){return this.ageGatePrompt}init(){return this.initialized?(this.logger.warn("ConsentEra Consent SDK already initialized"),Promise.resolve()):(this.initPromise||(this.initPromise=this.doInit().finally(()=>{this.initPromise=null})),this.initPromise)}async doInit(){try{this.logger.info("Initializing ConsentEra Consent SDK...");const e=await this.fetchConfig();if(this.destroyed)return;if(this.remoteConfig=e,this.config.tcf?.enabled&&(this.tcfManager=new g(this.config.tcf),await this.tcfManager.init()),this.config.gpp?.enabled&&(this.gppManager=new f(this.config.gpp),await this.gppManager.init()),this.destroyed)return;this.banner=new l(this.remoteConfig,this.config.theme,this),this.preferenceCenter=new h(this.remoteConfig,this.config.theme,this),this.initialized=!0,this.emit("ready"),this.config.callbacks?.onReady?.(),this.logger.info("ConsentEra Consent SDK initialized successfully")}catch(e){throw this.logger.error("Failed to initialize SDK:",e),this.config.callbacks?.onError?.(e),e}}authHeaders(){const e={"X-Tenant-Id":this.config.tenantId},t=this.config.siteKey||this.config.apiKey;return t&&(H()&&!this.config.siteKey&&W(this.config.apiKey)&&this.logger.warn("Consentera: SENDING A SECRET KEY FROM A BROWSER because unsafeAllowSecretKeyInBrowser is set. Every visitor to this page can read it. The cookie-consent plane needs only a public site key — rotate this one."),e["X-API-Key"]=t),e}async fetchConfig(){try{const e=await fetch(`${this.config.apiEndpoint}/api/v1/cookie-consent/config`,{headers:{...this.authHeaders(),"Accept-Language":this.config.language||"en"}});if(!e.ok)throw new Error(`Failed to fetch config: ${e.statusText}`);const t=await e.json();return this.remotePolicyVersion=t.policy_version,this.ageGateEnabled=t.age_gate_enabled,this.ageGatePrompt=t.age_gate_prompt,function(e,t){const n=[...e.categories].sort((e,t)=>e.sort_order-t.sort_order).map(e=>({id:e.key,name:e.name,description:e.description,required:e.mandatory,defaultEnabled:e.mandatory,category:se(e.key)})),i=e.banner?.messaging||{},s={title:i.title,description:i.description,acceptAllText:i.accept_text,rejectAllText:i.reject_text,customizeText:i.customize_text,saveText:i.save_text,showRejectAll:e.ui_options?.reject_all??!0,showCustomize:e.ui_options?.customize??!0,showPrivacyPolicy:!0,purposes:n};return{tenantId:e.tenant_id,banner:s,purposes:n,theme:t.theme,regulations:t.regulations||["DPDP"],languages:[e.language_code]}}(t,this.config)}catch(e){throw this.logger.error("Consentera: the tenant consent configuration could not be loaded.",e),e instanceof Error?e:new Error(String(e))}}showBanner(){this.initialized?(this.banner?.show(),this.emit("banner_shown"),this.config.callbacks?.onBannerShown?.()):this.logger.warn("SDK not initialized. Call init() first.")}hideBanner(){this.banner?.hide(),this.emit("banner_closed"),this.config.callbacks?.onBannerClosed?.()}showPreferences(){this.initialized?(this.preferenceCenter?.show(),this.emit("preferences_opened")):this.logger.warn("SDK not initialized. Call init() first.")}hidePreferences(){this.preferenceCenter?.hide()}async acceptAll(){return this.emit("accept_all"),this.saveConsent({essential:!0,functional:!0,analytics:!0,marketing:!0,personalization:!0,advertising:!0,thirdParty:!0})}async rejectAll(){return this.emit("reject_all"),this.saveConsent({essential:!0,functional:!1,analytics:!1,marketing:!1,personalization:!1,advertising:!1,thirdParty:!1})}async savePreferences(e){return this.emit("preferences_saved",e),this.saveConsent(e)}async saveConsent(e){try{const t=this.buildSelections(e),n=(await this.syncConsent(t)).effective_selections??t,i=this.buildConsentData(n);return this.storage.save(i),this.tcfManager&&await this.tcfManager.updateConsent(this.selectionsToPreferences(n)),this.gppManager&&await this.gppManager.updateConsent(this.selectionsToPreferences(n)),this.hideBanner(),this.hidePreferences(),this.emit("consent_given",i),this.config.callbacks?.onConsentGiven?.(i),this.config.callbacks?.onPreferencesUpdated?.(this.selectionsToPreferences(n)),{success:!0,consentId:i.consentId}}catch(e){return this.logger.error("Failed to save consent:",e),{success:!1,error:e.message}}}buildSelections(e){const t={};for(const n of this.remoteConfig?.purposes||[])t[n.id]=n.required||!!e[n.id];return t}selectionsToPreferences(e){const t={essential:!0,functional:!1,analytics:!1,marketing:!1,personalization:!1,advertising:!1,thirdParty:!1};for(const[n,i]of Object.entries(e))t[n]=i;return t}buildConsentData(e){const t=(this.remoteConfig?.purposes||[]).map(t=>({id:t.id,name:t.name,description:t.description,category:t.category,required:t.required,consented:t.required||!!e[t.id],legalBasis:t.required?"legitimate_interest":"consent"}));return{consentId:m(),timestamp:(new Date).toISOString(),version:this.remotePolicyVersion||"1.0",purposes:t,legalBasis:"consent",regulation:this.config.regulations?.[0]||"DPDP",metadata:{userAgent:navigator.userAgent,pageUrl:window.location.href,referrer:document.referrer,device:b()}}}async syncConsent(e){try{const t={device_id:this.deviceId,policy_version:this.remotePolicyVersion,selections:e,affirmative_action:{type:"button",ui_event_id:m(),captured_at:(new Date).toISOString()}};this.ageGateEnabled&&null!==this.pendingIsAdult&&(t.age_declaration={is_adult:this.pendingIsAdult});const n=new ne({apiEndpoint:this.config.apiEndpoint,apiPathPrefix:"/api/v1/cookie-consent",retry:{maxRetryAfterMs:5e3}},this.logger);return await n.request("POST","/preferences",t,{road:"public",headers:this.authHeaders()})}catch(e){throw this.logger.error("Consentera: consent sync failed — the decision was not recorded.",{message:e?.message}),this.emit("sync_failed",e),this.config.callbacks?.onSyncFailed?.(e),e}}getConsent(){return this.storage.get()}hasConsent(){return this.storage.exists()}async withdrawConsent(e){try{const t=this.getConsent();if(!t)return{success:!1,error:"No consent found"};const n={};for(const i of this.remoteConfig?.purposes||[]){if(i.required){n[i.id]=!0;continue}if(!e||e.includes(i.id))n[i.id]=!1;else{const e=t.purposes.find(e=>e.id===i.id)?.consented??!1;n[i.id]=e}}const i=(await this.syncConsent(n)).effective_selections??n,s=this.buildConsentData(i);return this.storage.save(s),this.tcfManager&&await this.tcfManager.updateConsent(this.selectionsToPreferences(i)),this.gppManager&&await this.gppManager.updateConsent(this.selectionsToPreferences(i)),this.emit("consent_withdrawn",e||[]),this.config.callbacks?.onConsentWithdrawn?.(e||[]),{success:!0,consentId:s.consentId}}catch(e){return{success:!1,error:e.message}}}isConsentGiven(e){const t=this.getConsent();if(!t)return!1;const n=t.purposes.find(t=>t.id===e);return n?.consented||!1}getPreferences(){const e=this.getConsent();if(!e)return{essential:!0,functional:!1,analytics:!1,marketing:!1,personalization:!1,advertising:!1,thirdParty:!1};const t={essential:!0};return e.purposes.forEach(e=>{t[e.id]=e.consented}),t}setLanguage(e){this.config.language=e}destroy(){this.destroyed=!0,this.banner?.destroy(),this.preferenceCenter?.destroy(),this.tcfManager?.destroy(),this.gppManager?.destroy(),this.removeAllListeners(),this.initialized=!1}}function se(e){const t=e.toLowerCase();return t.includes("essential")||t.includes("necess")||t.includes("strict")||t.includes("security")?"essential":t.includes("analyt")?"analytics":t.includes("market")||t.includes("advertis")?"marketing":t.includes("function")||t.includes("prefer")?"functional":t.includes("personal")?"personalization":"third_party"}function re(e){return`/consent/artifacts/${encodeURIComponent(e)}`}async function oe(e,t,n={}){const i=await e("GET",re(t),void 0,{session_id:n.sessionId||void 0},{raw:!0,signal:n.signal,timeoutMs:n.timeoutMs});if(200===i.status&&i.body&&"object"==typeof i.body)return{state:"recorded",artifact:i.body,requestId:i.requestId};if(202===i.status&&i.body&&"object"==typeof i.body){const e=i.body,t="number"==typeof e.retry_after&&e.retry_after>=0?1e3*e.retry_after:void 0;return{state:"pending",pending:e,retryAfterMs:i.retryAfterMs??t??1e3,requestId:i.requestId}}throw O({status:i.status,statusText:"not an artifact read answer (expected 200 or 202 with a JSON body)",body:i.body,requestId:i.requestId,method:"GET",path:re(t)})}const ae=new Set(["consentera:submitted","consentera:declined"]),ce=new Set(["granted","partial","denied"]);function de(e,t){if(!t.origin||e.origin!==t.origin)return null;if(void 0!==t.source&&e.source!==t.source)return null;const n=e.data;if(!n||"object"!=typeof n||"string"!=typeof n.type||!ae.has(n.type))return null;const i="string"==typeof n.session_id&&n.session_id?n.session_id:void 0,s="string"==typeof n.sessionId&&n.sessionId?n.sessionId:void 0;if(i&&s&&i!==s)return null;const r=i??s;if(!r||r!==t.sessionId)return null;const o="string"==typeof n.redirectUrl&&n.redirectUrl?n.redirectUrl:void 0,a=function(e){if("string"!=typeof e||!e)return null;try{return new URL(e,"https://collect.invalid").searchParams}catch{return null}}(o),c=n.choices&&"object"==typeof n.choices?n.choices:void 0,d=n.type,l="string"==typeof n.status&&ce.has(n.status)?n.status:function(e,t){const n=e?.get("status");if(n&&ce.has(n))return n;const i=Object.values(t??{}),s=i.filter(Boolean).length;return 0===s?"denied":s<i.length?"partial":"granted"}(a,"consentera:declined"===d?void 0:c);return{outcome:"decided",type:d,session_id:r,artifact_id:"string"==typeof n.artifact_id&&n.artifact_id?n.artifact_id:a?.get("artifact_id")??"",status:l,pending:!0===n.pending||"1"===a?.get("pending"),...o?{redirect_url:o}:{},...c?{choices:c}:{}}}function le(e,t={}){if("undefined"==typeof window||"undefined"==typeof document)throw new Error("openConsentPopup can only be used in browser environments");const n=function(e,t){if(!e)return null;let n;try{n=new URL(e,t)}catch{return`the session's callback_url (${e}) is not a URL`}return"https:"!==n.protocol&&"http:"!==n.protocol?`the session's callback_url is an app link (${n.protocol}//…). /collect then posts its decision to its own origin, which this page is not, so the popup would never hear it. Use redirectToConsent, or create the session with a callback_url on this page’s origin.`:n.origin!==t?`/collect posts the decision to the callback's origin (${n.origin}), and this page is ${t}. The browser drops a message addressed to another origin, so the popup would never hear it. Create the session with a callback_url on this page’s origin.`:null}(t.callbackUrl,window.location.origin);if(n)return Promise.reject(V(`Consentera: ${n}`,"CALLBACK_ORIGIN_MISMATCH"));let i;try{i=new URL(e.consent_url,window.location.href).origin}catch{return Promise.reject(V("Consentera: consent_url is not a URL.","CONSENT_URL_INVALID"))}const s=t.timeoutMs??6e5,r=t.title??"Your consent choices",o=t.container??document.body;return new Promise((t,n)=>{const a=document.activeElement,c=document.documentElement.style.overflow,d=document.createElement("div");d.dataset.consenteraPopup="true",d.style.cssText="position:fixed;inset:0;z-index:2147483000;display:flex;align-items:center;justify-content:center;padding:12px;background:rgba(15,23,42,.6)";const l=document.createElement("div");l.setAttribute("role","dialog"),l.setAttribute("aria-modal","true"),l.setAttribute("aria-label",r),l.style.cssText="display:flex;flex-direction:column;width:100%;max-width:680px;height:min(780px,100%);background:#fff;border-radius:14px;overflow:hidden;box-shadow:0 24px 64px rgba(0,0,0,.35)";const u=document.createElement("div");u.style.cssText="display:flex;justify-content:flex-end;padding:8px;border-bottom:1px solid #e5e7eb";const p=document.createElement("button");p.type="button",p.textContent="Close",p.setAttribute("aria-label",`Close — ${r}`),p.style.cssText="font:inherit;font-size:14px;padding:8px 14px;min-height:40px;border:1px solid #d1d5db;border-radius:8px;background:#fff;color:#111827;cursor:pointer";const h=document.createElement("iframe");h.title=r,h.src=e.consent_url,h.style.cssText="flex:1;width:100%;border:0;background:#fff",u.append(p),l.append(u,h),d.append(l),o.append(d),document.documentElement.style.overflow="hidden",p.focus();let g=!1;const f=e=>{g||(g=!0,clearTimeout(b),window.removeEventListener("message",_),document.removeEventListener("keydown",m,!0),d.remove(),document.documentElement.style.overflow=c,a&&"function"==typeof a.focus&&a.focus(),e())},y=()=>f(()=>t({outcome:"dismissed",session_id:e.consent_session_id})),_=n=>{const s=de(n,{origin:i,sessionId:e.consent_session_id,source:h.contentWindow});s&&f(()=>t(s))},m=e=>{"Escape"===e.key&&(e.preventDefault(),y())},b=setTimeout(()=>f(()=>n(new Error(`Consentera: the consent dialog had no decision within ${s}ms. The session is still valid — verify it server-side, or reopen it.`))),s);p.addEventListener("click",y),window.addEventListener("message",_),document.addEventListener("keydown",m,!0)})}class ue{request;config;logger;callbackUrls=new Map;constructor(e,t,n){this.request=e,this.config=t,this.logger=n}async createSession(e,t){const i=e.callback_url||this.config.callbackUrl,s=i?function(){const e="undefined"!=typeof globalThis?globalThis.crypto:void 0;if(!e||"function"!=typeof e.getRandomValues)throw V("Consentera: no crypto.getRandomValues here, so no unguessable callback state can be made. This SDK will not fall back to Math.random for it.","NO_SECURE_RANDOM");return Array.from(e.getRandomValues(new Uint8Array(32)),e=>e.toString(16).padStart(2,"0")).join("")}():void 0,o={ui_mode:e.ui_mode||"redirect",callback_url:i&&s?pe(i,s):void 0};e.data_principal&&Object.keys(e.data_principal).length>0&&(o.data_principal=e.data_principal),e.data_principal_id&&(o.data_principal_id=e.data_principal_id),e.age&&e.age.date_of_birth&&(o.age={date_of_birth:e.age.date_of_birth});const a=e.language||this.config.language;a&&(o.language=a),e.session_ref&&(o.session_ref=e.session_ref),e.notice_internal_name&&(o.notice_internal_name=e.notice_internal_name),void 0!==e.notice_version_number&&(o.notice_version_number=e.notice_version_number),e.customer_token&&(o.customer_token=e.customer_token),e.guardian_email&&(o.guardian_email=e.guardian_email),e.guardian_phone&&(o.guardian_phone=e.guardian_phone),e.guardian_relationship&&(o.guardian_relationship=e.guardian_relationship);const c=await this.request("POST","/consent/sessions",o,void 0,{idempotencyKey:t?.idempotencyKey??X(),signal:t?.signal,timeoutMs:t?.timeoutMs});return r("session",n.session(c.consent_session_id),JSON.stringify({session_id:c.consent_session_id,...s?{state:s}:{},notice_hash:c.notice_hash,data_principal_id:c.data_principal_id,created_at:(new Date).toISOString()}))||this.logger.warn('Consentera: browser storage is unavailable, so the callback handshake cannot survive a page navigation. Use ui_mode "popup", or verify the artifact server-side.'),"string"==typeof o.callback_url&&o.callback_url&&this.callbackUrls.set(c.consent_session_id,o.callback_url),this.logger.info("Consent session created",{session_id:c.consent_session_id}),c}redirectToConsent(e){if("undefined"==typeof window)throw new Error("redirectToConsent can only be used in browser environments");window.location.href=e.consent_url}openConsentPopup(e,t){return le(e,{...t,callbackUrl:t?.callbackUrl??this.callbackUrls.get(e.consent_session_id)??this.config.callbackUrl})}async getArtifact(e,t){return oe(this.request,e,t)}}function pe(e,t){let n;try{n=new URL(e)}catch{throw V(`Consentera: callback_url must be an absolute URL (the platform refuses anything else); got ${JSON.stringify(e.slice(0,80))}`,"INVALID_CALLBACK_URL")}if(n.searchParams.has("state"))throw V("Consentera: callback_url already carries a `state` parameter. This SDK puts its own callback state there to bind the return to this browser; use a different parameter name for yours.","CALLBACK_STATE_RESERVED");return n.searchParams.set("state",t),n.toString()}function he(e){try{const t=new URL(e);return`${t.origin}${t.pathname}`}catch{return}}function ge(e,t="minimal"){if("none"===t)return;if("undefined"==typeof window)return{user_agent:"Consentera SDK (SSR)",platform:"web"};const n=navigator.userAgent;let i="Unknown",s="",r="Unknown",o="",a="desktop";n.includes("Chrome")&&!n.includes("Edg")?(i="Chrome",s=n.match(/Chrome\/([\d.]+)/)?.[1]||""):n.includes("Safari")&&!n.includes("Chrome")?(i="Safari",s=n.match(/Version\/([\d.]+)/)?.[1]||""):n.includes("Firefox")?(i="Firefox",s=n.match(/Firefox\/([\d.]+)/)?.[1]||""):n.includes("Edg")&&(i="Edge",s=n.match(/Edg\/([\d.]+)/)?.[1]||""),n.includes("Windows")?(r="Windows",o=n.match(/Windows NT ([\d.]+)/)?.[1]||""):n.includes("Mac OS X")?(r="macOS",o=n.match(/Mac OS X ([\d_.]+)/)?.[1]?.replace(/_/g,".")||""):n.includes("Android")?(r="Android",o=n.match(/Android ([\d.]+)/)?.[1]||""):n.includes("iPhone")||n.includes("iPad")?(r="iOS",o=n.match(/OS ([\d_]+)/)?.[1]?.replace(/_/g,".")||""):n.includes("Linux")&&(r="Linux"),/Mobi|Android.*Mobile|iPhone/.test(n)?a="mobile":/iPad|Android(?!.*Mobile)|Tablet/.test(n)&&(a="tablet");const c={platform:"web",device_type:a,browser_name:i,os_name:r,session_id:e};return"minimal"===t?c:{...c,user_agent:n,screen_resolution:`${window.screen.width}x${window.screen.height}`,timezone:Intl.DateTimeFormat().resolvedOptions().timeZone,browser_version:s,os_version:o,page_url:he(window.location.href),referrer:document.referrer?he(document.referrer):void 0}}function fe(e,t="button"){return{type:t,ui_event_id:e,captured_at:(new Date).toISOString()}}function ye(e){if("data_principal_id"in e&&e.data_principal_id)return{data_principal_id:e.data_principal_id};const t=e.data_principal_identifiers;if(!t||0===Object.keys(t).length)throw new Error("Consentera: name the Data Principal with data_principal_id, or with data_principal_identifiers carrying the identifier fields of this organisation's integration key. data_principal_ref is refused by the API.");return{data_principal_identifiers:t}}function _e(e){const t=e,n={...t};n.applied="boolean"!=typeof t.applied||t.applied;const i=t.retry_after;return"number"==typeof i&&Number.isFinite(i)&&i>=0?n.retry_after=Math.ceil(i):delete n.retry_after,n}class me{request;logger;constructor(e,t){this.request=e,this.logger=t}async check(e,t,n){const i={...ye(e),purpose_code:t},s=async()=>_e(await this.request("POST","/consent/validate",i,void 0,{signal:n?.signal,timeoutMs:n?.timeoutMs}));let r=await s();if(n?.retryWhenPending&&!1===r.applied){const e=function(e){const t=e.retry_after??1;return 1e3*Math.min(t,5)}(r);this.logger.debug("Validate answer not yet applied; asking once more",{purpose:t,waitMs:e}),await(o=e,a=n.signal,new Promise((e,t)=>{if(a?.aborted)return void t(a.reason??new Error("aborted"));const n=setTimeout(()=>{a?.removeEventListener("abort",i),e()},o),i=()=>{clearTimeout(n),t(a?.reason??new Error("aborted"))};a?.addEventListener("abort",i,{once:!0})})),r=await s()}var o,a;return this.logger.debug("Consent validated",{namedBy:"data_principal_id"in e?"data_principal_id":"data_principal_identifiers",purpose:t,decision:r.decision}),r}async checkBulk(e,t,n){const i={...ye(e),purpose_codes:t},s=await this.request("POST","/consent/validate/bulk",i,void 0,{signal:n?.signal,timeoutMs:n?.timeoutMs}),r=Array.isArray(s?.results)?{...s,results:s.results.map(e=>_e(e))}:s;return this.logger.debug("Bulk consent validated",{namedBy:"data_principal_id"in e?"data_principal_id":"data_principal_identifiers",purposes:t.length,results:r.results?.map(e=>`${e.purpose_code}:${e.decision}`)}),r}async isAllowed(e,t,n){return"ALLOW"===(await this.check(e,t,n)).decision}async areAllowed(e,t){const n=await this.checkBulk(e,t),i=new Map;for(const e of n.results||[])i.set(e.purpose_code??e.purpose_id,"ALLOW"===e.decision);return i}}class be{request;logger;onChanged;contextMode;constructor(e,t,n){this.request=e,this.logger=t,this.contextMode=n?.contextMode??"minimal"}context(){return ge(void 0,this.contextMode)}mutation(e){return{idempotencyKey:e?.idempotencyKey??X(),signal:e?.signal,timeoutMs:e?.timeoutMs}}emitChanged(e,t,n){try{this.onChanged?.({dataPrincipalId:e,changes:t,source:n})}catch(e){this.logger.warn("consent.changed listener threw",{error:String(e)})}}async getUpdateContext(e,t){if(!/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(e))throw new Error("Consentera: getUpdateContext takes a data_principal_id (a uuid), not an identifier. This road deliberately accepts no identifiers — a query string is written verbatim into access logs, browser history and Referer headers. Resolve the id once with validate.check({ data_principal_identifiers: … }, purpose) and pass the data_principal_id from its response.");const n={data_principal_id:e};return t?.language_code&&(n.language_code=t.language_code),this.request("GET","/consent/update/context",void 0,n)}async update(e,t,n,i="btn_save_preferences",s){const r={data_principal_id:e,notice_version_id:n.noticeVersionId,notice_hash:n.noticeHash,language_code:n.languageCode||"en",updates:t,affirmative_action:fe(i),client_context:this.context()};await this.request("POST","/consent/update",r,void 0,this.mutation(s)),this.logger.info("Consent updated",{dataPrincipal:e,updates:t.map(e=>`${e.purpose_id}:${e.new_status}`)}),this.emitChanged(e,t.map(e=>({purpose_id:e.purpose_id,status:e.new_status})),"update")}async grant(e,t,n,i="btn_grant_consent",s){const r=t.map(e=>({purpose_id:e,new_status:"granted"}));return this.update(e,r,n,i,s)}async deny(e,t,n,i="btn_deny_consent",s){const r=t.map(e=>({purpose_id:e,new_status:"denied"}));return this.update(e,r,n,i,s)}async getWithdrawalContext(e){return this.request("GET","/consent/withdrawal/context",void 0,{data_principal_id:e})}async withdraw(e,t,n,i="btn_withdraw_consent",s){const r={data_principal_id:e,purposes:t,reason:n,affirmative_action:fe(i),client_context:this.context()};await this.request("POST","/consent/withdraw",r,void 0,this.mutation(s)),this.logger.info("Consent withdrawn",{dataPrincipal:e,purposes:t}),this.emitChanged(e,t.map(e=>({purpose_id:e,status:"withdrawn"})),"withdraw")}async withdrawBulk(e,t,n,i="btn_withdraw_all",s){const r={data_principal_id:e,purposes:t,reason:n,affirmative_action:fe(i),client_context:this.context()};await this.request("POST","/consent/withdraw/bulk",r,void 0,this.mutation(s)),this.logger.info("Bulk consent withdrawn",{dataPrincipal:e,purposes:t.length}),this.emitChanged(e,t.map(e=>({purpose_id:e,status:"withdrawn"})),"withdraw_bulk")}async getWithdrawalAnalytics(){return this.request("GET","/consent/withdrawal/analytics")}async getRenewalContext(e){return this.request("GET","/consent/renewal/context",void 0,{data_principal_id:e})}async renew(e,t,n,i="btn_renew_consent",s){const r={data_principal_id:e,purpose_ids:t,notice_hash:n,captured_at:fe(i).captured_at,client_context:this.context()},o=we(await this.request("POST","/consent/renew",r,void 0,{...this.mutation(s),raw:!0}));return this.logger.info("Consent renewed",{dataPrincipal:e,purposes:t}),this.emitChanged(e,o.body.renewed_consents.map(e=>({purpose_id:e.purpose_id,status:"granted"})),"renew"),o}async renewBulk(e,t,n,i="btn_renew_all",s){const r={data_principal_id:e,renewals:t.map(e=>({purpose_id:e})),notice_hash:n,captured_at:fe(i).captured_at,client_context:this.context()},o=we(await this.request("POST","/consent/renew/bulk",r,void 0,{...this.mutation(s),raw:!0}));return this.logger.info("Bulk consent renewed",{dataPrincipal:e,purposes:o.body.success_count}),o.body.renewed_consents.length>0&&this.emitChanged(e,o.body.renewed_consents.map(e=>({purpose_id:e.purpose_id,status:"granted"})),"renew"),o}}function we(e){const t=e?.body;if(!t)return e;const n="number"==typeof t.retry_after?t.retry_after:void 0,i="number"==typeof t.retry_after_seconds?t.retry_after_seconds:void 0,s=n??i;return void 0===s||t.retry_after===s&&t.retry_after_seconds===s?e:{...e,body:{...t,retry_after:s,retry_after_seconds:s}}}const ve=new Set(["granted","partial","denied"]);function Ce(e){return e&&ve.has(e)?e:"unknown"}class ke{logger;request;options;constructor(e,t,n={}){this.logger=e,this.request=t,this.options=n}readCallbackParams(e){let t;if(e)t="string"==typeof e?new URLSearchParams(e):e;else{if("undefined"==typeof window)throw new Error("readCallbackParams requires searchParams in non-browser environments");t=new URLSearchParams(window.location.search)}return{session_id:t.get("session_id")||t.get("consent_session_id")||"",artifact_id:t.get("artifact_id")||t.get("consent_artifact_id")||void 0,raw_status:t.get("status")||"",claimed_status:Ce(t.get("status")),pending:"1"===t.get("pending"),state:t.get("state")||void 0,sig:t.get("sig")||void 0,error:t.get("error")||void 0}}async parseCallback(e){const t=this.readCallbackParams(e),i={session_id:t.session_id,artifact_id:t.artifact_id,claimed_status:t.claimed_status,claimed_pending:t.pending,status:"unverified"};if(t.error)return this.finish({...i,status:"error",error:t.error,reason:"the platform returned an error"});if(!t.session_id)return this.finish({...i,status:"unverified",reason:"the callback names no session"});const r=s("session",n.session(t.session_id));if(!r)return this.finish({...i,reason:"no session was stored in this browser for that id — the session was created elsewhere, the tab was replaced, or the callback was not produced by this flow"});let a;try{a=JSON.parse(r)}catch{return o("session",n.session(t.session_id)),this.finish({...i,reason:"the stored session record could not be parsed"})}if(o("session",n.session(t.session_id)),a.session_id&&a.session_id!==t.session_id)return this.finish({...i,reason:"the stored session id does not match the callback"});if(!a.state)return this.finish({...i,reason:"the stored session has no callback state: it was created without a callback_url, or by an SDK version that bound the callback to the server challengeNonce (which the platform never returns). Read the consent back through your backend instead."});if(!t.state)return this.finish({...i,reason:"the callback carries no state, and the session was created with one"});if(!Ee(a.state,t.state))return this.finish({...i,reason:"the callback state does not match the stored session"});let c="absent";if(t.sig){if(!this.options.verifySignature)return this.finish({...i,signature:"unverifiable",reason:"the callback is signed and no verifier is configured. The signing key is your callback_signing_secret, which must not be in a browser: set `verifyCallbackSignature` to call your own server, which uses verifyCallbackSignature(secret, …) from this package."});let e=!1;try{e=await this.options.verifySignature({sessionId:t.session_id,artifactId:t.artifact_id??"",status:t.raw_status,sig:t.sig})}catch(e){return this.finish({...i,signature:"unverifiable",reason:`the signature verifier threw: ${String(e)}`})}if(!e)return this.finish({...i,signature:"invalid",reason:"the callback signature did not verify"});c="verified"}return"denied"===t.claimed_status?this.finish({...i,status:"denied",signature:c}):"unknown"===t.claimed_status?this.finish({...i,signature:c,reason:`the callback status ${JSON.stringify(t.raw_status)} is not a status the platform issues (granted | partial | denied). Read the consent back through your backend.`}):t.artifact_id?this.request?this.confirmArtifact(i,t.artifact_id,t.session_id,a.data_principal_id,c):this.finish({...i,signature:c,reason:"no transport available to confirm the artifact. Construct the handler through ConsentEraClient so it can call GET /consent/artifacts/{id}."}):this.finish({...i,signature:c,reason:"the callback claims success but names no artifact"})}async confirmArtifact(e,t,n,i,s){const r=this.options.artifactWaitMs??15e3,o=Date.now()+r;for(;;){let r;try{r=await oe(this.request,t,{sessionId:n})}catch(i){const r=i instanceof C?i.toString():String(i);return i instanceof C&&(404===i.status||"not_found"===i.kind)?this.finish({...e,signature:s,reason:`the platform has no artifact ${t} for session ${n}: it was never issued for this session, or will never be written. A forged or mismatched artifact_id reads exactly like this. (${r})`}):this.finish({...e,signature:s,reason:`the artifact could not be confirmed: ${r}`})}if("recorded"===r.state){const n=r.artifact;return n.artifact_id&&n.artifact_id!==t?this.finish({...e,signature:s,reason:"the platform returned a different artifact than the one read"}):i?n.data_principal_id!==i?this.finish({...e,signature:s,reason:"the artifact names a different Data Principal from this browser's session — it is not this consent"}):this.finish({...e,status:"completed",artifact:n,signature:s}):this.finish({...e,signature:s,reason:"the stored session names no data_principal_id, so the artifact cannot be tied to the person this session was created for. Create the session with this SDK version, or confirm the artifact server-side."})}const a=o-Date.now();if(a<=0||r.retryAfterMs>a)return this.finish({...e,status:"pending",signature:s,retryAfterMs:r.retryAfterMs,reason:`the platform says the consent was recorded and its artifact is still being written (${r.pending.reason||"pending"}); read it again in ${r.retryAfterMs}ms. This is NOT a failure and NOT a consent that did not happen.`});await new Promise(e=>setTimeout(e,r.retryAfterMs))}}isCallback(e){const t=new URLSearchParams(e||("undefined"!=typeof window?window.location.search:""));return t.has("session_id")||t.has("consent_session_id")}finish(e){return e.session_id&&r("session",n.callback(e.session_id),JSON.stringify(e)),"completed"===e.status?this.logger.info("Consent callback verified",{session_id:e.session_id,status:e.status}):"pending"===e.status?this.logger.info("Consent callback verified; artifact not readable yet",{session_id:e.session_id,status:e.status}):this.logger.warn("Consent callback NOT verified",{session_id:e.session_id,status:e.status,reason:e.reason}),e}}function Ee(e,t){if(e.length!==t.length)return!1;let n=0;for(let i=0;i<e.length;i++)n|=e.charCodeAt(i)^t.charCodeAt(i);return 0===n}class xe{request;logger;cachedConfig=null;constructor(e,t){this.request=e,this.logger=t}async getConfig(e){if(this.cachedConfig)return this.cachedConfig;const t={};e&&(t.language_code=e);const n=await this.request("GET","/df/config",void 0,t,{road:"public"});return this.cachedConfig=n,this.logger.info("DF config loaded",{tenant:n.tenant_name,purposes:n.purposes?.length}),n}async getPurposes(){const e=await this.request("GET","/df/purposes",void 0,void 0,{road:"public"});return Array.isArray(e)?e:e.purposes||[]}async getNoticePurposes(e,t){const n={};return e&&(n.notice_internal_name=e),t&&(n.language_code=t),this.request("GET","/df/notice/purposes",void 0,n,{road:"public"})}async getNoticeTemplate(e,t,n){const i={notice:e};t&&(i.notice_language=t),n&&(i.template_language=n);const s=await this.request("GET","/df/notice/template",void 0,i,{road:"public"});return this.logger.info("Notice template loaded",{notice:e,language:t}),s.data??s}async getSessionWidgetTemplate(e){return this.request("GET",`/consent/sessions/${encodeURIComponent(e)}/widget-template`,void 0,void 0,{road:"session"})}clearCache(){this.cachedConfig=null}}class Se{request;logger;constructor(e,t,n){this.request=e,this.logger=n}async getPortalToken(e,t){const n={data_principal_ref:e,redirect_path:t},i=await this.request("POST","/df/principal-token",n);return this.logger.info("Principal portal token generated",{dataPrincipal:e,expires_in:i.expires_in}),i}async getPortalUrl(e,t){return(await this.getPortalToken(e,t)).redirect_url}async openPortal(e,t){if("undefined"==typeof window)throw new Error("openPortal can only be used in browser environments");const n=await this.getPortalUrl(e,t);window.open(n,"_blank","noopener,noreferrer"),this.logger.info("Principal portal opened",{dataPrincipal:e})}}const Ie=["analytics_storage","ad_storage","ad_user_data","ad_personalization"];function Ae(){const e=globalThis;return"function"==typeof e.gtag?e.gtag:(e.dataLayer=e.dataLayer||[],function(...t){e.dataLayer.push(t)})}class De{mapping;state={};constructor(e){this.mapping=e}sendDefault(e=500){const t={wait_for_update:e};for(const e of Ie)this.mapping[e]?.length&&(t[e]="denied");Ae()("consent","default",t)}apply(e){this.state={...e},this.push()}onChanged(e){for(const t of e.changes)this.state[t.purpose_id]="granted"===t.status;this.push()}push(){const e={};for(const t of Ie){const n=this.mapping[t];n?.length&&(e[t]=n.every(e=>!0===this.state[e])?"granted":"denied")}Ae()("consent","update",e)}}if("undefined"!=typeof window){const e=document.currentScript;if("true"===e?.dataset?.autoInit){const t=e.dataset.siteKey,n=e.dataset.apiKey,i=e.dataset.tenantId,s=e.dataset.apiEndpoint??"";if((t||n)&&i)try{window.ConsentEra=new ie({...t?{siteKey:t}:{apiKey:n},tenantId:i,apiEndpoint:s,autoShow:!0})}catch(e){}}}"undefined"!=typeof window&&(window.Consentera=ie,window.ConsentEraConsent=ie),e.CallbackHandler=ke,e.ConsentBanner=l,e.ConsentEraApiError=B,e.ConsentEraClient=class extends y{config;logger;transport;session;validate;manage;callback;df;principal;consent;constructor(e){if(super(),!e.apiEndpoint&&!e.proxyEndpoint)throw V("Consentera: set `proxyEndpoint` (your own server route — required in a browser for consent lifecycle roads) or `apiEndpoint` (server-side use).","ENDPOINT_REQUIRED");if(!e.tenantId&&!e.proxyEndpoint)throw V("Consentera: tenantId is required with `apiEndpoint`. (Behind `proxyEndpoint` it is optional: your server supplies the tenant.)","TENANT_ID_REQUIRED");J({apiKey:e.apiKey,unsafeAllowSecretKeyInBrowser:e.unsafeAllowSecretKeyInBrowser,fix:'Move the key to your own server and point the SDK at it with `proxyEndpoint: "/api/consentera"`; the browser then sends no credential at all.'}),this.config=e,this.logger=new _(e.debug?"debug":"info"),this.transport=new ne({tenantId:e.tenantId,apiEndpoint:e.apiEndpoint,proxyEndpoint:e.proxyEndpoint,apiKey:e.apiKey,siteKey:e.siteKey,customHeaders:e.customHeaders,unsafeAllowSecretKeyInBrowser:e.unsafeAllowSecretKeyInBrowser,timeoutMs:e.timeoutMs,retry:e.retry,beforeSend:e.beforeSend,fetchImpl:e.fetchImpl},this.logger);const t=this.request.bind(this);this.session=new ue(t,e,this.logger),this.validate=new me(t,this.logger),this.manage=new be(t,this.logger,{contextMode:e.collectContext??"minimal"}),this.manage.onChanged=e=>this.emit("consent.changed",e),this.callback=new ke(this.logger,t,{verifySignature:e.verifyCallbackSignature,artifactWaitMs:e.artifactWaitMs}),this.df=new xe(t,this.logger),this.principal=new Se(t,e,this.logger),this.consent={createSession:this.session.createSession.bind(this.session),redirectToConsent:this.session.redirectToConsent.bind(this.session),openConsentPopup:this.session.openConsentPopup.bind(this.session),getArtifact:this.session.getArtifact.bind(this.session),validate:this.validate.check.bind(this.validate),validateBulk:this.validate.checkBulk.bind(this.validate),isAllowed:this.validate.isAllowed.bind(this.validate),areAllowed:this.validate.areAllowed.bind(this.validate),handleCallback:this.callback.parseCallback.bind(this.callback),isCallback:this.callback.isCallback.bind(this.callback),getContext:this.manage.getUpdateContext.bind(this.manage),update:this.manage.update.bind(this.manage),grant:this.manage.grant.bind(this.manage),deny:this.manage.deny.bind(this.manage),getWithdrawalContext:this.manage.getWithdrawalContext.bind(this.manage),withdraw:this.manage.withdraw.bind(this.manage),withdrawBulk:this.manage.withdrawBulk.bind(this.manage),getRenewalContext:this.manage.getRenewalContext.bind(this.manage),renew:this.manage.renew.bind(this.manage),renewBulk:this.manage.renewBulk.bind(this.manage)},this.logger.info("Consentera client initialised",{mode:e.proxyEndpoint?"proxy":"direct",tenantId:e.tenantId})}async request(e,t,n,i,s={}){try{return await this.transport.request(e,t,n,{...s,query:{...i,...s.query}})}catch(e){throw e instanceof C&&this.emit("error",e),e}}static buildClientContext=ge;static buildAffirmativeAction=fe;getConfig(){const e={...this.config};return e.apiKey&&(e.apiKey=`${e.apiKey.slice(0,9)}…`),e}},e.ConsentEraConsent=ie,e.ConsentManager=be,e.ConsentModeBridge=De,e.ConsentSession=ue,e.ConsentStorage=a,e.ConsentValidator=me,e.ConsenteraAuthError=E,e.ConsenteraConfigError=k,e.ConsenteraConflictError=T,e.ConsenteraError=C,e.ConsenteraGuardianError=A,e.ConsenteraIdentityError=I,e.ConsenteraNetworkError=$,e.ConsenteraNotFoundError=D,e.ConsenteraPermissionError=x,e.ConsenteraPlanLimitError=L,e.ConsenteraRateLimitError=P,e.ConsenteraServerError=R,e.ConsenteraTimeoutError=N,e.ConsenteraValidationError=S,e.DEFAULT_RETRY=z,e.DEFAULT_TIMEOUT_MS=1e4,e.DFConfigClient=xe,e.GPPManager=f,e.HttpTransport=ne,e.PreferenceCenter=h,e.PrincipalClient=Se,e.SDK_HEADER_VALUE=j,e.SDK_NAME="consent-sdk-js",e.SDK_PLATFORM="web",e.SDK_VERSION=F,e.TCFManager=g,e.VALIDATE_PENDING_RETRY_CAP_SECONDS=5,e.buildClientContext=ge,e.claimedCallbackStatus=Ce,e.default=ie,e.installConsentModeBridge=function(e,t){const n=new De(t);return n.sendDefault(),e.on("consent.changed",e=>n.onChanged(e)),n},e.isBrowser=H,e.isSecretKey=W,e.isSiteKey=Y,e.newRequestId=X,e.normalizeValidateResponse=_e,e.parseRetryAfterMs=v,e.principalBody=ye,e.readDecisionMessage=de,e.readStored=s,e.removeStored=o,e.storageAvailable=function(e){return null!==i(e)},e.storageKeys=n,e.verifyCallbackSignature=async function(e,t){if(!e||!t.sig)return!1;const n=function(){const e=globalThis.crypto?.subtle;if(e)return e;try{const e=require("node:crypto");return e?.webcrypto?.subtle}catch{return}}();if(!n)throw V("Consentera: no SubtleCrypto available, so the callback signature cannot be verified here. Verify it on a runtime that has WebCrypto (Node 20+, any modern browser) — but note the signing key is your callback_signing_secret and must not be in a browser.","NO_SUBTLE_CRYPTO");const i=new TextEncoder,s=await n.importKey("raw",i.encode(e),{name:"HMAC",hash:"SHA-256"},!1,["sign"]),r=`${t.sessionId}|${t.artifactId}|${t.status}`,o=await n.sign("HMAC",s,i.encode(r));return Ee(Array.from(new Uint8Array(o),e=>e.toString(16).padStart(2,"0")).join(""),t.sig.toLowerCase())},e.writeStored=r,Object.defineProperty(e,"__esModule",{value:!0})});
|
|
2
2
|
//# sourceMappingURL=consentera-consent.min.js.map
|