c15t
Version:
Headless JavaScript consent management platform for cookie banners, privacy preferences, consent storage, and script gating.
4,312 lines • 183 kB
JavaScript
"use strict";
var __webpack_require__ = {};
(()=>{
__webpack_require__.d = (exports1, getters, values)=>{
var define = (defs, kind)=>{
for(var key in defs)if (__webpack_require__.o(defs, key) && !__webpack_require__.o(exports1, key)) Object.defineProperty(exports1, key, {
enumerable: true,
[kind]: defs[key]
});
};
define(getters, "get");
define(values, "value");
};
})();
(()=>{
__webpack_require__.o = (obj, prop)=>Object.prototype.hasOwnProperty.call(obj, prop);
})();
(()=>{
__webpack_require__.r = (exports1)=>{
if ("u" > typeof Symbol && Symbol.toStringTag) Object.defineProperty(exports1, Symbol.toStringTag, {
value: 'Module'
});
Object.defineProperty(exports1, '__esModule', {
value: true
});
};
})();
var __webpack_exports__ = {};
__webpack_require__.r(__webpack_exports__);
__webpack_require__.d(__webpack_exports__, {
API_ENDPOINTS: ()=>API_ENDPOINTS,
C15tClient: ()=>C15tClient,
CustomClient: ()=>CustomClient,
OfflineClient: ()=>OfflineClient,
allConsentNames: ()=>allConsentNames,
applyPolicyPurposeAllowlist: ()=>applyPolicyPurposeAllowlist,
applyPolicyScopeForRuntimeGating: ()=>applyPolicyScopeForRuntimeGating,
buildPrefetchScript: ()=>buildPrefetchScript,
clearClientRegistry: ()=>clearClientRegistry,
clearConsentRuntimeCache: ()=>clearConsentRuntimeCache,
configureConsentManager: ()=>configureConsentManager,
consentTypes: ()=>consent_types_consentTypes,
createConsentManagerStore: ()=>createConsentManagerStore,
createIframeBlocker: ()=>createIframeBlocker,
deepMergeTranslations: ()=>translations_namespaceObject.deepMergeTranslations,
defaultTranslationConfig: ()=>defaultTranslationConfig,
deleteConsentFromStorage: ()=>deleteConsentFromStorage,
deleteCookie: ()=>deleteCookie,
detectBrowserLanguage: ()=>translations_namespaceObject.detectBrowserLanguage,
emitScriptDebugEvent: ()=>emitScriptDebugEvent,
filterConsentCategoriesByPolicy: ()=>filterConsentCategoriesByPolicy,
flattenPolicyActionGroups: ()=>flattenPolicyActionGroups,
generateSubjectId: ()=>generateSubjectId,
getConsentFromStorage: ()=>getConsentFromStorage,
getCookie: ()=>getCookie,
getEffectivePolicy: ()=>getEffectivePolicy,
getLoadedScriptIds: ()=>getLoadedScriptIds,
getOrCreateConsentRuntime: ()=>getOrCreateConsentRuntime,
getRootDomain: ()=>getRootDomain,
has: ()=>has,
hasPolicyHints: ()=>hasPolicyHints,
isScriptLoaded: ()=>isScriptLoaded,
isValidSubjectId: ()=>isValidSubjectId,
loadScripts: ()=>loadScripts,
mergeTranslationConfigs: ()=>translations_namespaceObject.mergeTranslationConfigs,
policyPackPresets: ()=>types_namespaceObject.policyPackPresets,
prepareTranslationConfig: ()=>translations_namespaceObject.prepareTranslationConfig,
resolvePolicyActionGroups: ()=>resolvePolicyActionGroups,
resolvePolicyAllowedActions: ()=>resolvePolicyAllowedActions,
resolvePolicyDirection: ()=>resolvePolicyDirection,
resolvePolicyOrderedActions: ()=>resolvePolicyOrderedActions,
resolvePolicyPrimaryActions: ()=>resolvePolicyPrimaryActions,
resolvePolicyUiProfile: ()=>resolvePolicyUiProfile,
saveConsentToStorage: ()=>saveConsentToStorage,
setCookie: ()=>setCookie,
shouldFillPolicyActions: ()=>shouldFillPolicyActions,
subscribeToScriptDebugEvents: ()=>subscribeToScriptDebugEvents,
unloadScripts: ()=>unloadScripts,
updateScripts: ()=>core_updateScripts,
validateUIAgainstPolicy: ()=>validateUIAgainstPolicy
});
const types_namespaceObject = require("@c15t/schema/types");
const translations_namespaceObject = require("@c15t/translations");
const LEADING_SLASHES_REGEX = /^\/+/;
function createResponseContext(isSuccess, data = null, error = null, response = null) {
return {
data,
error,
ok: isSuccess,
response
};
}
function createErrorResponse(message, status = 500, code = 'ERROR', cause) {
return createResponseContext(false, null, {
message,
status,
code,
cause
}, null);
}
function utils_createErrorResponse(message, status = 500, code = 'HANDLER_ERROR', cause) {
return createErrorResponse(message, status, code, cause);
}
async function executeHandler(endpointHandlers, handlerKey, options) {
const handler = endpointHandlers[handlerKey];
if (!handler) {
const errorResponse = utils_createErrorResponse(`No endpoint handler found for '${String(handlerKey)}'`, 404, 'ENDPOINT_NOT_FOUND');
if (options?.throw) throw new Error(`No endpoint handler found for '${String(handlerKey)}'`);
return errorResponse;
}
try {
const response = await handler(options);
const normalizedResponse = {
data: response.data,
error: response.error,
ok: response.ok ?? !response.error,
response: response.response
};
return normalizedResponse;
} catch (error) {
const errorResponse = utils_createErrorResponse(error instanceof Error ? error.message : String(error), 0, 'HANDLER_ERROR', error);
if (options?.throw) throw error;
return errorResponse;
}
}
async function customFetch(endpointHandlers, dynamicHandlers, path, options) {
const endpointName = path.replace(LEADING_SLASHES_REGEX, '').split('/')[0];
const handler = dynamicHandlers[path];
if (handler) try {
return await handler(options);
} catch (error) {
const errorResponse = utils_createErrorResponse(error instanceof Error ? error.message : String(error), 0, 'HANDLER_ERROR', error);
return errorResponse;
}
if (!endpointName || !(endpointName in endpointHandlers)) {
const errorResponse = utils_createErrorResponse(`No endpoint handler found for '${path}'`, 404, 'ENDPOINT_NOT_FOUND');
return errorResponse;
}
return await executeHandler(endpointHandlers, endpointName, options);
}
async function init_init(endpointHandlers, options) {
const handlerKey = 'init' in endpointHandlers && void 0 !== endpointHandlers.init ? 'init' : 'init';
return await executeHandler(endpointHandlers, handlerKey, options);
}
async function setConsent(endpointHandlers, options) {
return await executeHandler(endpointHandlers, 'setConsent', options);
}
class CustomClient {
endpointHandlers;
dynamicHandlers = {};
constructor(options){
this.endpointHandlers = options.endpointHandlers;
}
async init(options) {
return init_init(this.endpointHandlers, options);
}
async setConsent(options) {
return setConsent(this.endpointHandlers, options);
}
async identifyUser(options) {
if (this.endpointHandlers.identifyUser) return this.endpointHandlers.identifyUser(options);
const subjectId = options.body?.id;
if (!subjectId) return {
ok: false,
data: null,
response: null,
error: {
message: 'Subject ID is required to identify user',
status: 400,
code: 'MISSING_SUBJECT_ID'
}
};
return this.$fetch(`/subjects/${subjectId}`, {
...options,
method: 'PATCH'
});
}
registerHandler(path, handler) {
this.dynamicHandlers[path] = handler;
}
async $fetch(path, options) {
return customFetch(this.endpointHandlers, this.dynamicHandlers, path, options);
}
}
const DEFAULT_RETRY_CONFIG = {
maxRetries: 3,
initialDelayMs: 100,
backoffFactor: 2,
retryableStatusCodes: [
500,
502,
503,
504
],
nonRetryableStatusCodes: [
400,
401,
403,
404
],
retryOnNetworkError: true,
shouldRetry: void 0
};
const ABSOLUTE_URL_REGEX = /^(?:[a-z+]+:)?\/\//i;
const constants_LEADING_SLASHES_REGEX = LEADING_SLASHES_REGEX;
const noop = ()=>{};
function createDebugLogger(enabled) {
if (!enabled) return {
log: noop,
debug: noop
};
return {
log: (...args)=>console.log('[c15t]', ...args),
debug: (...args)=>console.debug('[c15t]', ...args)
};
}
let _debugLogger = createDebugLogger(false);
function getDebugLogger() {
return _debugLogger;
}
function setDebugEnabled(enabled) {
_debugLogger = createDebugLogger(enabled);
}
const C15T_VERSION_HEADER = 'x-c15t-version';
const headers_C15T_VERSION_HEADERS = {
[C15T_VERSION_HEADER]: "2.2.1"
};
const delay = (ms)=>new Promise((resolve)=>setTimeout(resolve, ms));
function getIdentifySubjectId(submission) {
return submission?.subjectId || submission?.id;
}
function generateUUID() {
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c)=>{
const r = 16 * Math.random() | 0;
const v = 'x' === c ? r : 0x3 & r | 0x8;
return v.toString(16);
});
}
function removeTrailingSlashes(str) {
let i = str.length;
while(i > 0 && '/' === str[i - 1])i--;
return str.slice(0, i);
}
function resolveUrl(backendURL, path) {
if (ABSOLUTE_URL_REGEX.test(backendURL)) {
const backendURLObj = new URL(backendURL);
const basePath = removeTrailingSlashes(backendURLObj.pathname);
const cleanPath = path.replace(constants_LEADING_SLASHES_REGEX, '');
const newPath = `${basePath}/${cleanPath}`;
backendURLObj.pathname = newPath;
return backendURLObj.toString();
}
const cleanBase = removeTrailingSlashes(backendURL);
const cleanPath = path.replace(constants_LEADING_SLASHES_REGEX, '');
return `${cleanBase}/${cleanPath}`;
}
const fetcher_createResponseContext = createResponseContext;
async function fetcher(context, path, options) {
const finalRetryConfig = {
...context.retryConfig,
...options?.retryConfig || {},
retryableStatusCodes: options?.retryConfig?.retryableStatusCodes ?? context.retryConfig.retryableStatusCodes ?? DEFAULT_RETRY_CONFIG.retryableStatusCodes,
nonRetryableStatusCodes: options?.retryConfig?.nonRetryableStatusCodes ?? context.retryConfig.nonRetryableStatusCodes ?? DEFAULT_RETRY_CONFIG.nonRetryableStatusCodes
};
const { maxRetries, initialDelayMs, backoffFactor, retryableStatusCodes, nonRetryableStatusCodes, retryOnNetworkError } = finalRetryConfig;
let attemptsMade = 0;
let currentDelay = initialDelayMs;
let lastErrorResponse = null;
while(attemptsMade <= (maxRetries ?? 0)){
const requestId = generateUUID();
const fetchImpl = context.customFetch || globalThis.fetch;
const resolvedUrl = resolveUrl(context.backendURL, path);
let url;
try {
url = new URL(resolvedUrl);
} catch {
url = new URL(resolvedUrl, window.location.origin);
}
if (options?.query) {
for (const [key, value] of Object.entries(options.query))if (void 0 !== value) url.searchParams.append(key, String(value));
}
const requestOptions = {
method: options?.method || 'GET',
mode: context.corsMode,
credentials: 'include',
headers: {
...headers_C15T_VERSION_HEADERS,
...context.headers,
'X-Request-ID': requestId,
...options?.headers
},
...options?.fetchOptions
};
if (options?.body && 'GET' !== requestOptions.method) requestOptions.body = JSON.stringify(options.body);
try {
const response = await fetchImpl(url.toString(), requestOptions);
let data = null;
let parseError = null;
try {
const contentType = response.headers.get('content-type');
if (contentType?.includes('application/json') && 204 !== response.status && '0' !== response.headers.get('content-length')) data = await response.json();
else if (204 === response.status) data = null;
} catch (err) {
parseError = err;
}
if (parseError) {
const errorResponse = fetcher_createResponseContext(false, null, {
message: 'Failed to parse response',
status: response.status,
code: 'PARSE_ERROR',
cause: parseError
}, response);
options?.onError?.(errorResponse, path);
if (options?.throw) throw new Error('Failed to parse response');
return errorResponse;
}
const isSuccess = response.status >= 200 && response.status < 300;
if (isSuccess) {
const successResponse = fetcher_createResponseContext(true, data, null, response);
options?.onSuccess?.(successResponse);
return successResponse;
}
const errorData = data;
const errorResponse = fetcher_createResponseContext(false, null, {
message: errorData?.message || `Request failed with status ${response.status}`,
status: response.status,
code: errorData?.code || 'API_ERROR',
details: errorData?.details || null
}, response);
lastErrorResponse = errorResponse;
let shouldRetryThisRequest = false;
if (nonRetryableStatusCodes?.includes(response.status)) {
getDebugLogger().debug(`Not retrying request to ${path} with status ${response.status} (nonRetryableStatusCodes)`);
shouldRetryThisRequest = false;
} else if ('function' == typeof finalRetryConfig.shouldRetry) try {
shouldRetryThisRequest = finalRetryConfig.shouldRetry(response, {
attemptsMade,
url: url.toString(),
method: requestOptions.method || 'GET'
});
getDebugLogger().debug(`Custom retry strategy for ${path} with status ${response.status}: ${shouldRetryThisRequest}`);
} catch {
shouldRetryThisRequest = retryableStatusCodes?.includes(response.status) ?? false;
getDebugLogger().debug(`Custom retry strategy failed, falling back to status code check: ${shouldRetryThisRequest}`);
}
else {
shouldRetryThisRequest = retryableStatusCodes?.includes(response.status) ?? false;
getDebugLogger().debug(`Standard retry check for ${path} with status ${response.status}: ${shouldRetryThisRequest}`);
}
if (!shouldRetryThisRequest || attemptsMade >= (maxRetries ?? 0)) {
options?.onError?.(errorResponse, path);
if (options?.throw) throw new Error(errorResponse.error?.message || 'Request failed');
return errorResponse;
}
attemptsMade++;
await delay(currentDelay ?? 0);
currentDelay = (currentDelay ?? 0) * (backoffFactor ?? 2);
} catch (fetchError) {
if (fetchError && 'Failed to parse response' === fetchError.message) throw fetchError;
const isNetworkError = !(fetchError instanceof Response);
const errorResponse = fetcher_createResponseContext(false, null, {
message: fetchError instanceof Error ? fetchError.message : String(fetchError),
status: 0,
code: 'NETWORK_ERROR',
cause: fetchError
}, null);
lastErrorResponse = errorResponse;
const shouldRetryThisRequest = isNetworkError && retryOnNetworkError;
if (!shouldRetryThisRequest || attemptsMade >= (maxRetries ?? 0)) {
options?.onError?.(errorResponse, path);
if (options?.throw) throw fetchError;
return errorResponse;
}
attemptsMade++;
await delay(currentDelay ?? 0);
currentDelay = (currentDelay ?? 0) * (backoffFactor ?? 2);
}
}
const maxRetriesErrorResponse = lastErrorResponse || fetcher_createResponseContext(false, null, {
message: `Request failed after ${maxRetries} retries`,
status: 0,
code: 'MAX_RETRIES_EXCEEDED'
}, null);
options?.onError?.(maxRetriesErrorResponse, path);
if (options?.throw) throw new Error(`Request failed after ${maxRetries} retries`);
return maxRetriesErrorResponse;
}
function getDefaultCookieOptions(config) {
return {
expiryDays: config?.defaultExpiryDays ?? 365,
crossSubdomain: config?.crossSubdomain ?? false,
domain: config?.defaultDomain ?? '',
path: '/',
secure: "u" > typeof window && 'https:' === window.location.protocol,
sameSite: 'Lax'
};
}
function getRootDomain() {
if ("u" < typeof window) return '';
const hostname = window.location.hostname;
if ('localhost' === hostname || /^\d+\.\d+\.\d+\.\d+$/.test(hostname)) return hostname;
const parts = hostname.split('.');
if (parts.length >= 2) return `.${parts.slice(-2).join('.')}`;
return hostname;
}
const COOKIE_KEY_MAP = {
consents: 'c',
consentInfo: 'i',
timestamp: 'ts',
iabCustomVendorConsents: 'icv',
iabCustomVendorLegitimateInterests: 'icvli',
time: 't',
type: 'y',
id: 'id',
subjectId: 'sid',
externalId: 'eid',
materialPolicyFingerprint: 'mpf',
identityProvider: 'idp'
};
const REVERSE_COOKIE_KEY_MAP = Object.entries(COOKIE_KEY_MAP).reduce((acc, [key, value])=>{
acc[value] = key;
return acc;
}, {});
function shortenFlatKeys(flattened) {
const shortened = {};
for (const [key, value] of Object.entries(flattened)){
const keys = key.split('.');
const shortenedKeys = keys.map((k)=>COOKIE_KEY_MAP[k] || k);
shortened[shortenedKeys.join('.')] = value;
}
return shortened;
}
function expandFlatKeys(shortened) {
const expanded = {};
for (const [key, value] of Object.entries(shortened)){
const keys = key.split('.');
const expandedKeys = keys.map((k)=>REVERSE_COOKIE_KEY_MAP[k] || k);
expanded[expandedKeys.join('.')] = value;
}
return expanded;
}
function flattenObject(obj, prefix = '') {
const flattened = {};
for (const [key, value] of Object.entries(obj)){
const newKey = prefix ? `${prefix}.${key}` : key;
if (null == value) flattened[newKey] = '';
else if ('boolean' == typeof value) {
if (value) flattened[newKey] = '1';
} else if ('object' != typeof value || Array.isArray(value)) flattened[newKey] = String(value);
else Object.assign(flattened, flattenObject(value, newKey));
}
return flattened;
}
function unflattenObject(flattened) {
const result = {};
for (const [key, value] of Object.entries(flattened)){
const keys = key.split('.');
if (0 === keys.length) continue;
let current = result;
for(let i = 0; i < keys.length - 1; i++){
const k = keys[i];
if (void 0 !== k) {
if (!current[k]) current[k] = {};
current = current[k];
}
}
const lastKey = keys[keys.length - 1];
if (void 0 !== lastKey) if ('1' === value) current[lastKey] = true;
else if ('0' === value) current[lastKey] = false;
else if ('' === value) current[lastKey] = null;
else if (Number.isNaN(Number(value)) || '' === value) current[lastKey] = value;
else current[lastKey] = Number(value);
}
return result;
}
function flatToString(flattened) {
return Object.entries(flattened).map(([key, value])=>`${key}:${value}`).join(',');
}
function stringToFlat(str) {
if (!str) return {};
const result = {};
const pairs = str.split(',');
for (const pair of pairs){
const colonIndex = pair.indexOf(':');
if (-1 === colonIndex) continue;
const key = pair.substring(0, colonIndex);
const value = pair.substring(colonIndex + 1);
result[key] = value;
}
return result;
}
function setCookie(name, value, options, config) {
if ("u" < typeof document) return;
const opts = {
...getDefaultCookieOptions(config),
...options
};
if (opts.crossSubdomain && !options?.domain) opts.domain = getRootDomain();
try {
let cookieValue;
if ('string' == typeof value) cookieValue = value;
else {
const flattened = flattenObject(value);
const shortened = shortenFlatKeys(flattened);
cookieValue = flatToString(shortened);
}
const date = new Date();
date.setTime(date.getTime() + 24 * opts.expiryDays * 3600000);
const expires = `expires=${date.toUTCString()}`;
const parts = [
`${name}=${cookieValue}`,
expires,
`path=${opts.path}`
];
if (opts.domain) parts.push(`domain=${opts.domain}`);
if (opts.secure) parts.push('secure');
if (opts.sameSite) parts.push(`SameSite=${opts.sameSite}`);
document.cookie = parts.join('; ');
} catch (error) {
console.warn(`Failed to set cookie "${name}":`, error);
}
}
function getCookie(name) {
if ("u" < typeof document) return null;
try {
const nameEQ = `${name}=`;
const cookies = document.cookie.split(';');
for (const cookie of cookies){
let c = cookie;
while(' ' === c.charAt(0))c = c.substring(1);
if (0 === c.indexOf(nameEQ)) {
const cookieValue = c.substring(nameEQ.length);
if (cookieValue.includes(':')) {
const shortened = stringToFlat(cookieValue);
const expanded = expandFlatKeys(shortened);
const nested = unflattenObject(expanded);
return nested;
}
return cookieValue;
}
}
return null;
} catch (error) {
console.warn(`Failed to get cookie "${name}":`, error);
return null;
}
}
function deleteCookie(name, options, config) {
if ("u" < typeof document) return;
const opts = {
...getDefaultCookieOptions(config),
...options
};
if (opts.crossSubdomain && !options?.domain) opts.domain = getRootDomain();
try {
const parts = [
`${name}=`,
'expires=Thu, 01 Jan 1970 00:00:00 GMT',
`path=${opts.path}`
];
if (opts.domain) parts.push(`domain=${opts.domain}`);
document.cookie = parts.join('; ');
} catch (error) {
console.warn(`Failed to delete cookie "${name}":`, error);
}
}
const defaultTranslationConfig = {
translations: {
en: translations_namespaceObject.enTranslations
},
defaultLanguage: 'en',
disableAutoLanguageSwitch: false
};
const consent_types_consentTypes = [
{
defaultValue: true,
description: 'These trackers are used for activities that are strictly necessary to operate or deliver the service you requested from us and, therefore, do not require you to consent.',
disabled: true,
display: true,
gdprType: 1,
name: 'necessary'
},
{
defaultValue: false,
description: 'These trackers enable basic interactions and functionalities that allow you to access selected features of our service and facilitate your communication with us.',
display: false,
gdprType: 2,
name: 'functionality'
},
{
defaultValue: false,
description: 'These trackers help us to measure traffic and analyze your behavior to improve our service.',
display: false,
gdprType: 4,
name: 'measurement'
},
{
defaultValue: false,
description: 'These trackers help us to improve the quality of your user experience and enable interactions with external content, networks, and platforms.',
display: false,
gdprType: 3,
name: 'experience'
},
{
defaultValue: false,
description: 'These trackers help us to deliver personalized ads or marketing content to you, and to measure their performance.',
display: false,
gdprType: 5,
name: 'marketing'
}
];
const allConsentNames = consent_types_consentTypes.map((consent)=>consent.name);
const STORAGE_KEY = 'privacy-consent-storage';
const initial_state_initialState = {
debug: false,
config: {
pkg: 'c15t',
version: "2.2.1",
mode: 'Unknown'
},
consents: consent_types_consentTypes.reduce((acc, consent)=>{
acc[consent.name] = consent.defaultValue;
return acc;
}, {}),
selectedConsents: consent_types_consentTypes.reduce((acc, consent)=>{
acc[consent.name] = consent.defaultValue;
return acc;
}, {}),
consentInfo: null,
branding: 'c15t',
activeUI: 'none',
isLoadingConsentInfo: false,
hasFetchedBanner: false,
lastBannerFetchData: null,
consentCategories: [
'necessary'
],
callbacks: {},
locationInfo: null,
overrides: void 0,
legalLinks: {},
translationConfig: defaultTranslationConfig,
user: void 0,
networkBlocker: void 0,
storageConfig: void 0,
includeNonDisplayedConsents: false,
consentTypes: consent_types_consentTypes,
iframeBlockerConfig: {
disableAutomaticBlocking: false
},
scripts: [],
loadedScripts: {},
scriptIdMap: {},
model: 'opt-in',
policyBanner: {},
policyDialog: {},
policyCategories: null,
policyScopeMode: null,
initDataSource: null,
initDataSourceDetail: null,
iab: null,
reloadOnConsentRevoked: true,
ssrDataUsed: false,
ssrSkippedReason: null
};
function sanitizeIdentifier(value) {
if ('string' != typeof value) return;
const normalized = value.trim();
if ('' === normalized || 'undefined' === normalized || 'null' === normalized) return;
return normalized;
}
function sanitizeSubjectIdentifiers(identifiers) {
const externalId = sanitizeIdentifier(identifiers.externalId);
const identityProvider = sanitizeIdentifier(identifiers.identityProvider);
return {
...externalId ? {
externalId
} : {},
...identityProvider ? {
identityProvider
} : {}
};
}
function sanitizeConsentInfo(consentInfo) {
if (!consentInfo) return consentInfo;
const sanitized = {
...consentInfo
};
const { externalId, identityProvider } = sanitizeSubjectIdentifiers({
externalId: sanitized.externalId,
identityProvider: sanitized.identityProvider
});
if (externalId) sanitized.externalId = externalId;
else delete sanitized.externalId;
if (identityProvider) sanitized.identityProvider = identityProvider;
else delete sanitized.identityProvider;
return sanitized;
}
function isLegacyConsentFormat(data) {
if ('object' != typeof data || null === data) return false;
const record = data;
const consentInfo = record.consentInfo;
if (!consentInfo || 'object' != typeof consentInfo) return false;
const hasLegacyId = 'string' == typeof consentInfo.id;
const hasSubjectId = 'string' == typeof consentInfo.subjectId;
return hasLegacyId && !hasSubjectId;
}
function migrateLegacyStorage(config) {
const newKey = config?.storageKey || "c15t";
const legacyKey = STORAGE_KEY;
if (newKey === legacyKey) return;
try {
if ("u" > typeof window && window.localStorage) {
const existingData = window.localStorage.getItem(newKey);
if (existingData) return void window.localStorage.removeItem(legacyKey);
const legacyData = window.localStorage.getItem(legacyKey);
if (legacyData) {
window.localStorage.setItem(newKey, legacyData);
window.localStorage.removeItem(legacyKey);
getDebugLogger().log(`Migrated consent data from "${legacyKey}" to "${newKey}"`);
}
}
} catch (error) {
console.warn('[c15t] Failed to migrate legacy storage:', error);
}
}
function saveConsentToStorage(data, options, config) {
let localStorageSuccess = false;
let cookieSuccess = false;
const storageKey = config?.storageKey || "c15t";
const existing = getConsentFromStorage(config);
const mergedData = {
...existing,
...data,
consentInfo: sanitizeConsentInfo(data.consentInfo || existing?.consentInfo ? {
...existing?.consentInfo ?? {},
...data.consentInfo ?? {}
} : void 0),
iabCustomVendorConsents: data.iabCustomVendorConsents ?? existing?.iabCustomVendorConsents,
iabCustomVendorLegitimateInterests: data.iabCustomVendorLegitimateInterests ?? existing?.iabCustomVendorLegitimateInterests
};
const cleanedData = {
...mergedData
};
if (!cleanedData.iabCustomVendorConsents || 0 === Object.keys(cleanedData.iabCustomVendorConsents).length) delete cleanedData.iabCustomVendorConsents;
if (!cleanedData.iabCustomVendorLegitimateInterests || 0 === Object.keys(cleanedData.iabCustomVendorLegitimateInterests).length) delete cleanedData.iabCustomVendorLegitimateInterests;
try {
if ("u" > typeof window && window.localStorage) {
window.localStorage.setItem(storageKey, JSON.stringify(cleanedData));
localStorageSuccess = true;
}
} catch (error) {
console.warn('Failed to save consent to localStorage:', error);
}
try {
setCookie(storageKey, cleanedData, options, config);
cookieSuccess = true;
} catch (error) {
console.warn('Failed to save consent to cookie:', error);
}
if (!localStorageSuccess && !cookieSuccess) throw new Error('Failed to save consent to any storage method');
}
function normalizeConsentData(data) {
const consents = data.consents || {};
const normalizedConsents = {
...consents
};
for (const consentName of allConsentNames)normalizedConsents[consentName] = consents[consentName] ?? false;
return {
...data,
consents: normalizedConsents
};
}
function getConsentFromStorage(config) {
migrateLegacyStorage(config);
const storageKey = config?.storageKey || "c15t";
let localStorageData = null;
let cookieData = null;
try {
if ("u" > typeof window && window.localStorage) {
const stored = window.localStorage.getItem(storageKey);
if (stored) localStorageData = JSON.parse(stored);
}
} catch (error) {
console.warn('Failed to read consent from localStorage:', error);
}
try {
cookieData = getCookie(storageKey);
} catch (error) {
console.warn('Failed to read consent from cookie:', error);
}
let chosenData = null;
let chosenSource = null;
if (cookieData) {
chosenData = cookieData;
chosenSource = 'cookie';
} else if (localStorageData) {
chosenData = localStorageData;
chosenSource = 'localStorage';
}
if (chosenData && chosenSource) {
const isCrossSubdomain = config?.crossSubdomain === true || !!config?.defaultDomain;
if ('localStorage' !== chosenSource || cookieData) {
if ('cookie' === chosenSource) try {
if ("u" > typeof window && window.localStorage) {
let normalizedCookieData = chosenData;
if ('object' == typeof normalizedCookieData && null !== normalizedCookieData && 'consents' in normalizedCookieData) normalizedCookieData = normalizeConsentData(normalizedCookieData);
let normalizedLocalStorageData = null;
try {
const stored = window.localStorage.getItem(storageKey);
if (stored) {
const parsed = JSON.parse(stored);
normalizedLocalStorageData = 'object' == typeof parsed && null !== parsed && 'consents' in parsed ? normalizeConsentData(parsed) : parsed;
}
} catch {
normalizedLocalStorageData = null;
}
const cookieJson = JSON.stringify(normalizedCookieData);
const localStorageJson = JSON.stringify(normalizedLocalStorageData);
if (cookieJson !== localStorageJson) {
window.localStorage.setItem(storageKey, cookieJson);
if (normalizedLocalStorageData) if (isCrossSubdomain) getDebugLogger().log('Updated localStorage with consent from cookie (cross-subdomain mode)');
else getDebugLogger().log('Updated localStorage with consent from cookie');
else getDebugLogger().log('Synced consent from cookie to localStorage');
}
}
} catch (error) {
console.warn('[c15t] Failed to sync consent to localStorage:', error);
}
} else try {
setCookie(storageKey, chosenData, void 0, config);
getDebugLogger().log('Synced consent from localStorage to cookie');
} catch (error) {
console.warn('[c15t] Failed to sync consent to cookie:', error);
}
}
if (chosenData && isLegacyConsentFormat(chosenData)) {
getDebugLogger().log('Detected legacy consent format (v1.x). Re-consent required for v2.0.');
deleteConsentFromStorage(void 0, config);
return null;
}
if (chosenData && 'object' == typeof chosenData) {
const normalizedData = normalizeConsentData(chosenData);
if ('object' == typeof normalizedData && null !== normalizedData && 'consentInfo' in normalizedData) {
const dataWithConsentInfo = normalizedData;
dataWithConsentInfo.consentInfo = sanitizeConsentInfo(dataWithConsentInfo.consentInfo);
return dataWithConsentInfo;
}
return normalizedData;
}
return chosenData;
}
function deleteConsentFromStorage(options, config) {
const storageKey = config?.storageKey || "c15t";
try {
if ("u" > typeof window && window.localStorage) {
window.localStorage.removeItem(storageKey);
if (storageKey !== STORAGE_KEY) window.localStorage.removeItem(STORAGE_KEY);
}
} catch (error) {
console.warn('Failed to remove consent from localStorage:', error);
}
try {
deleteCookie(storageKey, options, config);
if (storageKey !== STORAGE_KEY) deleteCookie(STORAGE_KEY, options, config);
} catch (error) {
console.warn('Failed to remove consent cookie:', error);
}
}
const API_ENDPOINTS = {
INIT: '/init',
POST_SUBJECT: '/subjects',
GET_SUBJECT: '/subjects',
PATCH_SUBJECT: '/subjects',
CHECK_CONSENT: '/consents/check',
LIST_SUBJECTS: '/subjects'
};
async function withFallback(context, endpoint, method, options, fallbackFn) {
try {
const response = await fetcher(context, endpoint, {
method,
...options
});
if (response.ok) return response;
console.warn(`API request failed, falling back to offline mode for ${endpoint}`);
return fallbackFn(options);
} catch (error) {
console.warn(`Error calling ${endpoint}, falling back to offline mode:`, error);
return fallbackFn(options);
}
}
async function offlineFallbackForIdentifyUser(options) {
const pendingSubmissionsKey = 'c15t-pending-identify-submissions';
const newSubjectId = getIdentifySubjectId(options?.body);
try {
if ("u" > typeof window && options?.body && window.localStorage) {
let pendingSubmissions = [];
try {
const storedSubmissions = window.localStorage.getItem(pendingSubmissionsKey);
if (storedSubmissions) pendingSubmissions = JSON.parse(storedSubmissions);
} catch (e) {
console.warn('Error parsing pending identify submissions:', e);
pendingSubmissions = [];
}
const newSubmission = options.body;
const isDuplicate = pendingSubmissions.some((submission)=>getIdentifySubjectId(submission) === newSubjectId && submission.externalId === newSubmission.externalId);
if (!isDuplicate) {
pendingSubmissions.push(newSubmission);
window.localStorage.setItem(pendingSubmissionsKey, JSON.stringify(pendingSubmissions));
getDebugLogger().log('Queued identify user submission for retry on next page load');
}
}
} catch (error) {
console.warn('Failed to write to localStorage in identify offline fallback:', error);
}
const response = fetcher_createResponseContext(true, null, null, null);
if (options?.onSuccess) await options.onSuccess(response);
return response;
}
async function identifyUser(context, storageConfig, options) {
const { body, ...restOptions } = options;
const subjectId = getIdentifySubjectId(body);
if (!body || !subjectId) return {
ok: false,
data: null,
response: null,
error: {
message: 'Subject ID is required to identify user',
status: 400,
code: 'MISSING_SUBJECT_ID'
}
};
const existingData = getConsentFromStorage(storageConfig);
saveConsentToStorage({
consents: existingData?.consents || {},
consentInfo: {
...existingData?.consentInfo,
time: existingData?.consentInfo?.time || Date.now(),
subjectId,
externalId: body.externalId,
identityProvider: body.identityProvider
}
}, void 0, storageConfig);
const path = `${API_ENDPOINTS.PATCH_SUBJECT}/${subjectId}`;
const { subjectId: _subjectId, id: _legacySubjectId, ...patchBody } = body;
return withFallback(context, path, 'PATCH', {
...restOptions,
body: patchBody
}, async (fallbackOptions)=>{
const fullBody = {
subjectId,
...fallbackOptions?.body
};
return offlineFallbackForIdentifyUser({
...fallbackOptions,
body: fullBody
});
});
}
function resolveNoPolicyFallback() {
return types_namespaceObject.policyDefaults.offlineNoBanner();
}
function resolveFallbackPolicy(options) {
if (options.explicitPolicy) return options.explicitPolicy;
return types_namespaceObject.policyDefaults.offlineOptInBanner();
}
function buildFallbackInitData(options) {
const data = {
jurisdiction: options.jurisdiction ?? 'NONE',
location: {
countryCode: options.countryCode ?? null,
regionCode: options.regionCode ?? null
},
translations: {
language: options.language ?? 'en',
translations: options.translations ?? translations_namespaceObject.enTranslations
},
branding: 'c15t',
gvl: options.gvl ?? null
};
if (options.policy) data.policy = options.policy;
if (options.policyDecision) data.policyDecision = options.policyDecision;
if (options.policySnapshotToken) data.policySnapshotToken = options.policySnapshotToken;
return data;
}
async function createFallbackContext(options, data) {
const response = fetcher_createResponseContext(true, data, null, null);
if (options?.onSuccess) await options.onSuccess(response);
return response;
}
async function offlineFallbackForConsentBanner(options, iabConfig) {
const fallbackPolicy = resolveFallbackPolicy({});
let gvl = null;
if (iabConfig?.enabled && 'iab' === fallbackPolicy.model) try {
const fetchGVL = iabConfig._module?.fetchGVL;
if (fetchGVL) {
const acceptLanguage = options?.headers?.['accept-language'];
gvl = await fetchGVL(iabConfig.vendorIds, acceptLanguage ? {
headers: {
'accept-language': acceptLanguage
}
} : void 0);
}
} catch (error) {
console.warn('Failed to fetch GVL in offline fallback:', error);
}
const fallbackData = buildFallbackInitData({
countryCode: options?.headers?.['x-c15t-country'] ?? null,
regionCode: options?.headers?.['x-c15t-region'] ?? null,
gvl,
policy: fallbackPolicy
});
return createFallbackContext(options, fallbackData);
}
async function hosted_init_init(context, options, iabConfig) {
try {
const response = await fetcher(context, API_ENDPOINTS.INIT, {
method: 'GET',
...options
});
if (response.ok) return response;
console.warn('API request failed, falling back to offline mode for consent banner');
return offlineFallbackForConsentBanner(options, iabConfig);
} catch (error) {
console.warn('Error fetching consent banner info, falling back to offline mode:', error);
return offlineFallbackForConsentBanner(options, iabConfig);
}
}
const PENDING_CONSENT_KEY = 'c15t-pending-consent-submissions';
const PENDING_IDENTIFY_KEY = 'c15t-pending-identify-submissions';
function checkPendingConsentSubmissions(_context, processPendingSubmissions) {
const pendingSubmissionsKey = PENDING_CONSENT_KEY;
if ("u" < typeof window || !window.localStorage) return;
try {
window.localStorage.setItem('c15t-storage-test-key', 'test');
window.localStorage.removeItem('c15t-storage-test-key');
const pendingSubmissionsStr = window.localStorage.getItem(pendingSubmissionsKey);
if (!pendingSubmissionsStr) return;
const pendingSubmissions = JSON.parse(pendingSubmissionsStr);
if (!pendingSubmissions.length) return void window.localStorage.removeItem(pendingSubmissionsKey);
getDebugLogger().log(`Found ${pendingSubmissions.length} pending consent submission(s) to retry`);
setTimeout(()=>{
processPendingSubmissions(pendingSubmissions);
}, 2000);
} catch (error) {
console.warn('Failed to check for pending consent submissions:', error);
}
}
async function processPendingConsentSubmissions(context, submissions) {
const pendingSubmissionsKey = PENDING_CONSENT_KEY;
const maxRetries = 3;
const remainingSubmissions = [
...submissions
];
for(let i = 0; i < maxRetries && remainingSubmissions.length > 0; i++){
const successfulSubmissions = [];
for(let j = 0; j < remainingSubmissions.length; j++){
const submission = remainingSubmissions[j];
if (submission) try {
const { externalId: externalSubjectId, identityProvider } = sanitizeSubjectIdentifiers({
externalId: submission.externalSubjectId,
identityProvider: submission.identityProvider
});
const sanitizedSubmission = {
...submission,
...externalSubjectId ? {
externalSubjectId
} : {},
...identityProvider ? {
identityProvider
} : {}
};
if (!externalSubjectId) delete sanitizedSubmission.externalSubjectId;
if (!identityProvider) delete sanitizedSubmission.identityProvider;
getDebugLogger().log('Retrying consent submission:', submission);
const response = await fetcher(context, API_ENDPOINTS.POST_SUBJECT, {
method: 'POST',
body: sanitizedSubmission
});
if (response.ok) {
getDebugLogger().log('Successfully resubmitted consent');
successfulSubmissions.push(j);
}
} catch (error) {
console.warn('Failed to resend consent submission:', error);
}
}
for(let k = successfulSubmissions.length - 1; k >= 0; k--){
const index = successfulSubmissions[k];
if (void 0 !== index) remainingSubmissions.splice(index, 1);
}
if (0 === remainingSubmissions.length) break;
if (i < maxRetries - 1) await delay(1000 * (i + 1));
}
try {
if ("u" > typeof window && window.localStorage) if (remainingSubmissions.length > 0) {
window.localStorage.setItem(pendingSubmissionsKey, JSON.stringify(remainingSubmissions));
getDebugLogger().log(`${remainingSubmissions.length} consent submissions still pending for future retry`);
} else {
window.localStorage.removeItem(pendingSubmissionsKey);
getDebugLogger().log('All pending consent submissions processed successfully');
}
} catch (error) {
console.warn('Error updating pending submissions storage:', error);
}
}
function checkPendingIdentifySubmissions(_context, processPendingSubmissions) {
if ("u" < typeof window || !window.localStorage) return;
try {
const pendingSubmissionsStr = window.localStorage.getItem(PENDING_IDENTIFY_KEY);
if (!pendingSubmissionsStr) return;
const pendingSubmissions = JSON.parse(pendingSubmissionsStr);
if (!pendingSubmissions.length) return void window.localStorage.removeItem(PENDING_IDENTIFY_KEY);
getDebugLogger().log(`Found ${pendingSubmissions.length} pending identify user submission(s) to retry`);
setTimeout(()=>{
processPendingSubmissions(pendingSubmissions);
}, 2500);
} catch (error) {
console.warn('Failed to check for pending identify submissions:', error);
}
}
async function processPendingIdentifySubmissions(context, submissions) {
const maxRetries = 3;
const remainingSubmissions = [
...submissions
];
for(let i = 0; i < maxRetries && remainingSubmissions.length > 0; i++){
const successfulSubmissions = [];
for(let j = 0; j < remainingSubmissions.length; j++){
const submission = remainingSubmissions[j];
if (!submission) continue;
const subjectId = getIdentifySubjectId(submission);
if (!subjectId) {
console.warn('Dropping pending identify submission without a subject ID');
successfulSubmissions.push(j);
continue;
}
if (!submission.externalId) {
console.warn('Dropping pending identify submission without an externalId');
successfulSubmissions.push(j);
continue;
}
try {
getDebugLogger().log('Retrying identify user submission:', submission);
const path = `${API_ENDPOINTS.PATCH_SUBJECT}/${subjectId}`;
const { subjectId: _subjectId, id: _legacySubjectId, ...patchBody } = submission;
const response = await fetcher(context, path, {
method: 'PATCH',
body: patchBody
});
if (response.ok) {
getDebugLogger().log('Successfully resubmitted identify user');
successfulSubmissions.push(j);
}
} catch (error) {
console.warn('Failed to resend identify user submission:', error);
}
}
for(let k = successfulSubmissions.length - 1; k >= 0; k--){
const index = successfulSubmissions[k];
if (void 0 !== index) remainingSubmissions.splice(index, 1);
}
if (0 === remainingSubmissions.length) break;
if (i < maxRetries - 1) await delay(1000 * (i + 1));
}
try {
if ("u" > typeof window && window.localStorage) if (remainingSubmissions.length > 0) {
window.localStorage.setItem(PENDING_IDENTIFY_KEY, JSON.stringify(remainingSubmissions));
getDebugLogger().log(`${remainingSubmissions.length} identify submissions still pending for future retry`);
} else {
window.localStorage.removeItem(PENDING_IDENTIFY_KEY);
getDebugLogger().log('All pending identify submissions processed successfully');
}
} catch (error) {
console.warn('Error updating pending identify submissions storage:', error);
}
}
async function offlineFallbackForSetConsent(storageConfig, options) {
const pendingSubmissionsKey = 'c15t-pending-consent-submissions';
const subjectId = options?.body?.subjectId;
try {
if ("u" > typeof window) {
saveConsentToStorage({
consents: options?.body?.preferences || {},
consentInfo: {
time: Date.now(),
subjectId,
externalId: options?.body?.externalSubjectId,
identityProvider: options?.body?.identityProvider
}
}, void 0, storageConfig);
if (options?.body && window.localStorage) {
let pendingSubmissions = [];
try {
const storedSubmissions = window.localStorage.getItem(pendingSubmissionsKey);
if (storedSubmissions) pendingSubmissions = JSON.parse(storedSubmissions);
} catch (e) {
console.warn('Error parsing pending submissions:', e);
pendingSubmissions = [];
}
const newSubmission = options.body;
const isDuplicate = pendingSubmissions.some((submission)=>JSON.stringify(submission) === JSON.stringify(newSubmission));
if (!isDuplicate) {
pendingSubmissions.push(newSubmission);
window.localStorage.setItem(pendingSubmissionsKey, JSON.stringify(pendingSubmissions));
getDebugLogger().log('Queued consent submission for retry on next page load');
}
}
}
} catch (error) {
console.warn('Failed to write to localStorage in offline fallback:', error);
}
const response = fetcher_createResponseContext(true, null, null, null);
if (options?.onSuccess) await options.onSuccess(response);
return response;
}
async function set_consent_setConsent(context, storageConfig, options) {
saveConsentToStorage({
consents: options?.body?.preferences || {},
consentInfo: {
time: Date.now(),
subjectId: options?.body?.subjectId,
externalId: options?.body?.externalSubjectId,
identityProvider: options?.body?.identityProvider
}
}, void 0, storageConfig);
const response = await withFallback(context, API_ENDPOINTS.POST_SUBJECT, 'POST', options, async (fallbackOptions)=>offlineFallbackForSetConsent(storageConfig, fallbackOptions));
return response;
}
class C15tClient {
backendURL;
storageConfig;
iabConfig;
headers;
customFetch;
corsMode;
retryConfig;
fetcherContext;
constructor(options){
this.backendURL = options.backendURL.endsWith('/') ? options.backendURL.slice(0, -1) : options.backendURL;
this.headers = {
'Content-Type': 'application/json',
...options.headers
};
this.customFetch = options.customFetch;
this.corsMode = options.corsMode || 'cors';
this.storageConfig = options.storageConfig;
this.iabConfig = options.iabConfig;
this.retryConfig = {
maxRetries: options.retryConfig?.maxRetries ?? DEFAULT_RETRY_CONFIG.maxRetries ?? 3,
initialDelayMs: options.retryConfig?.initialDelayMs ?? DEFAULT_RETRY_CONFIG.initialDelayMs ?? 100,
backoffFactor: options.retryConfig?.backoffFactor ?? DEFAULT_RETRY_CONFIG.backoffFactor ?? 2,
retryableStatusCodes: options.retryConfig?.retryableStatusCodes ?? DEFAULT_RETRY_CONFIG.retryableStatusCodes,
nonRetryableStatusCodes: options.retryConfig?.nonRetryableStatusCodes ?? DEFAULT_RETRY_CONFIG.nonRetryableStatusCodes,
shouldRetry: options.retryConfig?.shouldRetry ?? DEFAULT_RETRY_CONFIG.shouldRetry,
retryOnNetworkError: options.retryConfig?.retryOnNetworkError ?? DEFAULT_RETRY_CONFIG.retryOnNetworkError
};
this.fetcherContext = {
backendURL: this.backendURL,
headers: this.headers,
customFetch: this.customFetch,
corsMode: this.corsMode,
retryConfig: this.retryConfig
};
this.checkPendingConsentSubmissions();
this.checkPendingIdentifySubmissions();
}
async init(options) {
return hosted_init_init(this.fetcherContext, options, this.iabConfig);
}
async setConsent(options) {
return set_consent_setConsent(this.fetcherContext, this.storageConfig, options);
}
async identifyUser(options) {
return identifyUser(this.fetcherContext, this.storageConfig, options);
}
async $fetch(path, options) {
return fetcher(this.fetcherContext, path, options);
}
checkPendingConsentSubmissions() {
checkPendingConsentSubmissions(this.fetcherContext, (submissions)=>this.processPendingConsentSubmissions(submissions));
}
async processPendingConsentSubmissions(submissions) {
return processPendingConsentSubmissions(this.fetcherContext, submissions);
}
checkPendingIdentifySubmissions() {
checkPendingIdentifySubmissions(this.fetcherContext, (submissions)=>this.processPendingIdentifySubmissions(submissions));
}
async processPendingIdentifySubmissions(submissions) {
return processPendingIdentifySubmissions(this.fetcherContext, submissions);
}
}
function checkJurisdiction(countryCode, regionCode) {
const jurisdictions = {
EU: new Set([
'AT',
'BE',
'BG',
'HR',
'CY',
'CZ',
'DK',
'EE',
'FI',
'FR',
'DE',
'GR',
'HU',
'IE',
'IT',
'LV',
'LT',
'LU',
'MT',
'NL',
'PL',
'PT',
'RO',
'SK',
'SI',
'ES',
'SE'
]),
EEA: new Set([
'IS',
'NO',
'LI'
]),
UK: new Set([
'GB'
]),
CH: new Set([
'CH'
]),
BR: new Set([
'BR'
]),
CA: new Set([
'CA'
]),
AU: new Set([
'AU'
]),
JP: new Set([
'JP'
]),
KR: new Set([
'KR'
]),
CA_QC_REGIONS: new Set([
'QC'
])
};
let jurisdictionCode = 'NONE';
if (countryCode) {
const normalizedCountryCode = countryCode.toUpperCase();
const normalizedRegionCode = regionCode && 'string' == typeof regionCode ? (regionCode.includes('-') ? regionCode.split('-').pop() : regionCode).toUpperCase() : null;
if ('CA' === normalizedCountryCode && normalizedRegionCode && jurisdictions.CA_QC_REGIONS.has(normalizedRegionCode)) return 'QC_LAW25';
const jurisdictionMap = [
{
sets: [
jurisdictions.EU,
jurisdictions.EEA,
jurisdictions.UK
],
code: 'GDPR'
},
{
sets: [
jurisdictions.CH
],
code: 'CH'
},
{
sets: [
jurisdictions.BR
],
code: 'BR'
},
{
sets: [
jurisdictions.CA
],
code: 'PIPEDA'
},
{
sets: [
jurisdictions.AU
],
code: 'AU'
},
{
sets: [
jurisdictions.JP
],
code: 'APPI'
},
{
sets: [
jurisdictions.KR
],
code: 'PIPA'
}
];
for (const { sets, code } of jurisdictionMap)if (sets.some((set)=>set.has(normalizedCountryCode))) {
jurisdictionCode = code;
break;
}
}
return jurisdictionCode;
}
function utils_createResponseContext(data = null) {
return createResponseContext(true, data);
}
async function handleOfflineResponse(options) {
const emptyResponse = utils_createResponseContext();
if (options?.onSuccess) await options.onSuccess(emptyResponse);
return emptyResponse;
}
const DEFAULT_PROFILE = 'default';
function normalizeLanguage(value) {
if (!value) return;
const normalized = value.split(',')[0]?.split(';')[0]?.trim().toLowerCase();
if (!normalized) return;
return normalized.split('-')[0] ?? void 0;
}
function getProfileLanguages(profiles, profile) {
return Object.keys(profiles[profile]?.translations ?? {}).sort();
}
function resolveActiveProfile(input) {
const requestedProfile = input.policyProfile ?? input.defaultProfile;
return input.profiles[requestedProfile] ? requestedProfile : input.defaultProfile;
}
function resolveProfileFallbackLanguage(input) {
const configuredFallbackLanguage = normalizeLanguage(input.profile?.fallbackLanguage) ?? 'en';
const profileLanguages = Object.keys(input.profile?.translations ?? {}).sort();
if (profileLanguages.includes(configuredFallbackLanguage)) return configuredFallbackLanguage;
if (profileLanguages.includes('en')) return 'en';
return profileLanguages[0] ?? configuredFallbackLanguage;
}
function resolveOfflinePolicyTranslations(input) {
const profiles = input.i18n.messages ?? {};
const defaultProfile = input.i18n.defaultProfile ?? DEFAULT_PROFILE;
const profile = resolveActiveProfile({
profiles,
defaultProfile,
policyProfile: input.policyI18n?.messageProfile
});
const profileLanguages = getProfileLanguages(profiles, profile);
const fallbackLanguage = resolveProfileFallbackLanguage({
profile: profiles[profile]
});
const policyLanguage = normalizeLanguage(input.policyI18n?.language);
const requestedLanguage = policyLanguage ?? (0, translations_namespaceObject.selectLanguage)(profileLanguages, {
header: input.acceptLanguage,
fallback: fallbackLanguage
});
const resolvedLanguage = profiles[profile]?.translations[requestedLanguage] ? requestedLanguage : fallbackLanguage;
const base = translations_namespaceObject.enTranslations;
const custom = profiles[profile]?.translations[resolvedLanguage];
return {
language: resolvedLanguage,
translations: custom ? (0, translations_namespaceObject.deepMergeTranslations)(base, custom) : base
};
}
function resolveConfiguredFallbackLanguage(translations, defaultLanguage) {
const configuredLanguages = Object.keys(translations).sort();
const normalizedDefault = defaultLanguage?.toLowerCase();
if (normalizedDefault && configuredLanguages.includes(normalizedDefault)) return normalizedDefault;
if (configuredLanguages.includes('en')) return 'en';
return configuredLanguages[0] ?? 'en';
}
async function offline_init_init(initialTranslationConfig, options, iabConfig, policyConfig) {
const country = options?.headers?.['x-c15t-country'] ?? 'GB';
const region = options?.headers?.['x-c15t-region'] ?? null;
const headerLanguage = options?.headers?.['accept-language'] ?? null;
const jurisdictionCode = checkJurisdiction(country, region);
const configuredPolicies = policyConfig?.policyPacks;
const hasExplicitPolicies = policyConfig?.policyPacks !== void 0;
const i18nValidation = (0, types_namespaceObject.validatePolicyI18nConfig)({
i18n: policyConfig?.i18n,
policies: configuredPolicies
});
for (const warning of i18nValidation.warnings)console.warn(`[c15t] offlinePolicy.i18n: ${warning}`);
if (i18nValidation.errors.length > 0) throw new Error(`Invalid offlinePolicy.i18n configuration:\n${i18nValidation.errors.map((error)=>`- ${error}`).join('\n')}`);
if (configuredPolicies && configuredPolicies.length > 0) (0, types_namespaceObject.validatePolicies)(configuredPolicies, {
iabEnabled: iabConfig?.enabled === true
});
const resolvedPolicyDecision = configuredPolicies && configuredPolicies.length > 0 ? await (0, types_namespaceObject.resolvePolicyDecision)({
policies: configuredPolicies,
countryCode: country,
regionCode: region,
jurisdiction: jurisdictionCode,
iabEnabled: iabConfig?.enabled === true
}) : void 0;
const shouldUseSyntheticFallbackDefaults = !policyConfig?.policy && !resolvedPolicyDecision && !hasExplicitPolicies;
const resolvedPolicyConfig = {
...policyConfig,
policy: policyConfig?.policy ?? resolvedPolicyDecision?.policy ?? (hasExplicitPolicies ? resolveNoPolicyFallback() : void 0) ?? (shouldUseSyntheticFallbackDefaults ? resolveFallbackPolicy({}) : void 0),
policyDecision: policyConfig?.policyDecision ?? (resolvedPolicyDecision ? {
policyId: resolvedPolicyDecision.policy.id,
fingerprint: resolvedPolicyDecision.fingerprint,
matchedBy: resolvedPolicyDecision.matchedBy,
country,
region,
jurisdiction: jurisdictionCode
} : void 0)
};
let language;
let translationsForLanguage;
if (policyConfig?.i18n?.messages && Object.keys(policyConfig.i18n.messages).length > 0) {
const resolvedTranslations = resolveOfflinePolicyTranslations({
acceptLanguage: headerLanguage,
i18n: policyConfig.i18n,
policyI18n: resolvedPolicyConfig.policy?.i18n
});
language = resolvedTranslations.language;
translationsForLanguage = resolvedTranslations.translations;
} else if (initialTranslationConfig?.translations && Object.keys(initialTranslationConfig.translations).length > 0) {
const customTranslations = initialTranslationConfig.translations;
const availableLanguages = Object.keys(customTranslations);
const fallbackLanguage = resolveConfiguredFallbackLanguage(customTranslations, initialTranslationConfig.defaultLanguage);
language = (0, translations_namespaceObject.selectLanguage)(availableLanguages, {
header: headerLanguage,
fallback: fallbackLanguage
});
const base = translations_namespaceObject.enTranslations;
const customForLanguage = customTranslations[language] ?? {};
translationsForLanguage = (0, translations_namespaceObject.deepMergeTranslations)(base, customForLanguage);
} else {
const availableLanguages = Object.keys(defaultTranslationConfig.translations);
const fallbackLanguage = defaultTranslationConfig.defaultLanguage ?? 'en';
language = (0, translations_namespaceObject.selectLanguage)(availableLanguages, {
header: headerLanguage,
fallback: fallbackLanguage
});
translationsForLanguage = defaultTranslationConfig.translations[language];
}
let gvl = null;
if (iabConfig?.enabled && resolvedPolicyConfig.policy?.model === 'iab') if (iabConfig.gvl) gvl = iabConfig.gvl;
else try {
const fetchGVL = iabConfig._module?.fetchGVL;
if (fetchGVL) gvl = await fetchGVL(iabConfig.vendorIds, {
headers: {
'accept-language': language
}
});
} catch (error) {
console.warn('Failed to fetch GVL in offline mode:', error);
}
const responseData = buildFallbackInitData({
jurisdiction: jurisdictionCode,
countryCode: country,
regionCode: region,
language,
translations: translationsForLanguage,
gvl,
policy: resolvedPolicyConfig.policy,
policyDecision: resolvedPolicyConfig.policyDecision,
policySnapshotToken: resolvedPolicyConfig.policySnapshotToken
});
const response = utils_createResponseContext(responseData);
if (options?.onSuccess) await options.onSuccess(response);
return response;
}
async function offline_set_consent_setConsent(storageConfig, options) {
const subjectId = options?.body?.subjectId;
try {
if ("u" > typeof window) saveConsentToStorage({
consentInfo: {
time: Date.now(),
subjectId,
externalId: options?.body?.externalSubjectId,
identityProvider: options?.body?.identityProvider
},
consents: options?.body?.preferences || {}
}, void 0, storageConfig);
} catch (error) {
console.warn('Failed to write to storage:', error);
}
return await handleOfflineResponse(options);
}
class OfflineClient {
storageConfig;
initialTranslationConfig;
iabConfig;
policyConfig;
constructor(storageConfig, initialTranslationConfig, iabConfig, policyConfig){
this.storageConfig = storageConfig;
this.initialTranslationConfig = initialTranslationConfig;
this.iabConfig = iabConfig;
this.policyConfig = policyConfig;
}
async init(options) {
return offline_init_init(this.initialTranslationConfig, options, this.iabConfig, this.policyConfig);
}
async setConsent(options) {
return offline_set_consent_setConsent(this.storageConfig, options);
}
async identifyUser(options) {
console.warn('identifyUser called in offline mode - external ID will not be linked');
return handleOfflineResponse(options);
}
async $fetch(_path, options) {
return await handleOfflineResponse(options);
}
}
const DEFAULT_BACKEND_URL = '/api/c15t';
const DEFAULT_CLIENT_MODE = 'hosted';
let hasWarnedAboutLegacyC15tMode = false;
function normalizeClientMode(mode) {
if ('c15t' === mode) {
const nodeEnv = "u" > typeof globalThis && 'process' in globalThis ? globalThis.process?.env?.NODE_ENV : void 0;
if (!hasWarnedAboutLegacyC15tMode && 'production' !== nodeEnv) {
hasWarnedAboutLegacyC15tMode = true;
console.warn("[c15t] `mode: 'c15t'` is deprecated and will be removed in a future major release. Use `mode: 'hosted'` instead.");
}
return DEFAULT_CLIENT_MODE;
}
if ('offline' === mode || 'custom' === mode) return mode;
return DEFAULT_CLIENT_MODE;
}
function assertUnreachableMode(mode) {
throw new Error(`Unsupported client mode: ${String(mode)}`);
}
function resolveOfflinePolicyOption(options) {
if (void 0 !== options.offlinePolicy) return options.offlinePolicy;
return options.store?.offlinePolicy;
}
const clientRegistry = new Map();
function serializeStorageConfig(storageConfig) {
if (!storageConfig) return '';
const sorted = Object.keys(storageConfig).sort().map((key)=>{
const value = storageConfig[key];
if (null == value) return `${key}:null`;
return `${key}:${String(value)}`;
}).join('|');
return sorted;
}
function getClientCacheKey(options) {
const normalizedMode = normalizeClientMode(options.mode);
const resolvedOfflinePolicy = resolveOfflinePolicyOption(options);
const storageConfigPart = serializeStorageConfig(options.storageConfig);
const storageKey = storageConfigPart ? `:storage:${storageConfigPart}` : '';
if ('offline' === normalizedMode) {
const initialTranslations = options.store?.initialTranslationConfig?.translations;
const initialDefaultLanguage = options.store?.initialTranslationConfig?.defaultLanguage;
let translationPart = '';
translationPart = initialTranslations ? `:translations:${Object.keys(initialTranslations).sort().join(',')}` : '';
let defaultLanguagePart = '';
defaultLanguagePart = initialDefaultLanguage ? `:default-language:${initialDefaultLanguage}` : '';
const iabConfig = options.store?.iab;
let iabPart = '';
iabPart = iabConfig?.enabled ? ':iab:enabled' : '';
let offlinePolicyPart = '';
if (resolvedOfflinePolicy) offlinePolicyPart = `:policy:${(0, types_namespaceObject.createDeterministicFingerprintSync)(resolvedOfflinePolicy)}`;
return `offline${storageKey}${translationPart}${defaultLanguagePart}${iabPart}${offlinePolicyPart}`;
}
if ('custom' === normalizedMode) {
const handlers = 'endpointHandlers' in options ? options.endpointHandlers : void 0;
const handlerKeys = Object.keys(handlers || {}).sort().join(',');
return `custom:${handlerKeys}${storageKey}`;
}
let headersPart = '';
if ('headers' in options && options.headers) {
const headerKeys = Object.keys(options.headers).sort();
headersPart = `:headers:${headerKeys.map((k)=>`${k}=${options.headers?.[k]}`).join(',')}`;
}
return `hosted:${options.backendURL || ''}${headersPart}${storageKey}`;
}
function configureConsentManager(options) {
const cacheKey = getClientCacheKey(options);
if (clientRegistry.has(cacheKey)) {
if ('offline' !== options.mode && 'custom' !== options.mode && 'headers' in options && options.headers) {
const existingClient = clientRegistry.get(cacheKey);
if (existingClient instanceof C15tClient) existingClient.headers = {
'Content-Type': 'application/json',
...options.headers
};
}
const existingClient = clientRegistry.get(cacheKey);
if (existingClient) return new Proxy(existingClient, {
get (target, prop) {
return target[prop];
}
});
}
const mode = normalizeClientMode(options.mode);
let client;
switch(mode){
case 'custom':
{
const customOptions = options;
client = new CustomClient({
endpointHandlers: customOptions.endpointHandlers
});
break;
}
case 'offline':
{
const iabConfig = options.store?.iab;
const policyConfig = resolveOfflinePolicyOption(options);
client = new OfflineClient(options.storageConfig, options.store?.initialTranslationConfig, iabConfig ? {
enabled: iabConfig.enabled,
vendorIds: iabConfig.vendors,
gvl: iabConfig.gvl,
_module: iabConfig._module
} : void 0, policyConfig);
break;
}
case 'hosted':
{
const hostedOptions = options;
const iabConfig = options.store?.iab;
client = new C15tClient({
backendURL: hostedOptions.backendURL || DEFAULT_BACKEND_URL,
headers: hostedOptions.headers,
customFetch: hostedOptions.customFetch,
retryConfig: hostedOptions.retryConfig,
storageConfig: options.storageConfig,
iabConfig: iabConfig ? {
enabled: iabConfig.enabled,
vendorIds: iabConfig.vendors,
gvl: iabConfig.gvl,
_module: iabConfig._module
} : void 0
});
break;
}
default:
client = assertUnreachableMode(mode);
break;
}
clientRegistry.set(cacheKey, client);
return client;
}
function clearClientRegistry() {
clientRegistry.clear();
}
const BASE58_ALPHABET = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz';
function base58Encode(bytes) {
const base = BigInt(58);
let num = BigInt(0);
for (const byte of bytes)num = num * BigInt(256) + BigInt(byte);
const chars = [];
while(num > 0){
const remainder = num % base;
chars.unshift(BASE58_ALPHABET.charAt(Number(remainder)));
num /= base;
}
for (const byte of bytes)if (0 === byte) chars.unshift(BASE58_ALPHABET.charAt(0));
else break;
return chars.join('') || BASE58_ALPHABET.charAt(0);
}
const EPOCH_TIMESTAMP = 1700000000000;
function generateSubjectId() {
const buf = crypto.getRandomValues(new Uint8Array(20));
const t = Date.now() - EPOCH_TIMESTAMP;
const high = Math.floor(t / 0x100000000);
const low = t >>> 0;
buf[0] = high >>> 24 & 255;
buf[1] = high >>> 16 & 255;
buf[2] = high >>> 8 & 255;
buf[3] = 255 & high;
buf[4] = low >>> 24 & 255;
buf[5] = low >>> 16 & 255;
buf[6] = low >>> 8 & 255;
buf[7] = 255 & low;
return `sub_${base58Encode(buf)}`;
}
function isValidSubjectId(id) {
if (!id.startsWith('sub_')) return false;
const encoded = id.slice(4);
if (0 === encoded.length) return false;
for (const char of encoded)if (!BASE58_ALPHABET.includes(char)) return false;
return true;
}
function isConsentCategory(value) {
return allConsentNames.includes(value);
}
function flattenLayout(layout) {
if (!layout) return [];
return layout.flatMap((group)=>Array.isArray(group) ? group : [
group
]);
}
function applyPolicyPurposeAllowlist(preferences, allowedPurposeIds) {
if (!allowedPurposeIds || 0 === allowedPurposeIds.length || allowedPurposeIds.includes('*')) return preferences;
const allowed = new Set([
'necessary',
...allowedPurposeIds
]);
const next = {};
for (const [key, value] of Object.entries(preferences))next[key] = allowed.has(key) ? value : false;
return next;
}
function stripDisallowedPreferenceKeys(preferences, allowedPurposeIds) {
if (!allowedPurposeIds || 0 === allowedPurposeIds.length || allowedPurposeIds.includes('*')) return preferences;
const allowed = new Set([
'necessary',
...allowedPurposeIds
]);
const next = {};
for (const [key, value] of Object.entries(preferences))if (allowed.has(key)) next[key] = value;
return next;
}
function filterConsentCategoriesByPolicy(categories, allowedPurposeIds) {
const uniqueCategories = Array.from(new Set(categories));
if (!allowedPurposeIds || 0 === allowedPurposeIds.length || allowedPurposeIds.includes('*')) return uniqueCategories;
const allowedCategories = new Set([
'necessary',
...allowedPurposeIds.filter(isConsentCategory)
]);
const filtered = uniqueCategories.filter((category)=>allowedCategories.has(category));
if (!filtered.includes('necessary')) filtered.unshift('necessary');
return filtered;
}
function shouldEnforcePolicyCategoryScope(allowedPurposeIds, scopeMode = 'permissive') {
return 'strict' === scopeMode && Array.isArray(allowedPurposeIds) && allowedPurposeIds.length > 0 && !allowedPurposeIds.includes('*');
}
function applyPolicyScopeForRuntimeGating(consents, _allowedPurposeIds, _scopeMode = 'permissive') {
return consents;
}
function getEffectivePolicy(initData) {
return initData?.policy;
}
function validateUIAgainstPolicy(params) {
const { policy, state } = params;
if (!policy) return [];
const issues = [];
if (policy.ui?.mode && state.mode !== policy.ui.mode) issues.push({
code: 'mode_mismatch',
message: `UI mode "${state.mode}" does not match policy mode "${policy.ui.mode}".`
});
const surfacePolicy = 'banner' === state.mode ? policy.ui?.banner : 'dialog' === state.mode ? policy.ui?.dialog : void 0;
const allowedActions = surfacePolicy?.allowedActions;
if (allowedActions && allowedActions.length > 0) {
const disallowed = state.actions.filter((action)=>!allowedActions.includes(action));
if (disallowed.length > 0) issues.push({
code: 'action_not_allowed',
message: `UI renders actions not allowed by policy: ${disallowed.join(', ')}`
});
}
const expectedLayout = surfacePolicy?.layout;
if (expectedLayout && expectedLayout.length > 0) {
const expected = flattenLayout(expectedLayout);
const actual = state.actions.filter((action)=>expected.includes(action));
if (expected.join('|') !== actual.join('|')) issues.push({
code: 'group_layout_mismatch',
message: `UI action order "${actual.join(', ')}" does not match policy layout "${expected.join(', ')}".`
});
}
if (surfacePolicy?.direction && state.direction) {
if (surfacePolicy.direction !== state.direction) issues.push({
code: 'direction_mismatch',
message: `UI action direction "${state.direction}" does not match policy action direction "${surfacePolicy.direction}".`
});
}
if (surfacePolicy?.uiProfile && state.uiProfile) {
if (surfacePolicy.uiProfile !== state.uiProfile) issues.push({
code: 'ui_profile_mismatch',
message: `UI profile "${state.uiProfile}" does not match policy UI profile "${surfacePolicy.uiProfile}".`
});
}
if ('boolean' == typeof surfacePolicy?.scrollLock && 'boolean' == typeof state.scrollLock && surfacePolicy.scrollLock !== state.scrollLock) issues.push({
code: 'scroll_lock_mismatch',
message: `UI scroll lock "${state.scrollLock ? 'on' : 'off'}" does not match policy scroll lock "${surfacePolicy.scrollLock ? 'on' : 'off'}".`
});
return issues;
}
function validateNonEmptyConditions(conditions, conditionType) {
if (0 === conditions.length) throw new TypeError(`${conditionType} condition cannot be empty`);
}
function evaluateCategoryCondition(category, consents) {
if (!(category in consents)) throw new Error(`Consent category "${category}" not found in consent state`);
return consents[category] || false;
}
function evaluateAndCondition(andCondition, consents) {
const andConditions = Array.isArray(andCondition) ? andCondition : [
andCondition
];
validateNonEmptyConditions(andConditions, 'AND');
return andConditions.every((subCondition)=>evaluateConditionRecursive(subCondition, consents));
}
function evaluateOrCondition(orCondition, consents) {
const orConditions = Array.isArray(orCondition) ? orCondition : [
orCondition
];
validateNonEmptyConditions(orConditions, 'OR');
return orConditions.some((subCondition)=>evaluateConditionRecursive(subCondition, consents));
}
function evaluateConditionRecursive(condition, consents) {
if ('string' == typeof condition) return evaluateCategoryCondition(condition, consents);
if ('object' == typeof condition && null !== condition) {
if ('and' in condition) return evaluateAndCondition(condition.and, consents);
if ('or' in condition) return evaluateOrCondition(condition.or, consents);
if ('not' in condition) return !evaluateConditionRecursive(condition.not, consents);
}
throw new TypeError(`Invalid condition structure: ${JSON.stringify(condition)}`);
}
function has(condition, consents, options) {
const runtimeConsents = options ? applyPolicyScopeForRuntimeGating(consents, options.policyCategories, options.policyScopeMode) : consents;
return evaluateConditionRecursive(condition, runtimeConsents);
}
function extractConsentNamesFromCondition(condition) {
const categories = new Set();
function recurse(cond) {
if ('string' == typeof cond) return void categories.add(cond);
if ('object' == typeof cond && null !== cond) {
if ('and' in cond) {
const conditions = Array.isArray(cond.and) ? cond.and : [
cond.and
];
conditions.forEach(recurse);
} else if ('or' in cond) {
const conditions = Array.isArray(cond.or) ? cond.or : [
cond.or
];
conditions.forEach(recurse);
} else if ('not' in cond) recurse(cond.not);
}
}
recurse(condition);
return Array.from(categories);
}
function createDefaultConsentState() {
return {
experience: false,
functionality: false,
marketing: false,
measurement: false,
necessary: true
};
}
function determineRequiredConsent(iframe) {
const categoryAttr = iframe.getAttribute('data-category');
if (!categoryAttr) return;
if (!allConsentNames.includes(categoryAttr)) throw new Error(`Invalid category attribute "${categoryAttr}" on iframe. Must be one of: ${allConsentNames.join(', ')}`);
return categoryAttr;
}
function processIframeElement(iframe, consents) {
const dataSrc = iframe.getAttribute('data-src');
const requiredConsent = determineRequiredConsent(iframe);
if (!requiredConsent) return;
const hasConsent = has(requiredConsent, consents);
if (hasConsent) {
if (dataSrc && !iframe.src) {
iframe.src = dataSrc;
iframe.removeAttribute('data-src');
}
} else if (iframe.src) iframe.removeAttribute('src');
}
function createIframeBlocker(config = {}, initialConsents) {
const blockerConfig = {
disableAutomaticBlocking: false,
...config
};
let consents = initialConsents || createDefaultConsentState();
function processIframes() {
const iframes = document.querySelectorAll('iframe');
iframes.forEach((iframe)=>{
processIframeElement(iframe, consents);
});
}
function setupMutationObserver() {
const observer = new MutationObserver((mutations)=>{
mutations.forEach((mutation)=>{
mutation.addedNodes.forEach((node)=>{
if (node.nodeType === Node.ELEMENT_NODE) {
const element = node;
if (element.tagName && 'IFRAME' === element.tagName.toUpperCase()) processIframeElement(element, consents);
const iframes = element.querySelectorAll?.('iframe');
if (iframes) iframes.forEach((iframe)=>{
processIframeElement(iframe, consents);
});
}
});
});
});
observer.observe(document.body, {
childList: true,
subtree: true
});
return observer;
}
let mutationObserver = null;
if (!blockerConfig.disableAutomaticBlocking) {
processIframes();
mutationObserver = setupMutationObserver();
}
return {
updateConsents: (newConsents)=>{
consents = {
...consents,
...newConsents
};
processIframes();
},
processIframes,
destroy: ()=>{
if (mutationObserver) {
mutationObserver.disconnect();
mutationObserver = null;
}
}
};
}
function getIframeConsentCategories() {
if ("u" < typeof document) return [];
const iframes = document.querySelectorAll('iframe[data-category]');
const categories = new Set();
if (!iframes) return [];
iframes.forEach((iframe)=>{
const categoryAttr = iframe.getAttribute('data-category');
if (!categoryAttr) return;
const category = categoryAttr.trim();
if (allConsentNames.includes(category)) categories.add(category);
});
return Array.from(categories);
}
function processAllIframes(consents) {
if ("u" < typeof document) return;
const iframes = document.querySelectorAll('iframe');
if (!iframes) return;
iframes.forEach((iframe)=>{
processIframeElement(iframe, consents);
});
}
function setupIframeObserver(getConsents, onCategoriesDiscovered) {
const observer = new MutationObserver((mutations)=>{
const currentConsents = getConsents();
let hasNewCategories = false;
mutations.forEach((mutation)=>{
mutation.addedNodes.forEach((node)=>{
if (node.nodeType === Node.ELEMENT_NODE) {
const element = node;
if (element.tagName && 'IFRAME' === element.tagName.toUpperCase()) {
processIframeElement(element, currentConsents);
if (element.hasAttribute('data-category')) hasNewCategories = true;
}
const iframes = element.querySelectorAll?.('iframe');
if (iframes && iframes.length > 0) iframes.forEach((iframe)=>{
processIframeElement(iframe, currentConsents);
if (iframe.hasAttribute('data-category')) hasNewCategories = true;
});
}
});
});
if (hasNewCategories && onCategoriesDiscovered) {
const categories = getIframeConsentCategories();
if (categories.length > 0) onCategoriesDiscovered(categories);
}
});
observer.observe(document.body, {
childList: true,
subtree: true
});
return observer;
}
const DEFAULT_POLICY_ACTIONS = [
'reject',
'accept',
'customize'
];
function dedupeActions(actions) {
if (!actions || 0 === actions.length) return [];
return [
...new Set(actions)
];
}
function resolvePolicyAllowedActions(params) {
const allowed = dedupeActions(params.allowedActions);
return allowed.length > 0 ? allowed : [
...DEFAULT_POLICY_ACTIONS
];
}
function flattenPolicyActionGroups(layout) {
if (!layout || 0 === layout.length) return [];
return layout.flatMap((group)=>Array.isArray(group) ? group : [
group
]);
}
function resolvePolicyActionGroups(params) {
const allowedActions = dedupeActions(params.allowedActions);
if (0 === allowedActions.length) return [];
if (!params.layout || 0 === params.layout.length) return [
[
...allowedActions
]
];
const allowedSet = new Set(allowedActions);
const groups = [];
const seen = new Set();
for (const group of params.layout){
const actions = dedupeActions(Array.isArray(group) ? group : [
group
]).filter((action)=>{
if (!allowedSet.has(action) || seen.has(action)) return false;
seen.add(action);
return true;
});
if (actions.length > 0) groups.push(actions);
}
return groups.length > 0 ? groups : [
[
...allowedActions
]
];
}
function resolvePolicyOrderedActions(params) {
return flattenPolicyActionGroups(resolvePolicyActionGroups({
allowedActions: params.allowedActions,
layout: params.layout
}));
}
function resolvePolicyPrimaryActions(params) {
const defaultPrimary = params.orderedActions.includes('customize') ? [
'customize'
] : [];
if (!params.primaryActions || 0 === params.primaryActions.length) return defaultPrimary;
const filtered = params.primaryActions.filter((action)=>params.orderedActions.includes(action));
return filtered.length > 0 ? filtered : defaultPrimary;
}
function resolvePolicyDirection(direction) {
if ('column' === direction) return 'column';
return 'row';
}
function resolvePolicyUiProfile(profile) {
if ('balanced' === profile || 'compact' === profile || 'strict' === profile) return profile;
return 'compact';
}
function shouldFillPolicyActions(params) {
const effectiveUiProfile = resolvePolicyUiProfile(params.uiProfile);
const actionCount = new Set(params.actionGroups.flat()).size;
const isSplitLayout = params.actionGroups.length > 1;
const isColumn = 'column' === params.direction;
return 'strict' === effectiveUiProfile || 'balanced' === effectiveUiProfile && (actionCount <= 2 || 3 === actionCount && (isSplitLayout || isColumn));
}
function hasPolicyHints(surface) {
if (!surface) return false;
return Object.values(surface).some((value)=>{
if (Array.isArray(value)) return value.length > 0;
return void 0 !== value;
});
}
function global_privacy_control_hasGlobalPrivacyControlSignal() {
if ("u" < typeof window) return false;
try {
const navigatorWithGPC = window.navigator;
const value = navigatorWithGPC.globalPrivacyControl;
return true === value || '1' === value;
} catch {
return false;
}
}
const request_context_ABSOLUTE_URL_REGEX = /^https?:\/\//;
function trimTrailingSlash(value) {
if ('/' === value) return value;
return value.endsWith('/') ? value.slice(0, -1) : value;
}
function normalizeAbsoluteURL(url) {
const normalized = url.toString();
return normalized.endsWith('/') ? normalized.slice(0, -1) : normalized;
}
function request_context_buildRequestContextHeaders(overrides) {
const headers = {};
if (overrides?.country) headers['x-c15t-country'] = overrides.country;
if (overrides?.region) headers['x-c15t-region'] = overrides.region;
if (overrides?.language) headers['accept-language'] = overrides.language;
return headers;
}
function canonicalizeBrowserBackendURL(backendURL) {
try {
const normalizedBackendURL = trimTrailingSlash(backendURL);
if (request_context_ABSOLUTE_URL_REGEX.test(normalizedBackendURL)) return normalizeAbsoluteURL(new URL(normalizedBackendURL));
if (!normalizedBackendURL.startsWith('/')) return;
return normalizeAbsoluteURL(new URL(normalizedBackendURL, window.location.origin));
} catch {
return;
}
}
function createRuntimeRequestContextMatcher(options) {
const normalizedBackendURL = canonicalizeBrowserBackendURL(options.backendURL);
if (!normalizedBackendURL) return;
const detectedGpc = global_privacy_control_hasGlobalPrivacyControlSignal();
return {
backendURL: normalizedBackendURL,
country: options.overrides?.country,
region: options.overrides?.region,
language: options.overrides?.language,
gpc: options.overrides?.gpc ?? ('boolean' == typeof detectedGpc ? detectedGpc : false),
credentials: options.credentials ?? 'include'
};
}
function matchesStoredRequestContext(stored, matcher) {
if (stored.backendURL !== matcher.backendURL) return false;
if (stored.gpc !== matcher.gpc) return false;
if (void 0 !== matcher.country && stored.country !== matcher.country) return false;
if (void 0 !== matcher.region && stored.region !== matcher.region) return false;
if (void 0 !== matcher.language && stored.language !== matcher.language) return false;
if (void 0 !== stored.credentials && stored.credentials !== matcher.credentials) return false;
return true;
}
const WINDOW_PROMISES_KEY = '__c15tInitialDataPromises';
function getBrowserWindow() {
if ("u" < typeof window) return;
return window;
}
function getMatchingPrefetchEntry(options) {
const browserWindow = getBrowserWindow();
if (!browserWindow) return;
const matcher = createRuntimeRequestContextMatcher({
backendURL: options.backendURL,
overrides: options.overrides,
credentials: options.credentials
});
if (!matcher) return;
const entries = Object.values(browserWindow[WINDOW_PROMISES_KEY] ?? {});
const matches = entries.filter((entry)=>{
const requestContext = entry.requestContext;
return requestContext ? matchesStoredRequestContext(requestContext, matcher) : false;
});
return 1 === matches.length ? matches[0] : void 0;
}
function getMatchingPrefetchedInitialData(options) {
return getMatchingPrefetchEntry(options)?.promise;
}
function buildPrefetchScript(options) {
const payload = {
backendURL: options.backendURL,
credentials: options.credentials ?? 'include',
headers: {
...headers_C15T_VERSION_HEADERS,
...request_context_buildRequestContextHeaders(options.overrides)
},
requestContext: {
country: options.overrides?.country ?? null,
region: options.overrides?.region ?? null,
language: options.overrides?.language ?? null
}
};
const json = JSON.stringify(payload).replace(/</g, '\\u003c');
return `(() => {
const mapKey = '${WINDOW_PROMISES_KEY}';
if (typeof window === 'undefined') {
return;
}
const payload = ${json};
const trimTrailingSlash = (value) => {
if (value === '/') {
return value;
}
return value.endsWith('/') ? value.slice(0, -1) : value;
};
const canonicalizeBackendURL = (backendURL) => {
try {
const normalizedBackendURL = trimTrailingSlash(backendURL);
if (/^https?:\\/\\//.test(normalizedBackendURL)) {
return trimTrailingSlash(new URL(normalizedBackendURL).toString());
}
if (!normalizedBackendURL.startsWith('/')) {
return undefined;
}
return trimTrailingSlash(
new URL(normalizedBackendURL, window.location.origin).toString()
);
} catch {
return undefined;
}
};
const buildCacheKey = (url, credentials, headers, gpc) => {
const sortedHeaders = Object.entries(headers)
.sort(([leftKey], [rightKey]) => leftKey.localeCompare(rightKey))
.map(([key, value]) => key + ':' + value)
.join('|');
return url + '|' + credentials + '|gpc:' + String(gpc) + '|' + sortedHeaders;
};
const detectGpc = () => {
try {
const value = window.navigator.globalPrivacyControl;
return value === true || value === '1';
} catch {
return false;
}
};
const backendURL = canonicalizeBackendURL(payload.backendURL);
if (!backendURL) {
return;
}
const gpc = detectGpc();
const requestContext = {
backendURL,
country: payload.requestContext.country,
region: payload.requestContext.region,
language: payload.requestContext.language,
gpc,
credentials: payload.credentials
};
const url = backendURL + '/init';
const cacheKey = buildCacheKey(url, payload.credentials, payload.headers, gpc);
const promises = (window[mapKey] = window[mapKey] || {});
if (promises[cacheKey]) {
return;
}
const promise = fetch(url, {
method: 'GET',
credentials: payload.credentials,
headers: payload.headers
})
.then((response) => (response.ok ? response.json() : undefined))
.then((init) => (init
? {
init,
gvl: init.gvl,
metadata: {
requestContext
}
}
: undefined))
.catch(() => undefined);
promises[cacheKey] = {
promise,
requestContext
};
})();`;
}
const REGISTRY_KEY = '__c15tScriptDebugListeners';
let fallbackListeners = null;
function getListeners() {
if ("u" < typeof window) {
if (!fallbackListeners) fallbackListeners = new Set();
return fallbackListeners;
}
const host = window;
const existing = host[REGISTRY_KEY];
if (existing) return existing;
const listeners = new Set();
host[REGISTRY_KEY] = listeners;
return listeners;
}
function emitScriptDebugEvent(event) {
const fullEvent = {
...event,
timestamp: Date.now()
};
for (const listener of getListeners())try {
listener(fullEvent);
} catch (error) {
console.error("Failed to handle c15t script debug event listener", error);
}
return fullEvent;
}
function subscribeToScriptDebugEvents(listener) {
const listeners = getListeners();
listeners.add(listener);
return ()=>{
listeners.delete(listener);
};
}
function generateRandomScriptId() {
if ("u" > typeof crypto && crypto.randomUUID) return crypto.randomUUID().replace(/-/g, '').substring(0, 8);
if ("u" > typeof crypto && crypto.getRandomValues) {
const array = new Uint8Array(4);
crypto.getRandomValues(array);
return Array.from(array, (byte)=>byte.toString(36)).join('').padEnd(8, '0').substring(0, 8);
}
const randomStr = Math.random().toString(36).substring(2);
return randomStr.padEnd(8, '0').substring(0, 8);
}
function getScriptElementId(scriptId, anonymizeId, scriptIdMap) {
if (anonymizeId) {
if (scriptIdMap[scriptId]) return scriptIdMap[scriptId];
scriptIdMap[scriptId] = generateRandomScriptId();
return scriptIdMap[scriptId];
}
return `c15t-script-${scriptId}`;
}
const loadedScripts = new Map();
function hasLoadedScript(src) {
return loadedScripts.has(src);
}
function getLoadedScript(src) {
return loadedScripts.get(src);
}
function setLoadedScript(src, element) {
loadedScripts.set(src, element);
}
function deleteLoadedScript(src) {
loadedScripts.delete(src);
}
function utils_getLoadedScriptsSnapshot() {
return loadedScripts;
}
function hasIABConsent(script, iabConsent) {
if (void 0 !== script.vendorId) {
const vendorKey = String(script.vendorId);
if (!iabConsent.vendorConsents[vendorKey]) return false;
}
if (script.iabPurposes && script.iabPurposes.length > 0) {
const hasAllPurposeConsents = script.iabPurposes.every((purposeId)=>true === iabConsent.purposeConsents[purposeId]);
if (!hasAllPurposeConsents) return false;
}
if (script.iabLegIntPurposes && script.iabLegIntPurposes.length > 0) {
const hasAllLegIntConsents = script.iabLegIntPurposes.every((purposeId)=>true === iabConsent.purposeLegitimateInterests[purposeId]);
if (!hasAllLegIntConsents) return false;
}
if (script.iabSpecialFeatures && script.iabSpecialFeatures.length > 0) {
const hasAllSpecialFeatures = script.iabSpecialFeatures.every((featureId)=>true === iabConsent.specialFeatureOptIns[featureId]);
if (!hasAllSpecialFeatures) return false;
}
return true;
}
function scriptHasConsent(script, consents, options) {
if (options?.model === 'iab' && options.iabConsent && (void 0 !== script.vendorId || script.iabPurposes || script.iabLegIntPurposes || script.iabSpecialFeatures)) return hasIABConsent(script, options.iabConsent);
return has(script.category, consents);
}
function emitLifecycleEvent(script, action, message, info, data) {
emitScriptDebugEvent({
source: "script-loader",
scope: 'lifecycle',
action,
message,
scriptId: script.id,
elementId: info?.elementId,
hasConsent: info?.hasConsent,
callback: getCallbackFromAction(action, data),
data
});
}
function getCallbackFromAction(action, data) {
if ('callback_start' === action || 'callback_complete' === action || 'callback_error' === action) return data?.callback;
}
function getErrorMessage(error) {
if (error instanceof Error) return error.message;
if ('string' == typeof error) return error;
return 'Unknown error';
}
function invokeScriptCallback(script, callbackName, callback, info) {
if (!callback) return;
emitLifecycleEvent(script, 'callback_start', `${callbackName} started`, info, {
callback: callbackName
});
try {
callback(info);
emitLifecycleEvent(script, 'callback_complete', `${callbackName} completed`, info, {
callback: callbackName
});
} catch (error) {
emitLifecycleEvent(script, 'callback_error', `${callbackName} failed`, info, {
callback: callbackName,
error: getErrorMessage(error)
});
throw error;
}
}
function loadScripts(scripts, consents, scriptIdMap = {}, options) {
const loadedScriptIds = [];
scripts.forEach((script)=>{
const hasConsent = scriptHasConsent(script, consents, options);
if (!script.alwaysLoad && !hasConsent) return void emitLifecycleEvent(script, 'skipped', 'Script skipped due to missing consent', {
hasConsent
}, {
reason: 'missing_consent'
});
if (hasLoadedScript(script.id)) {
const callbackInfo = {
id: script.id,
elementId: getScriptElementId(script.id, false !== script.anonymizeId, scriptIdMap),
hasConsent,
consents
};
emitLifecycleEvent(script, 'already_loaded', 'Script already loaded; running consent sync', callbackInfo);
invokeScriptCallback(script, 'onConsentChange', script.onConsentChange, callbackInfo);
return;
}
if (script.src && script.textContent) throw new Error(`Script '${script.id}' cannot have both 'src' and 'textContent'. Choose one.`);
if (!script.src && !script.textContent && !script.callbackOnly) throw new Error(`Script '${script.id}' must have either 'src', 'textContent', or 'callbackOnly' set to true.`);
if (true === script.callbackOnly) {
const shouldAnonymize = false !== script.anonymizeId;
const elementId = getScriptElementId(script.id, shouldAnonymize, scriptIdMap);
const callbackInfo = {
id: script.id,
elementId,
consents,
hasConsent
};
invokeScriptCallback(script, 'onBeforeLoad', script.onBeforeLoad, callbackInfo);
invokeScriptCallback(script, 'onLoad', script.onLoad, callbackInfo);
setLoadedScript(script.id, null);
loadedScriptIds.push(script.id);
emitLifecycleEvent(script, 'loaded', "Callback-only script marked as loaded", callbackInfo, {
callbackOnly: true
});
return;
}
const shouldAnonymize = false !== script.anonymizeId;
const elementId = getScriptElementId(script.id, shouldAnonymize, scriptIdMap);
if (true === script.persistAfterConsentRevoked) {
const existingElement = document.getElementById(elementId);
if (existingElement) {
const callbackInfo = {
id: script.id,
hasConsent,
elementId,
consents,
element: existingElement
};
emitLifecycleEvent(script, 'already_loaded', "Persisted script element already exists; reusing it", callbackInfo, {
reason: 'persisted_element'
});
invokeScriptCallback(script, 'onConsentChange', script.onConsentChange, callbackInfo);
invokeScriptCallback(script, 'onLoad', script.onLoad, callbackInfo);
setLoadedScript(script.id, existingElement);
loadedScriptIds.push(script.id);
emitLifecycleEvent(script, 'loaded', "Existing script element marked as loaded", callbackInfo, {
reusedElement: true
});
return;
}
}
const scriptElement = document.createElement("script");
scriptElement.id = elementId;
if (script.src) scriptElement.src = script.src;
else if (script.textContent) scriptElement.textContent = script.textContent;
if (script.fetchPriority) scriptElement.fetchPriority = script.fetchPriority;
if (script.async) scriptElement.async = true;
else if (false === script.async) scriptElement.async = false;
if (script.defer) scriptElement.defer = true;
const nonce = script.nonce ?? options?.nonce;
if (nonce) scriptElement.nonce = nonce;
if (script.attributes) Object.entries(script.attributes).forEach(([key, value])=>{
scriptElement.setAttribute(key, value);
});
const callbackInfo = {
id: script.id,
hasConsent,
elementId,
consents,
element: scriptElement
};
if (script.onLoad) if (script.textContent) setTimeout(()=>{
invokeScriptCallback(script, 'onLoad', script.onLoad, {
...callbackInfo
});
}, 0);
else {
emitLifecycleEvent(script, 'load_listener_attached', 'Attached load listener', callbackInfo);
scriptElement.addEventListener('load', ()=>{
invokeScriptCallback(script, 'onLoad', script.onLoad, {
...callbackInfo
});
});
}
if (script.onError) if (script.textContent) ;
else {
emitLifecycleEvent(script, 'error_listener_attached', 'Attached error listener', callbackInfo);
scriptElement.addEventListener('error', ()=>{
invokeScriptCallback(script, 'onError', script.onError, {
...callbackInfo,
error: new Error(`Failed to load script: ${script.src}`)
});
});
}
invokeScriptCallback(script, 'onBeforeLoad', script.onBeforeLoad, callbackInfo);
const target = script.target ?? 'head';
const targetElement = 'body' === target ? document.body : document.head;
if (!targetElement) throw new Error(`Document ${target} is not available for script injection`);
targetElement.appendChild(scriptElement);
emitLifecycleEvent(script, 'element_appended', `Script element appended to ${target}`, callbackInfo, {
target
});
setLoadedScript(script.id, scriptElement);
loadedScriptIds.push(script.id);
emitLifecycleEvent(script, 'loaded', 'Script marked as loaded', callbackInfo);
});
return loadedScriptIds;
}
function unloadScripts(scripts, consents, scriptIdMap = {}, options) {
const unloadedScriptIds = [];
scripts.forEach((script)=>{
const hasConsent = scriptHasConsent(script, consents, options);
if (!hasLoadedScript(script.id)) return;
if (script.alwaysLoad) return;
if (!hasConsent) {
const scriptElement = getLoadedScript(script.id);
const callbackInfo = {
id: script.id,
elementId: getScriptElementId(script.id, false !== script.anonymizeId, scriptIdMap),
hasConsent,
consents,
element: scriptElement && null !== scriptElement ? scriptElement : void 0
};
if (true === script.callbackOnly || null === scriptElement) {
deleteLoadedScript(script.id);
unloadedScriptIds.push(script.id);
emitLifecycleEvent(script, 'unloaded', "Callback-only script marked as unloaded", callbackInfo, {
callbackOnly: true
});
} else if (scriptElement) if (script.persistAfterConsentRevoked) {
deleteLoadedScript(script.id);
unloadedScriptIds.push(script.id);
emitLifecycleEvent(script, 'unloaded', "Persistent script marked as unloaded without removing element", callbackInfo, {
removedElement: false,
persistAfterConsentRevoked: true
});
} else {
scriptElement.remove();
deleteLoadedScript(script.id);
unloadedScriptIds.push(script.id);
emitLifecycleEvent(script, 'unloaded', 'Script element removed after consent revocation', callbackInfo, {
removedElement: true
});
}
}
});
return unloadedScriptIds;
}
function core_updateScripts(scripts, consents, scriptIdMap = {}, options) {
const unloaded = unloadScripts(scripts, consents, scriptIdMap, options);
const loaded = loadScripts(scripts, consents, scriptIdMap, options);
return {
loaded,
unloaded
};
}
function isScriptLoaded(scriptId) {
return hasLoadedScript(scriptId);
}
function getLoadedScriptIds() {
return Array.from(utils_getLoadedScriptsSnapshot().keys());
}
function reloadScript(scriptId, scripts, consents, scriptIdMap = {}, options) {
const script = scripts.find((s)=>s.id === scriptId);
if (!script) return false;
if (hasLoadedScript(scriptId)) {
const scriptElement = getLoadedScript(scriptId);
if (true === script.callbackOnly || null === scriptElement) deleteLoadedScript(scriptId);
else if (scriptElement) {
if (!script.persistAfterConsentRevoked) scriptElement.remove();
deleteLoadedScript(scriptId);
}
}
if (!script.alwaysLoad && !scriptHasConsent(script, consents, options)) {
emitLifecycleEvent(script, 'skipped', 'Reload skipped due to missing consent', {
hasConsent: false,
elementId: getScriptElementId(script.id, false !== script.anonymizeId, scriptIdMap)
}, {
reason: 'reload_missing_consent'
});
return false;
}
loadScripts([
script
], consents, scriptIdMap, options);
return true;
}
function createScriptManager(getState, setState) {
const updateScriptsFn = ()=>{
const { scripts, consents, scriptIdMap, model, iab, nonce, policyCategories, policyScopeMode } = getState();
const iabConsent = iab?.config.enabled ? {
vendorConsents: iab.vendorConsents,
vendorLegitimateInterests: iab.vendorLegitimateInterests,
purposeConsents: iab.purposeConsents,
purposeLegitimateInterests: iab.purposeLegitimateInterests,
specialFeatureOptIns: iab.specialFeatureOptIns
} : void 0;
const runtimeConsents = applyPolicyScopeForRuntimeGating(consents, policyCategories, policyScopeMode);
const result = core_updateScripts(scripts, runtimeConsents, scriptIdMap, {
model,
iabConsent,
nonce
});
const newLoadedScripts = {
...getState().loadedScripts
};
result.loaded.forEach((id)=>{
newLoadedScripts[id] = true;
});
result.unloaded.forEach((id)=>{
newLoadedScripts[id] = false;
});
setState({
loadedScripts: newLoadedScripts
});
return result;
};
return {
updateScripts: ()=>updateScriptsFn(),
setScripts: (scripts)=>{
const state = getState();
const newScriptIdMap = {
...state.scriptIdMap
};
scripts.forEach((script)=>{
if (false !== script.anonymizeId) newScriptIdMap[script.id] = generateRandomScriptId();
});
const newCategories = scripts.flatMap((script)=>extractConsentNamesFromCondition(script.category));
setState({
scripts: [
...state.scripts,
...scripts
],
scriptIdMap: newScriptIdMap
});
getState().updateConsentCategories(newCategories);
updateScriptsFn();
},
removeScript: (scriptId)=>{
const state = getState();
if (hasLoadedScript(scriptId)) {
const scriptElement = getLoadedScript(scriptId);
if (scriptElement) {
scriptElement.remove();
deleteLoadedScript(scriptId);
}
}
const newScriptIdMap = {
...state.scriptIdMap
};
delete newScriptIdMap[scriptId];
setState({
scripts: state.scripts.filter((script)=>script.id !== scriptId),
loadedScripts: {
...state.loadedScripts,
[scriptId]: false
},
scriptIdMap: newScriptIdMap
});
},
reloadScript: (scriptId)=>{
const state = getState();
const iabConsent = state.iab?.config.enabled ? {
vendorConsents: state.iab.vendorConsents,
vendorLegitimateInterests: state.iab.vendorLegitimateInterests,
purposeConsents: state.iab.purposeConsents,
purposeLegitimateInterests: state.iab.purposeLegitimateInterests,
specialFeatureOptIns: state.iab.specialFeatureOptIns
} : void 0;
const runtimeConsents = applyPolicyScopeForRuntimeGating(state.consents, state.policyCategories, state.policyScopeMode);
return reloadScript(scriptId, state.scripts, runtimeConsents, state.scriptIdMap, {
model: state.model,
iabConsent,
nonce: state.nonce
});
},
isScriptLoaded: (scriptId)=>isScriptLoaded(scriptId),
getLoadedScriptIds: ()=>getLoadedScriptIds()
};
}
const vanilla_namespaceObject = require("zustand/vanilla");
function createIframeManager(get, _set) {
let observer = null;
let isInitialized = false;
return {
initializeIframeBlocker: ()=>{
if (isInitialized) return;
if ("u" < typeof document) return;
const state = get();
const runtimeConsents = applyPolicyScopeForRuntimeGating(state.consents, state.policyCategories, state.policyScopeMode);
if (state.iframeBlockerConfig?.disableAutomaticBlocking) return;
const discoverAndRegisterCategories = ()=>{
const iframeCategories = getIframeConsentCategories();
if (iframeCategories.length > 0) get().updateConsentCategories(iframeCategories);
};
if ('loading' === document.readyState) document.addEventListener('DOMContentLoaded', discoverAndRegisterCategories);
else discoverAndRegisterCategories();
setTimeout(discoverAndRegisterCategories, 100);
processAllIframes(runtimeConsents);
observer = setupIframeObserver(()=>{
const nextState = get();
return applyPolicyScopeForRuntimeGating(nextState.consents, nextState.policyCategories, nextState.policyScopeMode);
}, (categories)=>get().updateConsentCategories(categories));
isInitialized = true;
},
updateIframeConsents: ()=>{
if (!isInitialized) return;
if ("u" < typeof document) return;
const state = get();
const { consents, iframeBlockerConfig } = state;
if (iframeBlockerConfig?.disableAutomaticBlocking) return;
processAllIframes(applyPolicyScopeForRuntimeGating(consents, state.policyCategories, state.policyScopeMode));
},
destroyIframeBlocker: ()=>{
if (!isInitialized) return;
if ("u" < typeof document) return;
const state = get();
const { iframeBlockerConfig } = state;
if (iframeBlockerConfig?.disableAutomaticBlocking) return;
if (observer) {
observer.disconnect();
observer = null;
}
isInitialized = false;
}
};
}
const PENDING_CONSENT_SYNC_KEY = 'c15t:pending-consent-sync';
function shouldReloadOnConsentChange(previousConsents, newConsents, previousConsentInfo, reloadOnConsentRevoked, consentTypes) {
if (!reloadOnConsentRevoked) return false;
if (null === previousConsentInfo) return false;
const disabledNames = new Set(consentTypes.filter((t)=>t.disabled).map((t)=>t.name));
const wasAnyConsentRevoked = Object.entries(newConsents).some(([key, value])=>!disabledNames.has(key) && true === previousConsents[key] && false === value);
return wasAnyConsentRevoked;
}
function haveConsentsChanged(previousConsents, nextConsents, consentTypes) {
return consentTypes.some((consentType)=>previousConsents[consentType.name] !== nextConsents[consentType.name]);
}
function getConsentCategoryLists(consents, consentCategories, consentTypes) {
const activeCategories = new Set(consentCategories);
const allowedCategories = [];
const deniedCategories = [];
for (const consentType of consentTypes)if (activeCategories.has(consentType.name)) if (consents[consentType.name]) allowedCategories.push(consentType.name);
else deniedCategories.push(consentType.name);
return {
allowedCategories,
deniedCategories
};
}
async function saveConsents({ manager, type, get, set, options, emitConsentChanged }) {
const { callbacks, selectedConsents, consents, consentTypes, updateScripts, updateIframeConsents, updateNetworkBlockerConsents, consentCategories, locationInfo, model, consentInfo, reloadOnConsentRevoked, lastBannerFetchData } = get();
const previousConsents = {
...consents
};
const previousConsentInfo = consentInfo;
const newConsents = {
...selectedConsents ?? consents ?? {}
};
const givenAt = Date.now();
if ('all' === type) {
for (const consent of consentTypes)if (consentCategories.includes(consent.name)) newConsents[consent.name] = true;
} else if ('necessary' === type) for (const consent of consentTypes)newConsents[consent.name] = true === consent.disabled ? consent.defaultValue : false;
const effectivePolicy = getEffectivePolicy(lastBannerFetchData);
const policyCategories = effectivePolicy?.consent?.categories;
const shouldEnforcePolicyScope = shouldEnforcePolicyCategoryScope(policyCategories, effectivePolicy?.consent?.scopeMode ?? null);
const effectiveConsents = shouldEnforcePolicyScope ? applyPolicyPurposeAllowlist(newConsents, policyCategories) : newConsents;
const requestPreferences = shouldEnforcePolicyScope ? stripDisallowedPreferenceKeys(effectiveConsents, policyCategories) : effectiveConsents;
const didChange = haveConsentsChanged(previousConsents, effectiveConsents, consentTypes);
const nextConsentCategoryLists = getConsentCategoryLists(effectiveConsents, consentCategories, consentTypes);
const previousConsentCategoryLists = getConsentCategoryLists(previousConsents, consentCategories, consentTypes);
const consentChangedPayload = didChange ? {
preferences: effectiveConsents,
previousPreferences: previousConsents,
allowedCategories: nextConsentCategoryLists.allowedCategories,
deniedCategories: nextConsentCategoryLists.deniedCategories,
previousAllowedCategories: previousConsentCategoryLists.allowedCategories,
previousDeniedCategories: previousConsentCategoryLists.deniedCategories
} : null;
const materialPolicyFingerprint = lastBannerFetchData?.policy ? await (0, types_namespaceObject.createMaterialPolicyFingerprint)(lastBannerFetchData.policy) : void 0;
let subjectId = consentInfo?.subjectId;
if (!subjectId) subjectId = generateSubjectId();
const storedIdentifiers = sanitizeSubjectIdentifiers({
externalId: get().consentInfo?.externalId,
identityProvider: get().consentInfo?.identityProvider
});
const userIdentifiers = sanitizeSubjectIdentifiers({
externalId: get().user?.id,
identityProvider: get().user?.identityProvider
});
const externalId = storedIdentifiers.externalId ?? userIdentifiers.externalId;
const identityProvider = storedIdentifiers.identityProvider ?? userIdentifiers.identityProvider;
const nextConsentInfo = {
time: givenAt,
subjectId,
materialPolicyFingerprint,
...externalId ? {
externalId
} : {},
...identityProvider ? {
identityProvider
} : {}
};
const needsReload = shouldReloadOnConsentChange(previousConsents, effectiveConsents, previousConsentInfo, reloadOnConsentRevoked, consentTypes);
set({
consents: effectiveConsents,
selectedConsents: effectiveConsents,
activeUI: 'none',
consentInfo: nextConsentInfo
});
saveConsentToStorage({
consents: effectiveConsents,
consentInfo: nextConsentInfo
}, void 0, get().storageConfig);
if (needsReload) {
const pendingSync = {
type,
subjectId,
preferences: requestPreferences,
givenAt,
jurisdiction: locationInfo?.jurisdiction ?? void 0,
jurisdictionModel: model,
domain: window.location.hostname,
uiSource: options?.uiSource ?? 'api',
policySnapshotToken: lastBannerFetchData?.policySnapshotToken,
...externalId ? {
externalId
} : {},
...identityProvider ? {
identityProvider
} : {}
};
try {
localStorage.setItem(PENDING_CONSENT_SYNC_KEY, JSON.stringify(pendingSync));
} catch {}
callbacks.onConsentSet?.({
preferences: effectiveConsents
});
if (consentChangedPayload) emitConsentChanged?.(consentChangedPayload);
callbacks.onBeforeConsentRevocationReload?.({
preferences: effectiveConsents
});
window.location.reload();
return;
}
await new Promise((resolve)=>setTimeout(resolve, 0));
updateIframeConsents();
updateScripts();
updateNetworkBlockerConsents();
callbacks.onConsentSet?.({
preferences: effectiveConsents
});
if (consentChangedPayload) emitConsentChanged?.(consentChangedPayload);
const consent = await manager.setConsent({
body: {
type: 'cookie_banner',
domain: window.location.hostname,
preferences: requestPreferences,
subjectId,
jurisdiction: locationInfo?.jurisdiction ?? void 0,
jurisdictionModel: model ?? void 0,
givenAt,
uiSource: options?.uiSource ?? 'api',
consentAction: type,
policySnapshotToken: lastBannerFetchData?.policySnapshotToken,
...externalId ? {
externalSubjectId: externalId
} : {},
...identityProvider ? {
identityProvider
} : {}
}
});
if (!consent.ok) {
const errorMsg = consent.error?.message ?? 'Failed to save consents';
callbacks.onError?.({
error: errorMsg
});
if (!callbacks.onError) console.error(errorMsg);
}
}
function determineModel(jurisdiction, iabEnabled) {
if (null == jurisdiction || 'NONE' === jurisdiction) return null;
if (iabEnabled && [
'UK_GDPR',
'GDPR'
].includes(jurisdiction)) return 'iab';
if ([
'UK_GDPR',
'GDPR',
'CH',
'BR',
'APPI',
'PIPA',
'QC_LAW25'
].includes(jurisdiction)) return 'opt-in';
if ([
'CCPA',
'AU',
'PIPEDA'
].includes(jurisdiction)) return 'opt-out';
return 'opt-in';
}
function calculateAutoGrantedConsents(shouldAutoGrant, hasGpcSignal) {
if (!shouldAutoGrant) return null;
return {
necessary: true,
functionality: true,
experience: true,
marketing: !hasGpcSignal,
measurement: !hasGpcSignal
};
}
function computeAutoGrantInfo(jurisdiction, iabEnabled, consentInfo, policyModel, gpcOverride, policyGpc) {
const consentModel = 'none' === policyModel ? null : policyModel ?? determineModel(jurisdiction, iabEnabled);
const shouldCheckGpc = void 0 !== policyGpc ? policyGpc : true;
const hasGpcSignal = shouldCheckGpc ? void 0 !== gpcOverride ? gpcOverride : global_privacy_control_hasGlobalPrivacyControlSignal() : false;
const shouldAutoGrantConsents = (null === consentModel || 'opt-out' === consentModel) && null === consentInfo;
const autoGrantedConsents = calculateAutoGrantedConsents(shouldAutoGrantConsents, hasGpcSignal);
return {
consentModel,
autoGrantedConsents
};
}
function buildStoreUpdate(data, config, effectiveIABEnabled, initSourceMetadata) {
const { get, initialTranslationConfig } = config;
const { consentInfo, consentTypes } = get();
const { translations, location } = data;
const { consentModel, autoGrantedConsents } = computeAutoGrantInfo(data.jurisdiction ?? null, effectiveIABEnabled, consentInfo, data.policy?.model, config.get().overrides?.gpc, data.policy?.consent?.gpc);
const update = {
model: consentModel,
isLoadingConsentInfo: false,
branding: data.branding ?? 'c15t',
hasFetchedBanner: true,
lastBannerFetchData: data,
locationInfo: {
countryCode: location?.countryCode ?? null,
regionCode: location?.regionCode ?? null,
jurisdiction: data.jurisdiction ?? null
},
policyBanner: {
allowedActions: data.policy?.ui?.banner?.allowedActions,
primaryActions: data.policy?.ui?.banner?.primaryActions,
layout: data.policy?.ui?.banner?.layout,
direction: data.policy?.ui?.banner?.direction,
uiProfile: data.policy?.ui?.banner?.uiProfile,
scrollLock: data.policy?.ui?.banner?.scrollLock
},
policyDialog: {
allowedActions: data.policy?.ui?.dialog?.allowedActions,
primaryActions: data.policy?.ui?.dialog?.primaryActions,
layout: data.policy?.ui?.dialog?.layout,
direction: data.policy?.ui?.dialog?.direction,
uiProfile: data.policy?.ui?.dialog?.uiProfile,
scrollLock: data.policy?.ui?.dialog?.scrollLock
},
policyCategories: data.policy?.consent?.categories ?? null,
policyScopeMode: data.policy?.consent?.scopeMode ?? null,
initDataSource: initSourceMetadata?.initDataSource ?? null,
initDataSourceDetail: initSourceMetadata?.initDataSourceDetail ?? null
};
if (null === consentInfo) if (data.policy?.ui?.mode) update.activeUI = data.policy.ui.mode;
else update.activeUI = consentModel ? 'banner' : 'none';
if (autoGrantedConsents) {
update.consents = autoGrantedConsents;
update.selectedConsents = autoGrantedConsents;
}
const policyCategories = data.policy?.consent?.categories;
const hasStrictPolicyCategoryAllowlist = shouldEnforcePolicyCategoryScope(policyCategories, data.policy?.consent?.scopeMode ?? null);
if (hasStrictPolicyCategoryAllowlist) {
const uniqueAllowedCategories = filterConsentCategoriesByPolicy(allConsentNames, policyCategories);
update.consentCategories = uniqueAllowedCategories;
update.consents = applyPolicyPurposeAllowlist(update.consents ?? get().consents, uniqueAllowedCategories);
update.selectedConsents = applyPolicyPurposeAllowlist(update.selectedConsents ?? get().selectedConsents, uniqueAllowedCategories);
}
const preselectedCategories = data.policy?.consent?.preselectedCategories;
const shouldApplyPreselectedCategories = null === consentInfo && !autoGrantedConsents && Array.isArray(preselectedCategories) && preselectedCategories.length > 0;
if (shouldApplyPreselectedCategories) {
const displayedConsentNames = update.consentCategories ?? get().consentCategories;
const preselectedScope = hasStrictPolicyCategoryAllowlist ? filterConsentCategoriesByPolicy(displayedConsentNames, policyCategories) : displayedConsentNames;
const allowedPreselectedCategories = filterConsentCategoriesByPolicy(preselectedScope, preselectedCategories);
const preselectedSet = new Set(allowedPreselectedCategories);
const selectedConsentBaseline = update.selectedConsents ?? get().selectedConsents;
update.selectedConsents = consentTypes.length > 0 ? consentTypes.reduce((acc, consent)=>{
acc[consent.name] = true === consent.disabled ? consent.defaultValue : preselectedSet.has(consent.name);
return acc;
}, {}) : Object.fromEntries(Object.keys(selectedConsentBaseline).map((category)=>[
category,
'necessary' === category || preselectedSet.has(category)
]));
}
if (translations?.language && translations?.translations) {
let customMessages;
customMessages = initialTranslationConfig?.translations ? {
translations: initialTranslationConfig.translations
} : void 0;
update.translationConfig = (0, translations_namespaceObject.prepareTranslationConfig)({
translations: {
[translations.language]: translations.translations
},
disableAutoLanguageSwitch: true,
defaultLanguage: translations.language
}, customMessages);
}
return update;
}
function triggerCallbacks(data, config, autoGrantedConsents) {
const { get } = config;
const { callbacks } = get();
const { translations } = data;
if (autoGrantedConsents) callbacks?.onConsentSet?.({
preferences: autoGrantedConsents
});
if (translations?.language && translations?.translations) callbacks?.onBannerFetched?.({
jurisdiction: data.jurisdiction,
location: data.location,
translations: {
language: translations.language,
translations: translations.translations
}
});
}
function getDefaultConsents(consentTypes) {
return consentTypes.reduce((acc, consent)=>{
acc[consent.name] = consent.defaultValue;
return acc;
}, {});
}
async function updateStore(data, config, _hasLocalStorageAccess, prefetchedGVL, initSourceMetadata) {
const { set, get } = config;
const initialState = get();
const currentPolicyFingerprint = data.policy ? await (0, types_namespaceObject.createMaterialPolicyFingerprint)(data.policy) : void 0;
if (initialState.consentInfo && currentPolicyFingerprint) {
const storedPolicyFingerprint = initialState.consentInfo.materialPolicyFingerprint;
if (storedPolicyFingerprint && storedPolicyFingerprint !== currentPolicyFingerprint) {
const resetConsents = getDefaultConsents(initialState.consentTypes);
deleteConsentFromStorage(void 0, initialState.storageConfig);
set({
consents: resetConsents,
selectedConsents: resetConsents,
consentInfo: null
});
} else if (!storedPolicyFingerprint) {
const updatedConsentInfo = {
...initialState.consentInfo,
materialPolicyFingerprint: currentPolicyFingerprint
};
saveConsentToStorage({
consents: initialState.consents,
consentInfo: updatedConsentInfo
}, void 0, initialState.storageConfig);
set({
consentInfo: updatedConsentInfo
});
}
}
const { consentInfo } = get();
let iab = get().iab;
if (config.iabConfig && !iab) {
const iabModule = config.iabConfig._module;
if (iabModule) {
iab = iabModule.createIABManager(config.iabConfig, get, set, config.manager);
set({
iab
});
} else console.error('[c15t] IAB config provided without IAB module. Install @c15t/iab and use the iab() wrapper: `import { iab } from "@c15t/iab"; iab({ cmpId: ... })`');
}
const serverDisabledGVL = iab?.config.enabled && !prefetchedGVL;
const effectiveIABEnabled = iab?.config.enabled && !serverDisabledGVL;
if (serverDisabledGVL) console.warn('IAB mode disabled: Server returned 200 without GVL. Client IAB settings overridden.');
const { consentModel, autoGrantedConsents } = computeAutoGrantInfo(data.jurisdiction ?? null, effectiveIABEnabled, consentInfo, data.policy?.model, get().overrides?.gpc, data.policy?.consent?.gpc);
const storeUpdate = buildStoreUpdate(data, config, effectiveIABEnabled, initSourceMetadata);
if (serverDisabledGVL && iab) storeUpdate.iab = {
...iab,
config: {
...iab.config,
enabled: false
}
};
else if (iab && null != data.cmpId) storeUpdate.iab = {
...iab,
config: {
...iab.config,
cmpId: data.cmpId
}
};
set(storeUpdate);
triggerCallbacks(data, config, autoGrantedConsents);
get().updateScripts();
if (effectiveIABEnabled && 'iab' === consentModel && iab) {
const serverCustomVendors = data.customVendors ?? [];
const clientCustomVendors = iab.config.customVendors ?? [];
const serverVendorIds = new Set(serverCustomVendors.map((v)=>v.id));
const mergedCustomVendors = [
...serverCustomVendors,
...clientCustomVendors.filter((v)=>!serverVendorIds.has(v.id))
];
const mergedConfig = {
...iab.config,
customVendors: mergedCustomVendors,
...null != data.cmpId && {
cmpId: data.cmpId
}
};
const iabModule = config.iabConfig?._module;
if (iabModule) iabModule.initializeIABMode(mergedConfig, {
set,
get
}, prefetchedGVL).catch((err)=>{
console.error('Failed to initialize IAB mode in updateStore:', err);
});
}
}
function checkLocalStorageAccess(set) {
try {
if (window.localStorage) {
window.localStorage.setItem('c15t-storage-test-key', 'test');
window.localStorage.removeItem('c15t-storage-test-key');
return true;
}
} catch (error) {
console.warn('localStorage not available, skipping consent banner:', error);
set({
isLoadingConsentInfo: false,
activeUI: 'none'
});
}
return false;
}
function shouldReuseSSRData(config, data) {
const requestContext = data.metadata?.requestContext;
if (!requestContext || !config.backendURL) return true;
const matcher = createRuntimeRequestContextMatcher({
backendURL: config.backendURL,
overrides: config.get().overrides,
credentials: config.requestCredentials
});
if (!matcher) return true;
return matchesStoredRequestContext(requestContext, matcher);
}
async function initConsentManager(config) {
const { get, set, manager } = config;
const { callbacks } = get();
if ("u" < typeof window) return;
const hasLocalStorageAccess = checkLocalStorageAccess(set);
if (!hasLocalStorageAccess) return;
set({
isLoadingConsentInfo: true
});
processPendingConsentSync(manager, callbacks);
const ssrResult = await tryUseSSRData(config);
if (ssrResult) return ssrResult;
return fetchFromAPI(config, hasLocalStorageAccess, manager, callbacks);
}
async function tryUseSSRData(config) {
const { ssrData, set } = config;
if (!ssrData) return void set({
ssrDataUsed: false,
ssrSkippedReason: 'no_data'
});
const data = await ssrData;
if (data?.init && !shouldReuseSSRData(config, data)) return void set({
ssrDataUsed: false,
ssrSkippedReason: 'context_mismatch'
});
if (data?.init) {
const initSourceMetadata = inferSSRInitSourceMetadata(data);
await updateStore(data.init, config, true, data.gvl, {
initDataSource: initSourceMetadata.initDataSource,
initDataSourceDetail: initSourceMetadata.initDataSourceDetail
});
set({
ssrDataUsed: true,
ssrSkippedReason: null
});
return data.init;
}
set({
ssrDataUsed: false,
ssrSkippedReason: 'fetch_failed'
});
}
async function fetchFromAPI(config, hasLocalStorageAccess, manager, callbacks) {
const { set, get } = config;
try {
const { language, country, region } = config.get().overrides ?? {};
const initContext = await manager.init({
headers: {
...language && {
'accept-language': language
},
...country && {
'x-c15t-country': country
},
...region && {
'x-c15t-region': region
}
},
onError: callbacks.onError ? (context)=>{
callbacks.onError?.({
error: context.error?.message || 'Unknown error'
});
} : void 0
});
const { data, error } = initContext;
if (error || !data) throw new Error(`Failed to fetch consent banner info: ${error?.message}`);
const initSourceMetadata = inferInitSourceMetadata(initContext, get().config.mode);
await updateStore(data, config, hasLocalStorageAccess, data.gvl ?? void 0, initSourceMetadata);
return data;
} catch (error) {
console.error('Error fetching consent banner information:', error);
set({
isLoadingConsentInfo: false,
activeUI: 'none'
});
const errorMessage = error instanceof Error ? error.message : 'Unknown error fetching consent banner information';
callbacks.onError?.({
error: errorMessage
});
return;
}
}
function inferInitSourceMetadata(initContext, mode) {
const response = initContext?.response ?? null;
if (response) {
const cache = inspectBackendCache(response.headers);
if (cache.isCacheHit) return {
initDataSource: 'backend-cache-hit',
initDataSourceDetail: cache.detail
};
return {
initDataSource: 'backend',
initDataSourceDetail: cache.detail
};
}
if ('offline' === mode) return {
initDataSource: 'offline-mode',
initDataSourceDetail: null
};
if ('custom' === mode) return {
initDataSource: 'custom',
initDataSourceDetail: null
};
if ('hosted' === mode || 'c15t' === mode) return {
initDataSource: 'offline-fallback',
initDataSourceDetail: null
};
return {
initDataSource: 'backend',
initDataSourceDetail: null
};
}
function inferSSRInitSourceMetadata(data) {
const cache = data.metadata?.cache;
const requestDurationMs = data.metadata?.requestDurationMs;
const detailParts = [
'via=ssr'
];
if (cache?.detail) detailParts.push(cache.detail);
if ('number' == typeof requestDurationMs && Number.isFinite(requestDurationMs)) detailParts.push(`duration=${Math.max(0, Math.round(requestDurationMs))}ms`);
const detail = detailParts.length > 0 ? detailParts.join(', ') : null;
if (cache?.isHit === true) return {
initDataSource: 'backend-cache-hit',
initDataSourceDetail: detail
};
if (cache) return {
initDataSource: 'backend',
initDataSourceDetail: detail
};
return {
initDataSource: 'ssr',
initDataSourceDetail: detail
};
}
function inspectBackendCache(headers) {
const cacheHeaders = [
'x-vercel-cache',
'cf-cache-status',
'x-cache',
'cache-status'
];
let headerDetail = null;
let headerIndicatesHit = false;
for (const headerName of cacheHeaders){
const headerValue = headers.get(headerName);
if (headerValue) {
headerDetail = `${headerName}=${headerValue}`;
headerIndicatesHit = /\b(hit|stale|revalidated|updating)\b/i.test(headerValue);
break;
}
}
const ageHeader = headers.get('age');
const ageValue = ageHeader ? Number.parseInt(ageHeader, 10) : NaN;
const ageIndicatesCache = Number.isFinite(ageValue) && ageValue > 0;
const ageDetail = ageIndicatesCache ? `age=${ageValue}` : null;
const detail = headerDetail && ageDetail ? `${headerDetail}, ${ageDetail}` : headerDetail ?? ageDetail;
return {
isCacheHit: headerIndicatesHit || ageIndicatesCache,
detail
};
}
function processPendingConsentSync(manager, callbacks) {
try {
const pendingSync = localStorage.getItem(PENDING_CONSENT_SYNC_KEY);
if (!pendingSync) return;
localStorage.removeItem(PENDING_CONSENT_SYNC_KEY);
const data = JSON.parse(pendingSync);
const { externalId: externalSubjectId, identityProvider } = sanitizeSubjectIdentifiers({
externalId: data.externalId,
identityProvider: data.identityProvider
});
manager.setConsent({
body: {
type: 'cookie_banner',
domain: data.domain,
preferences: data.preferences,
subjectId: data.subjectId,
jurisdiction: data.jurisdiction,
jurisdictionModel: data.jurisdictionModel ?? void 0,
givenAt: data.givenAt,
uiSource: data.uiSource ?? 'api',
policySnapshotToken: data.policySnapshotToken,
...externalSubjectId ? {
externalSubjectId
} : {},
...identityProvider ? {
identityProvider
} : {}
}
}).then((result)=>{
if (!result.ok) {
const errorMsg = result.error?.message ?? 'Failed to sync consent after reload';
callbacks.onError?.({
error: errorMsg
});
if (!callbacks.onError) console.error('Failed to sync consent after reload:', errorMsg);
}
}).catch((err)=>{
const errorMsg = err instanceof Error ? err.message : 'Failed to sync consent after reload';
callbacks.onError?.({
error: errorMsg
});
if (!callbacks.onError) console.error('Failed to sync consent after reload:', err);
});
} catch {}
}
function normalizeMethod(method) {
if (!method) return 'GET';
return method.toUpperCase();
}
function createUrl(rawUrl) {
if (!rawUrl) return null;
try {
if ("u" < typeof window) return null;
return new URL(rawUrl, window.location.href);
} catch {
return null;
}
}
function hostnameMatchesRule(hostname, rule) {
if (!hostname) return false;
const ruleDomain = rule.domain.trim().toLowerCase();
const targetHost = hostname.trim().toLowerCase();
if (!ruleDomain || !targetHost) return false;
if (targetHost === ruleDomain) return true;
const suffix = `.${ruleDomain}`;
const hasSuffix = targetHost.endsWith(suffix);
return hasSuffix;
}
function pathMatchesRule(pathname, rule) {
const hasPathFilter = 'string' == typeof rule.pathIncludes;
if (!hasPathFilter) return true;
if (!pathname) return false;
return pathname.includes(rule.pathIncludes);
}
function methodMatchesRule(method, rule) {
if (!rule.methods || 0 === rule.methods.length) return true;
if (!method) return false;
const upperMethod = normalizeMethod(method);
return rule.methods.some((allowedMethod)=>normalizeMethod(allowedMethod) === upperMethod);
}
function shouldApplyRule(url, method, rule) {
if (!hostnameMatchesRule(url.hostname, rule)) return false;
if (!pathMatchesRule(url.pathname, rule)) return false;
if (!methodMatchesRule(method, rule)) return false;
return true;
}
function shouldBlockRequest(request, consents, config) {
if (!config) return {
shouldBlock: false
};
const isEnabled = false !== config.enabled;
if (!isEnabled) return {
shouldBlock: false
};
if (!config.rules || 0 === config.rules.length) return {
shouldBlock: false
};
const url = createUrl(request.url);
if (!url) return {
shouldBlock: false
};
const method = normalizeMethod(request.method);
for (const rule of config.rules){
const applies = shouldApplyRule(url, method, rule);
if (!applies) continue;
const hasRequiredConsent = has(rule.category, consents);
if (!hasRequiredConsent) return {
shouldBlock: true,
rule
};
}
return {
shouldBlock: false
};
}
function createNetworkBlockerManager(get, _set) {
let originalFetch = null;
let originalXhrOpen = null;
let originalXhrSend = null;
let isInitialized = false;
let blockingConsents = null;
const notifyBlockedRequest = (config, info)=>{
if (!config) return;
if (false !== config.logBlockedRequests) {
const ruleId = info.rule?.id ?? 'unknown';
console.warn('[c15t] Network request blocked by consent manager', {
method: info.method,
url: info.url,
ruleId
});
}
if (config.onRequestBlocked) config.onRequestBlocked(info);
};
const getBlockingConsents = ()=>{
if (blockingConsents) return blockingConsents;
return get().consents;
};
const patchFetch = ()=>{
if ("u" < typeof window) return;
const hasFetch = 'function' == typeof window.fetch;
if (!hasFetch) return;
if (originalFetch) return;
originalFetch = window.fetch;
window.fetch = (input, init)=>{
const state = get();
const config = state.networkBlocker;
if (!originalFetch) throw new Error('Network blocker fetch wrapper not initialized.');
const hasRules = config?.enabled && config?.rules && config?.rules.length > 0;
if (!hasRules) return originalFetch.call(window, input, init);
let method = 'GET';
if (init?.method) method = init.method;
else if (input instanceof Request) method = input.method;
let url;
url = 'string' == typeof input || input instanceof URL ? input.toString() : input.url;
const consents = getBlockingConsents();
const { shouldBlock, rule } = shouldBlockRequest({
url,
method
}, consents, config);
if (shouldBlock) {
notifyBlockedRequest(config, {
method,
url,
rule
});
const blockedResponse = new Response(null, {
status: 451,
statusText: 'Request blocked by consent manager'
});
return Promise.resolve(blockedResponse);
}
return originalFetch.call(window, input, init);
};
};
const patchXmlHttpRequest = ()=>{
if ("u" < typeof window) return;
const hasXhr = void 0 !== window.XMLHttpRequest && 'function' == typeof window.XMLHttpRequest.prototype.open && 'function' == typeof window.XMLHttpRequest.prototype.send;
if (!hasXhr) return;
if (originalXhrOpen || originalXhrSend) return;
originalXhrOpen = window.XMLHttpRequest.prototype.open;
originalXhrSend = window.XMLHttpRequest.prototype.send;
window.XMLHttpRequest.prototype.open = function(method, url, async, user, password) {
const internal = this;
internal.__c15tMethod = method;
internal.__c15tUrl = url;
if (!originalXhrOpen) throw new Error('Network blocker XHR open wrapper not initialized.');
return originalXhrOpen.call(this, method, url, async ?? true, user, password);
};
window.XMLHttpRequest.prototype.send = function(body) {
const state = get();
const config = state.networkBlocker;
const isEnabled = config?.enabled !== false;
const hasRules = isEnabled && config?.rules && config?.rules.length > 0;
if (hasRules) {
const internal = this;
const method = internal.__c15tMethod || 'GET';
const url = internal.__c15tUrl || '';
const consents = getBlockingConsents();
const { shouldBlock, rule } = shouldBlockRequest({
url,
method
}, consents, config);
if (shouldBlock) {
notifyBlockedRequest(config, {
method,
url,
rule
});
try {
this.abort();
} catch {}
const progressEvent = new ProgressEvent('error');
if ('function' == typeof this.onerror) this.onerror(progressEvent);
this.dispatchEvent(progressEvent);
return;
}
}
if (!originalXhrSend) throw new Error('Network blocker XHR send wrapper not initialized.');
return originalXhrSend.call(this, body);
};
};
return {
initializeNetworkBlocker: ()=>{
if (isInitialized) return;
if ("u" < typeof window) return;
const state = get();
const config = state.networkBlocker;
const hasRules = config?.enabled && config?.rules && config?.rules.length > 0;
if (!hasRules) return;
blockingConsents = state.consents;
patchFetch();
patchXmlHttpRequest();
isInitialized = true;
},
updateNetworkBlockerConsents: ()=>{
if (!isInitialized) return;
blockingConsents = get().consents;
},
setNetworkBlocker: (config)=>{
const isEnabled = config?.enabled !== false;
const shouldEnable = isEnabled && config?.rules && config?.rules.length > 0;
const partial = {
networkBlocker: config
};
_set(partial);
if (!shouldEnable) {
if (!isInitialized) return;
if ("u" < typeof window) return;
if (originalFetch) {
window.fetch = originalFetch;
originalFetch = null;
}
if (originalXhrOpen && originalXhrSend) {
window.XMLHttpRequest.prototype.open = originalXhrOpen;
window.XMLHttpRequest.prototype.send = originalXhrSend;
originalXhrOpen = null;
originalXhrSend = null;
}
blockingConsents = null;
isInitialized = false;
return;
}
if (!isInitialized) {
blockingConsents = get().consents;
patchFetch();
patchXmlHttpRequest();
isInitialized = true;
}
},
destroyNetworkBlocker: ()=>{
if (!isInitialized) return;
if ("u" < typeof window) return;
if (originalFetch) {
window.fetch = originalFetch;
originalFetch = null;
}
if (originalXhrOpen && originalXhrSend) {
window.XMLHttpRequest.prototype.open = originalXhrOpen;
window.XMLHttpRequest.prototype.send = originalXhrSend;
originalXhrOpen = null;
originalXhrSend = null;
}
blockingConsents = null;
isInitialized = false;
}
};
}
function coalesceInFlight(requests, key, createRequest) {
const existingRequest = requests.get(key);
if (existingRequest) return existingRequest;
const request = createRequest().finally(()=>{
if (requests.get(key) === request) requests.delete(key);
});
requests.set(key, request);
return request;
}
function isLegalDocumentConsentInput(input) {
return (0, types_namespaceObject.isLegalDocumentType)(input.type);
}
const getStoredConsent = (config)=>{
if ("u" < typeof window) return null;
try {
return getConsentFromStorage(config);
} catch (e) {
console.error('Failed to retrieve stored consent:', e);
return null;
}
};
const createConsentManagerStore = (manager, options = {})=>{
const internalOptions = options;
const { namespace = 'c15tStore', iab, ssrData: _unusedSsrData, initialConsentCategories, initialTranslationConfig: legacyInitialTranslationConfig, initialI18nConfig, enabled = true, debug: _unusedDebug, ...storeConfigOptions } = options;
const hasInitialTranslationInput = Boolean(legacyInitialTranslationConfig || initialI18nConfig);
const normalizedInitialTranslationConfig = hasInitialTranslationInput ? (0, translations_namespaceObject.resolveTranslationInput)(legacyInitialTranslationConfig, initialI18nConfig) : void 0;
setDebugEnabled(true === options.debug);
const storedConsent = getStoredConsent(options.storageConfig);
const getInitialConsentState = ()=>{
if (!enabled) {
const grantedConsents = consent_types_consentTypes.reduce((acc, consent)=>{
acc[consent.name] = true;
return acc;
}, {});
return {
consents: grantedConsents,
selectedConsents: grantedConsents,
consentInfo: {
time: Date.now()
},
activeUI: 'none',
isLoadingConsentInfo: false
};
}
if (storedConsent) return {
consents: storedConsent.consents,
selectedConsents: storedConsent.consents,
consentInfo: storedConsent.consentInfo,
user: storedConsent.consentInfo?.externalId ? {
id: storedConsent.consentInfo.externalId,
identityProvider: storedConsent.consentInfo.identityProvider
} : void 0,
activeUI: 'none',
isLoadingConsentInfo: false
};
return {
activeUI: 'none',
isLoadingConsentInfo: true
};
};
const consentChangeListeners = new Set();
const inFlightConsentSaves = new Map();
const inFlightPolicyConsents = new Map();
const store = (0, vanilla_namespaceObject.createStore)((set, get)=>({
...initial_state_initialState,
...storeConfigOptions,
namespace,
iab: null,
...initialConsentCategories && {
consentCategories: initialConsentCategories
},
...getInitialConsentState(),
setActiveUI: (ui, options = {})=>{
if ('none' === ui || 'dialog' === ui) return void set({
activeUI: ui
});
if (options.force) return void set({
activeUI: 'banner'
});
const state = get();
const stored = getStoredConsent();
if (!stored && !state.consentInfo && !state.isLoadingConsentInfo) set({
activeUI: 'banner'
});
},
setSelectedConsent: (name, value)=>{
set((state)=>{
const consentType = state.consentTypes.find((type)=>type.name === name);
if (consentType?.disabled) return state;
return {
selectedConsents: {
...state.selectedConsents,
[name]: value
}
};
});
},
saveConsents: (type, options)=>{
const requestKey = JSON.stringify([
type,
options?.uiSource ?? null,
'custom' === type ? get().selectedConsents : null
]);
return coalesceInFlight(inFlightConsentSaves, requestKey, ()=>saveConsents({
manager,
type,
get,
set,
options,
emitConsentChanged: (payload)=>{
get().callbacks.onConsentChanged?.(payload);
for (const listener of consentChangeListeners)listener(payload);
}
}));
},
setConsent: (name, value)=>{
set((state)=>{
const consentType = state.consentTypes.find((type)=>type.name === name);
if (consentType?.disabled) return state;
const newConsents = {
...state.consents,
[name]: value
};
return {
selectedConsents: newConsents
};
});
get().saveConsents('custom');
},
resetConsents: ()=>{
set(()=>{
const consents = consent_types_consentTypes.reduce((acc, consent)=>{
acc[consent.name] = consent.defaultValue;
return acc;
}, {});
const resetState = {
consents,
selectedConsents: consents,
consentInfo: null
};
deleteConsentFromStorage(void 0, options.storageConfig);
return resetState;
});
},
setConsentCategories: (types)=>set(()=>{
const { policyCategories, policyScopeMode } = get();
if (shouldEnforcePolicyCategoryScope(policyCategories, policyScopeMode)) return {
consentCategories: filterConsentCategoriesByPolicy(types, policyCategories)
};
return {
consentCategories: Array.from(new Set(types))
};
}),
setCallback: (name, callback)=>{
const currentState = get();
set((state)=>({
callbacks: {
...state.callbacks,
[name]: callback
}
}));
if ('onConsentSet' === name && callback && 'function' == typeof callback) callback?.({
preferences: currentState.consents
});
if ('onBannerFetched' === name && currentState.hasFetchedBanner && currentState.lastBannerFetchData && callback && 'function' == typeof callback) {
const { lastBannerFetchData } = currentState;
const jurisdictionCode = lastBannerFetchData.jurisdiction ?? 'NONE';
callback?.({
jurisdiction: {
code: jurisdictionCode,
message: ''
},
location: {
countryCode: lastBannerFetchData.location.countryCode ?? null,
regionCode: lastBannerFetchData.location.regionCode ?? null
},
translations: {
language: lastBannerFetchData.translations.language,
translations: lastBannerFetchData.translations.translations
}
});
}
},
subscribeToConsentChanges: (listener)=>{
consentChangeListeners.add(listener);
return ()=>{
consentChangeListeners.delete(listener);
};
},
setLocationInfo: (location)=>set({
locationInfo: location
}),
initConsentManager: ()=>{
if (!enabled) return Promise.resolve(void 0);
return initConsentManager({
manager,
ssrData: options.ssrData,
backendURL: internalOptions.__internal?.backendURL,
requestCredentials: internalOptions.__internal?.requestCredentials,
initialTranslationConfig: normalizedInitialTranslationConfig,
iabConfig: iab,
get,
set
});
},
getDisplayedConsents: ()=>{
const { consentCategories, consentTypes } = get();
return consentTypes.filter((consent)=>consentCategories.includes(consent.name));
},
hasConsented: ()=>{
const { consentInfo } = get();
return null != consentInfo;
},
has: (condition)=>{
const { consents, policyCategories, policyScopeMode } = get();
return has(condition, consents, {
policyCategories,
policyScopeMode
});
},
setTranslationConfig: (config)=>{
set({
translationConfig: config
});
},
updateConsentCategories: (newCategories)=>{
const { consentCategories: currentConsentCategories, policyCategories, policyScopeMode } = get();
const allCategoriesSet = new Set([
...currentConsentCategories,
...newCategories
]);
let consentCategories;
consentCategories = shouldEnforcePolicyCategoryScope(policyCategories, policyScopeMode) ? filterConsentCategoriesByPolicy(Array.from(allCategoriesSet), policyCategories) : Array.from(allCategoriesSet);
set({
consentCategories
});
},
identifyUser: async (user)=>{
const currentInfo = get().consentInfo;
const subjectId = currentInfo?.subjectId;
set({
user
});
if (!subjectId) return;
if (String(currentInfo?.externalId) === String(user.id) && currentInfo?.identityProvider === user.identityProvider) return;
await manager.identifyUser({
body: {
subjectId,
externalId: user.id,
identityProvider: user.identityProvider
}
});
set({
consentInfo: {
...currentInfo,
time: currentInfo?.time || Date.now(),
subjectId,
externalId: user.id,
identityProvider: user.identityProvider
}
});
},
unstable_acceptPolicyConsent: (input)=>{
const requestKey = JSON.stringify([
get().consentInfo?.subjectId ?? null,
input
]);
return coalesceInFlight(inFlightPolicyConsents, requestKey, async ()=>{
const currentState = get();
const currentInfo = currentState.consentInfo;
const subjectId = currentInfo?.subjectId ?? generateSubjectId();
const storedIdentifiers = sanitizeSubjectIdentifiers({
externalId: currentInfo?.externalId,
identityProvider: currentInfo?.identityProvider
});
const userIdentifiers = sanitizeSubjectIdentifiers({
externalId: currentState.user?.id,
identityProvider: currentState.user?.identityProvider
});
const inputIdentifiers = sanitizeSubjectIdentifiers({
externalId: input.externalId,
identityProvider: input.identityProvider
});
const externalId = inputIdentifiers.externalId ?? storedIdentifiers.externalId ?? userIdentifiers.externalId;
const identityProvider = inputIdentifiers.identityProvider ?? storedIdentifiers.identityProvider ?? userIdentifiers.identityProvider;
const domain = input.domain ?? ("u" > typeof window ? window.location.hostname : 'localhost');
const legalDocumentConsent = isLegalDocumentConsentInput(input);
let legalDocumentFields = {};
if (legalDocumentConsent) if (input.documentSnapshotToken) legalDocumentFields = {
documentSnapshotToken: input.documentSnapshotToken
};
else if (input.policyHash) legalDocumentFields = {
policyHash: input.policyHash
};
else if (input.policyId) legalDocumentFields = {
policyId: input.policyId
};
else throw new Error('Legal document consent requires documentSnapshotToken, policyHash, or policyId.');
const givenAt = input.givenAt ?? Date.now();
const response = await manager.setConsent({
body: {
type: input.type,
subjectId,
domain,
givenAt,
uiSource: input.uiSource ?? 'api',
...legalDocumentFields,
...input.metadata ? {
metadata: input.metadata
} : {},
...input.preferences ? {
preferences: input.preferences
} : {},
...externalId ? {
externalSubjectId: externalId
} : {},
...identityProvider ? {
identityProvider
} : {}
}
});
if (!response.ok || !response.data) {
const errorMsg = response.error?.message ?? 'Failed to accept policy consent';
get().callbacks.onError?.({
error: errorMsg
});
const error = new Error(errorMsg);
error.code = response.error?.code;
error.details = response.error?.details ?? null;
error.status = response.error?.status;
throw error;
}
const consent = {
...response.data,
givenAt: response.data.givenAt instanceof Date ? response.data.givenAt : new Date(response.data.givenAt)
};
const latestState = get();
const latestInfo = latestState.consentInfo;
const nextConsentInfo = {
...latestInfo,
time: consent.givenAt.getTime(),
subjectId,
...externalId ? {
externalId
} : {},
...identityProvider ? {
identityProvider
} : {}
};
set({
consentInfo: nextConsentInfo,
...externalId ? {
user: {
id: externalId,
identityProvider
}
} : {}
});
saveConsentToStorage({
consents: latestState.consents,
consentInfo: nextConsentInfo
}, void 0, latestState.storageConfig);
return consent;
});
},
setOverrides: async (overrides)=>{
set({
overrides: {
...get().overrides,
...overrides
}
});
if (!enabled) return;
return await initConsentManager({
manager,
backendURL: internalOptions.__internal?.backendURL,
requestCredentials: internalOptions.__internal?.requestCredentials,
initialTranslationConfig: normalizedInitialTranslationConfig,
iabConfig: iab,
get,
set
});
},
setLanguage: async (language)=>await get().setOverrides({
...get().overrides ?? {},
language
}),
...createScriptManager(get, set),
...createIframeManager(get, set),
...createNetworkBlockerManager(get, set)
}));
store.getState().initializeIframeBlocker();
if (options.networkBlocker) {
store.setState({
networkBlocker: options.networkBlocker
});
store.getState().initializeNetworkBlocker();
}
if (options.scripts && options.scripts.length > 0) store.getState().updateConsentCategories(options.scripts.flatMap((script)=>extractConsentNamesFromCondition(script.category)));
if ("u" > typeof window) {
window[namespace] = store;
store.getState().callbacks.onConsentSet?.({
preferences: store.getState().consents
});
if (options.user) store.getState().identifyUser(options.user);
if (enabled) store.getState().initConsentManager();
else store.getState().updateScripts();
}
return store;
};
const runtime_DEFAULT_BACKEND_URL = '/api/c15t';
const managerCache = new Map();
const storeCache = new Map();
function normalizeRuntimeMode(mode) {
if ('offline' === mode || 'custom' === mode) return mode;
return 'hosted';
}
function generateRuntimeCacheKey(options) {
const enabledKey = false === options.enabled ? 'disabled' : 'enabled';
const normalizedMode = normalizeRuntimeMode(options.mode);
const cacheParts = [
normalizedMode,
options.backendURL ?? 'default',
options.endpointHandlers ? 'custom' : 'none',
options.storageConfig?.storageKey ?? 'default',
options.defaultLanguage ?? 'default',
options.languageSetKey ?? 'default',
options.offlinePolicyKey ?? 'default',
options.headersKey ?? 'default',
enabledKey
];
return cacheParts.join(':');
}
function generateHeadersKey(headers) {
if (!headers) return;
const entries = Object.entries(headers).sort(([a], [b])=>a.localeCompare(b));
if (0 === entries.length) return;
return entries.map(([key, value])=>`${key}=${value}`).join(',');
}
function getOrCreateConsentRuntime(options, pkgInfo) {
const optionBag = options;
const { mode, backendURL, store, i18n, translations, storageConfig, enabled, iab, offlinePolicy, consentCategories, debug, headers, nonce, customFetch: _unusedCustomFetch, retryConfig: _unusedRetryConfig, endpointHandlers: _unusedEndpointHandlers, ...storeOptionOverrides } = optionBag;
const { initialI18nConfig: _unusedTopLevelInitialI18nConfig, initialTranslationConfig: _unusedTopLevelInitialTranslationConfig, ssrData: topLevelSSRData, config: topLevelConfig, ...cleanStoreOptionOverrides } = storeOptionOverrides;
const { initialI18nConfig: _unusedStoreInitialI18nConfig, initialTranslationConfig: _unusedStoreInitialTranslationConfig, ssrData: storeSSRData, config: storeConfig, ...storeWithoutTranslationInputs } = store ?? {};
const preferredLegacyTranslationConfig = translations ?? store?.initialTranslationConfig;
const preferredI18nConfig = i18n ?? store?.initialI18nConfig;
const normalizedInitialTranslationConfig = (0, translations_namespaceObject.resolveTranslationInput)(preferredLegacyTranslationConfig, preferredI18nConfig);
const normalizedI18nConfig = normalizedInitialTranslationConfig ? (0, translations_namespaceObject.normalizeI18nConfig)(normalizedInitialTranslationConfig) : void 0;
const normalizedLanguageSet = normalizedI18nConfig ? Object.keys(normalizedI18nConfig.messages).sort() : [];
const resolvedIab = iab ?? storeWithoutTranslationInputs.iab;
const resolvedOfflinePolicy = offlinePolicy ?? storeWithoutTranslationInputs.offlinePolicy;
const resolvedStorageConfig = storageConfig ?? storeWithoutTranslationInputs.storageConfig;
const resolvedEnabled = enabled ?? storeWithoutTranslationInputs.enabled;
const resolvedNonce = nonce ?? storeWithoutTranslationInputs.nonce;
const resolvedBackendURL = backendURL || runtime_DEFAULT_BACKEND_URL;
const explicitSSRData = topLevelSSRData ?? storeSSRData;
const cacheKey = generateRuntimeCacheKey({
mode,
backendURL,
endpointHandlers: 'endpointHandlers' in options ? options.endpointHandlers : void 0,
storageConfig: resolvedStorageConfig,
defaultLanguage: normalizedI18nConfig?.locale,
languageSetKey: normalizedLanguageSet.length > 0 ? normalizedLanguageSet.join(',') : void 0,
offlinePolicyKey: resolvedOfflinePolicy ? JSON.stringify(resolvedOfflinePolicy) : void 0,
headersKey: generateHeadersKey(headers),
enabled: resolvedEnabled
});
let consentManager = managerCache.get(cacheKey);
if (!consentManager) {
const normalizedStoreOptions = {
...storeWithoutTranslationInputs,
initialTranslationConfig: normalizedInitialTranslationConfig,
iab: resolvedIab,
offlinePolicy: resolvedOfflinePolicy
};
consentManager = 'offline' === mode ? configureConsentManager({
mode: 'offline',
store: normalizedStoreOptions,
storageConfig: resolvedStorageConfig
}) : 'custom' === mode && 'endpointHandlers' in options ? configureConsentManager({
mode: 'custom',
endpointHandlers: options.endpointHandlers,
store: normalizedStoreOptions,
storageConfig: resolvedStorageConfig
}) : configureConsentManager({
mode: 'c15t' === mode ? 'c15t' : 'hosted',
backendURL: backendURL || runtime_DEFAULT_BACKEND_URL,
headers,
store: normalizedStoreOptions,
storageConfig: resolvedStorageConfig
});
managerCache.set(cacheKey, consentManager);
}
let consentStore = storeCache.get(cacheKey);
if (consentStore) {
if (consentStore.getState().nonce !== resolvedNonce) consentStore.setState({
nonce: resolvedNonce
});
} else {
const normalizedMode = normalizeRuntimeMode(mode);
const userConfig = storeConfig ?? topLevelConfig;
const autoPrefetchedSSRData = 'hosted' !== normalizedMode || "u" < typeof window || explicitSSRData ? void 0 : getMatchingPrefetchedInitialData({
backendURL: resolvedBackendURL,
overrides: options.overrides,
credentials: 'include'
});
const resolvedSSRData = explicitSSRData ?? autoPrefetchedSSRData;
consentStore = createConsentManagerStore(consentManager, {
config: {
...userConfig ?? {},
pkg: pkgInfo?.pkg || 'c15t',
version: pkgInfo?.version || "2.2.1",
mode: normalizedMode,
meta: {
...userConfig?.meta ?? {},
...'hosted' === normalizedMode ? {
backendURL: resolvedBackendURL,
requestCredentials: 'include'
} : {}
}
},
...cleanStoreOptionOverrides,
...storeWithoutTranslationInputs,
iab: resolvedIab,
offlinePolicy: resolvedOfflinePolicy,
storageConfig: resolvedStorageConfig,
enabled: resolvedEnabled,
nonce: resolvedNonce,
initialTranslationConfig: normalizedInitialTranslationConfig,
initialConsentCategories: consentCategories,
ssrData: resolvedSSRData,
debug,
__internal: 'hosted' === normalizedMode ? {
backendURL: resolvedBackendURL,
requestCredentials: 'include'
} : void 0
});
storeCache.set(cacheKey, consentStore);
}
return {
consentManager,
consentStore,
cacheKey
};
}
function clearConsentRuntimeCache() {
managerCache.clear();
storeCache.clear();
clearClientRegistry();
}
exports.API_ENDPOINTS = __webpack_exports__.API_ENDPOINTS;
exports.C15tClient = __webpack_exports__.C15tClient;
exports.CustomClient = __webpack_exports__.CustomClient;
exports.OfflineClient = __webpack_exports__.OfflineClient;
exports.allConsentNames = __webpack_exports__.allConsentNames;
exports.applyPolicyPurposeAllowlist = __webpack_exports__.applyPolicyPurposeAllowlist;
exports.applyPolicyScopeForRuntimeGating = __webpack_exports__.applyPolicyScopeForRuntimeGating;
exports.buildPrefetchScript = __webpack_exports__.buildPrefetchScript;
exports.clearClientRegistry = __webpack_exports__.clearClientRegistry;
exports.clearConsentRuntimeCache = __webpack_exports__.clearConsentRuntimeCache;
exports.configureConsentManager = __webpack_exports__.configureConsentManager;
exports.consentTypes = __webpack_exports__.consentTypes;
exports.createConsentManagerStore = __webpack_exports__.createConsentManagerStore;
exports.createIframeBlocker = __webpack_exports__.createIframeBlocker;
exports.deepMergeTranslations = __webpack_exports__.deepMergeTranslations;
exports.defaultTranslationConfig = __webpack_exports__.defaultTranslationConfig;
exports.deleteConsentFromStorage = __webpack_exports__.deleteConsentFromStorage;
exports.deleteCookie = __webpack_exports__.deleteCookie;
exports.detectBrowserLanguage = __webpack_exports__.detectBrowserLanguage;
exports.emitScriptDebugEvent = __webpack_exports__.emitScriptDebugEvent;
exports.filterConsentCategoriesByPolicy = __webpack_exports__.filterConsentCategoriesByPolicy;
exports.flattenPolicyActionGroups = __webpack_exports__.flattenPolicyActionGroups;
exports.generateSubjectId = __webpack_exports__.generateSubjectId;
exports.getConsentFromStorage = __webpack_exports__.getConsentFromStorage;
exports.getCookie = __webpack_exports__.getCookie;
exports.getEffectivePolicy = __webpack_exports__.getEffectivePolicy;
exports.getLoadedScriptIds = __webpack_exports__.getLoadedScriptIds;
exports.getOrCreateConsentRuntime = __webpack_exports__.getOrCreateConsentRuntime;
exports.getRootDomain = __webpack_exports__.getRootDomain;
exports.has = __webpack_exports__.has;
exports.hasPolicyHints = __webpack_exports__.hasPolicyHints;
exports.isScriptLoaded = __webpack_exports__.isScriptLoaded;
exports.isValidSubjectId = __webpack_exports__.isValidSubjectId;
exports.loadScripts = __webpack_exports__.loadScripts;
exports.mergeTranslationConfigs = __webpack_exports__.mergeTranslationConfigs;
exports.policyPackPresets = __webpack_exports__.policyPackPresets;
exports.prepareTranslationConfig = __webpack_exports__.prepareTranslationConfig;
exports.resolvePolicyActionGroups = __webpack_exports__.resolvePolicyActionGroups;
exports.resolvePolicyAllowedActions = __webpack_exports__.resolvePolicyAllowedActions;
exports.resolvePolicyDirection = __webpack_exports__.resolvePolicyDirection;
exports.resolvePolicyOrderedActions = __webpack_exports__.resolvePolicyOrderedActions;
exports.resolvePolicyPrimaryActions = __webpack_exports__.resolvePolicyPrimaryActions;
exports.resolvePolicyUiProfile = __webpack_exports__.resolvePolicyUiProfile;
exports.saveConsentToStorage = __webpack_exports__.saveConsentToStorage;
exports.setCookie = __webpack_exports__.setCookie;
exports.shouldFillPolicyActions = __webpack_exports__.shouldFillPolicyActions;
exports.subscribeToScriptDebugEvents = __webpack_exports__.subscribeToScriptDebugEvents;
exports.unloadScripts = __webpack_exports__.unloadScripts;
exports.updateScripts = __webpack_exports__.updateScripts;
exports.validateUIAgainstPolicy = __webpack_exports__.validateUIAgainstPolicy;
for(var __rspack_i in __webpack_exports__)if (-1 === [
"API_ENDPOINTS",
"C15tClient",
"CustomClient",
"OfflineClient",
"allConsentNames",
"applyPolicyPurposeAllowlist",
"applyPolicyScopeForRuntimeGating",
"buildPrefetchScript",
"clearClientRegistry",
"clearConsentRuntimeCache",
"configureConsentManager",
"consentTypes",
"createConsentManagerStore",
"createIframeBlocker",
"deepMergeTranslations",
"defaultTranslationConfig",
"deleteConsentFromStorage",
"deleteCookie",
"detectBrowserLanguage",
"emitScriptDebugEvent",
"filterConsentCategoriesByPolicy",
"flattenPolicyActionGroups",
"generateSubjectId",
"getConsentFromStorage",
"getCookie",
"getEffectivePolicy",
"getLoadedScriptIds",
"getOrCreateConsentRuntime",
"getRootDomain",
"has",
"hasPolicyHints",
"isScriptLoaded",
"isValidSubjectId",
"loadScripts",
"mergeTranslationConfigs",
"policyPackPresets",
"prepareTranslationConfig",
"resolvePolicyActionGroups",
"resolvePolicyAllowedActions",
"resolvePolicyDirection",
"resolvePolicyOrderedActions",
"resolvePolicyPrimaryActions",
"resolvePolicyUiProfile",
"saveConsentToStorage",
"setCookie",
"shouldFillPolicyActions",
"subscribeToScriptDebugEvents",
"unloadScripts",
"updateScripts",
"validateUIAgainstPolicy"
].indexOf(__rspack_i)) exports[__rspack_i] = __webpack_exports__[__rspack_i];
Object.defineProperty(exports, '__esModule', {
value: true
});