@react-lgpd-consent/core 0.5.0 → 0.6.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/CHANGELOG.md CHANGED
@@ -1,4 +1,23 @@
1
1
  # Changelog - @react-lgpd-consent/core
2
2
 
3
+ ## 0.6.1
4
+
5
+ ### Patch Changes
6
+
7
+ - [#105](https://github.com/lucianoedipo/react-lgpd-consent/pull/105) [`1deb3bb`](https://github.com/lucianoedipo/react-lgpd-consent/commit/1deb3bb56853165f7ec231e73d7b1d271e51b8f1) Thanks [@lucianoedipo](https://github.com/lucianoedipo)! - chore: sincronizar pnpm-lock.yaml com versões bumpeadas
8
+
9
+ ## 0.6.0
10
+
11
+ ### Minor Changes
12
+
13
+ - [#103](https://github.com/lucianoedipo/react-lgpd-consent/pull/103) [`4c9ebf2`](https://github.com/lucianoedipo/react-lgpd-consent/commit/4c9ebf231ff58168294f2fde405298b7087016ca) Thanks [@lucianoedipo](https://github.com/lucianoedipo)! - feat: adicionar diagnósticos de peer dependencies e sistema de troubleshooting
14
+ - ✨ Novo sistema de diagnóstico automático para peer deps
15
+ - 🔍 Detecta múltiplas instâncias de React (causa "Invalid hook call")
16
+ - 📋 Verifica versões de React (18-19) e MUI (5-7)
17
+ - 📖 Nova página TROUBLESHOOTING.md com soluções detalhadas
18
+ - 🔧 Mensagens acionáveis no console em modo desenvolvimento
19
+ - 🚀 Configuração de Turborepo para builds otimizados
20
+ - 📦 Configuração de Changesets para versionamento automatizado
21
+
3
22
  As notas de versão completas são mantidas no arquivo `CHANGELOG.md` da raiz do repositório.
4
23
  Este pacote segue a numeração conjunta da biblioteca `react-lgpd-consent`.
package/dist/index.cjs CHANGED
@@ -55,6 +55,15 @@ function createProjectPreferences(config, defaultValue = false) {
55
55
  });
56
56
  return preferences;
57
57
  }
