@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.cjs CHANGED
@@ -333,6 +333,7 @@ function setDebugLogging(enabled, level = 2 /* INFO */) {
333
333
  // src/utils/cookieUtils.ts
334
334
  var DEFAULT_STORAGE_NAMESPACE = "lgpd-consent";
335
335
  var DEFAULT_STORAGE_VERSION = "1";
336
+ var DEFAULT_MAX_AGE_SECONDS = 365 * 24 * 60 * 60;
336
337
  function buildConsentStorageKey(options) {
337
338
  const namespaceRaw = options?.namespace?.trim() || DEFAULT_STORAGE_NAMESPACE;
338
339
  const versionRaw = options?.version?.trim() || DEFAULT_STORAGE_VERSION;
@@ -342,12 +343,31 @@ function buildConsentStorageKey(options) {
342
343
  }
343
344
  var DEFAULT_COOKIE_OPTS = {
344
345
  name: "cookieConsent",
346
+ maxAge: DEFAULT_MAX_AGE_SECONDS,
345
347
  maxAgeDays: 365,
346
348
  sameSite: "Lax",
347
349
  secure: globalThis.window === void 0 ? false : globalThis.window.location.protocol === "https:",
348
350
  path: "/",
349
351
  domain: void 0
350
352
  };
353
+ function resolveCookieOptions(opts) {
354
+ const protocols = [
355
+ typeof globalThis.window !== "undefined" ? globalThis.window?.location?.protocol : void 0,
356
+ typeof globalThis.location !== "undefined" ? globalThis.location?.protocol : void 0
357
+ ].filter(Boolean);
358
+ const forceHttps = globalThis.__LGPD_FORCE_HTTPS__ === true;
359
+ const isHttps = forceHttps || protocols.includes("https:");
360
+ const maxAgeSecondsFromDays = typeof opts?.maxAgeDays === "number" ? Math.max(0, opts.maxAgeDays * 24 * 60 * 60) : null;
361
+ const maxAgeSeconds = typeof opts?.maxAge === "number" ? Math.max(0, opts.maxAge) : maxAgeSecondsFromDays ?? DEFAULT_MAX_AGE_SECONDS;
362
+ return {
363
+ name: opts?.name ?? DEFAULT_COOKIE_OPTS.name,
364
+ maxAge: maxAgeSeconds,
365
+ sameSite: opts?.sameSite ?? DEFAULT_COOKIE_OPTS.sameSite ?? "Lax",
366
+ secure: typeof opts?.secure === "boolean" ? opts.secure : isHttps ? true : DEFAULT_COOKIE_OPTS.secure ?? false,
367
+ path: opts?.path ?? DEFAULT_COOKIE_OPTS.path ?? "/",
368
+ domain: opts?.domain ?? DEFAULT_COOKIE_OPTS.domain ?? void 0
369
+ };
370
+ }
351
371
  var COOKIE_SCHEMA_VERSION = "1.0";
352
372
  function readConsentCookie(name = DEFAULT_COOKIE_OPTS.name) {
353
373
  logger.debug("Reading consent cookie", { name });
@@ -363,6 +383,10 @@ function readConsentCookie(name = DEFAULT_COOKIE_OPTS.name) {
363
383
  try {
364
384
  const data = JSON.parse(raw);
365
385
  logger.cookieOperation("read", name, data);
386
+ if (!data || typeof data !== "object") {
387
+ logger.warn("Consent cookie malformed: payload is not an object");
388
+ return null;
389
+ }
366
390
  if (!data.version) {
367
391
  logger.debug("Migrating legacy cookie format");
368
392
  return migrateLegacyCookie(data);
@@ -371,7 +395,11 @@ function readConsentCookie(name = DEFAULT_COOKIE_OPTS.name) {
371
395
  logger.warn(`Cookie version mismatch: ${data.version} != ${COOKIE_SCHEMA_VERSION}`);
372
396
  return null;
373
397
  }
374
- return data;
398
+ const preferences = data && typeof data.preferences === "object" ? data.preferences : { necessary: true };
399
+ return {
400
+ ...data,
401
+ preferences: ensureNecessaryAlwaysOn(preferences)
402
+ };
375
403
  } catch (error) {
376
404
  logger.error("Error parsing consent cookie", error);
377
405
  return null;
@@ -394,12 +422,12 @@ function migrateLegacyCookie(legacyData) {
394
422
  }
395
423
  }
396
424
  function writeConsentCookie(state, config, opts, source = "banner") {
397
- if (typeof document === "undefined") {
425
+ if (typeof document === "undefined" || typeof window === "undefined" || globalThis.__LGPD_SSR__ === true) {
398
426
  logger.debug("Cookie write skipped: server-side environment");
399
427
  return;
400
428
  }
401
429
  const now = (/* @__PURE__ */ new Date()).toISOString();
402
- const o = { ...DEFAULT_COOKIE_OPTS, ...opts };
430
+ const o = resolveCookieOptions(opts);
403
431
  const preferences = ensureNecessaryAlwaysOn(state.preferences);
404
432
  const cookieData = {
405
433
  version: COOKIE_SCHEMA_VERSION,
@@ -411,8 +439,9 @@ function writeConsentCookie(state, config, opts, source = "banner") {
411
439
  projectConfig: config
412
440
  };
413
441
  logger.cookieOperation("write", o.name, cookieData);
442
+ const expires = new Date(Date.now() + o.maxAge * 1e3);
414
443
  Cookies__default.default.set(o.name, JSON.stringify(cookieData), {
415
- expires: o.maxAgeDays,
444
+ expires,
416
445
  sameSite: o.sameSite,
417
446
  secure: o.secure,
418
447
  path: o.path,
@@ -446,14 +475,14 @@ function removeConsentCookie(opts) {
446
475
  logger.debug("Cookie removal skipped: server-side environment");
447
476
  return;
448
477
  }
449
- const o = { ...DEFAULT_COOKIE_OPTS, ...opts };
478
+ const o = resolveCookieOptions(opts);
450
479
  logger.cookieOperation("delete", o.name);
451
480
  Cookies__default.default.remove(o.name, { path: o.path, domain: o.domain });
452
481
  logger.info("Consent cookie removed");
453
482
  }
454
483
 
455
484
  // src/utils/dataLayerEvents.ts
456
- var LIBRARY_VERSION = "0.7.0";
485
+ var LIBRARY_VERSION = "0.7.1";
457
486
  function ensureDataLayer() {
458
487
  var _a;
459
488
  if (globalThis.window === void 0) return;
@@ -466,7 +495,8 @@ function pushConsentInitializedEvent(categories) {
466
495
  event: "consent_initialized",
467
496
  consent_version: LIBRARY_VERSION,
468
497
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
469
- categories
498
+ categories,
499
+ preferences: categories
470
500
  };
471
501
  globalThis.window.dataLayer?.push(event);
472
502
  }
@@ -480,6 +510,7 @@ function pushConsentUpdatedEvent(categories, origin, previousCategories) {
480
510
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
481
511
  origin,
482
512
  categories,
513
+ preferences: categories,
483
514
  changed_categories: changedCategories
484
515
  };
485
516
  globalThis.window.dataLayer?.push(event);
@@ -1089,58 +1120,8 @@ var GUIDANCE_PRESETS = {
1089
1120
  };
1090
1121
 
1091
1122
  // src/utils/peerDepsCheck.ts
1092
- function detectMultipleReactInstances() {
1093
- if (globalThis.window === void 0) return false;
1094
- try {
1095
- const reactSymbols = Object.getOwnPropertySymbols(globalThis.window).map(String).filter((name) => name.includes("react"));
1096
- if (reactSymbols.length > 1) {
1097
- return true;
1098
- }
1099
- const ReactModule = window.React;
1100
- if (ReactModule && Array.isArray(ReactModule)) {
1101
- return true;
1102
- }
1103
- const hasMultipleVersions = window.__REACT_DEVTOOLS_GLOBAL_HOOK__?.renderers?.size > 1;
1104
- return hasMultipleVersions || false;
1105
- } catch {
1106
- return false;
1107
- }
1108
- }
1109
- function getPackageVersion(packageName) {
1110
- if (globalThis.window === void 0) return null;
1111
- try {
1112
- const pkg = window[packageName];
1113
- if (pkg?.version) return pkg.version;
1114
- const React6 = window.React;
1115
- if (packageName === "react" && React6?.version) {
1116
- return React6.version;
1117
- }
1118
- return null;
1119
- } catch {
1120
- return null;
1121
- }
1122
- }
1123
- function isVersionInRange(version, minMajor, maxMajor) {
1124
- const major = Number.parseInt(version.split(".")[0], 10);
1125
- return major >= minMajor && major <= maxMajor;
1126
- }
1127
- function checkPeerDeps(options = {}) {
1128
- const { skipInProduction = true, logWarnings = true } = options;
1129
- const result = {
1130
- ok: true,
1131
- warnings: [],
1132
- errors: []
1133
- };
1134
- const isProduction = typeof process !== "undefined" && process.env?.NODE_ENV === "production";
1135
- if (skipInProduction && isProduction) {
1136
- return result;
1137
- }
1138
- if (globalThis.window === void 0) {
1139
- return result;
1140
- }
1141
- if (detectMultipleReactInstances()) {
1142
- result.ok = false;
1143
- const errorMsg = `
1123
+ var MESSAGES_PT_BR = {
1124
+ MULTIPLE_REACT_INSTANCES: `
1144
1125
  \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
1145
1126
  \u2551 \u26A0\uFE0F ERRO: M\xFAltiplas inst\xE2ncias de React detectadas \u2551
1146
1127
  \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
@@ -1202,22 +1183,13 @@ function checkPeerDeps(options = {}) {
1202
1183
  https://github.com/lucianoedipo/react-lgpd-consent/blob/main/TROUBLESHOOTING.md#multiple-react-instances
1203
1184
 
1204
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
1205
- `;
1206
- result.errors.push(errorMsg);
1207
- if (logWarnings) {
1208
- console.error(errorMsg);
1209
- }
1210
- }
1211
- const reactVersion = getPackageVersion("react");
1212
- if (reactVersion) {
1213
- if (!isVersionInRange(reactVersion, 18, 19)) {
1214
- result.ok = false;
1215
- const errorMsg = `
1186
+ `,
1187
+ UNSUPPORTED_REACT_VERSION: (version) => `
1216
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
1217
1189
  \u2551 \u26A0\uFE0F AVISO: Vers\xE3o do React n\xE3o suportada \u2551
1218
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
1219
1191
 
1220
- \u{1F4E6} Vers\xE3o detectada: React ${reactVersion}
1192
+ \u{1F4E6} Vers\xE3o detectada: React ${version}
1221
1193
  \u2705 Vers\xF5es suportadas: React 18.x ou 19.x
1222
1194
 
1223
1195
  \u{1F50D} O react-lgpd-consent requer React 18.2.0+ ou React 19.x
@@ -1235,27 +1207,13 @@ function checkPeerDeps(options = {}) {
1235
1207
  https://github.com/lucianoedipo/react-lgpd-consent/blob/main/TROUBLESHOOTING.md#react-version
1236
1208
 
1237
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
1238
- `;
1239
- result.errors.push(errorMsg);
1240
- if (logWarnings) {
1241
- console.error(errorMsg);
1242
- }
1243
- }
1244
- }
1245
- const muiVersion = window["@mui/material"]?.version;
1246
- if (muiVersion) {
1247
- if (!isVersionInRange(muiVersion, 5, 7)) {
1248
- result.warnings.push(
1249
- `MUI vers\xE3o ${muiVersion} detectada. Vers\xF5es suportadas: 5.15.0+, 6.x ou 7.x. Alguns componentes podem n\xE3o funcionar corretamente.`
1250
- );
1251
- if (logWarnings) {
1252
- logger.warn(
1253
- `
1210
+ `,
1211
+ UNSUPPORTED_MUI_VERSION: (version) => `
1254
1212
  \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
1255
1213
  \u2551 \u26A0\uFE0F AVISO: Vers\xE3o do Material-UI fora do range recomendado \u2551
1256
1214
  \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
1257
1215
 
1258
- \u{1F4E6} Vers\xE3o detectada: @mui/material ${muiVersion}
1216
+ \u{1F4E6} Vers\xE3o detectada: @mui/material ${version}
1259
1217
  \u2705 Vers\xF5es suportadas: 5.15.0+, 6.x, 7.x
1260
1218
 
1261
1219
  \u{1F50D} Componentes de UI (@react-lgpd-consent/mui) podem apresentar problemas.
@@ -1273,8 +1231,227 @@ function checkPeerDeps(options = {}) {
1273
1231
  https://github.com/lucianoedipo/react-lgpd-consent/blob/main/TROUBLESHOOTING.md#mui-version
1274
1232
 
1275
1233
  \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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
- );
1234
+ `,
1235
+ 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.`
1236
+ };
1237
+ var MESSAGES_EN = {
1238
+ MULTIPLE_REACT_INSTANCES: `
1239
+ \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
1240
+ \u2551 \u26A0\uFE0F ERROR: Multiple React instances detected \u2551
1241
+ \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
1242
+
1243
+ \u{1F534} Problem:
1244
+ Your project is loading more than one copy of React, causing the error:
1245
+ "Invalid hook call. Hooks can only be called inside of the body of a
1246
+ function component."
1247
+
1248
+ \u{1F50D} Probable cause:
1249
+ \u2022 pnpm/Yarn PnP without proper peer dependency hoisting
1250
+ \u2022 node_modules with duplicate React (classic npm/yarn)
1251
+ \u2022 Webpack/Vite with multiple resolutions of the same package
1252
+
1253
+ \u2705 Solutions:
1254
+
1255
+ \u{1F4E6} PNPM (RECOMMENDED):
1256
+ Add to root package.json:
1257
+ {
1258
+ "pnpm": {
1259
+ "overrides": {
1260
+ "react": "$react",
1261
+ "react-dom": "$react-dom"
1262
+ }
1263
+ }
1264
+ }
1265
+ Run: pnpm install
1266
+
1267
+ \u{1F4E6} NPM/Yarn:
1268
+ Add to root package.json:
1269
+ {
1270
+ "overrides": {
1271
+ "react": "^18.2.0 || ^19.0.0",
1272
+ "react-dom": "^18.2.0 || ^19.0.0"
1273
+ }
1274
+ }
1275
+ Run: npm install (or yarn install)
1276
+
1277
+ \u{1F527} Webpack:
1278
+ Add to webpack.config.js:
1279
+ module.exports = {
1280
+ resolve: {
1281
+ alias: {
1282
+ react: path.resolve('./node_modules/react'),
1283
+ 'react-dom': path.resolve('./node_modules/react-dom'),
1284
+ }
1285
+ }
1286
+ }
1287
+
1288
+ \u26A1 Vite:
1289
+ Add to vite.config.js:
1290
+ export default {
1291
+ resolve: {
1292
+ dedupe: ['react', 'react-dom']
1293
+ }
1294
+ }
1295
+
1296
+ \u{1F4DA} Documentation:
1297
+ https://github.com/lucianoedipo/react-lgpd-consent/blob/main/TROUBLESHOOTING.md#multiple-react-instances
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_REACT_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: Unsupported React version \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: React ${version}
1307
+ \u2705 Supported versions: React 18.x or 19.x
1308
+
1309
+ \u{1F50D} react-lgpd-consent requires React 18.2.0+ or React 19.x
1310
+
1311
+ \u2705 Solution:
1312
+ Update React to a supported version:
1313
+
1314
+ npm install react@^18.2.0 react-dom@^18.2.0
1315
+
1316
+ or
1317
+
1318
+ npm install react@^19.0.0 react-dom@^19.0.0
1319
+
1320
+ \u{1F4DA} Documentation:
1321
+ https://github.com/lucianoedipo/react-lgpd-consent/blob/main/TROUBLESHOOTING.md#react-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
+ UNSUPPORTED_MUI_VERSION: (version) => `
1326
+ \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
1327
+ \u2551 \u26A0\uFE0F WARNING: Material-UI version out of recommended range \u2551
1328
+ \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
1329
+
1330
+ \u{1F4E6} Detected version: @mui/material ${version}
1331
+ \u2705 Supported versions: 5.15.0+, 6.x, 7.x
1332
+
1333
+ \u{1F50D} UI components (@react-lgpd-consent/mui) may have issues.
1334
+
1335
+ \u2705 Solution:
1336
+ Update MUI to a supported version:
1337
+
1338
+ npm install @mui/material@^7.0.0 @emotion/react @emotion/styled
1339
+
1340
+ or keep 5.15.0+:
1341
+
1342
+ npm install @mui/material@^5.15.0 @emotion/react @emotion/styled
1343
+
1344
+ \u{1F4DA} Documentation:
1345
+ https://github.com/lucianoedipo/react-lgpd-consent/blob/main/TROUBLESHOOTING.md#mui-version
1346
+
1347
+ \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
1348
+ `,
1349
+ 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.`
1350
+ };
1351
+ var MESSAGES_BY_LOCALE = {
1352
+ "pt-BR": MESSAGES_PT_BR,
1353
+ en: MESSAGES_EN
1354
+ };
1355
+ var currentLocale = "pt-BR";
1356
+ var customMessages = {};
1357
+ function setPeerDepsLocale(locale) {
1358
+ currentLocale = locale;
1359
+ }
1360
+ function getPeerDepsLocale() {
1361
+ return currentLocale;
1362
+ }
1363
+ function setPeerDepsMessages(messages) {
1364
+ customMessages = { ...customMessages, ...messages };
1365
+ }
1366
+ function resetPeerDepsMessages() {
1367
+ customMessages = {};
1368
+ }
1369
+ function getMessages() {
1370
+ const baseMessages = MESSAGES_BY_LOCALE[currentLocale];
1371
+ if (Object.keys(customMessages).length === 0) {
1372
+ return baseMessages;
1373
+ }
1374
+ return {
1375
+ MULTIPLE_REACT_INSTANCES: customMessages.MULTIPLE_REACT_INSTANCES ?? baseMessages.MULTIPLE_REACT_INSTANCES,
1376
+ UNSUPPORTED_REACT_VERSION: customMessages.UNSUPPORTED_REACT_VERSION ?? baseMessages.UNSUPPORTED_REACT_VERSION,
1377
+ UNSUPPORTED_MUI_VERSION: customMessages.UNSUPPORTED_MUI_VERSION ?? baseMessages.UNSUPPORTED_MUI_VERSION,
1378
+ MUI_OUT_OF_RANGE: customMessages.MUI_OUT_OF_RANGE ?? baseMessages.MUI_OUT_OF_RANGE
1379
+ };
1380
+ }
1381
+ function detectMultipleReactInstances() {
1382
+ if (globalThis.window === void 0) return false;
1383
+ try {
1384
+ const reactSymbols = Object.getOwnPropertySymbols(globalThis.window).map(String).filter((name) => name.includes("react"));
1385
+ if (reactSymbols.length > 1) {
1386
+ return true;
1387
+ }
1388
+ const ReactModule = window.React;
1389
+ if (ReactModule && Array.isArray(ReactModule)) {
1390
+ return true;
1391
+ }
1392
+ const hasMultipleVersions = window.__REACT_DEVTOOLS_GLOBAL_HOOK__?.renderers?.size > 1;
1393
+ return hasMultipleVersions || false;
1394
+ } catch {
1395
+ return false;
1396
+ }
1397
+ }
1398
+ function getPackageVersion(packageName) {
1399
+ if (globalThis.window === void 0) return null;
1400
+ try {
1401
+ const pkg = window[packageName];
1402
+ if (pkg?.version) return pkg.version;
1403
+ const React6 = window.React;
1404
+ if (packageName === "react" && React6?.version) {
1405
+ return React6.version;
1406
+ }
1407
+ return null;
1408
+ } catch {
1409
+ return null;
1410
+ }
1411
+ }
1412
+ function isVersionInRange(version, minMajor, maxMajor) {
1413
+ const major = Number.parseInt(version.split(".")[0], 10);
1414
+ return major >= minMajor && major <= maxMajor;
1415
+ }
1416
+ function checkPeerDeps(options = {}) {
1417
+ const { skipInProduction = true, logWarnings = true } = options;
1418
+ const result = {
1419
+ ok: true,
1420
+ warnings: [],
1421
+ errors: []
1422
+ };
1423
+ const isProduction = typeof process !== "undefined" && process.env?.NODE_ENV === "production";
1424
+ if (skipInProduction && isProduction) {
1425
+ return result;
1426
+ }
1427
+ if (globalThis.window === void 0) {
1428
+ return result;
1429
+ }
1430
+ const messages = getMessages();
1431
+ if (detectMultipleReactInstances()) {
1432
+ result.ok = false;
1433
+ result.errors.push(messages.MULTIPLE_REACT_INSTANCES);
1434
+ if (logWarnings) {
1435
+ console.error(messages.MULTIPLE_REACT_INSTANCES);
1436
+ }
1437
+ }
1438
+ const reactVersion = getPackageVersion("react");
1439
+ if (reactVersion) {
1440
+ if (!isVersionInRange(reactVersion, 18, 19)) {
1441
+ result.ok = false;
1442
+ const errorMsg = messages.UNSUPPORTED_REACT_VERSION(reactVersion);
1443
+ result.errors.push(errorMsg);
1444
+ if (logWarnings) {
1445
+ console.error(errorMsg);
1446
+ }
1447
+ }
1448
+ }
1449
+ const muiVersion = window["@mui/material"]?.version;
1450
+ if (muiVersion) {
1451
+ if (!isVersionInRange(muiVersion, 5, 7)) {
1452
+ result.warnings.push(messages.MUI_OUT_OF_RANGE(muiVersion));
1453
+ if (logWarnings) {
1454
+ logger.warn(messages.UNSUPPORTED_MUI_VERSION(muiVersion));
1278
1455
  }
1279
1456
  }
1280
1457
  }
@@ -1730,6 +1907,13 @@ function ConsentProvider({
1730
1907
  return base;
1731
1908
  }, [cookieOpts, storage?.domain, storage?.namespace, storage?.version]);
1732
1909
  const consentVersion = storage?.version?.trim() || "1";
1910
+ React4__namespace.useEffect(() => {
1911
+ try {
1912
+ ;
1913
+ globalThis.__LGPD_CONSENT_COOKIE__ = cookie.name;
1914
+ } catch {
1915
+ }
1916
+ }, [cookie.name]);
1733
1917
  const finalCategoriesConfig = React4__namespace.useMemo(() => {
1734
1918
  const isProd = typeof process !== "undefined" && process.env?.NODE_ENV === "production";
1735
1919
  if (!categories) return DEFAULT_PROJECT_CATEGORIES;
@@ -2092,51 +2276,99 @@ function ConsentGate(props) {
2092
2276
 
2093
2277
  // src/utils/scriptLoader.ts
2094
2278
  var LOADING_SCRIPTS = /* @__PURE__ */ new Map();
2095
- function loadScript(id, src, category = null, attrs = {}, nonce) {
2279
+ var DEFAULT_POLL_INTERVAL = 100;
2280
+ function resolveCookieNames(preferred) {
2281
+ const inferred = globalThis.__LGPD_CONSENT_COOKIE__ ?? null;
2282
+ const names = [preferred, inferred, "cookieConsent", "lgpd-consent__v1"].filter(
2283
+ Boolean
2284
+ );
2285
+ return Array.from(new Set(names));
2286
+ }
2287
+ function parseConsentFromCookie(names) {
2288
+ const raw = document.cookie;
2289
+ if (!raw) return null;
2290
+ const cookies = raw.split("; ").reduce((acc, part) => {
2291
+ const [k, ...rest] = part.split("=");
2292
+ acc[k] = rest.join("=");
2293
+ return acc;
2294
+ }, {});
2295
+ for (const name of names) {
2296
+ const value = cookies[name];
2297
+ if (!value) continue;
2298
+ try {
2299
+ const parsed = JSON.parse(decodeURIComponent(value));
2300
+ if (!parsed.consented || parsed.isModalOpen) continue;
2301
+ return parsed;
2302
+ } catch {
2303
+ continue;
2304
+ }
2305
+ }
2306
+ return null;
2307
+ }
2308
+ function hasCategoryConsent(snapshot, category) {
2309
+ if (!snapshot.consented || snapshot.isModalOpen) return false;
2310
+ if (category === null) return true;
2311
+ return Boolean(snapshot.preferences?.[category]);
2312
+ }
2313
+ function loadScript(id, src, category = null, attrs = {}, nonce, options) {
2096
2314
  if (typeof document === "undefined") return Promise.resolve();
2097
2315
  if (document.getElementById(id)) return Promise.resolve();
2098
2316
  const existingPromise = LOADING_SCRIPTS.get(id);
2099
2317
  if (existingPromise) return existingPromise;
2318
+ const pollInterval = options?.pollIntervalMs ?? DEFAULT_POLL_INTERVAL;
2319
+ const names = resolveCookieNames(options?.cookieName);
2320
+ const mergedAttrs = { ...attrs };
2100
2321
  const promise = new Promise((resolve, reject) => {
2322
+ const inject = () => {
2323
+ const s = document.createElement("script");
2324
+ s.id = id;
2325
+ s.src = src;
2326
+ s.async = mergedAttrs.async !== "false";
2327
+ const scriptNonce = mergedAttrs.nonce || nonce;
2328
+ if (scriptNonce) {
2329
+ s.nonce = scriptNonce;
2330
+ mergedAttrs.nonce = scriptNonce;
2331
+ }
2332
+ for (const [k, v] of Object.entries(mergedAttrs)) s.setAttribute(k, v);
2333
+ s.onload = () => {
2334
+ LOADING_SCRIPTS.delete(id);
2335
+ resolve();
2336
+ };
2337
+ s.onerror = () => {
2338
+ LOADING_SCRIPTS.delete(id);
2339
+ reject(new Error(`Failed to load script: ${src}`));
2340
+ };
2341
+ document.body.appendChild(s);
2342
+ };
2343
+ const snapshot = options?.consentSnapshot;
2344
+ const skipChecks = options?.skipConsentCheck === true;
2345
+ if (skipChecks) {
2346
+ inject();
2347
+ return;
2348
+ }
2349
+ if (snapshot) {
2350
+ if (!hasCategoryConsent(snapshot, category)) {
2351
+ reject(
2352
+ new Error(
2353
+ `Consent not granted for category '${category ?? "none"}' when attempting to load ${id}`
2354
+ )
2355
+ );
2356
+ return;
2357
+ }
2358
+ inject();
2359
+ return;
2360
+ }
2101
2361
  const checkConsent = () => {
2102
- const consentCookie = document.cookie.split("; ").find((row) => row.startsWith("cookieConsent="))?.split("=")[1];
2103
- if (!consentCookie) {
2104
- setTimeout(checkConsent, 100);
2362
+ const consent = parseConsentFromCookie(names);
2363
+ if (!consent) {
2364
+ setTimeout(checkConsent, pollInterval);
2105
2365
  return;
2106
2366
  }
2107
- try {
2108
- const consent = JSON.parse(decodeURIComponent(consentCookie));
2109
- if (!consent.consented || consent.isModalOpen) {
2110
- setTimeout(checkConsent, 100);
2111
- return;
2112
- }
2113
- if (category && !consent.preferences[category]) {
2114
- setTimeout(checkConsent, 100);
2115
- return;
2116
- }
2117
- const s = document.createElement("script");
2118
- s.id = id;
2119
- s.src = src;
2120
- s.async = true;
2121
- const mergedAttrs = { ...attrs };
2122
- const scriptNonce = mergedAttrs.nonce || nonce;
2123
- if (scriptNonce) {
2124
- s.nonce = scriptNonce;
2125
- mergedAttrs.nonce = scriptNonce;
2126
- }
2127
- for (const [k, v] of Object.entries(mergedAttrs)) s.setAttribute(k, v);
2128
- s.onload = () => {
2129
- LOADING_SCRIPTS.delete(id);
2130
- resolve();
2131
- };
2132
- s.onerror = () => {
2133
- LOADING_SCRIPTS.delete(id);
2134
- reject(new Error(`Failed to load script: ${src}`));
2135
- };
2136
- document.body.appendChild(s);
2137
- } catch {
2138
- setTimeout(checkConsent, 100);
2367
+ if (!hasCategoryConsent(consent, category)) {
2368
+ setTimeout(checkConsent, pollInterval);
2369
+ return;
2139
2370
  }
2371
+ inject();
2140
2372
  };
2141
2373
  checkConsent();
2142
2374
  });
@@ -2300,14 +2532,107 @@ function validateNecessaryClassification(integrations, enabledCategories) {
2300
2532
  }
2301
2533
 
2302
2534
  // src/utils/ConsentScriptLoader.tsx
2535
+ var scriptRegistry = /* @__PURE__ */ new Map();
2536
+ var queueListeners = /* @__PURE__ */ new Set();
2537
+ function notifyQueue() {
2538
+ queueListeners.forEach((listener) => {
2539
+ try {
2540
+ listener();
2541
+ } catch {
2542
+ }
2543
+ });
2544
+ }
2545
+ function subscribeQueue(listener) {
2546
+ queueListeners.add(listener);
2547
+ return () => {
2548
+ queueListeners.delete(listener);
2549
+ };
2550
+ }
2551
+ function createInternalScript(def) {
2552
+ return {
2553
+ ...def,
2554
+ status: "pending",
2555
+ lastAllowed: false,
2556
+ registeredAt: Date.now(),
2557
+ token: Date.now() + Math.random(),
2558
+ priority: def.priority ?? 0,
2559
+ allowReload: def.allowReload ?? false,
2560
+ onConsentUpdate: def.onConsentUpdate
2561
+ };
2562
+ }
2563
+ function registerScript(def) {
2564
+ const entry = createInternalScript(def);
2565
+ scriptRegistry.set(def.id, entry);
2566
+ notifyQueue();
2567
+ return () => {
2568
+ const current = scriptRegistry.get(def.id);
2569
+ if (current && current.token === entry.token) {
2570
+ scriptRegistry.delete(def.id);
2571
+ notifyQueue();
2572
+ }
2573
+ };
2574
+ }
2575
+ function getExecutableScripts(consent) {
2576
+ const allowedScripts = [];
2577
+ scriptRegistry.forEach((script) => {
2578
+ const categoryAllowed = script.category === "necessary" || consent.consented && Boolean(consent.preferences?.[script.category]);
2579
+ if (!categoryAllowed) {
2580
+ script.lastAllowed = false;
2581
+ return;
2582
+ }
2583
+ if (script.status === "running") return;
2584
+ if (script.status === "executed" && !script.allowReload) return;
2585
+ if (script.status === "executed" && script.allowReload && script.lastAllowed) return;
2586
+ script.lastAllowed = true;
2587
+ allowedScripts.push(script);
2588
+ });
2589
+ return allowedScripts.sort((a, b) => {
2590
+ if (a.category === "necessary" && b.category !== "necessary") return -1;
2591
+ if (b.category === "necessary" && a.category !== "necessary") return 1;
2592
+ if (a.category !== b.category) return a.category.localeCompare(b.category);
2593
+ if (a.priority !== b.priority) return (b.priority ?? 0) - (a.priority ?? 0);
2594
+ return a.registeredAt - b.registeredAt;
2595
+ });
2596
+ }
2597
+ async function processQueue(consent, devLogging) {
2598
+ const scripts = getExecutableScripts(consent);
2599
+ let order = 0;
2600
+ for (const script of scripts) {
2601
+ order += 1;
2602
+ script.status = "running";
2603
+ if (devLogging) {
2604
+ logger.info("[ConsentScriptLoader] executando script", {
2605
+ id: script.id,
2606
+ category: script.category,
2607
+ priority: script.priority ?? 0,
2608
+ order
2609
+ });
2610
+ }
2611
+ try {
2612
+ await Promise.resolve(script.execute());
2613
+ } catch (error) {
2614
+ logger.error(`\u274C Failed to execute script ${script.id}`, error);
2615
+ } finally {
2616
+ script.status = "executed";
2617
+ if (script.onConsentUpdate) {
2618
+ script.onConsentUpdate(consent);
2619
+ }
2620
+ }
2621
+ }
2622
+ }
2303
2623
  function ConsentScriptLoader({
2304
2624
  integrations,
2305
2625
  reloadOnChange = false,
2306
2626
  nonce
2307
2627
  }) {
2308
2628
  const { preferences, consented } = useConsent();
2629
+ const isHydrated = useConsentHydration();
2309
2630
  const categories = useCategories();
2310
- const loadedScripts = React4__namespace.useRef(/* @__PURE__ */ new Set());
2631
+ const [queueVersion, bumpQueueVersion] = React4__namespace.useState(0);
2632
+ React4__namespace.useEffect(() => {
2633
+ const unsubscribe = subscribeQueue(() => bumpQueueVersion((v) => v + 1));
2634
+ return unsubscribe;
2635
+ }, []);
2311
2636
  React4__namespace.useEffect(() => {
2312
2637
  try {
2313
2638
  const ids = (integrations || []).map((i) => i.id);
@@ -2368,15 +2693,44 @@ function ConsentScriptLoader({
2368
2693
  console.groupEnd();
2369
2694
  }
2370
2695
  }, [integrations, categories]);
2696
+ const processedIntegrationsRef = React4__namespace.useRef(/* @__PURE__ */ new Map());
2371
2697
  React4__namespace.useEffect(() => {
2372
- if (!consented) return;
2373
- const timeoutId = setTimeout(async () => {
2374
- for (const integration of integrations) {
2375
- const shouldLoad = preferences[integration.category];
2376
- const alreadyLoaded = loadedScripts.current.has(integration.id);
2377
- if (shouldLoad && (!alreadyLoaded || reloadOnChange)) {
2378
- try {
2379
- const mergedAttrs = integration.attrs ?? {};
2698
+ const cleanups = [];
2699
+ const currentIds = /* @__PURE__ */ new Set();
2700
+ integrations.forEach((integration) => {
2701
+ currentIds.add(integration.id);
2702
+ const structuralHash = JSON.stringify({
2703
+ category: integration.category,
2704
+ src: integration.src,
2705
+ priority: integration.priority,
2706
+ hasBootstrap: Boolean(integration.bootstrap),
2707
+ hasInit: Boolean(integration.init),
2708
+ hasOnConsentUpdate: Boolean(integration.onConsentUpdate)
2709
+ });
2710
+ const existingHash = processedIntegrationsRef.current.get(integration.id);
2711
+ if (existingHash === structuralHash && scriptRegistry.has(integration.id)) {
2712
+ return;
2713
+ }
2714
+ processedIntegrationsRef.current.set(integration.id, structuralHash);
2715
+ if (integration.bootstrap) {
2716
+ cleanups.push(
2717
+ registerScript({
2718
+ id: `${integration.id}__bootstrap`,
2719
+ category: "necessary",
2720
+ priority: (integration.priority ?? 0) + 1e3,
2721
+ execute: integration.bootstrap
2722
+ })
2723
+ );
2724
+ }
2725
+ cleanups.push(
2726
+ registerScript({
2727
+ id: integration.id,
2728
+ category: integration.category,
2729
+ priority: integration.priority,
2730
+ allowReload: reloadOnChange,
2731
+ onConsentUpdate: integration.onConsentUpdate,
2732
+ execute: async () => {
2733
+ const mergedAttrs = integration.attrs ? { ...integration.attrs } : {};
2380
2734
  const scriptNonce = integration.nonce ?? nonce;
2381
2735
  if (scriptNonce && !mergedAttrs.nonce) mergedAttrs.nonce = scriptNonce;
2382
2736
  await loadScript(
@@ -2384,26 +2738,54 @@ function ConsentScriptLoader({
2384
2738
  integration.src,
2385
2739
  integration.category,
2386
2740
  mergedAttrs,
2387
- scriptNonce
2741
+ scriptNonce,
2742
+ { skipConsentCheck: true }
2388
2743
  );
2389
2744
  if (integration.init) {
2390
2745
  integration.init();
2391
2746
  }
2392
- loadedScripts.current.add(integration.id);
2393
- } catch (error) {
2394
- logger.error(`\u274C Failed to load script: ${integration.id}`, error);
2395
2747
  }
2748
+ })
2749
+ );
2750
+ });
2751
+ processedIntegrationsRef.current.forEach((_, id) => {
2752
+ if (!currentIds.has(id)) {
2753
+ processedIntegrationsRef.current.delete(id);
2754
+ const script = scriptRegistry.get(id);
2755
+ if (script) {
2756
+ scriptRegistry.delete(id);
2757
+ }
2758
+ const bootstrapScript = scriptRegistry.get(`${id}__bootstrap`);
2759
+ if (bootstrapScript) {
2760
+ scriptRegistry.delete(`${id}__bootstrap`);
2396
2761
  }
2397
2762
  }
2398
- }, 0);
2399
- return () => clearTimeout(timeoutId);
2400
- }, [preferences, consented, integrations, reloadOnChange, nonce]);
2763
+ });
2764
+ return () => cleanups.forEach((fn) => fn());
2765
+ }, [integrations, reloadOnChange, nonce]);
2766
+ React4__namespace.useEffect(() => {
2767
+ if (!isHydrated) return;
2768
+ void processQueue({ consented, preferences }, process.env.NODE_ENV !== "production");
2769
+ }, [consented, preferences, isHydrated, queueVersion]);
2770
+ React4__namespace.useEffect(() => {
2771
+ if (!isHydrated) return;
2772
+ scriptRegistry.forEach((script) => {
2773
+ if (script.status !== "executed") return;
2774
+ if (typeof script.onConsentUpdate !== "function") return;
2775
+ script.onConsentUpdate({ consented, preferences });
2776
+ });
2777
+ }, [consented, preferences, isHydrated]);
2401
2778
  return null;
2402
2779
  }
2403
2780
  function useConsentScriptLoader() {
2404
2781
  const { preferences, consented } = useConsent();
2782
+ const isHydrated = useConsentHydration();
2405
2783
  return React4__namespace.useCallback(
2406
2784
  async (integration, nonce) => {
2785
+ if (!isHydrated) {
2786
+ logger.warn(`\u26A0\uFE0F Cannot load script ${integration.id}: Consent not hydrated yet`);
2787
+ return false;
2788
+ }
2407
2789
  if (!consented) {
2408
2790
  logger.warn(`\u26A0\uFE0F Cannot load script ${integration.id}: No consent given`);
2409
2791
  return false;
@@ -2424,7 +2806,11 @@ function useConsentScriptLoader() {
2424
2806
  integration.src,
2425
2807
  integration.category,
2426
2808
  mergedAttrs,
2427
- scriptNonce
2809
+ scriptNonce,
2810
+ {
2811
+ consentSnapshot: { consented, preferences },
2812
+ skipConsentCheck: true
2813
+ }
2428
2814
  );
2429
2815
  if (integration.init) {
2430
2816
  integration.init();
@@ -2435,11 +2821,62 @@ function useConsentScriptLoader() {
2435
2821
  return false;
2436
2822
  }
2437
2823
  },
2438
- [preferences, consented]
2824
+ [preferences, consented, isHydrated]
2439
2825
  );
2440
2826
  }
2441
2827
 
2442
2828
  // src/utils/scriptIntegrations.ts
2829
+ function buildConsentModeSignals(preferences) {
2830
+ const analytics = preferences.analytics ? "granted" : "denied";
2831
+ const marketing = preferences.marketing ? "granted" : "denied";
2832
+ return {
2833
+ ad_storage: marketing,
2834
+ ad_user_data: marketing,
2835
+ ad_personalization: marketing,
2836
+ analytics_storage: analytics
2837
+ };
2838
+ }
2839
+ function pushToLayer(entry, dataLayerName) {
2840
+ if (typeof globalThis.window === "undefined") return;
2841
+ const registry = globalThis.window;
2842
+ const name = dataLayerName ?? "dataLayer";
2843
+ const layer = registry[name] ?? [];
2844
+ registry[name] = layer;
2845
+ layer.push(entry);
2846
+ }
2847
+ function ensureGtag(dataLayerName = "dataLayer") {
2848
+ if (typeof globalThis.window === "undefined") return null;
2849
+ const w = window;
2850
+ const registry = w;
2851
+ const layer = registry[dataLayerName] ?? [];
2852
+ registry[dataLayerName] = layer;
2853
+ if (typeof w.gtag !== "function") {
2854
+ const gtag = (...args) => {
2855
+ layer.push(args);
2856
+ };
2857
+ w.gtag = gtag;
2858
+ }
2859
+ return w.gtag;
2860
+ }
2861
+ function applyDefaultConsentMode(dataLayerName) {
2862
+ const payload = buildConsentModeSignals({
2863
+ analytics: false,
2864
+ marketing: false
2865
+ });
2866
+ const gtag = ensureGtag(dataLayerName);
2867
+ if (gtag) {
2868
+ gtag("consent", "default", payload);
2869
+ }
2870
+ pushToLayer(["consent", "default", payload], dataLayerName);
2871
+ }
2872
+ function applyConsentModeUpdate(preferences, dataLayerName) {
2873
+ const payload = buildConsentModeSignals(preferences);
2874
+ const gtag = ensureGtag(dataLayerName);
2875
+ if (gtag) {
2876
+ gtag("consent", "update", payload);
2877
+ }
2878
+ pushToLayer(["consent", "update", payload], dataLayerName);
2879
+ }
2443
2880
  function createGoogleAnalyticsIntegration(config) {
2444
2881
  const src = config.scriptUrl ?? `https://www.googletagmanager.com/gtag/js?id=${config.measurementId}`;
2445
2882
  return {
@@ -2467,17 +2904,17 @@ function createGoogleAnalyticsIntegration(config) {
2467
2904
  provider: "Google Analytics"
2468
2905
  }
2469
2906
  ],
2907
+ bootstrap: () => {
2908
+ applyDefaultConsentMode();
2909
+ },
2910
+ onConsentUpdate: ({ preferences }) => {
2911
+ applyConsentModeUpdate(preferences);
2912
+ },
2470
2913
  init: () => {
2471
- if (globalThis.window !== void 0) {
2472
- const w = window;
2473
- w.dataLayer = w.dataLayer ?? [];
2474
- const gtag = (...args) => {
2475
- w.dataLayer.push(...args);
2476
- };
2477
- w.gtag = gtag;
2478
- gtag("js", /* @__PURE__ */ new Date());
2479
- gtag("config", config.measurementId, config.config ?? {});
2480
- }
2914
+ const gtag = ensureGtag();
2915
+ if (!gtag) return;
2916
+ gtag("js", /* @__PURE__ */ new Date());
2917
+ gtag("config", config.measurementId, config.config ?? {});
2481
2918
  },
2482
2919
  attrs: { async: "true" }
2483
2920
  };
@@ -2489,6 +2926,12 @@ function createGoogleTagManagerIntegration(config) {
2489
2926
  category: "analytics",
2490
2927
  src,
2491
2928
  cookies: ["_gcl_au"],
2929
+ bootstrap: () => {
2930
+ applyDefaultConsentMode(config.dataLayerName);
2931
+ },
2932
+ onConsentUpdate: ({ preferences }) => {
2933
+ applyConsentModeUpdate(preferences, config.dataLayerName);
2934
+ },
2492
2935
  init: () => {
2493
2936
  if (globalThis.window !== void 0) {
2494
2937
  const dataLayerName = config.dataLayerName || "dataLayer";
@@ -3077,17 +3520,22 @@ exports.ensureNecessaryAlwaysOn = ensureNecessaryAlwaysOn;
3077
3520
  exports.extractCategoriesFromIntegrations = extractCategoriesFromIntegrations;
3078
3521
  exports.getAllProjectCategories = getAllProjectCategories;
3079
3522
  exports.getCookiesInfoForCategory = getCookiesInfoForCategory;
3523
+ exports.getPeerDepsLocale = getPeerDepsLocale;
3080
3524
  exports.loadScript = loadScript;
3081
3525
  exports.logDeveloperGuidance = logDeveloperGuidance;
3082
3526
  exports.logger = logger;
3083
3527
  exports.openPreferencesModal = openPreferencesModal;
3084
3528
  exports.pushConsentInitializedEvent = pushConsentInitializedEvent;
3085
3529
  exports.pushConsentUpdatedEvent = pushConsentUpdatedEvent;
3530
+ exports.registerScript = registerScript;
3531
+ exports.resetPeerDepsMessages = resetPeerDepsMessages;
3086
3532
  exports.resolveTexts = resolveTexts;
3087
3533
  exports.runPeerDepsCheck = runPeerDepsCheck;
3088
3534
  exports.setCookieCatalogOverrides = setCookieCatalogOverrides;
3089
3535
  exports.setCookieCategoryOverrides = setCookieCategoryOverrides;
3090
3536
  exports.setDebugLogging = setDebugLogging;
3537
+ exports.setPeerDepsLocale = setPeerDepsLocale;
3538
+ exports.setPeerDepsMessages = setPeerDepsMessages;
3091
3539
  exports.suggestCategoryForScript = suggestCategoryForScript;
3092
3540
  exports.useCategories = useCategories;
3093
3541
  exports.useCategoryStatus = useCategoryStatus;