react-lgpd-consent 0.2.0 → 0.2.2

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
@@ -36,15 +36,55 @@ function readConsentCookie(name = DEFAULT_COOKIE_OPTS.name) {
36
36
  const raw = import_js_cookie.default.get(name);
37
37
  if (!raw) return null;
38
38
  try {
39
- return JSON.parse(raw);
39
+ const data = JSON.parse(raw);
40
+ if (!data.version) {
41
+ return migrateLegacyCookie(data);
42
+ }
43
+ if (data.version !== COOKIE_SCHEMA_VERSION) {
44
+ console.warn(
45
+ `[react-lgpd-consent] Cookie version mismatch: ${data.version} != ${COOKIE_SCHEMA_VERSION}`
46
+ );
47
+ return null;
48
+ }
49
+ return data;
40
50
  } catch {
41
51
  return null;
42
52
  }
43
53
  }
44
- function writeConsentCookie(state, opts) {
54
+ function migrateLegacyCookie(legacyData) {
55
+ try {
56
+ const now = (/* @__PURE__ */ new Date()).toISOString();
57
+ return {
58
+ version: COOKIE_SCHEMA_VERSION,
59
+ consented: legacyData.consented || false,
60
+ preferences: legacyData.preferences || { necessary: true },
61
+ consentDate: now,
62
+ // Não temos o original, usar data atual
63
+ lastUpdate: now,
64
+ source: "banner",
65
+ // Assumir origem banner
66
+ isModalOpen: false
67
+ // Nunca persistir estado de UI
68
+ };
69
+ } catch {
70
+ return null;
71
+ }
72
+ }
73
+ function writeConsentCookie(state, source = "banner", opts) {
45
74
  if (typeof document === "undefined") return;
75
+ const now = (/* @__PURE__ */ new Date()).toISOString();
46
76
  const o = { ...DEFAULT_COOKIE_OPTS, ...opts };
47
- import_js_cookie.default.set(o.name, JSON.stringify(state), {
77
+ const cookieData = {
78
+ version: COOKIE_SCHEMA_VERSION,
79
+ consented: state.consented,
80
+ preferences: state.preferences,
81
+ consentDate: state.consentDate || now,
82
+ // Preservar data original ou usar atual
83
+ lastUpdate: now,
84
+ source
85
+ // isModalOpen NÃO é persistido (campo de UI apenas)
86
+ };
87
+ import_js_cookie.default.set(o.name, JSON.stringify(cookieData), {
48
88
  expires: o.maxAgeDays,
49
89
  sameSite: o.sameSite,
50
90
  secure: o.secure,
@@ -56,7 +96,7 @@ function removeConsentCookie(opts) {
56
96
  const o = { ...DEFAULT_COOKIE_OPTS, ...opts };
57
97
  import_js_cookie.default.remove(o.name, { path: o.path });
58
98
  }
59
- var import_js_cookie, DEFAULT_COOKIE_OPTS;
99
+ var import_js_cookie, DEFAULT_COOKIE_OPTS, COOKIE_SCHEMA_VERSION;
60
100
  var init_cookieUtils = __esm({
61
101
  "src/utils/cookieUtils.ts"() {
62
102
  "use strict";
@@ -68,6 +108,7 @@ var init_cookieUtils = __esm({
68
108
  secure: typeof window !== "undefined" ? window.location.protocol === "https:" : false,
69
109
  path: "/"
70
110
  };
111
+ COOKIE_SCHEMA_VERSION = "1.0";
71
112
  }
72
113
  });
73
114
 
@@ -145,74 +186,212 @@ var init_theme = __esm({
145
186
  }
146
187
  });
147
188
 
189
+ // src/utils/developerGuidance.ts
190
+ function analyzeDeveloperConfiguration(config) {
191
+ const guidance = {
192
+ warnings: [],
193
+ suggestions: [],
194
+ activeCategoriesInfo: [],
195
+ usingDefaults: !config
196
+ };
197
+ const finalConfig = config || DEFAULT_PROJECT_CATEGORIES;
198
+ if (!config) {
199
+ guidance.warnings.push(
200
+ 'LGPD-CONSENT: Nenhuma configura\xE7\xE3o de categorias especificada. Usando padr\xE3o: necessary + analytics. Para produ\xE7\xE3o, recomenda-se especificar explicitamente as categorias via prop "categories".'
201
+ );
202
+ }
203
+ guidance.activeCategoriesInfo.push({
204
+ id: "necessary",
205
+ name: "Cookies Necess\xE1rios",
206
+ description: "Essenciais para funcionamento b\xE1sico do site",
207
+ essential: true,
208
+ uiRequired: false
209
+ // Não precisa de toggle (sempre ativo)
210
+ });
211
+ const enabledCategories = finalConfig.enabledCategories || [];
212
+ const categoryNames = {
213
+ analytics: {
214
+ name: "Cookies Anal\xEDticos",
215
+ description: "Medem uso e performance do site"
216
+ },
217
+ functional: {
218
+ name: "Cookies Funcionais",
219
+ description: "Melhoram experi\xEAncia e funcionalidades"
220
+ },
221
+ marketing: {
222
+ name: "Cookies de Marketing",
223
+ description: "Publicidade direcionada e campanhas"
224
+ },
225
+ social: {
226
+ name: "Cookies de Redes Sociais",
227
+ description: "Integra\xE7\xE3o com plataformas sociais"
228
+ },
229
+ personalization: {
230
+ name: "Cookies de Personaliza\xE7\xE3o",
231
+ description: "Adaptam conte\xFAdo \xE0s prefer\xEAncias do usu\xE1rio"
232
+ }
233
+ };
234
+ enabledCategories.forEach((categoryId) => {
235
+ const categoryInfo = categoryNames[categoryId];
236
+ if (categoryInfo) {
237
+ guidance.activeCategoriesInfo.push({
238
+ id: categoryId,
239
+ name: categoryInfo.name,
240
+ description: categoryInfo.description,
241
+ essential: false,
242
+ uiRequired: true
243
+ // Precisa de toggle na UI
244
+ });
245
+ }
246
+ });
247
+ const customCategories = finalConfig.customCategories || [];
248
+ customCategories.forEach((category) => {
249
+ guidance.activeCategoriesInfo.push({
250
+ id: category.id,
251
+ name: category.name,
252
+ description: category.description,
253
+ essential: category.essential === true,
254
+ uiRequired: category.essential !== true
255
+ // Apenas não-essenciais precisam toggle
256
+ });
257
+ });
258
+ const totalToggleable = guidance.activeCategoriesInfo.filter(
259
+ (c) => c.uiRequired
260
+ ).length;
261
+ if (totalToggleable === 0) {
262
+ guidance.suggestions.push(
263
+ 'Apenas cookies necess\xE1rios est\xE3o configurados. Para compliance completo LGPD, considere adicionar categorias como "analytics" ou "functional" conforme uso real.'
264
+ );
265
+ }
266
+ if (totalToggleable > 5) {
267
+ guidance.warnings.push(
268
+ `${totalToggleable} categorias opcionais detectadas. UI com muitas op\xE7\xF5es pode ' +
269
+ 'prejudicar experi\xEAncia do usu\xE1rio. Considere agrupar categorias similares.`
270
+ );
271
+ }
272
+ const poorDescriptions = customCategories.filter(
273
+ (c) => !c.description || c.description.length < 10
274
+ );
275
+ if (poorDescriptions.length > 0) {
276
+ guidance.warnings.push(
277
+ `Categorias customizadas com descri\xE7\xF5es inadequadas: ${poorDescriptions.map((c) => c.id).join(", ")}. Descri\xE7\xF5es claras s\xE3o obrigat\xF3rias para compliance LGPD.`
278
+ );
279
+ }
280
+ return guidance;
281
+ }
282
+ function logDeveloperGuidance(guidance) {
283
+ const isProduction = typeof globalThis !== "undefined" && globalThis.__LGPD_PRODUCTION__ || typeof window !== "undefined" && !window.__LGPD_DEV__;
284
+ if (isProduction) return;
285
+ if (guidance.warnings.length > 0) {
286
+ console.group("\u{1F7E8} LGPD-CONSENT: Avisos de Configura\xE7\xE3o");
287
+ guidance.warnings.forEach((warning) => console.warn(warning));
288
+ console.groupEnd();
289
+ }
290
+ if (guidance.suggestions.length > 0) {
291
+ console.group("\u{1F4A1} LGPD-CONSENT: Sugest\xF5es");
292
+ guidance.suggestions.forEach((suggestion) => console.info(suggestion));
293
+ console.groupEnd();
294
+ }
295
+ if (guidance.usingDefaults) {
296
+ console.info(
297
+ '\u{1F4CB} LGPD-CONSENT: Usando configura\xE7\xE3o padr\xE3o. Para personalizar, use a prop "categories" no ConsentProvider.'
298
+ );
299
+ }
300
+ console.group("\u{1F527} LGPD-CONSENT: Categorias Ativas (para UI customizada)");
301
+ console.table(
302
+ guidance.activeCategoriesInfo.map((cat) => ({
303
+ ID: cat.id,
304
+ Nome: cat.name,
305
+ "Toggle UI?": cat.uiRequired ? "\u2705 SIM" : "\u274C N\xC3O (sempre ativo)",
306
+ "Essencial?": cat.essential ? "\u{1F512} SIM" : "\u2699\uFE0F N\xC3O"
307
+ }))
308
+ );
309
+ console.groupEnd();
310
+ }
311
+ var React, DEFAULT_PROJECT_CATEGORIES;
312
+ var init_developerGuidance = __esm({
313
+ "src/utils/developerGuidance.ts"() {
314
+ "use strict";
315
+ React = __toESM(require("react"), 1);
316
+ DEFAULT_PROJECT_CATEGORIES = {
317
+ enabledCategories: ["analytics"],
318
+ // Só analytics além de necessary
319
+ customCategories: []
320
+ };
321
+ }
322
+ });
323
+
148
324
  // src/context/CategoriesContext.tsx
149
325
  function CategoriesProvider({
326
+ children,
150
327
  categories,
151
- children
328
+ // LEGACY: prop antiga (apenas customCategories)
329
+ config
330
+ // NOVO: configuração completa
152
331
  }) {
153
- const value = React.useMemo(() => categories || [], [categories]);
154
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CategoriesCtx.Provider, { value, children });
332
+ const contextValue = React2.useMemo(() => {
333
+ let finalConfig;
334
+ if (categories && !config) {
335
+ finalConfig = {
336
+ enabledCategories: DEFAULT_PROJECT_CATEGORIES.enabledCategories,
337
+ customCategories: categories
338
+ };
339
+ } else {
340
+ finalConfig = config || DEFAULT_PROJECT_CATEGORIES;
341
+ }
342
+ const guidance = analyzeDeveloperConfiguration(
343
+ config || (categories ? { customCategories: categories } : void 0)
344
+ );
345
+ const toggleableCategories = guidance.activeCategoriesInfo.filter(
346
+ (cat) => cat.uiRequired
347
+ );
348
+ return {
349
+ config: finalConfig,
350
+ guidance,
351
+ toggleableCategories,
352
+ allCategories: guidance.activeCategoriesInfo,
353
+ legacyCategories: categories || []
354
+ };
355
+ }, [config, categories]);
356
+ React2.useEffect(() => {
357
+ logDeveloperGuidance(contextValue.guidance);
358
+ }, [contextValue.guidance]);
359
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CategoriesContext.Provider, { value: contextValue, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CategoriesCtx.Provider, { value: contextValue.legacyCategories, children }) });
155
360
  }
156
- function useCustomCategories() {
157
- return React.useContext(CategoriesCtx);
361
+ function useCategories() {
362
+ const context = React2.useContext(CategoriesContext);
363
+ if (!context) {
364
+ throw new Error(
365
+ "useCategories deve ser usado dentro de CategoriesProvider. Certifique-se de que o ConsentProvider est\xE1 envolvendo seu componente."
366
+ );
367
+ }
368
+ return context;
158
369
  }
159
- function useAllCategories() {
160
- const customCategories = useCustomCategories();
161
- return React.useMemo(() => {
162
- const defaultCategories = [
163
- {
164
- id: "necessary",
165
- name: "Cookies Necess\xE1rios",
166
- description: "Essenciais para o funcionamento b\xE1sico do site. Incluem cookies de sess\xE3o, autentica\xE7\xE3o e seguran\xE7a.",
167
- essential: true,
168
- cookies: ["PHPSESSID", "JSESSIONID", "cookieConsent", "csrf_token"]
169
- },
170
- {
171
- id: "analytics",
172
- name: "Analytics e Estat\xEDsticas",
173
- description: "Permitem medir audi\xEAncia e desempenho, gerando estat\xEDsticas an\xF4nimas de uso.",
174
- essential: false,
175
- cookies: ["_ga", "_ga_*", "_gid", "_gat", "gtag"]
176
- },
177
- {
178
- id: "functional",
179
- name: "Cookies Funcionais",
180
- description: "Melhoram a experi\xEAncia do usu\xE1rio, lembrando prefer\xEAncias e configura\xE7\xF5es.",
181
- essential: false,
182
- cookies: ["language", "theme", "timezone", "preferences"]
183
- },
184
- {
185
- id: "marketing",
186
- name: "Marketing e Publicidade",
187
- description: "Utilizados para publicidade direcionada e medi\xE7\xE3o de campanhas publicit\xE1rias.",
188
- essential: false,
189
- cookies: ["_fbp", "fr", "tr", "ads_*", "doubleclick"]
190
- },
191
- {
192
- id: "social",
193
- name: "Redes Sociais",
194
- description: "Permitem compartilhamento e integra\xE7\xE3o com redes sociais como Facebook, YouTube, etc.",
195
- essential: false,
196
- cookies: ["__Secure-*", "sb", "datr", "c_user", "social_*"]
197
- },
198
- {
199
- id: "personalization",
200
- name: "Personaliza\xE7\xE3o",
201
- description: "Adaptam o conte\xFAdo e interface \xE0s prefer\xEAncias individuais do usu\xE1rio.",
202
- essential: false,
203
- cookies: ["personalization_*", "content_*", "layout_*"]
204
- }
205
- ];
206
- return [...defaultCategories, ...customCategories];
207
- }, [customCategories]);
370
+ function useCategoryStatus(categoryId) {
371
+ const { allCategories } = useCategories();
372
+ const category = allCategories.find((cat) => cat.id === categoryId);
373
+ return {
374
+ isActive: !!category,
375
+ isEssential: category?.essential || false,
376
+ needsToggle: category?.uiRequired || false,
377
+ name: category?.name,
378
+ description: category?.description
379
+ };
208
380
  }
209
- var React, import_jsx_runtime, CategoriesCtx;
381
+ function useCustomCategories() {
382
+ return React2.useContext(CategoriesCtx);
383
+ }
384
+ var React2, import_jsx_runtime, CategoriesContext, CategoriesCtx;
210
385
  var init_CategoriesContext = __esm({
211
386
  "src/context/CategoriesContext.tsx"() {
212
387
  "use strict";
213
- React = __toESM(require("react"), 1);
388
+ React2 = __toESM(require("react"), 1);
389
+ init_developerGuidance();
214
390
  import_jsx_runtime = require("react/jsx-runtime");
215
- CategoriesCtx = React.createContext([]);
391
+ CategoriesContext = React2.createContext(
392
+ null
393
+ );
394
+ CategoriesCtx = React2.createContext([]);
216
395
  }
217
396
  });
218
397
 
@@ -292,12 +471,25 @@ function PreferencesModal({
292
471
  }) {
293
472
  const { preferences, setPreferences, closePreferences, isModalOpen } = useConsent();
294
473
  const texts = useConsentTexts();
295
- const [tempPreferences, setTempPreferences] = (0, import_react.useState)(preferences);
474
+ const { toggleableCategories } = useCategories();
475
+ const [tempPreferences, setTempPreferences] = (0, import_react.useState)(
476
+ () => {
477
+ const initialPrefs = { necessary: true };
478
+ toggleableCategories.forEach((category) => {
479
+ initialPrefs[category.id] = preferences[category.id] ?? false;
480
+ });
481
+ return initialPrefs;
482
+ }
483
+ );
296
484
  (0, import_react.useEffect)(() => {
297
485
  if (isModalOpen) {
298
- setTempPreferences(preferences);
486
+ const syncedPrefs = { necessary: true };
487
+ toggleableCategories.forEach((category) => {
488
+ syncedPrefs[category.id] = preferences[category.id] ?? false;
489
+ });
490
+ setTempPreferences(syncedPrefs);
299
491
  }
300
- }, [isModalOpen, preferences]);
492
+ }, [isModalOpen, preferences, toggleableCategories]);
301
493
  const open = DialogProps2?.open ?? isModalOpen ?? false;
302
494
  const handleSave = () => {
303
495
  setPreferences(tempPreferences);
@@ -318,38 +510,23 @@ function PreferencesModal({
318
510
  /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_DialogContent.default, { dividers: true, children: [
319
511
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_Typography2.default, { variant: "body2", sx: { mb: 2 }, children: texts.modalIntro }),
320
512
  /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_FormGroup.default, { children: [
321
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
322
- import_FormControlLabel.default,
323
- {
324
- control: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
325
- import_Switch.default,
326
- {
327
- checked: tempPreferences.analytics,
328
- onChange: (e) => setTempPreferences((prev) => ({
329
- ...prev,
330
- analytics: e.target.checked
331
- }))
332
- }
333
- ),
334
- label: "Cookies Anal\xEDticos (medem uso do site)"
335
- }
336
- ),
337
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
513
+ toggleableCategories.map((category) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
338
514
  import_FormControlLabel.default,
339
515
  {
340
516
  control: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
341
517
  import_Switch.default,
342
518
  {
343
- checked: tempPreferences.marketing,
519
+ checked: tempPreferences[category.id] ?? false,
344
520
  onChange: (e) => setTempPreferences((prev) => ({
345
521
  ...prev,
346
- marketing: e.target.checked
522
+ [category.id]: e.target.checked
347
523
  }))
348
524
  }
349
525
  ),
350
- label: "Cookies de Marketing/Publicidade"
351
- }
352
- ),
526
+ label: `${category.name} - ${category.description}`
527
+ },
528
+ category.id
529
+ )),
353
530
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
354
531
  import_FormControlLabel.default,
355
532
  {
@@ -382,6 +559,7 @@ var init_PreferencesModal = __esm({
382
559
  import_Switch = __toESM(require("@mui/material/Switch"), 1);
383
560
  import_Typography2 = __toESM(require("@mui/material/Typography"), 1);
384
561
  import_react = require("react");
562
+ init_CategoriesContext();
385
563
  init_useConsent();
386
564
  init_Branding();
387
565
  import_jsx_runtime3 = require("react/jsx-runtime");
@@ -398,6 +576,18 @@ function createInitialPreferences(customCategories) {
398
576
  }
399
577
  return prefs;
400
578
  }
579
+ function createFullConsentState(consented, preferences, source, isModalOpen = false, existingState) {
580
+ const now = (/* @__PURE__ */ new Date()).toISOString();
581
+ return {
582
+ version: "1.0",
583
+ consented,
584
+ preferences,
585
+ consentDate: existingState?.consentDate || now,
586
+ lastUpdate: now,
587
+ source,
588
+ isModalOpen
589
+ };
590
+ }
401
591
  function reducer(state, action) {
402
592
  switch (action.type) {
403
593
  case "ACCEPT_ALL": {
@@ -405,11 +595,7 @@ function reducer(state, action) {
405
595
  Object.keys(prefs).forEach((key) => {
406
596
  prefs[key] = true;
407
597
  });
408
- return {
409
- consented: true,
410
- preferences: prefs,
411
- isModalOpen: false
412
- };
598
+ return createFullConsentState(true, prefs, "banner", false, state);
413
599
  }
414
600
  case "REJECT_ALL": {
415
601
  const prefs = createInitialPreferences(action.customCategories);
@@ -420,11 +606,7 @@ function reducer(state, action) {
420
606
  }
421
607
  });
422
608
  }
423
- return {
424
- consented: true,
425
- preferences: prefs,
426
- isModalOpen: false
427
- };
609
+ return createFullConsentState(true, prefs, "banner", false, state);
428
610
  }
429
611
  case "SET_CATEGORY":
430
612
  return {
@@ -432,38 +614,50 @@ function reducer(state, action) {
432
614
  preferences: {
433
615
  ...state.preferences,
434
616
  [action.category]: action.value
435
- }
617
+ },
618
+ lastUpdate: (/* @__PURE__ */ new Date()).toISOString()
436
619
  };
437
620
  case "SET_PREFERENCES":
438
- return {
439
- ...state,
440
- consented: true,
441
- preferences: action.preferences,
442
- isModalOpen: false
443
- };
621
+ return createFullConsentState(
622
+ true,
623
+ action.preferences,
624
+ "modal",
625
+ false,
626
+ state
627
+ );
444
628
  case "OPEN_MODAL":
445
629
  return { ...state, isModalOpen: true };
446
630
  case "CLOSE_MODAL":
447
- return { ...state, isModalOpen: false, consented: true };
448
- // houve interação
631
+ return createFullConsentState(
632
+ true,
633
+ state.preferences,
634
+ "modal",
635
+ false,
636
+ state
637
+ );
449
638
  case "RESET": {
450
- return {
451
- consented: false,
452
- preferences: createInitialPreferences(action.customCategories),
453
- isModalOpen: false
454
- };
639
+ return createFullConsentState(
640
+ false,
641
+ createInitialPreferences(action.customCategories),
642
+ "programmatic",
643
+ false
644
+ );
455
645
  }
456
646
  case "HYDRATE":
457
- return { ...action.state };
647
+ return { ...action.state, isModalOpen: false };
648
+ // Nunca hidratar com modal aberto
458
649
  default:
459
650
  return state;
460
651
  }
461
652
  }
462
653
  function ConsentProvider({
463
654
  initialState,
655
+ categories,
656
+ // NOVO: configuração completa de categorias
464
657
  texts: textsProp,
465
658
  theme,
466
659
  customCategories,
660
+ // LEGACY: compatibilidade
467
661
  scriptIntegrations,
468
662
  PreferencesModalComponent,
469
663
  preferencesModalProps = {},
@@ -474,29 +668,41 @@ function ConsentProvider({
474
668
  cookie: cookieOpts,
475
669
  children
476
670
  }) {
477
- const texts = React2.useMemo(
671
+ const texts = React3.useMemo(
478
672
  () => ({ ...DEFAULT_TEXTS, ...textsProp ?? {} }),
479
673
  [textsProp]
480
674
  );
481
- const cookie = React2.useMemo(
675
+ const cookie = React3.useMemo(
482
676
  () => ({ ...DEFAULT_COOKIE_OPTS, ...cookieOpts ?? {} }),
483
677
  [cookieOpts]
484
678
  );
485
- const appliedTheme = React2.useMemo(
679
+ const appliedTheme = React3.useMemo(
486
680
  () => theme || defaultConsentTheme,
487
681
  [theme]
488
682
  );
489
- const boot = React2.useMemo(() => {
683
+ const finalCategoriesConfig = React3.useMemo(() => {
684
+ if (categories) return categories;
685
+ if (customCategories) {
686
+ return {
687
+ enabledCategories: ["analytics"],
688
+ // padrão quando usando API antiga
689
+ customCategories
690
+ };
691
+ }
692
+ return void 0;
693
+ }, [categories, customCategories]);
694
+ const boot = React3.useMemo(() => {
490
695
  if (initialState) return { ...initialState, isModalOpen: false };
491
- return {
492
- consented: false,
493
- preferences: createInitialPreferences(customCategories),
494
- isModalOpen: false
495
- };
696
+ return createFullConsentState(
697
+ false,
698
+ createInitialPreferences(customCategories),
699
+ "banner",
700
+ false
701
+ );
496
702
  }, [initialState, customCategories]);
497
- const [state, dispatch] = React2.useReducer(reducer, boot);
498
- const [isHydrated, setIsHydrated] = React2.useState(false);
499
- React2.useEffect(() => {
703
+ const [state, dispatch] = React3.useReducer(reducer, boot);
704
+ const [isHydrated, setIsHydrated] = React3.useState(false);
705
+ React3.useEffect(() => {
500
706
  if (!initialState) {
501
707
  const saved = readConsentCookie(cookie.name);
502
708
  if (saved?.consented) {
@@ -506,24 +712,24 @@ function ConsentProvider({
506
712
  }
507
713
  setIsHydrated(true);
508
714
  }, [cookie.name, initialState]);
509
- React2.useEffect(() => {
510
- if (state.consented) writeConsentCookie(state, cookie);
715
+ React3.useEffect(() => {
716
+ if (state.consented) writeConsentCookie(state, state.source, cookie);
511
717
  }, [state, cookie]);
512
- const prevConsented = React2.useRef(state.consented);
513
- React2.useEffect(() => {
718
+ const prevConsented = React3.useRef(state.consented);
719
+ React3.useEffect(() => {
514
720
  if (!prevConsented.current && state.consented && onConsentGiven) {
515
721
  setTimeout(() => onConsentGiven(state), 150);
516
722
  }
517
723
  prevConsented.current = state.consented;
518
724
  }, [state, onConsentGiven]);
519
- const prevPrefs = React2.useRef(state.preferences);
520
- React2.useEffect(() => {
725
+ const prevPrefs = React3.useRef(state.preferences);
726
+ React3.useEffect(() => {
521
727
  if (state.consented && onPreferencesSaved && prevPrefs.current !== state.preferences) {
522
728
  setTimeout(() => onPreferencesSaved(state.preferences), 150);
523
729
  prevPrefs.current = state.preferences;
524
730
  }
525
731
  }, [state, onPreferencesSaved]);
526
- const api = React2.useMemo(() => {
732
+ const api = React3.useMemo(() => {
527
733
  const acceptAll = () => dispatch({ type: "ACCEPT_ALL", customCategories });
528
734
  const rejectAll = () => dispatch({ type: "REJECT_ALL", customCategories });
529
735
  const setPreference = (category, value) => dispatch({ type: "SET_CATEGORY", category, value });
@@ -547,53 +753,55 @@ function ConsentProvider({
547
753
  resetConsent
548
754
  };
549
755
  }, [state, cookie, customCategories]);
550
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_styles2.ThemeProvider, { theme: appliedTheme, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(StateCtx.Provider, { value: state, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(ActionsCtx.Provider, { value: api, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(TextsCtx.Provider, { value: texts, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(HydrationCtx.Provider, { value: isHydrated, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(CategoriesProvider, { categories: customCategories, children: [
551
- children,
552
- !disableAutomaticModal && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(React2.Suspense, { fallback: null, children: PreferencesModalComponent ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(PreferencesModalComponent, { ...preferencesModalProps }) : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(PreferencesModal2, { hideBranding }) })
553
- ] }) }) }) }) }) });
756
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_styles2.ThemeProvider, { theme: appliedTheme, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(StateCtx.Provider, { value: state, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(ActionsCtx.Provider, { value: api, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(TextsCtx.Provider, { value: texts, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(HydrationCtx.Provider, { value: isHydrated, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
757
+ CategoriesProvider,
758
+ {
759
+ config: finalCategoriesConfig,
760
+ categories: customCategories,
761
+ children: [
762
+ children,
763
+ !disableAutomaticModal && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(React3.Suspense, { fallback: null, children: PreferencesModalComponent ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(PreferencesModalComponent, { ...preferencesModalProps }) : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(PreferencesModal2, { hideBranding }) })
764
+ ]
765
+ }
766
+ ) }) }) }) }) });
554
767
  }
555
768
  function useConsentStateInternal() {
556
- const ctx = React2.useContext(StateCtx);
769
+ const ctx = React3.useContext(StateCtx);
557
770
  if (!ctx)
558
771
  throw new Error("useConsentState must be used within ConsentProvider");
559
772
  return ctx;
560
773
  }
561
774
  function useConsentActionsInternal() {
562
- const ctx = React2.useContext(ActionsCtx);
775
+ const ctx = React3.useContext(ActionsCtx);
563
776
  if (!ctx)
564
777
  throw new Error("useConsentActions must be used within ConsentProvider");
565
778
  return ctx;
566
779
  }
567
780
  function useConsentTextsInternal() {
568
- const ctx = React2.useContext(TextsCtx);
781
+ const ctx = React3.useContext(TextsCtx);
569
782
  return ctx;
570
783
  }
571
784
  function useConsentHydrationInternal() {
572
- return React2.useContext(HydrationCtx);
785
+ return React3.useContext(HydrationCtx);
573
786
  }
574
- var React2, import_styles2, import_jsx_runtime4, PreferencesModal2, DEFAULT_PREFERENCES, DEFAULT_TEXTS, StateCtx, ActionsCtx, TextsCtx, HydrationCtx;
787
+ var React3, import_styles2, import_jsx_runtime4, PreferencesModal2, DEFAULT_PREFERENCES, DEFAULT_TEXTS, StateCtx, ActionsCtx, TextsCtx, HydrationCtx;
575
788
  var init_ConsentContext = __esm({
576
789
  "src/context/ConsentContext.tsx"() {
577
790
  "use strict";
578
- React2 = __toESM(require("react"), 1);
791
+ React3 = __toESM(require("react"), 1);
579
792
  import_styles2 = require("@mui/material/styles");
580
793
  init_cookieUtils();
581
794
  init_theme();
582
795
  init_CategoriesContext();
583
796
  import_jsx_runtime4 = require("react/jsx-runtime");
584
- PreferencesModal2 = React2.lazy(
797
+ PreferencesModal2 = React3.lazy(
585
798
  () => Promise.resolve().then(() => (init_PreferencesModal(), PreferencesModal_exports)).then((m) => ({
586
799
  default: m.PreferencesModal
587
800
  }))
588
801
  );
589
802
  DEFAULT_PREFERENCES = {
590
- necessary: true,
803
+ necessary: true
591
804
  // Sempre ativo (essencial)
592
- analytics: false,
593
- functional: false,
594
- marketing: false,
595
- social: false,
596
- personalization: false
597
805
  };
598
806
  DEFAULT_TEXTS = {
599
807
  // Textos básicos
@@ -624,10 +832,10 @@ var init_ConsentContext = __esm({
624
832
  transferCountries: void 0
625
833
  // Exibido se definido
626
834
  };
627
- StateCtx = React2.createContext(null);
628
- ActionsCtx = React2.createContext(null);
629
- TextsCtx = React2.createContext(DEFAULT_TEXTS);
630
- HydrationCtx = React2.createContext(false);
835
+ StateCtx = React3.createContext(null);
836
+ ActionsCtx = React3.createContext(null);
837
+ TextsCtx = React3.createContext(DEFAULT_TEXTS);
838
+ HydrationCtx = React3.createContext(false);
631
839
  }
632
840
  });
633
841
 
@@ -669,14 +877,17 @@ __export(index_exports, {
669
877
  ConsentProvider: () => ConsentProvider,
670
878
  ConsentScriptLoader: () => ConsentScriptLoader,
671
879
  CookieBanner: () => CookieBanner,
880
+ DEFAULT_PROJECT_CATEGORIES: () => DEFAULT_PROJECT_CATEGORIES,
672
881
  FloatingPreferencesButton: () => FloatingPreferencesButton,
673
882
  PreferencesModal: () => PreferencesModal,
883
+ analyzeDeveloperConfiguration: () => analyzeDeveloperConfiguration,
674
884
  createGoogleAnalyticsIntegration: () => createGoogleAnalyticsIntegration,
675
885
  createGoogleTagManagerIntegration: () => createGoogleTagManagerIntegration,
676
886
  createUserWayIntegration: () => createUserWayIntegration,
677
887
  defaultConsentTheme: () => defaultConsentTheme,
678
888
  loadScript: () => loadScript,
679
- useAllCategories: () => useAllCategories,
889
+ useCategories: () => useCategories,
890
+ useCategoryStatus: () => useCategoryStatus,
680
891
  useConsent: () => useConsent,
681
892
  useConsentHydration: () => useConsentHydration,
682
893
  useConsentScriptLoader: () => useConsentScriptLoader,
@@ -910,15 +1121,15 @@ function loadScript(id, src, category = null, attrs = {}) {
910
1121
  init_theme();
911
1122
 
912
1123
  // src/utils/ConsentScriptLoader.tsx
913
- var React3 = __toESM(require("react"), 1);
1124
+ var React4 = __toESM(require("react"), 1);
914
1125
  init_useConsent();
915
1126
  function ConsentScriptLoader({
916
1127
  integrations,
917
1128
  reloadOnChange = false
918
1129
  }) {
919
1130
  const { preferences, consented } = useConsent();
920
- const loadedScripts = React3.useRef(/* @__PURE__ */ new Set());
921
- React3.useEffect(() => {
1131
+ const loadedScripts = React4.useRef(/* @__PURE__ */ new Set());
1132
+ React4.useEffect(() => {
922
1133
  if (!consented) return;
923
1134
  integrations.forEach(async (integration) => {
924
1135
  const shouldLoad = preferences[integration.category];
@@ -949,7 +1160,7 @@ function ConsentScriptLoader({
949
1160
  }
950
1161
  function useConsentScriptLoader() {
951
1162
  const { preferences, consented } = useConsent();
952
- return React3.useCallback(
1163
+ return React4.useCallback(
953
1164
  async (integration) => {
954
1165
  if (!consented) {
955
1166
  console.warn(
@@ -1046,6 +1257,9 @@ var COMMON_INTEGRATIONS = {
1046
1257
  googleTagManager: createGoogleTagManagerIntegration,
1047
1258
  userway: createUserWayIntegration
1048
1259
  };
1260
+
1261
+ // src/index.ts
1262
+ init_developerGuidance();
1049
1263
  // Annotate the CommonJS export names for ESM import in node:
1050
1264
  0 && (module.exports = {
1051
1265
  COMMON_INTEGRATIONS,
@@ -1053,14 +1267,17 @@ var COMMON_INTEGRATIONS = {
1053
1267
  ConsentProvider,
1054
1268
  ConsentScriptLoader,
1055
1269
  CookieBanner,
1270
+ DEFAULT_PROJECT_CATEGORIES,
1056
1271
  FloatingPreferencesButton,
1057
1272
  PreferencesModal,
1273
+ analyzeDeveloperConfiguration,
1058
1274
  createGoogleAnalyticsIntegration,
1059
1275
  createGoogleTagManagerIntegration,
1060
1276
  createUserWayIntegration,
1061
1277
  defaultConsentTheme,
1062
1278
  loadScript,
1063
- useAllCategories,
1279
+ useCategories,
1280
+ useCategoryStatus,
1064
1281
  useConsent,
1065
1282
  useConsentHydration,
1066
1283
  useConsentScriptLoader,