58
+ function ensureNecessaryAlwaysOn(preferences) {
59
+ if (preferences.necessary === true) {
60
+ return { ...preferences, necessary: true };
61
+ }
62
+ return {
63
+ ...preferences,
64
+ necessary: true
65
+ };
66
+ }
58
67
  function validateProjectPreferences(preferences, config) {
59
68
  const validPreferences = {
60
69
  necessary: true
@@ -322,12 +331,22 @@ function setDebugLogging(enabled, level = 2 /* INFO */) {
322
331
  }
323
332
 
324
333
  // src/utils/cookieUtils.ts
334
+ var DEFAULT_STORAGE_NAMESPACE = "lgpd-consent";
335
+ var DEFAULT_STORAGE_VERSION = "1";
336
+ function buildConsentStorageKey(options) {
337
+ const namespaceRaw = options?.namespace?.trim() || DEFAULT_STORAGE_NAMESPACE;
338
+ const versionRaw = options?.version?.trim() || DEFAULT_STORAGE_VERSION;
339
+ const sanitizedNamespace = namespaceRaw.replace(/[^a-z0-9._-]+/gi, "-").toLowerCase();
340
+ const sanitizedVersion = versionRaw.replace(/[^a-z0-9._-]+/gi, "-").toLowerCase();
341
+ return `${sanitizedNamespace}__v${sanitizedVersion}`;
342
+ }
325
343
  var DEFAULT_COOKIE_OPTS = {
326
344
  name: "cookieConsent",
327
345
  maxAgeDays: 365,
328
346
  sameSite: "Lax",
329
347
  secure: typeof window !== "undefined" ? window.location.protocol === "https:" : false,
330
- path: "/"
348
+ path: "/",
349
+ domain: void 0
331
350
  };
332
351
  var COOKIE_SCHEMA_VERSION = "1.0";
333
352
  function readConsentCookie(name = DEFAULT_COOKIE_OPTS.name) {
@@ -381,10 +400,11 @@ function writeConsentCookie(state, config, opts, source = "banner") {
381
400
  }
382
401
  const now = (/* @__PURE__ */ new Date()).toISOString();
383
402
  const o = { ...DEFAULT_COOKIE_OPTS, ...opts };
403
+ const preferences = ensureNecessaryAlwaysOn(state.preferences);
384
404
  const cookieData = {
385
405
  version: COOKIE_SCHEMA_VERSION,
386
406
  consented: state.consented,
387
- preferences: state.preferences,
407
+ preferences,
388
408
  consentDate: state.consentDate || now,
389
409
  lastUpdate: now,
390
410
  source,
@@ -395,7 +415,8 @@ function writeConsentCookie(state, config, opts, source = "banner") {
395
415
  expires: o.maxAgeDays,
396
416
  sameSite: o.sameSite,
397
417
  secure: o.secure,
398
- path: o.path
418
+ path: o.path,
419
+ domain: o.domain
399
420
  });
400
421
  logger.info("Consent cookie saved", {
401
422
  consented: cookieData.consented,
@@ -410,12 +431,12 @@ function removeConsentCookie(opts) {
410
431
  }
411
432
  const o = { ...DEFAULT_COOKIE_OPTS, ...opts };
412
433
  logger.cookieOperation("delete", o.name);
413
- Cookies__default.default.remove(o.name, { path: o.path });
434
+ Cookies__default.default.remove(o.name, { path: o.path, domain: o.domain });
414
435
  logger.info("Consent cookie removed");
415
436
  }
416
437
 
417
438
  // src/utils/dataLayerEvents.ts
418
- var LIBRARY_VERSION = "0.5.0";
439
+ var LIBRARY_VERSION = "0.6.1";
419
440
  function ensureDataLayer() {
420
441
  if (typeof window === "undefined") return;
421
442
  if (!window.dataLayer) {
@@ -1051,6 +1072,207 @@ var GUIDANCE_PRESETS = {
1051
1072
  }
1052
1073
  };
1053
1074
 
1075
+ // src/utils/peerDepsCheck.ts
1076
+ function detectMultipleReactInstances() {
1077
+ if (typeof window === "undefined") return false;
1078
+ try {
1079
+ const reactSymbols = Object.getOwnPropertySymbols(window).map((sym) => String(sym)).filter((name) => name.includes("react"));
1080
+ if (reactSymbols.length > 1) {
1081
+ return true;
1082
+ }
1083
+ const ReactModule = window.React;
1084
+ if (ReactModule && Array.isArray(ReactModule)) {
1085
+ return true;
1086
+ }
1087
+ const hasMultipleVersions = window.__REACT_DEVTOOLS_GLOBAL_HOOK__?.renderers?.size > 1;
1088
+ return hasMultipleVersions || false;
1089
+ } catch {
1090
+ return false;
1091
+ }
1092
+ }
1093
+ function getPackageVersion(packageName) {
1094
+ if (typeof window === "undefined") return null;
1095
+ try {
1096
+ const pkg = window[packageName];
1097
+ if (pkg?.version) return pkg.version;
1098
+ const React6 = window.React;
1099
+ if (packageName === "react" && React6?.version) {
1100
+ return React6.version;
1101
+ }
1102
+ return null;
1103
+ } catch {
1104
+ return null;
1105
+ }
1106
+ }
1107
+ function isVersionInRange(version, minMajor, maxMajor) {
1108
+ const major = parseInt(version.split(".")[0], 10);
1109
+ return major >= minMajor && major <= maxMajor;
1110
+ }
1111
+ function checkPeerDeps(options = {}) {
1112
+ const { skipInProduction = true, logWarnings = true } = options;
1113
+ const result = {
1114
+ ok: true,
1115
+ warnings: [],
1116
+ errors: []
1117
+ };
1118
+ const isProduction = typeof process !== "undefined" && process.env?.NODE_ENV === "production";
1119
+ if (skipInProduction && isProduction) {
1120
+ return result;
1121
+ }
1122
+ if (typeof window === "undefined") {
1123
+ return result;
1124
+ }
1125
+ if (detectMultipleReactInstances()) {
1126
+ result.ok = false;
1127
+ const errorMsg = `
1128
+ \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
1129
+ \u2551 \u26A0\uFE0F ERRO: M\xFAltiplas inst\xE2ncias de React detectadas \u2551
1130
+ \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
1131
+
1132
+ \u{1F534} Problema:
1133
+ Seu projeto est\xE1 carregando mais de uma c\xF3pia do React, causando o erro:
1134
+ "Invalid hook call. Hooks can only be called inside of the body of a
1135
+ function component."
1136
+
1137
+ \u{1F50D} Causa prov\xE1vel:
1138
+ \u2022 pnpm/Yarn PnP sem hoisting adequado de peer dependencies
1139
+ \u2022 node_modules com React duplicado (npm/yarn cl\xE1ssico)
1140
+ \u2022 Webpack/Vite com m\xFAltiplas resolu\xE7\xF5es do mesmo pacote
1141
+
1142
+ \u2705 Solu\xE7\xF5es:
1143
+
1144
+ \u{1F4E6} PNPM (RECOMENDADO):
1145
+ Adicione ao package.json raiz:
1146
+ {
1147
+ "pnpm": {
1148
+ "overrides": {
1149
+ "react": "$react",
1150
+ "react-dom": "$react-dom"
1151
+ }
1152
+ }
1153
+ }
1154
+ Execute: pnpm install
1155
+
1156
+ \u{1F4E6} NPM/Yarn:
1157
+ Adicione ao package.json raiz:
1158
+ {
1159
+ "overrides": {
1160
+ "react": "^18.2.0 || ^19.0.0",
1161
+ "react-dom": "^18.2.0 || ^19.0.0"
1162
+ }
1163
+ }
1164
+ Execute: npm install (ou yarn install)
1165
+
1166
+ \u{1F527} Webpack:
1167
+ Adicione ao webpack.config.js:
1168
+ module.exports = {
1169
+ resolve: {
1170
+ alias: {
1171
+ react: path.resolve('./node_modules/react'),
1172
+ 'react-dom': path.resolve('./node_modules/react-dom'),
1173
+ }
1174
+ }
1175
+ }
1176
+
1177
+ \u26A1 Vite:
1178
+ Adicione ao vite.config.js:
1179
+ export default {
1180
+ resolve: {
1181
+ dedupe: ['react', 'react-dom']
1182
+ }
1183
+ }
1184
+
1185
+ \u{1F4DA} Documenta\xE7\xE3o:
1186
+ https://github.com/lucianoedipo/react-lgpd-consent/blob/main/TROUBLESHOOTING.md#multiple-react-instances
1187
+
1188
+ \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
1189
+ `;
1190
+ result.errors.push(errorMsg);
1191
+ if (logWarnings) {
1192
+ console.error(errorMsg);
1193
+ }
1194
+ }
1195
+ const reactVersion = getPackageVersion("react");
1196
+ if (reactVersion) {
1197
+ if (!isVersionInRange(reactVersion, 18, 19)) {
1198
+ result.ok = false;
1199
+ const errorMsg = `
1200
+ \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
1201
+ \u2551 \u26A0\uFE0F AVISO: Vers\xE3o do React n\xE3o suportada \u2551
1202
+ \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
1203
+
1204
+ \u{1F4E6} Vers\xE3o detectada: React ${reactVersion}
1205
+ \u2705 Vers\xF5es suportadas: React 18.x ou 19.x
1206
+
1207
+ \u{1F50D} O react-lgpd-consent requer React 18.2.0+ ou React 19.x
1208
+
1209
+ \u2705 Solu\xE7\xE3o:
1210
+ Atualize o React para uma vers\xE3o suportada:
1211
+
1212
+ npm install react@^18.2.0 react-dom@^18.2.0
1213
+
1214
+ ou
1215
+
1216
+ npm install react@^19.0.0 react-dom@^19.0.0
1217
+
1218
+ \u{1F4DA} Documenta\xE7\xE3o:
1219
+ https://github.com/lucianoedipo/react-lgpd-consent/blob/main/TROUBLESHOOTING.md#react-version
1220
+
1221
+ \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
1222
+ `;
1223
+ result.errors.push(errorMsg);
1224
+ if (logWarnings) {
1225
+ console.error(errorMsg);
1226
+ }
1227
+ }
1228
+ }
1229
+ const muiVersion = window["@mui/material"]?.version;
1230
+ if (muiVersion) {
1231
+ if (!isVersionInRange(muiVersion, 5, 7)) {
1232
+ result.warnings.push(
1233
+ `MUI vers\xE3o ${muiVersion} detectada. Vers\xF5es suportadas: 5.15.0+, 6.x ou 7.x. Alguns componentes podem n\xE3o funcionar corretamente.`
1234
+ );
1235
+ if (logWarnings) {
1236
+ logger.warn(
1237
+ `
1238
+ \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
1239
+ \u2551 \u26A0\uFE0F AVISO: Vers\xE3o do Material-UI fora do range recomendado \u2551
1240
+ \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
1241
+
1242
+ \u{1F4E6} Vers\xE3o detectada: @mui/material ${muiVersion}
1243
+ \u2705 Vers\xF5es suportadas: 5.15.0+, 6.x, 7.x
1244
+
1245
+ \u{1F50D} Componentes de UI (@react-lgpd-consent/mui) podem apresentar problemas.
1246
+
1247
+ \u2705 Solu\xE7\xE3o:
1248
+ Atualize o MUI para uma vers\xE3o suportada:
1249
+
1250
+ npm install @mui/material@^7.0.0 @emotion/react @emotion/styled
1251
+
1252
+ ou mantenha 5.15.0+:
1253
+
1254
+ npm install @mui/material@^5.15.0 @emotion/react @emotion/styled
1255
+
1256
+ \u{1F4DA} Documenta\xE7\xE3o:
1257
+ https://github.com/lucianoedipo/react-lgpd-consent/blob/main/TROUBLESHOOTING.md#mui-version
1258
+
1259
+ \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
1260
+ `
1261
+ );
1262
+ }
1263
+ }
1264
+ }
1265
+ return result;
1266
+ }
1267
+ function runPeerDepsCheck() {
1268
+ const result = checkPeerDeps({ logWarnings: true });
1269
+ if (result.ok && result.warnings.length === 0) {
1270
+ logger.debug("\u2705 Peer dependencies check: OK");
1271
+ } else if (result.warnings.length > 0) {
1272
+ logger.warn("\u26A0\uFE0F Peer dependencies check: avisos detectados");
1273
+ }
1274
+ }
1275
+
1054
1276
  // src/utils/validation.ts
1055
1277
  var isDev = () => typeof process !== "undefined" && process.env.NODE_ENV !== "production";
1056
1278
  function validateConsentProviderProps(props) {
@@ -1323,10 +1545,11 @@ function useDesignTokens() {
1323
1545
  }
1324
1546
  function createFullConsentState(consented, preferences, source, projectConfig, isModalOpen = false, existingState) {
1325
1547
  const now = (/* @__PURE__ */ new Date()).toISOString();
1548
+ const enforcedPreferences = ensureNecessaryAlwaysOn(preferences);
1326
1549
  return {
1327
1550
  version: "1.0",
1328
1551
  consented,
1329
- preferences,
1552
+ preferences: enforcedPreferences,
1330
1553
  consentDate: existingState?.consentDate || now,
1331
1554
  lastUpdate: now,
1332
1555
  source,
@@ -1390,7 +1613,11 @@ function reducer(state, action) {
1390
1613
  });
1391
1614
  return newState;
1392
1615
  }
1393
- case "SET_CATEGORY":
1616
+ case "SET_CATEGORY": {
1617
+ if (action.category === "necessary") {
1618
+ logger.warn("Attempt to toggle necessary category ignored for compliance reasons.");
1619
+ return state;
1620
+ }
1394
1621
  logger.debug("Category preference changed", {
1395
1622
  category: action.category,
1396
1623
  value: action.value
@@ -1403,9 +1630,12 @@ function reducer(state, action) {
1403
1630
  },
1404
1631
  lastUpdate: (/* @__PURE__ */ new Date()).toISOString()
1405
1632
  };
1406
- case "SET_PREFERENCES":
1407
- logger.info("Preferences saved", { preferences: action.preferences });
1408
- return createFullConsentState(true, action.preferences, "modal", action.config, false, state);
1633
+ }
1634
+ case "SET_PREFERENCES": {
1635
+ const sanitized = ensureNecessaryAlwaysOn(action.preferences);
1636
+ logger.info("Preferences saved", { preferences: sanitized });
1637
+ return createFullConsentState(true, sanitized, "modal", action.config, false, state);
1638
+ }
1409
1639
  case "OPEN_MODAL":
1410
1640
  return { ...state, isModalOpen: true };
1411
1641
  case "CLOSE_MODAL":
@@ -1456,16 +1686,25 @@ function ConsentProvider({
1456
1686
  onConsentGiven,
1457
1687
  onPreferencesSaved,
1458
1688
  cookie: cookieOpts,
1689
+ storage,
1690
+ onConsentVersionChange,
1459
1691
  disableDeveloperGuidance,
1460
1692
  guidanceConfig,
1461
1693
  children,
1462
1694
  disableDiscoveryLog
1463
1695
  }) {
1464
1696
  const texts = React4__namespace.useMemo(() => ({ ...DEFAULT_TEXTS, ...textsProp ?? {} }), [textsProp]);
1465
- const cookie = React4__namespace.useMemo(
1466
- () => ({ ...DEFAULT_COOKIE_OPTS, ...cookieOpts ?? {} }),
1467
- [cookieOpts]
1468
- );
1697
+ const cookie = React4__namespace.useMemo(() => {
1698
+ const base = { ...DEFAULT_COOKIE_OPTS, ...cookieOpts ?? {} };
1699
+ base.name = cookieOpts?.name ?? buildConsentStorageKey({
1700
+ namespace: storage?.namespace,
1701
+ version: storage?.version
1702
+ });
1703
+ if (!base.domain && storage?.domain) {
1704
+ base.domain = storage.domain;
1705
+ }
1706
+ return base;
1707
+ }, [cookieOpts, storage?.domain, storage?.namespace, storage?.version]);
1469
1708
  const finalCategoriesConfig = React4__namespace.useMemo(() => {
1470
1709
  const isProd = typeof process !== "undefined" && process.env?.NODE_ENV === "production";
1471
1710
  if (!categories) return DEFAULT_PROJECT_CATEGORIES;
@@ -1483,6 +1722,12 @@ function ConsentProvider({
1483
1722
  }, [categories]);
1484
1723
  const didWarnAboutMissingUI = React4__namespace.useRef(false);
1485
1724
  useDeveloperGuidance(finalCategoriesConfig, disableDeveloperGuidance, guidanceConfig);
1725
+ React4__namespace.useEffect(() => {
1726
+ const isProd = typeof process !== "undefined" && process.env?.NODE_ENV === "production";
1727
+ if (!isProd && !disableDeveloperGuidance) {
1728
+ runPeerDepsCheck();
1729
+ }
1730
+ }, [disableDeveloperGuidance]);
1486
1731
  React4__namespace.useEffect(() => {
1487
1732
  const isProd = typeof process !== "undefined" && process.env?.NODE_ENV === "production";
1488
1733
  if (!isProd && PreferencesModalComponent) {
@@ -1502,6 +1747,8 @@ function ConsentProvider({
1502
1747
  );
1503
1748
  }, [initialState, finalCategoriesConfig]);
1504
1749
  const [state, dispatch] = React4__namespace.useReducer(reducer, boot);
1750
+ const previousCookieRef = React4__namespace.useRef(cookie);
1751
+ const skipCookiePersistRef = React4__namespace.useRef(false);
1505
1752
  const [isHydrated, setIsHydrated] = React4__namespace.useState(false);
1506
1753
  const previousPreferencesRef = React4__namespace.useRef(state.preferences);
1507
1754
  React4__namespace.useEffect(() => {
@@ -1517,6 +1764,35 @@ function ConsentProvider({
1517
1764
  }
1518
1765
  setIsHydrated(true);
1519
1766
  }, [cookie.name, initialState, finalCategoriesConfig]);
1767
+ React4__namespace.useEffect(() => {
1768
+ const previousCookie = previousCookieRef.current;
1769
+ const isSameCookie = previousCookie.name === cookie.name && previousCookie.domain === cookie.domain && previousCookie.path === cookie.path;
1770
+ if (isSameCookie) {
1771
+ previousCookieRef.current = cookie;
1772
+ return;
1773
+ }
1774
+ skipCookiePersistRef.current = true;
1775
+ removeConsentCookie(previousCookie);
1776
+ const reset = () => {
1777
+ removeConsentCookie(cookie);
1778
+ dispatch({ type: "RESET", config: finalCategoriesConfig });
1779
+ };
1780
+ reset();
1781
+ if (onConsentVersionChange) {
1782
+ onConsentVersionChange({
1783
+ previousKey: previousCookie.name,
1784
+ nextKey: cookie.name,
1785
+ resetConsent: reset
1786
+ });
1787
+ }
1788
+ previousCookieRef.current = cookie;
1789
+ }, [cookie, finalCategoriesConfig, onConsentVersionChange, dispatch]);
1790
+ function resetSkipCookiePersistOnConsentRevoked() {
1791
+ if (skipCookiePersistRef.current && !state.consented) {
1792
+ skipCookiePersistRef.current = false;
1793
+ }
1794
+ }
1795
+ React4__namespace.useEffect(resetSkipCookiePersistOnConsentRevoked, [state.consented]);
1520
1796
  React4__namespace.useEffect(() => {
1521
1797
  if (isHydrated) {
1522
1798
  pushConsentInitializedEvent(state.preferences);
@@ -1526,7 +1802,9 @@ function ConsentProvider({
1526
1802
  }
1527
1803
  }, [isHydrated]);
1528
1804
  React4__namespace.useEffect(() => {
1529
- if (state.consented) writeConsentCookie(state, finalCategoriesConfig, cookie);
1805
+ if (!state.consented) return;
1806
+ if (skipCookiePersistRef.current) return;
1807
+ writeConsentCookie(state, finalCategoriesConfig, cookie);
1530
1808
  }, [state, cookie, finalCategoriesConfig]);
1531
1809
  const prevConsented = React4__namespace.useRef(state.consented);
1532
1810
  React4__namespace.useEffect(() => {
@@ -1554,15 +1832,22 @@ function ConsentProvider({
1554
1832
  const api = React4__namespace.useMemo(() => {
1555
1833
  const acceptAll = () => dispatch({ type: "ACCEPT_ALL", config: finalCategoriesConfig });
1556
1834
  const rejectAll = () => dispatch({ type: "REJECT_ALL", config: finalCategoriesConfig });
1557
- const setPreference = (category, value) => dispatch({ type: "SET_CATEGORY", category, value });
1835
+ const setPreference = (category, value) => {
1836
+ if (category === "necessary") {
1837
+ logger.warn("setPreference: attempt to toggle necessary category ignored.");
1838
+ return;
1839
+ }
1840
+ dispatch({ type: "SET_CATEGORY", category, value });
1841
+ };
1558
1842
  const setPreferences = (preferences) => {
1843
+ const sanitized = ensureNecessaryAlwaysOn(preferences);
1559
1844
  dispatch({
1560
1845
  type: "SET_PREFERENCES",
1561
- preferences,
1846
+ preferences: sanitized,
1562
1847
  config: finalCategoriesConfig
1563
1848
  });
1564
1849
  if (onPreferencesSaved) {
1565
- setTimeout(() => onPreferencesSaved(preferences), 150);
1850
+ setTimeout(() => onPreferencesSaved(sanitized), 150);
1566
1851
  }
1567
1852
  };
1568
1853
  const openPreferences = () => dispatch({ type: "OPEN_MODAL" });
@@ -2577,7 +2862,9 @@ exports.TEXT_TEMPLATES = TEXT_TEMPLATES;
2577
2862
  exports.analyzeDeveloperConfiguration = analyzeDeveloperConfiguration;
2578
2863
  exports.analyzeIntegrationCategories = analyzeIntegrationCategories;
2579
2864
  exports.autoConfigureCategories = autoConfigureCategories;
2865
+ exports.buildConsentStorageKey = buildConsentStorageKey;
2580
2866
  exports.categorizeDiscoveredCookies = categorizeDiscoveredCookies;
2867
+ exports.checkPeerDeps = checkPeerDeps;
2581
2868
  exports.createClarityIntegration = createClarityIntegration;
2582
2869
  exports.createCorporateIntegrations = createCorporateIntegrations;
2583
2870
  exports.createECommerceIntegrations = createECommerceIntegrations;
@@ -2594,6 +2881,7 @@ exports.createZendeskChatIntegration = createZendeskChatIntegration;
2594
2881
  exports.defaultTexts = defaultTexts;
2595
2882
  exports.detectConsentCookieName = detectConsentCookieName;
2596
2883
  exports.discoverRuntimeCookies = discoverRuntimeCookies;
2884
+ exports.ensureNecessaryAlwaysOn = ensureNecessaryAlwaysOn;
2597
2885
  exports.extractCategoriesFromIntegrations = extractCategoriesFromIntegrations;
2598
2886
  exports.getAllProjectCategories = getAllProjectCategories;
2599
2887
  exports.getCookiesInfoForCategory = getCookiesInfoForCategory;
@@ -2604,6 +2892,7 @@ exports.openPreferencesModal = openPreferencesModal;
2604
2892
  exports.pushConsentInitializedEvent = pushConsentInitializedEvent;
2605
2893
  exports.pushConsentUpdatedEvent = pushConsentUpdatedEvent;
2606
2894
  exports.resolveTexts = resolveTexts;
2895
+ exports.runPeerDepsCheck = runPeerDepsCheck;
2607
2896
  exports.setCookieCatalogOverrides = setCookieCatalogOverrides;
2608
2897
  exports.setCookieCategoryOverrides = setCookieCategoryOverrides;
2609
2898
  exports.setDebugLogging = setDebugLogging;