@react-lgpd-consent/core 0.7.0 → 0.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -309,6 +309,7 @@ function setDebugLogging(enabled, level = 2 /* INFO */) {
309
309
  // src/utils/cookieUtils.ts
310
310
  var DEFAULT_STORAGE_NAMESPACE = "lgpd-consent";
311
311
  var DEFAULT_STORAGE_VERSION = "1";
312
+ var DEFAULT_MAX_AGE_SECONDS = 365 * 24 * 60 * 60;
312
313
  function buildConsentStorageKey(options) {
313
314
  const namespaceRaw = options?.namespace?.trim() || DEFAULT_STORAGE_NAMESPACE;
314
315
  const versionRaw = options?.version?.trim() || DEFAULT_STORAGE_VERSION;
@@ -318,12 +319,31 @@ function buildConsentStorageKey(options) {
318
319
  }
319
320
  var DEFAULT_COOKIE_OPTS = {
320
321
  name: "cookieConsent",
322
+ maxAge: DEFAULT_MAX_AGE_SECONDS,
321
323
  maxAgeDays: 365,
322
324
  sameSite: "Lax",
323
325
  secure: globalThis.window === void 0 ? false : globalThis.window.location.protocol === "https:",
324
326
  path: "/",
325
327
  domain: void 0
326
328
  };
329
+ function resolveCookieOptions(opts) {
330
+ const protocols = [
331
+ typeof globalThis.window !== "undefined" ? globalThis.window?.location?.protocol : void 0,
332
+ typeof globalThis.location !== "undefined" ? globalThis.location?.protocol : void 0
333
+ ].filter(Boolean);
334
+ const forceHttps = globalThis.__LGPD_FORCE_HTTPS__ === true;
335
+ const isHttps = forceHttps || protocols.includes("https:");
336
+ const maxAgeSecondsFromDays = typeof opts?.maxAgeDays === "number" ? Math.max(0, opts.maxAgeDays * 24 * 60 * 60) : null;
337
+ const maxAgeSeconds = typeof opts?.maxAge === "number" ? Math.max(0, opts.maxAge) : maxAgeSecondsFromDays ?? DEFAULT_MAX_AGE_SECONDS;
338
+ return {
339
+ name: opts?.name ?? DEFAULT_COOKIE_OPTS.name,
340
+ maxAge: maxAgeSeconds,
341
+ sameSite: opts?.sameSite ?? DEFAULT_COOKIE_OPTS.sameSite ?? "Lax",
342
+ secure: typeof opts?.secure === "boolean" ? opts.secure : isHttps ? true : DEFAULT_COOKIE_OPTS.secure ?? false,
343
+ path: opts?.path ?? DEFAULT_COOKIE_OPTS.path ?? "/",
344
+ domain: opts?.domain ?? DEFAULT_COOKIE_OPTS.domain ?? void 0
345
+ };
346
+ }
327
347
  var COOKIE_SCHEMA_VERSION = "1.0";
328
348
  function readConsentCookie(name = DEFAULT_COOKIE_OPTS.name) {
329
349
  logger.debug("Reading consent cookie", { name });
@@ -339,6 +359,10 @@ function readConsentCookie(name = DEFAULT_COOKIE_OPTS.name) {
339
359
  try {
340
360
  const data = JSON.parse(raw);
341
361
  logger.cookieOperation("read", name, data);
362
+ if (!data || typeof data !== "object") {
363
+ logger.warn("Consent cookie malformed: payload is not an object");
364
+ return null;
365
+ }
342
366
  if (!data.version) {
343
367
  logger.debug("Migrating legacy cookie format");
344
368
  return migrateLegacyCookie(data);
@@ -347,7 +371,11 @@ function readConsentCookie(name = DEFAULT_COOKIE_OPTS.name) {
347
371
  logger.warn(`Cookie version mismatch: ${data.version} != ${COOKIE_SCHEMA_VERSION}`);
348
372
  return null;
349
373
  }
350
- return data;
374
+ const preferences = data && typeof data.preferences === "object" ? data.preferences : { necessary: true };
375
+ return {
376
+ ...data,
377
+ preferences: ensureNecessaryAlwaysOn(preferences)
378
+ };
351
379
  } catch (error) {
352
380
  logger.error("Error parsing consent cookie", error);
353
381
  return null;
@@ -370,12 +398,12 @@ function migrateLegacyCookie(legacyData) {
370
398
  }
371
399
  }
372
400
  function writeConsentCookie(state, config, opts, source = "banner") {
373
- if (typeof document === "undefined") {
401
+ if (typeof document === "undefined" || typeof window === "undefined" || globalThis.__LGPD_SSR__ === true) {
374
402
  logger.debug("Cookie write skipped: server-side environment");
375
403
  return;
376
404
  }
377
405
  const now = (/* @__PURE__ */ new Date()).toISOString();
378
- const o = { ...DEFAULT_COOKIE_OPTS, ...opts };
406
+ const o = resolveCookieOptions(opts);
379
407
  const preferences = ensureNecessaryAlwaysOn(state.preferences);
380
408
  const cookieData = {
381
409
  version: COOKIE_SCHEMA_VERSION,
@@ -387,8 +415,9 @@ function writeConsentCookie(state, config, opts, source = "banner") {
387
415
  projectConfig: config
388
416
  };
389
417
  logger.cookieOperation("write", o.name, cookieData);
418
+ const expires = new Date(Date.now() + o.maxAge * 1e3);
390
419
  Cookies.set(o.name, JSON.stringify(cookieData), {
391
- expires: o.maxAgeDays,
420
+ expires,
392
421
  sameSite: o.sameSite,
393
422
  secure: o.secure,
394
423
  path: o.path,
@@ -422,14 +451,14 @@ function removeConsentCookie(opts) {
422
451
  logger.debug("Cookie removal skipped: server-side environment");
423
452
  return;
424
453
  }
425
- const o = { ...DEFAULT_COOKIE_OPTS, ...opts };
454
+ const o = resolveCookieOptions(opts);
426
455
  logger.cookieOperation("delete", o.name);
427
456
  Cookies.remove(o.name, { path: o.path, domain: o.domain });
428
457
  logger.info("Consent cookie removed");
429
458
  }
430
459
 
431
460
  // src/utils/dataLayerEvents.ts
432
- var LIBRARY_VERSION = "0.7.0";
461
+ var LIBRARY_VERSION = "0.7.1";
433
462
  function ensureDataLayer() {
434
463
  var _a;
435
464
  if (globalThis.window === void 0) return;
@@ -442,7 +471,8 @@ function pushConsentInitializedEvent(categories) {
442
471
  event: "consent_initialized",
443
472
  consent_version: LIBRARY_VERSION,
444
473
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
445
- categories
474
+ categories,
475
+ preferences: categories
446
476
  };
447
477
  globalThis.window.dataLayer?.push(event);
448
478
  }
@@ -456,6 +486,7 @@ function pushConsentUpdatedEvent(categories, origin, previousCategories) {
456
486
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
457
487
  origin,
458
488
  categories,
489
+ preferences: categories,
459
490
  changed_categories: changedCategories
460
491
  };
461
492
  globalThis.window.dataLayer?.push(event);
@@ -1065,58 +1096,8 @@ var GUIDANCE_PRESETS = {
1065
1096
  };
1066
1097
 
1067
1098
  // src/utils/peerDepsCheck.ts
1068
- function detectMultipleReactInstances() {
1069
- if (globalThis.window === void 0) return false;
1070
- try {
1071
- const reactSymbols = Object.getOwnPropertySymbols(globalThis.window).map(String).filter((name) => name.includes("react"));
1072
- if (reactSymbols.length > 1) {
1073
- return true;
1074
- }
1075
- const ReactModule = window.React;
1076
- if (ReactModule && Array.isArray(ReactModule)) {
1077
- return true;
1078
- }
1079
- const hasMultipleVersions = window.__REACT_DEVTOOLS_GLOBAL_HOOK__?.renderers?.size > 1;
1080
- return hasMultipleVersions || false;
1081
- } catch {
1082
- return false;
1083
- }
1084
- }
1085
- function getPackageVersion(packageName) {
1086
- if (globalThis.window === void 0) return null;
1087
- try {
1088
- const pkg = window[packageName];
1089
- if (pkg?.version) return pkg.version;
1090
- const React6 = window.React;
1091
- if (packageName === "react" && React6?.version) {
1092
- return React6.version;
1093
- }
1094
- return null;
1095
- } catch {
1096
- return null;
1097
- }
1098
- }
1099
- function isVersionInRange(version, minMajor, maxMajor) {
1100
- const major = Number.parseInt(version.split(".")[0], 10);
1101
- return major >= minMajor && major <= maxMajor;
1102
- }
1103
- function checkPeerDeps(options = {}) {
1104
- const { skipInProduction = true, logWarnings = true } = options;
1105
- const result = {
1106
- ok: true,
1107
- warnings: [],
1108
- errors: []
1109
- };
1110
- const isProduction = typeof process !== "undefined" && process.env?.NODE_ENV === "production";
1111
- if (skipInProduction && isProduction) {
1112
- return result;
1113
- }
1114
- if (globalThis.window === void 0) {
1115
- return result;
1116
- }
1117
- if (detectMultipleReactInstances()) {
1118
- result.ok = false;
1119
- const errorMsg = `
1099
+ var MESSAGES_PT_BR = {
1100
+ MULTIPLE_REACT_INSTANCES: `
1120
1101
  \u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557
1121
1102
  \u2551 \u26A0\uFE0F ERRO: M\xFAltiplas inst\xE2ncias de React detectadas \u2551
1122
1103
  \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D
@@ -1178,22 +1159,13 @@ function checkPeerDeps(options = {}) {
1178
1159
  https://github.com/lucianoedipo/react-lgpd-consent/blob/main/TROUBLESHOOTING.md#multiple-react-instances
1179
1160
 
1180
1161
  \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
1181
- `;
1182
- result.errors.push(errorMsg);
1183
- if (logWarnings) {
1184
- console.error(errorMsg);
1185
- }
1186
- }
1187
- const reactVersion = getPackageVersion("react");
1188
- if (reactVersion) {
1189
- if (!isVersionInRange(reactVersion, 18, 19)) {
1190
- result.ok = false;
1191
- const errorMsg = `
1162
+ `,
1163
+ UNSUPPORTED_REACT_VERSION: (version) => `
1192
1164
  \u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557
1193
1165
  \u2551 \u26A0\uFE0F AVISO: Vers\xE3o do React n\xE3o suportada \u2551
1194
1166
  \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D
1195
1167
 
1196
- \u{1F4E6} Vers\xE3o detectada: React ${reactVersion}
1168
+ \u{1F4E6} Vers\xE3o detectada: React ${version}
1197
1169
  \u2705 Vers\xF5es suportadas: React 18.x ou 19.x
1198
1170
 
1199
1171
  \u{1F50D} O react-lgpd-consent requer React 18.2.0+ ou React 19.x
@@ -1211,27 +1183,13 @@ function checkPeerDeps(options = {}) {
1211
1183
  https://github.com/lucianoedipo/react-lgpd-consent/blob/main/TROUBLESHOOTING.md#react-version
1212
1184
 
1213
1185
  \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
1214
- `;
1215
- result.errors.push(errorMsg);
1216
- if (logWarnings) {
1217
- console.error(errorMsg);
1218
- }
1219
- }
1220
- }
1221
- const muiVersion = window["@mui/material"]?.version;
1222
- if (muiVersion) {
1223
- if (!isVersionInRange(muiVersion, 5, 7)) {
1224
- result.warnings.push(
1225
- `MUI vers\xE3o ${muiVersion} detectada. Vers\xF5es suportadas: 5.15.0+, 6.x ou 7.x. Alguns componentes podem n\xE3o funcionar corretamente.`
1226
- );
1227
- if (logWarnings) {
1228
- logger.warn(
1229
- `
1186
+ `,
1187
+ UNSUPPORTED_MUI_VERSION: (version) => `
1230
1188
  \u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557
1231
1189
  \u2551 \u26A0\uFE0F AVISO: Vers\xE3o do Material-UI fora do range recomendado \u2551
1232
1190
  \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D
1233
1191
 
1234
- \u{1F4E6} Vers\xE3o detectada: @mui/material ${muiVersion}
1192
+ \u{1F4E6} Vers\xE3o detectada: @mui/material ${version}
1235
1193
  \u2705 Vers\xF5es suportadas: 5.15.0+, 6.x, 7.x
1236
1194
 
1237
1195
  \u{1F50D} Componentes de UI (@react-lgpd-consent/mui) podem apresentar problemas.
@@ -1249,8 +1207,227 @@ function checkPeerDeps(options = {}) {
1249
1207
  https://github.com/lucianoedipo/react-lgpd-consent/blob/main/TROUBLESHOOTING.md#mui-version
1250
1208
 
1251
1209
  \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
1252
- `
1253
- );
1210
+ `,
1211
+ MUI_OUT_OF_RANGE: (version) => `MUI vers\xE3o ${version} detectada. Vers\xF5es suportadas: 5.15.0+, 6.x ou 7.x. Alguns componentes podem n\xE3o funcionar corretamente.`
1212
+ };
1213
+ var MESSAGES_EN = {
1214
+ MULTIPLE_REACT_INSTANCES: `
1215
+ \u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557
1216
+ \u2551 \u26A0\uFE0F ERROR: Multiple React instances detected \u2551
1217
+ \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D
1218
+
1219
+ \u{1F534} Problem:
1220
+ Your project is loading more than one copy of React, causing the error:
1221
+ "Invalid hook call. Hooks can only be called inside of the body of a
1222
+ function component."
1223
+
1224
+ \u{1F50D} Probable cause:
1225
+ \u2022 pnpm/Yarn PnP without proper peer dependency hoisting
1226
+ \u2022 node_modules with duplicate React (classic npm/yarn)
1227
+ \u2022 Webpack/Vite with multiple resolutions of the same package
1228
+
1229
+ \u2705 Solutions:
1230
+
1231
+ \u{1F4E6} PNPM (RECOMMENDED):
1232
+ Add to root package.json:
1233
+ {
1234
+ "pnpm": {
1235
+ "overrides": {
1236
+ "react": "$react",
1237
+ "react-dom": "$react-dom"
1238
+ }
1239
+ }
1240
+ }
1241
+ Run: pnpm install
1242
+
1243
+ \u{1F4E6} NPM/Yarn:
1244
+ Add to root package.json:
1245
+ {
1246
+ "overrides": {
1247
+ "react": "^18.2.0 || ^19.0.0",
1248
+ "react-dom": "^18.2.0 || ^19.0.0"
1249
+ }
1250
+ }
1251
+ Run: npm install (or yarn install)
1252
+
1253
+ \u{1F527} Webpack:
1254
+ Add to webpack.config.js:
1255
+ module.exports = {
1256
+ resolve: {
1257
+ alias: {
1258
+ react: path.resolve('./node_modules/react'),
1259
+ 'react-dom': path.resolve('./node_modules/react-dom'),
1260
+ }
1261
+ }
1262
+ }
1263
+
1264
+ \u26A1 Vite:
1265
+ Add to vite.config.js:
1266
+ export default {
1267
+ resolve: {
1268
+ dedupe: ['react', 'react-dom']
1269
+ }
1270
+ }
1271
+
1272
+ \u{1F4DA} Documentation:
1273
+ https://github.com/lucianoedipo/react-lgpd-consent/blob/main/TROUBLESHOOTING.md#multiple-react-instances
1274
+
1275
+ \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
1276
+ `,
1277
+ UNSUPPORTED_REACT_VERSION: (version) => `
1278
+ \u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557
1279
+ \u2551 \u26A0\uFE0F WARNING: Unsupported React version \u2551
1280
+ \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D
1281
+
1282
+ \u{1F4E6} Detected version: React ${version}
1283
+ \u2705 Supported versions: React 18.x or 19.x
1284
+
1285
+ \u{1F50D} react-lgpd-consent requires React 18.2.0+ or React 19.x
1286
+
1287
+ \u2705 Solution:
1288
+ Update React to a supported version:
1289
+
1290
+ npm install react@^18.2.0 react-dom@^18.2.0
1291
+
1292
+ or
1293
+
1294
+ npm install react@^19.0.0 react-dom@^19.0.0
1295
+
1296
+ \u{1F4DA} Documentation:
1297
+ https://github.com/lucianoedipo/react-lgpd-consent/blob/main/TROUBLESHOOTING.md#react-version
1298
+
1299
+ \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
1300
+ `,
1301
+ UNSUPPORTED_MUI_VERSION: (version) => `
1302
+ \u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557
1303
+ \u2551 \u26A0\uFE0F WARNING: Material-UI version out of recommended range \u2551
1304
+ \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D
1305
+
1306
+ \u{1F4E6} Detected version: @mui/material ${version}
1307
+ \u2705 Supported versions: 5.15.0+, 6.x, 7.x
1308
+
1309
+ \u{1F50D} UI components (@react-lgpd-consent/mui) may have issues.
1310
+
1311
+ \u2705 Solution:
1312
+ Update MUI to a supported version:
1313
+
1314
+ npm install @mui/material@^7.0.0 @emotion/react @emotion/styled
1315
+
1316
+ or keep 5.15.0+:
1317
+
1318
+ npm install @mui/material@^5.15.0 @emotion/react @emotion/styled
1319
+
1320
+ \u{1F4DA} Documentation:
1321
+ https://github.com/lucianoedipo/react-lgpd-consent/blob/main/TROUBLESHOOTING.md#mui-version
1322
+
1323
+ \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
1324
+ `,
1325
+ MUI_OUT_OF_RANGE: (version) => `MUI version ${version} detected. Supported versions: 5.15.0+, 6.x or 7.x. Some components may not work properly.`
1326
+ };
1327
+ var MESSAGES_BY_LOCALE = {
1328
+ "pt-BR": MESSAGES_PT_BR,
1329
+ en: MESSAGES_EN
1330
+ };
1331
+ var currentLocale = "pt-BR";
1332
+ var customMessages = {};
1333
+ function setPeerDepsLocale(locale) {
1334
+ currentLocale = locale;
1335
+ }
1336
+ function getPeerDepsLocale() {
1337
+ return currentLocale;
1338
+ }
1339
+ function setPeerDepsMessages(messages) {
1340
+ customMessages = { ...customMessages, ...messages };
1341
+ }
1342
+ function resetPeerDepsMessages() {
1343
+ customMessages = {};
1344
+ }
1345
+ function getMessages() {
1346
+ const baseMessages = MESSAGES_BY_LOCALE[currentLocale];
1347
+ if (Object.keys(customMessages).length === 0) {
1348
+ return baseMessages;
1349
+ }
1350
+ return {
1351
+ MULTIPLE_REACT_INSTANCES: customMessages.MULTIPLE_REACT_INSTANCES ?? baseMessages.MULTIPLE_REACT_INSTANCES,
1352
+ UNSUPPORTED_REACT_VERSION: customMessages.UNSUPPORTED_REACT_VERSION ?? baseMessages.UNSUPPORTED_REACT_VERSION,
1353
+ UNSUPPORTED_MUI_VERSION: customMessages.UNSUPPORTED_MUI_VERSION ?? baseMessages.UNSUPPORTED_MUI_VERSION,
1354
+ MUI_OUT_OF_RANGE: customMessages.MUI_OUT_OF_RANGE ?? baseMessages.MUI_OUT_OF_RANGE
1355
+ };
1356
+ }
1357
+ function detectMultipleReactInstances() {
1358
+ if (globalThis.window === void 0) return false;
1359
+ try {
1360
+ const reactSymbols = Object.getOwnPropertySymbols(globalThis.window).map(String).filter((name) => name.includes("react"));
1361
+ if (reactSymbols.length > 1) {
1362
+ return true;
1363
+ }
1364
+ const ReactModule = window.React;
1365
+ if (ReactModule && Array.isArray(ReactModule)) {
1366
+ return true;
1367
+ }
1368
+ const hasMultipleVersions = window.__REACT_DEVTOOLS_GLOBAL_HOOK__?.renderers?.size > 1;
1369
+ return hasMultipleVersions || false;
1370
+ } catch {
1371
+ return false;
1372
+ }
1373
+ }
1374
+ function getPackageVersion(packageName) {
1375
+ if (globalThis.window === void 0) return null;
1376
+ try {
1377
+ const pkg = window[packageName];
1378
+ if (pkg?.version) return pkg.version;
1379
+ const React6 = window.React;
1380
+ if (packageName === "react" && React6?.version) {
1381
+ return React6.version;
1382
+ }
1383
+ return null;
1384
+ } catch {
1385
+ return null;
1386
+ }
1387
+ }
1388
+ function isVersionInRange(version, minMajor, maxMajor) {
1389
+ const major = Number.parseInt(version.split(".")[0], 10);
1390
+ return major >= minMajor && major <= maxMajor;
1391
+ }
1392
+ function checkPeerDeps(options = {}) {
1393
+ const { skipInProduction = true, logWarnings = true } = options;
1394
+ const result = {
1395
+ ok: true,
1396
+ warnings: [],
1397
+ errors: []
1398
+ };
1399
+ const isProduction = typeof process !== "undefined" && process.env?.NODE_ENV === "production";
1400
+ if (skipInProduction && isProduction) {
1401
+ return result;
1402
+ }
1403
+ if (globalThis.window === void 0) {
1404
+ return result;
1405
+ }
1406
+ const messages = getMessages();
1407
+ if (detectMultipleReactInstances()) {
1408
+ result.ok = false;
1409
+ result.errors.push(messages.MULTIPLE_REACT_INSTANCES);
1410
+ if (logWarnings) {
1411
+ console.error(messages.MULTIPLE_REACT_INSTANCES);
1412
+ }
1413
+ }
1414
+ const reactVersion = getPackageVersion("react");
1415
+ if (reactVersion) {
1416
+ if (!isVersionInRange(reactVersion, 18, 19)) {
1417
+ result.ok = false;
1418
+ const errorMsg = messages.UNSUPPORTED_REACT_VERSION(reactVersion);
1419
+ result.errors.push(errorMsg);
1420
+ if (logWarnings) {
1421
+ console.error(errorMsg);
1422
+ }
1423
+ }
1424
+ }
1425
+ const muiVersion = window["@mui/material"]?.version;
1426
+ if (muiVersion) {
1427
+ if (!isVersionInRange(muiVersion, 5, 7)) {
1428
+ result.warnings.push(messages.MUI_OUT_OF_RANGE(muiVersion));
1429
+ if (logWarnings) {
1430
+ logger.warn(messages.UNSUPPORTED_MUI_VERSION(muiVersion));
1254
1431
  }
1255
1432
  }
1256
1433
  }
@@ -1706,6 +1883,13 @@ function ConsentProvider({
1706
1883
  return base;
1707
1884
  }, [cookieOpts, storage?.domain, storage?.namespace, storage?.version]);
1708
1885
  const consentVersion = storage?.version?.trim() || "1";
1886
+ React4.useEffect(() => {
1887
+ try {
1888
+ ;
1889
+ globalThis.__LGPD_CONSENT_COOKIE__ = cookie.name;
1890
+ } catch {
1891
+ }
1892
+ }, [cookie.name]);
1709
1893
  const finalCategoriesConfig = React4.useMemo(() => {
1710
1894
  const isProd = typeof process !== "undefined" && process.env?.NODE_ENV === "production";
1711
1895
  if (!categories) return DEFAULT_PROJECT_CATEGORIES;
@@ -2068,51 +2252,99 @@ function ConsentGate(props) {
2068
2252
 
2069
2253
  // src/utils/scriptLoader.ts
2070
2254
  var LOADING_SCRIPTS = /* @__PURE__ */ new Map();
2071
- function loadScript(id, src, category = null, attrs = {}, nonce) {
2255
+ var DEFAULT_POLL_INTERVAL = 100;
2256
+ function resolveCookieNames(preferred) {
2257
+ const inferred = globalThis.__LGPD_CONSENT_COOKIE__ ?? null;
2258
+ const names = [preferred, inferred, "cookieConsent", "lgpd-consent__v1"].filter(
2259
+ Boolean
2260
+ );
2261
+ return Array.from(new Set(names));
2262
+ }
2263
+ function parseConsentFromCookie(names) {
2264
+ const raw = document.cookie;
2265
+ if (!raw) return null;
2266
+ const cookies = raw.split("; ").reduce((acc, part) => {
2267
+ const [k, ...rest] = part.split("=");
2268
+ acc[k] = rest.join("=");
2269
+ return acc;
2270
+ }, {});
2271
+ for (const name of names) {
2272
+ const value = cookies[name];
2273
+ if (!value) continue;
2274
+ try {
2275
+ const parsed = JSON.parse(decodeURIComponent(value));
2276
+ if (!parsed.consented || parsed.isModalOpen) continue;
2277
+ return parsed;
2278
+ } catch {
2279
+ continue;
2280
+ }
2281
+ }
2282
+ return null;
2283
+ }
2284
+ function hasCategoryConsent(snapshot, category) {
2285
+ if (!snapshot.consented || snapshot.isModalOpen) return false;
2286
+ if (category === null) return true;
2287
+ return Boolean(snapshot.preferences?.[category]);
2288
+ }
2289
+ function loadScript(id, src, category = null, attrs = {}, nonce, options) {
2072
2290
  if (typeof document === "undefined") return Promise.resolve();
2073
2291
  if (document.getElementById(id)) return Promise.resolve();
2074
2292
  const existingPromise = LOADING_SCRIPTS.get(id);
2075
2293
  if (existingPromise) return existingPromise;
2294
+ const pollInterval = options?.pollIntervalMs ?? DEFAULT_POLL_INTERVAL;
2295
+ const names = resolveCookieNames(options?.cookieName);
2296
+ const mergedAttrs = { ...attrs };
2076
2297
  const promise = new Promise((resolve, reject) => {
2298
+ const inject = () => {
2299
+ const s = document.createElement("script");
2300
+ s.id = id;
2301
+ s.src = src;
2302
+ s.async = mergedAttrs.async !== "false";
2303
+ const scriptNonce = mergedAttrs.nonce || nonce;
2304
+ if (scriptNonce) {
2305
+ s.nonce = scriptNonce;
2306
+ mergedAttrs.nonce = scriptNonce;
2307
+ }
2308
+ for (const [k, v] of Object.entries(mergedAttrs)) s.setAttribute(k, v);
2309
+ s.onload = () => {
2310
+ LOADING_SCRIPTS.delete(id);
2311
+ resolve();
2312
+ };
2313
+ s.onerror = () => {
2314
+ LOADING_SCRIPTS.delete(id);
2315
+ reject(new Error(`Failed to load script: ${src}`));
2316
+ };
2317
+ document.body.appendChild(s);
2318
+ };
2319
+ const snapshot = options?.consentSnapshot;
2320
+ const skipChecks = options?.skipConsentCheck === true;
2321
+ if (skipChecks) {
2322
+ inject();
2323
+ return;
2324
+ }
2325
+ if (snapshot) {
2326
+ if (!hasCategoryConsent(snapshot, category)) {
2327
+ reject(
2328
+ new Error(
2329
+ `Consent not granted for category '${category ?? "none"}' when attempting to load ${id}`
2330
+ )
2331
+ );
2332
+ return;
2333
+ }
2334
+ inject();
2335
+ return;
2336
+ }
2077
2337
  const checkConsent = () => {
2078
- const consentCookie = document.cookie.split("; ").find((row) => row.startsWith("cookieConsent="))?.split("=")[1];
2079
- if (!consentCookie) {
2080
- setTimeout(checkConsent, 100);
2338
+ const consent = parseConsentFromCookie(names);
2339
+ if (!consent) {
2340
+ setTimeout(checkConsent, pollInterval);
2081
2341
  return;
2082
2342
  }
2083
- try {
2084
- const consent = JSON.parse(decodeURIComponent(consentCookie));
2085
- if (!consent.consented || consent.isModalOpen) {
2086
- setTimeout(checkConsent, 100);
2087
- return;
2088
- }
2089
- if (category && !consent.preferences[category]) {
2090
- setTimeout(checkConsent, 100);
2091
- return;
2092
- }
2093
- const s = document.createElement("script");
2094
- s.id = id;
2095
- s.src = src;
2096
- s.async = true;
2097
- const mergedAttrs = { ...attrs };
2098
- const scriptNonce = mergedAttrs.nonce || nonce;
2099
- if (scriptNonce) {
2100
- s.nonce = scriptNonce;
2101
- mergedAttrs.nonce = scriptNonce;
2102
- }
2103
- for (const [k, v] of Object.entries(mergedAttrs)) s.setAttribute(k, v);
2104
- s.onload = () => {
2105
- LOADING_SCRIPTS.delete(id);
2106
- resolve();
2107
- };
2108
- s.onerror = () => {
2109
- LOADING_SCRIPTS.delete(id);
2110
- reject(new Error(`Failed to load script: ${src}`));
2111
- };
2112
- document.body.appendChild(s);
2113
- } catch {
2114
- setTimeout(checkConsent, 100);
2343
+ if (!hasCategoryConsent(consent, category)) {
2344
+ setTimeout(checkConsent, pollInterval);
2345
+ return;
2115
2346
  }
2347
+ inject();
2116
2348
  };
2117
2349
  checkConsent();
2118
2350
  });
@@ -2276,14 +2508,107 @@ function validateNecessaryClassification(integrations, enabledCategories) {
2276
2508
  }
2277
2509
 
2278
2510
  // src/utils/ConsentScriptLoader.tsx
2511
+ var scriptRegistry = /* @__PURE__ */ new Map();
2512
+ var queueListeners = /* @__PURE__ */ new Set();
2513
+ function notifyQueue() {
2514
+ queueListeners.forEach((listener) => {
2515
+ try {
2516
+ listener();
2517
+ } catch {
2518
+ }
2519
+ });
2520
+ }
2521
+ function subscribeQueue(listener) {
2522
+ queueListeners.add(listener);
2523
+ return () => {
2524
+ queueListeners.delete(listener);
2525
+ };
2526
+ }
2527
+ function createInternalScript(def) {
2528
+ return {
2529
+ ...def,
2530
+ status: "pending",
2531
+ lastAllowed: false,
2532
+ registeredAt: Date.now(),
2533
+ token: Date.now() + Math.random(),
2534
+ priority: def.priority ?? 0,
2535
+ allowReload: def.allowReload ?? false,
2536
+ onConsentUpdate: def.onConsentUpdate
2537
+ };
2538
+ }
2539
+ function registerScript(def) {
2540
+ const entry = createInternalScript(def);
2541
+ scriptRegistry.set(def.id, entry);
2542
+ notifyQueue();
2543
+ return () => {
2544
+ const current = scriptRegistry.get(def.id);
2545
+ if (current && current.token === entry.token) {
2546
+ scriptRegistry.delete(def.id);
2547
+ notifyQueue();
2548
+ }
2549
+ };
2550
+ }
2551
+ function getExecutableScripts(consent) {
2552
+ const allowedScripts = [];
2553
+ scriptRegistry.forEach((script) => {
2554
+ const categoryAllowed = script.category === "necessary" || consent.consented && Boolean(consent.preferences?.[script.category]);
2555
+ if (!categoryAllowed) {
2556
+ script.lastAllowed = false;
2557
+ return;
2558
+ }
2559
+ if (script.status === "running") return;
2560
+ if (script.status === "executed" && !script.allowReload) return;
2561
+ if (script.status === "executed" && script.allowReload && script.lastAllowed) return;
2562
+ script.lastAllowed = true;
2563
+ allowedScripts.push(script);
2564
+ });
2565
+ return allowedScripts.sort((a, b) => {
2566
+ if (a.category === "necessary" && b.category !== "necessary") return -1;
2567
+ if (b.category === "necessary" && a.category !== "necessary") return 1;
2568
+ if (a.category !== b.category) return a.category.localeCompare(b.category);
2569
+ if (a.priority !== b.priority) return (b.priority ?? 0) - (a.priority ?? 0);
2570
+ return a.registeredAt - b.registeredAt;
2571
+ });
2572
+ }
2573
+ async function processQueue(consent, devLogging) {
2574
+ const scripts = getExecutableScripts(consent);
2575
+ let order = 0;
2576
+ for (const script of scripts) {
2577
+ order += 1;
2578
+ script.status = "running";
2579
+ if (devLogging) {
2580
+ logger.info("[ConsentScriptLoader] executando script", {
2581
+ id: script.id,
2582
+ category: script.category,
2583
+ priority: script.priority ?? 0,
2584
+ order
2585
+ });
2586
+ }
2587
+ try {
2588
+ await Promise.resolve(script.execute());
2589
+ } catch (error) {
2590
+ logger.error(`\u274C Failed to execute script ${script.id}`, error);
2591
+ } finally {
2592
+ script.status = "executed";
2593
+ if (script.onConsentUpdate) {
2594
+ script.onConsentUpdate(consent);
2595
+ }
2596
+ }
2597
+ }
2598
+ }
2279
2599
  function ConsentScriptLoader({
2280
2600
  integrations,
2281
2601
  reloadOnChange = false,
2282
2602
  nonce
2283
2603
  }) {
2284
2604
  const { preferences, consented } = useConsent();
2605
+ const isHydrated = useConsentHydration();
2285
2606
  const categories = useCategories();
2286
- const loadedScripts = React4.useRef(/* @__PURE__ */ new Set());
2607
+ const [queueVersion, bumpQueueVersion] = React4.useState(0);
2608
+ React4.useEffect(() => {
2609
+ const unsubscribe = subscribeQueue(() => bumpQueueVersion((v) => v + 1));
2610
+ return unsubscribe;
2611
+ }, []);
2287
2612
  React4.useEffect(() => {
2288
2613
  try {
2289
2614
  const ids = (integrations || []).map((i) => i.id);
@@ -2344,15 +2669,44 @@ function ConsentScriptLoader({
2344
2669
  console.groupEnd();
2345
2670
  }
2346
2671
  }, [integrations, categories]);
2672
+ const processedIntegrationsRef = React4.useRef(/* @__PURE__ */ new Map());
2347
2673
  React4.useEffect(() => {
2348
- if (!consented) return;
2349
- const timeoutId = setTimeout(async () => {
2350
- for (const integration of integrations) {
2351
- const shouldLoad = preferences[integration.category];
2352
- const alreadyLoaded = loadedScripts.current.has(integration.id);
2353
- if (shouldLoad && (!alreadyLoaded || reloadOnChange)) {
2354
- try {
2355
- const mergedAttrs = integration.attrs ?? {};
2674
+ const cleanups = [];
2675
+ const currentIds = /* @__PURE__ */ new Set();
2676
+ integrations.forEach((integration) => {
2677
+ currentIds.add(integration.id);
2678
+ const structuralHash = JSON.stringify({
2679
+ category: integration.category,
2680
+ src: integration.src,
2681
+ priority: integration.priority,
2682
+ hasBootstrap: Boolean(integration.bootstrap),
2683
+ hasInit: Boolean(integration.init),
2684
+ hasOnConsentUpdate: Boolean(integration.onConsentUpdate)
2685
+ });
2686
+ const existingHash = processedIntegrationsRef.current.get(integration.id);
2687
+ if (existingHash === structuralHash && scriptRegistry.has(integration.id)) {
2688
+ return;
2689
+ }
2690
+ processedIntegrationsRef.current.set(integration.id, structuralHash);
2691
+ if (integration.bootstrap) {
2692
+ cleanups.push(
2693
+ registerScript({
2694
+ id: `${integration.id}__bootstrap`,
2695
+ category: "necessary",
2696
+ priority: (integration.priority ?? 0) + 1e3,
2697
+ execute: integration.bootstrap
2698
+ })
2699
+ );
2700
+ }
2701
+ cleanups.push(
2702
+ registerScript({
2703
+ id: integration.id,
2704
+ category: integration.category,
2705
+ priority: integration.priority,
2706
+ allowReload: reloadOnChange,
2707
+ onConsentUpdate: integration.onConsentUpdate,
2708
+ execute: async () => {
2709
+ const mergedAttrs = integration.attrs ? { ...integration.attrs } : {};
2356
2710
  const scriptNonce = integration.nonce ?? nonce;
2357
2711
  if (scriptNonce && !mergedAttrs.nonce) mergedAttrs.nonce = scriptNonce;
2358
2712
  await loadScript(
@@ -2360,26 +2714,54 @@ function ConsentScriptLoader({
2360
2714
  integration.src,
2361
2715
  integration.category,
2362
2716
  mergedAttrs,
2363
- scriptNonce
2717
+ scriptNonce,
2718
+ { skipConsentCheck: true }
2364
2719
  );
2365
2720
  if (integration.init) {
2366
2721
  integration.init();
2367
2722
  }
2368
- loadedScripts.current.add(integration.id);
2369
- } catch (error) {
2370
- logger.error(`\u274C Failed to load script: ${integration.id}`, error);
2371
2723
  }
2724
+ })
2725
+ );
2726
+ });
2727
+ processedIntegrationsRef.current.forEach((_, id) => {
2728
+ if (!currentIds.has(id)) {
2729
+ processedIntegrationsRef.current.delete(id);
2730
+ const script = scriptRegistry.get(id);
2731
+ if (script) {
2732
+ scriptRegistry.delete(id);
2733
+ }
2734
+ const bootstrapScript = scriptRegistry.get(`${id}__bootstrap`);
2735
+ if (bootstrapScript) {
2736
+ scriptRegistry.delete(`${id}__bootstrap`);
2372
2737
  }
2373
2738
  }
2374
- }, 0);
2375
- return () => clearTimeout(timeoutId);
2376
- }, [preferences, consented, integrations, reloadOnChange, nonce]);
2739
+ });
2740
+ return () => cleanups.forEach((fn) => fn());
2741
+ }, [integrations, reloadOnChange, nonce]);
2742
+ React4.useEffect(() => {
2743
+ if (!isHydrated) return;
2744
+ void processQueue({ consented, preferences }, process.env.NODE_ENV !== "production");
2745
+ }, [consented, preferences, isHydrated, queueVersion]);
2746
+ React4.useEffect(() => {
2747
+ if (!isHydrated) return;
2748
+ scriptRegistry.forEach((script) => {
2749
+ if (script.status !== "executed") return;
2750
+ if (typeof script.onConsentUpdate !== "function") return;
2751
+ script.onConsentUpdate({ consented, preferences });
2752
+ });
2753
+ }, [consented, preferences, isHydrated]);
2377
2754
  return null;
2378
2755
  }
2379
2756
  function useConsentScriptLoader() {
2380
2757
  const { preferences, consented } = useConsent();
2758
+ const isHydrated = useConsentHydration();
2381
2759
  return React4.useCallback(
2382
2760
  async (integration, nonce) => {
2761
+ if (!isHydrated) {
2762
+ logger.warn(`\u26A0\uFE0F Cannot load script ${integration.id}: Consent not hydrated yet`);
2763
+ return false;
2764
+ }
2383
2765
  if (!consented) {
2384
2766
  logger.warn(`\u26A0\uFE0F Cannot load script ${integration.id}: No consent given`);
2385
2767
  return false;
@@ -2400,7 +2782,11 @@ function useConsentScriptLoader() {
2400
2782
  integration.src,
2401
2783
  integration.category,
2402
2784
  mergedAttrs,
2403
- scriptNonce
2785
+ scriptNonce,
2786
+ {
2787
+ consentSnapshot: { consented, preferences },
2788
+ skipConsentCheck: true
2789
+ }
2404
2790
  );
2405
2791
  if (integration.init) {
2406
2792
  integration.init();
@@ -2411,11 +2797,62 @@ function useConsentScriptLoader() {
2411
2797
  return false;
2412
2798
  }
2413
2799
  },
2414
- [preferences, consented]
2800
+ [preferences, consented, isHydrated]
2415
2801
  );
2416
2802
  }
2417
2803
 
2418
2804
  // src/utils/scriptIntegrations.ts
2805
+ function buildConsentModeSignals(preferences) {
2806
+ const analytics = preferences.analytics ? "granted" : "denied";
2807
+ const marketing = preferences.marketing ? "granted" : "denied";
2808
+ return {
2809
+ ad_storage: marketing,
2810
+ ad_user_data: marketing,
2811
+ ad_personalization: marketing,
2812
+ analytics_storage: analytics
2813
+ };
2814
+ }
2815
+ function pushToLayer(entry, dataLayerName) {
2816
+ if (typeof globalThis.window === "undefined") return;
2817
+ const registry = globalThis.window;
2818
+ const name = dataLayerName ?? "dataLayer";
2819
+ const layer = registry[name] ?? [];
2820
+ registry[name] = layer;
2821
+ layer.push(entry);
2822
+ }
2823
+ function ensureGtag(dataLayerName = "dataLayer") {
2824
+ if (typeof globalThis.window === "undefined") return null;
2825
+ const w = window;
2826
+ const registry = w;
2827
+ const layer = registry[dataLayerName] ?? [];
2828
+ registry[dataLayerName] = layer;
2829
+ if (typeof w.gtag !== "function") {
2830
+ const gtag = (...args) => {
2831
+ layer.push(args);
2832
+ };
2833
+ w.gtag = gtag;
2834
+ }
2835
+ return w.gtag;
2836
+ }
2837
+ function applyDefaultConsentMode(dataLayerName) {
2838
+ const payload = buildConsentModeSignals({
2839
+ analytics: false,
2840
+ marketing: false
2841
+ });
2842
+ const gtag = ensureGtag(dataLayerName);
2843
+ if (gtag) {
2844
+ gtag("consent", "default", payload);
2845
+ }
2846
+ pushToLayer(["consent", "default", payload], dataLayerName);
2847
+ }
2848
+ function applyConsentModeUpdate(preferences, dataLayerName) {
2849
+ const payload = buildConsentModeSignals(preferences);
2850
+ const gtag = ensureGtag(dataLayerName);
2851
+ if (gtag) {
2852
+ gtag("consent", "update", payload);
2853
+ }
2854
+ pushToLayer(["consent", "update", payload], dataLayerName);
2855
+ }
2419
2856
  function createGoogleAnalyticsIntegration(config) {
2420
2857
  const src = config.scriptUrl ?? `https://www.googletagmanager.com/gtag/js?id=${config.measurementId}`;
2421
2858
  return {
@@ -2443,17 +2880,17 @@ function createGoogleAnalyticsIntegration(config) {
2443
2880
  provider: "Google Analytics"
2444
2881
  }
2445
2882
  ],
2883
+ bootstrap: () => {
2884
+ applyDefaultConsentMode();
2885
+ },
2886
+ onConsentUpdate: ({ preferences }) => {
2887
+ applyConsentModeUpdate(preferences);
2888
+ },
2446
2889
  init: () => {
2447
- if (globalThis.window !== void 0) {
2448
- const w = window;
2449
- w.dataLayer = w.dataLayer ?? [];
2450
- const gtag = (...args) => {
2451
- w.dataLayer.push(...args);
2452
- };
2453
- w.gtag = gtag;
2454
- gtag("js", /* @__PURE__ */ new Date());
2455
- gtag("config", config.measurementId, config.config ?? {});
2456
- }
2890
+ const gtag = ensureGtag();
2891
+ if (!gtag) return;
2892
+ gtag("js", /* @__PURE__ */ new Date());
2893
+ gtag("config", config.measurementId, config.config ?? {});
2457
2894
  },
2458
2895
  attrs: { async: "true" }
2459
2896
  };
@@ -2465,6 +2902,12 @@ function createGoogleTagManagerIntegration(config) {
2465
2902
  category: "analytics",
2466
2903
  src,
2467
2904
  cookies: ["_gcl_au"],
2905
+ bootstrap: () => {
2906
+ applyDefaultConsentMode(config.dataLayerName);
2907
+ },
2908
+ onConsentUpdate: ({ preferences }) => {
2909
+ applyConsentModeUpdate(preferences, config.dataLayerName);
2910
+ },
2468
2911
  init: () => {
2469
2912
  if (globalThis.window !== void 0) {
2470
2913
  const dataLayerName = config.dataLayerName || "dataLayer";
@@ -3013,4 +3456,4 @@ var TEXT_TEMPLATES = {
3013
3456
  }
3014
3457
  };
3015
3458
 
3016
- export { ANPD_CATEGORY_PRESETS, COMMON_INTEGRATIONS, ConsentGate, ConsentProvider, ConsentScriptLoader, DEFAULT_PROJECT_CATEGORIES, DesignProvider, EXPANDED_DEFAULT_TEXTS, GUIDANCE_PRESETS, INTEGRATION_TEMPLATES, LogLevel, TEXT_TEMPLATES, analyzeDeveloperConfiguration, analyzeIntegrationCategories, autoConfigureCategories, buildConsentStorageKey, categorizeDiscoveredCookies, checkPeerDeps, createAnpdCategoriesConfig, createClarityIntegration, createConsentAuditEntry, createCorporateIntegrations, createECommerceIntegrations, createFacebookPixelIntegration, createGoogleAnalyticsIntegration, createGoogleTagManagerIntegration, createHotjarIntegration, createIntercomIntegration, createMixpanelIntegration, createProjectPreferences, createSaaSIntegrations, createUserWayIntegration, createZendeskChatIntegration, defaultTexts, detectConsentCookieName, discoverRuntimeCookies, ensureNecessaryAlwaysOn, extractCategoriesFromIntegrations, getAllProjectCategories, getCookiesInfoForCategory, loadScript, logDeveloperGuidance, logger, openPreferencesModal, pushConsentInitializedEvent, pushConsentUpdatedEvent, resolveTexts, runPeerDepsCheck, setCookieCatalogOverrides, setCookieCategoryOverrides, setDebugLogging, suggestCategoryForScript, useCategories, useCategoryStatus, useConsent, useConsentHydration, useConsentScriptLoader, useConsentTexts, useDataLayerEvents, useDesignTokens, useDeveloperGuidance, useOpenPreferencesModal, validateIntegrationCategories, validateNecessaryClassification, validateProjectPreferences };
3459
+ export { ANPD_CATEGORY_PRESETS, COMMON_INTEGRATIONS, ConsentGate, ConsentProvider, ConsentScriptLoader, DEFAULT_PROJECT_CATEGORIES, DesignProvider, EXPANDED_DEFAULT_TEXTS, GUIDANCE_PRESETS, INTEGRATION_TEMPLATES, LogLevel, TEXT_TEMPLATES, analyzeDeveloperConfiguration, analyzeIntegrationCategories, autoConfigureCategories, buildConsentStorageKey, categorizeDiscoveredCookies, checkPeerDeps, createAnpdCategoriesConfig, createClarityIntegration, createConsentAuditEntry, createCorporateIntegrations, createECommerceIntegrations, createFacebookPixelIntegration, createGoogleAnalyticsIntegration, createGoogleTagManagerIntegration, createHotjarIntegration, createIntercomIntegration, createMixpanelIntegration, createProjectPreferences, createSaaSIntegrations, createUserWayIntegration, createZendeskChatIntegration, defaultTexts, detectConsentCookieName, discoverRuntimeCookies, ensureNecessaryAlwaysOn, extractCategoriesFromIntegrations, getAllProjectCategories, getCookiesInfoForCategory, getPeerDepsLocale, loadScript, logDeveloperGuidance, logger, openPreferencesModal, pushConsentInitializedEvent, pushConsentUpdatedEvent, registerScript, resetPeerDepsMessages, resolveTexts, runPeerDepsCheck, setCookieCatalogOverrides, setCookieCategoryOverrides, setDebugLogging, setPeerDepsLocale, setPeerDepsMessages, suggestCategoryForScript, useCategories, useCategoryStatus, useConsent, useConsentHydration, useConsentScriptLoader, useConsentTexts, useDataLayerEvents, useDesignTokens, useDeveloperGuidance, useOpenPreferencesModal, validateIntegrationCategories, validateNecessaryClassification, validateProjectPreferences };