@react-lgpd-consent/core 0.7.2 → 0.8.0

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,36 +1,52 @@
1
1
  # Changelog - @react-lgpd-consent/core
2
2
 
3
- ## 0.7.2
3
+ ## 0.8.0
4
4
 
5
- ### Patch Changes
6
-
7
- - [#134](https://github.com/lucianoedipo/react-lgpd-consent/pull/134) [`44b885b`](https://github.com/lucianoedipo/react-lgpd-consent/commit/44b885b7c81f1cb5822e2dcfde2975e69e8d4893) Thanks [@github-actions](https://github.com/apps/github-actions)! - ### Correções
8
- - 1d15920 fix: corrigir formatação da verificação de múltiplas versões do React
5
+ ### Minor Changes
9
6
 
10
- ### Funcionalidades
11
- - d641639 feat: adicionar gerenciamento de eventos para categorias obrigatórias no CategoriesProvider
12
- - b04c00b feat: add tests for CookieBanner, FloatingPreferencesButton, and PreferencesModal components
13
- - b04c00b Introduce interactive changeset script for easier versioning in monorepos.
7
+ - [#142](https://github.com/lucianoedipo/react-lgpd-consent/pull/142) [`29b4464`](https://github.com/lucianoedipo/react-lgpd-consent/commit/29b4464bd0ea878a21573a0752aca3adc31743d9) Thanks [@lucianoedipo](https://github.com/lucianoedipo)! - ### Correcoes
8
+ - 1d15920 fix: corrigir formatacao da verificacao de multiplas versoes do React
14
9
 
15
10
  ### Refactors e compatibilidade
16
- - ef2968a refactor: replace direct document and window references with globalThis for better compatibility
17
- - ef2968a Updated scriptLoader, cookieDiscovery, cookieUtils, dataLayerEvents, developerGuidance, peerDepsCheck, scriptIntegrations, and scriptLoader files to use globalThis.document and globalThis.window.
18
- - ef2968a Improved cookie handling functions to check for document and window availability using globalThis.
19
- - ef2968a Enhanced tests to reference globalThis for consistency.
20
- - ef2968a Cleaned up code formatting and comments for clarity.
21
- - b04c00b Refactor coverage check script to use node imports.
22
- - b04c00b Adjust TypeDoc script for ESM compatibility.
23
- - 3af2fcb Fix path resolution in tsconfig for better module imports.
11
+ - ed5fa36 refactor: aprimorar documentacao e estrutura de validacao do ConsentProvider
12
+ - 709e977 refactor: melhorar validacao e sanitizacao das categorias no ConsentProvider
13
+ - 6ee4d03 feat: recalcular categorias no CategoriesContext ao adicionar novas entradas
14
+ - 6ee4d03 refactor: melhorar descoberta e validacao de cookies
15
+ - 6ee4d03 refactor: refinar helpers de integracao para maior clareza
16
+ - 6ee4d03 refactor: simplificar logica de sincronizacao de versoes no script sync-versions.mjs
17
+ - ef2968a refactor: trocar acessos diretos a window/document por globalThis em loaders, utils e testes
18
+ - 3af2fcb fix: ajustar path resolution no tsconfig para melhor import de modulos
19
+
20
+ ### Funcionalidades
21
+ - 6ee4d03 feat: aprimorar acessibilidade e gerenciamento de consentimento
22
+ - 6ee4d03 feat: estender matchers Jest com jest-axe para acessibilidade
23
+ - 6ee4d03 feat: revisar textos de consentimento para mais clareza
24
+ - 6ee4d03 feat: introduzir modos de bloqueio no ConsentProvider
25
+ - 6ee4d03 feat: adicionar testes para hard blocking no ConsentProvider
26
+ - d641639 feat: adicionar gerenciamento de eventos para categorias obrigatorias no CategoriesProvider
24
27
 
25
28
  ### Testes
26
- - b04c00b Implement tests for CookieBanner to verify rendering based on consent and debug mode.
27
- - b04c00b Enhance FloatingPreferencesButton tests to check for localized text via props.
28
- - b04c00b Extend PreferencesModal tests to cover temporary preference resets, active scripts rendering, and custom text application.
29
-
30
- ### Documentação
31
- - d430eef docs: atualizar instruções para agentes com visão geral do projeto e comandos essenciais
32
- - d430eef Updated documentation to reflect changes in globalThis usage.
33
- - d430eef Update API documentation to include new integration functions and ESM/CJS testing configurations.
29
+ - b04c00b feat: adicionar testes para CookieBanner, FloatingPreferencesButton e PreferencesModal
30
+ - b04c00b testes: validar renderizacao do CookieBanner por consentimento e modo debug
31
+ - b04c00b testes: verificar textos localizados no FloatingPreferencesButton via props
32
+ - b04c00b testes: cobrir reset temporario, scripts ativos e textos customizados no PreferencesModal
33
+ - testes: cobrir fluxo SSR de leitura/remoção/escrita de cookie consent
34
+
35
+ ### Ferramentas
36
+ - b04c00b feat: script interativo de changeset para versionamento em monorepos
37
+ - b04c00b refactor: coverage-check usando node imports
38
+ - b04c00b refactor: ajustar script do TypeDoc para compatibilidade ESM
39
+
40
+ ### Documentacao
41
+ - 6ee4d03 docs: atualizar README e exemplos com novos recursos
42
+ - d430eef docs: atualizar instrucoes para agentes com comandos essenciais
43
+ - d430eef docs: refletir uso de globalThis na documentacao
44
+ - d430eef docs: atualizar API com novas integracoes e configuracoes de testes ESM/CJS
45
+ - docs: quickstart com comentarios e nota sobre injecao automatica de UI MUI
46
+
47
+ ### A11y
48
+ - a11y: PreferencesModal com aria-describedby
49
+ - a11y: testes de teclado (Escape + retorno de foco)
34
50
 
35
51
  ## 0.7.1
36
52
 
package/README.md CHANGED
@@ -100,13 +100,13 @@ import { ConsentProvider } from '@react-lgpd-consent/core'
100
100
  ### Presets ANPD
101
101
 
102
102
  ```tsx
103
- import { createAnpdCategories, ANPD_CATEGORY_PRESETS } from '@react-lgpd-consent/core'
103
+ import { createAnpdCategoriesConfig, ANPD_CATEGORY_PRESETS } from '@react-lgpd-consent/core'
104
104
 
105
105
  // Preset BÁSICO
106
- const basic = createAnpdCategories({ include: ['analytics'] })
106
+ const basic = createAnpdCategoriesConfig({ include: ['analytics'] })
107
107
 
108
108
  // Preset COMPLETO
109
- const full = createAnpdCategories({
109
+ const full = createAnpdCategoriesConfig({
110
110
  include: ['analytics', 'marketing', 'functional', 'social', 'personalization']
111
111
  })
112
112
  ```
package/dist/index.cjs CHANGED
@@ -64,27 +64,27 @@ var EXPANDED_DEFAULT_TEXTS = {
64
64
  categories: {
65
65
  necessary: {
66
66
  name: "Cookies Necess\xE1rios",
67
- description: "Essenciais para o funcionamento b\xE1sico do site",
67
+ description: "Essenciais para o funcionamento b\xE1sico do site e n\xE3o podem ser desativados",
68
68
  examples: "Sess\xE3o, seguran\xE7a, prefer\xEAncias de idioma"
69
69
  },
70
70
  analytics: {
71
71
  name: "Cookies de Analytics",
72
- description: "Ajudam a entender como os visitantes usam o site",
72
+ description: "Opcionais. Ajudam a entender como os visitantes usam o site",
73
73
  examples: "Google Analytics, contadores de p\xE1gina"
74
74
  },
75
75
  marketing: {
76
76
  name: "Cookies de Marketing",
77
- description: "Usados para personalizar an\xFAncios e ofertas",
77
+ description: "Opcionais. Usados para personalizar an\xFAncios e ofertas",
78
78
  examples: "Facebook Pixel, Google Ads, remarketing"
79
79
  },
80
80
  functional: {
81
81
  name: "Cookies Funcionais",
82
- description: "Melhoram a funcionalidade e personaliza\xE7\xE3o",
82
+ description: "Opcionais. Melhoram a funcionalidade e personaliza\xE7\xE3o",
83
83
  examples: "Chat, mapas, v\xEDdeos embarcados"
84
84
  },
85
85
  performance: {
86
86
  name: "Cookies de Performance",
87
- description: "Coletam informa\xE7\xF5es sobre velocidade e estabilidade",
87
+ description: "Opcionais. Coletam informa\xE7\xF5es sobre velocidade e estabilidade",
88
88
  examples: "Monitoramento de erro, otimiza\xE7\xE3o de velocidade"
89
89
  }
90
90
  },
@@ -557,7 +557,6 @@ function buildConsentStorageKey(options) {
557
557
  var DEFAULT_COOKIE_OPTS = {
558
558
  name: "cookieConsent",
559
559
  maxAge: DEFAULT_MAX_AGE_SECONDS,
560
- maxAgeDays: 365,
561
560
  sameSite: "Lax",
562
561
  secure: false,
563
562
  path: "/",
@@ -566,18 +565,23 @@ var DEFAULT_COOKIE_OPTS = {
566
565
  function resolveCookieOptions(opts) {
567
566
  const currentWindow = globalThis.window;
568
567
  const currentLocation = globalThis.location;
569
- const protocols = [currentWindow?.location?.protocol, currentLocation?.protocol].filter(
570
- Boolean
571
- );
568
+ const protocols = [currentWindow?.location?.protocol, currentLocation?.protocol].filter(Boolean);
572
569
  const forceHttps = globalThis.__LGPD_FORCE_HTTPS__ === true;
573
570
  const isHttps = forceHttps || protocols.includes("https:");
574
- const maxAgeSecondsFromDays = typeof opts?.maxAgeDays === "number" ? Math.max(0, opts.maxAgeDays * 24 * 60 * 60) : null;
575
- const maxAgeSeconds = typeof opts?.maxAge === "number" ? Math.max(0, opts.maxAge) : maxAgeSecondsFromDays ?? DEFAULT_MAX_AGE_SECONDS;
571
+ const maxAgeSeconds = typeof opts?.maxAge === "number" ? Math.max(0, opts.maxAge) : typeof opts?.maxAgeDays === "number" ? Math.max(0, opts.maxAgeDays) * 24 * 60 * 60 : DEFAULT_MAX_AGE_SECONDS;
572
+ let secure;
573
+ if (typeof opts?.secure === "boolean") {
574
+ secure = opts.secure;
575
+ } else if (isHttps) {
576
+ secure = true;
577
+ } else {
578
+ secure = DEFAULT_COOKIE_OPTS.secure;
579
+ }
576
580
  return {
577
581
  name: opts?.name ?? DEFAULT_COOKIE_OPTS.name,
578
582
  maxAge: maxAgeSeconds,
579
583
  sameSite: opts?.sameSite ?? DEFAULT_COOKIE_OPTS.sameSite ?? "Lax",
580
- secure: typeof opts?.secure === "boolean" ? opts.secure : isHttps ? true : DEFAULT_COOKIE_OPTS.secure,
584
+ secure,
581
585
  path: opts?.path ?? DEFAULT_COOKIE_OPTS.path ?? "/",
582
586
  domain: opts?.domain ?? DEFAULT_COOKIE_OPTS.domain ?? void 0
583
587
  };
@@ -696,7 +700,7 @@ function removeConsentCookie(opts) {
696
700
  }
697
701
 
698
702
  // src/utils/dataLayerEvents.ts
699
- var LIBRARY_VERSION = "0.7.2";
703
+ var LIBRARY_VERSION = "0.8.0";
700
704
  function ensureDataLayer() {
701
705
  const currentWindow = globalThis.window;
702
706
  if (!currentWindow) return;
@@ -1732,14 +1736,88 @@ function runPeerDepsCheck() {
1732
1736
 
1733
1737
  // src/utils/validation.ts
1734
1738
  var isDev = () => typeof process !== "undefined" && process.env.NODE_ENV !== "production";
1739
+ var sanitizeCategories = (categories) => {
1740
+ const enabled = [...new Set(categories.enabledCategories ?? [])];
1741
+ const sanitizedEnabled = enabled.filter((c) => c !== "necessary");
1742
+ return {
1743
+ enabled,
1744
+ sanitizedEnabled,
1745
+ custom: categories.customCategories ?? []
1746
+ };
1747
+ };
1748
+ var collectZodIssues = (z, categories, issues) => {
1749
+ if (!z || !categories) return;
1750
+ const CustomCategorySchema = z.object({
1751
+ id: z.string().min(1, "customCategories[].id deve ser uma string n\xE3o vazia"),
1752
+ name: z.string().min(1, "customCategories[].name deve ser uma string n\xE3o vazia"),
1753
+ description: z.string().min(1, "customCategories[].description deve ser uma string n\xE3o vazia"),
1754
+ essential: z.boolean().optional(),
1755
+ cookies: z.array(z.string().min(1)).optional()
1756
+ });
1757
+ const ProjectCategoriesConfigSchema = z.object({
1758
+ enabledCategories: z.array(z.string().min(1)).optional(),
1759
+ customCategories: z.array(CustomCategorySchema).optional()
1760
+ }).strict();
1761
+ const res = ProjectCategoriesConfigSchema.safeParse(categories);
1762
+ if (!res.success) {
1763
+ res.error.issues.forEach(
1764
+ (issue) => issues.push({ path: `categories.${issue.path.join(".")}`, message: issue.message })
1765
+ );
1766
+ }
1767
+ const customParse = z.array(CustomCategorySchema).safeParse(categories.customCategories ?? []);
1768
+ if (!customParse.success) {
1769
+ customParse.error.issues.forEach(
1770
+ (issue) => issues.push({ path: `customCategories.${issue.path.join(".")}`, message: issue.message })
1771
+ );
1772
+ }
1773
+ };
1774
+ var collectCategoryWarnings = (input) => {
1775
+ const warnings = [];
1776
+ const { enabled, sanitizedEnabled, custom } = input;
1777
+ if (enabled.includes("necessary")) {
1778
+ warnings.push("'necessary' \xE9 sempre inclu\xEDda automaticamente \u2014 remova de enabledCategories.");
1779
+ }
1780
+ const invalidEnabled = sanitizedEnabled.filter((c) => typeof c !== "string" || c.trim() === "");
1781
+ if (invalidEnabled.length > 0) {
1782
+ warnings.push(
1783
+ `enabledCategories cont\xE9m valores inv\xE1lidos: ${invalidEnabled.map(String).join(", ")} \u2014 remova ou corrija os IDs de categoria`
1784
+ );
1785
+ }
1786
+ const ids = /* @__PURE__ */ new Set();
1787
+ const dupes = [];
1788
+ ["necessary", ...sanitizedEnabled].forEach((id) => {
1789
+ if (ids.has(id)) dupes.push(id);
1790
+ ids.add(id);
1791
+ });
1792
+ custom?.forEach((c) => {
1793
+ if (ids.has(c.id)) dupes.push(c.id);
1794
+ ids.add(c.id);
1795
+ });
1796
+ if (dupes.length > 0) {
1797
+ warnings.push(
1798
+ `IDs de categoria duplicados detectados: ${Array.from(new Set(dupes)).join(
1799
+ ", "
1800
+ )} \u2014 verifique 'enabledCategories' e 'customCategories'.`
1801
+ );
1802
+ }
1803
+ return warnings;
1804
+ };
1805
+ var reportValidationMessages = (warnings, errors, issues) => {
1806
+ if (warnings.length > 0) {
1807
+ logger.warn("Valida\xE7\xE3o do ConsentProvider:", ...warnings);
1808
+ }
1809
+ if (errors.length > 0 || issues.length > 0) {
1810
+ issues.forEach((i) => errors.push(`Prop inv\xE1lida: ${i.path} \u2014 ${i.message}`));
1811
+ logger.error("Erros de configura\xE7\xE3o do ConsentProvider:", ...errors);
1812
+ }
1813
+ };
1735
1814
  function validateConsentProviderProps(props) {
1736
1815
  const warnings = [];
1737
1816
  const errors = [];
1738
1817
  const sanitized = {};
1739
1818
  if (!isDev()) {
1740
1819
  if (props.categories) {
1741
- const enabled = [...new Set(props.categories.enabledCategories ?? [])];
1742
- const sanitizedEnabled = enabled.filter((c) => c !== "necessary");
1820
+ const { sanitizedEnabled } = sanitizeCategories(props.categories);
1743
1821
  sanitized.categories = {
1744
1822
  enabledCategories: sanitizedEnabled,
1745
1823
  customCategories: props.categories.customCategories
@@ -1754,94 +1832,27 @@ function validateConsentProviderProps(props) {
1754
1832
  z = void 0;
1755
1833
  }
1756
1834
  const issues = [];
1757
- if (z) {
1758
- const CustomCategorySchema = z.object({
1759
- id: z.string().min(1, "customCategories[].id deve ser uma string n\xE3o vazia"),
1760
- name: z.string().min(1, "customCategories[].name deve ser uma string n\xE3o vazia"),
1761
- description: z.string().min(1, "customCategories[].description deve ser uma string n\xE3o vazia"),
1762
- essential: z.boolean().optional(),
1763
- cookies: z.array(z.string().min(1)).optional()
1764
- });
1765
- const ProjectCategoriesConfigSchema = z.object({
1766
- enabledCategories: z.array(z.string().min(1)).optional(),
1767
- customCategories: z.array(CustomCategorySchema).optional()
1768
- }).strict();
1769
- const res = ProjectCategoriesConfigSchema.safeParse(props.categories);
1770
- if (!res.success) {
1771
- res.error.issues.forEach(
1772
- (issue) => issues.push({ path: `categories.${issue.path.join(".")}`, message: issue.message })
1773
- );
1774
- }
1775
- }
1776
- if (!props.categories) {
1777
- warnings.push(
1778
- "Prop 'categories' n\xE3o fornecida \u2014 o ConsentProvider requer configura\xE7\xE3o de categorias."
1779
- );
1780
- } else {
1781
- const cat = props.categories;
1782
- const enabled = [...new Set(cat.enabledCategories ?? [])];
1783
- if (enabled.includes("necessary")) {
1784
- warnings.push("'necessary' \xE9 sempre inclu\xEDda automaticamente \u2014 remova de enabledCategories.");
1785
- }
1786
- const sanitizedEnabled = enabled.filter((c) => c !== "necessary");
1787
- const invalidEnabled = sanitizedEnabled.filter((c) => typeof c !== "string" || c.trim() === "");
1788
- if (invalidEnabled.length > 0) {
1789
- warnings.push(
1790
- `enabledCategories cont\xE9m valores inv\xE1lidos: ${invalidEnabled.map(String).join(", ")} \u2014 remova ou corrija os IDs de categoria`
1791
- );
1792
- }
1793
- const custom = cat.customCategories ?? [];
1794
- if (z) {
1795
- const CustomCategorySchema = z.object({
1796
- id: z.string().min(1),
1797
- name: z.string().min(1),
1798
- description: z.string().min(1),
1799
- essential: z.boolean().optional(),
1800
- cookies: z.array(z.string().min(1)).optional()
1801
- });
1802
- const customParse = z.array(CustomCategorySchema).safeParse(custom);
1803
- if (!customParse.success) {
1804
- customParse.error.issues.forEach(
1805
- (issue) => issues.push({ path: `customCategories.${issue.path.join(".")}`, message: issue.message })
1806
- );
1807
- }
1808
- }
1809
- const ids = /* @__PURE__ */ new Set();
1810
- const dupes = [];
1811
- ["necessary", ...sanitizedEnabled].forEach((id) => {
1812
- if (ids.has(id)) dupes.push(id);
1813
- ids.add(id);
1814
- });
1815
- custom.forEach((c) => {
1816
- if (ids.has(c.id)) dupes.push(c.id);
1817
- ids.add(c.id);
1818
- });
1819
- if (dupes.length > 0) {
1820
- warnings.push(
1821
- `IDs de categoria duplicados detectados: ${Array.from(new Set(dupes)).join(
1822
- ", "
1823
- )} \u2014 verifique 'enabledCategories' e 'customCategories'.`
1824
- );
1825
- }
1835
+ collectZodIssues(z, props.categories, issues);
1836
+ if (props.categories) {
1837
+ const { enabled, sanitizedEnabled, custom } = sanitizeCategories(props.categories);
1838
+ warnings.push(...collectCategoryWarnings({ enabled, sanitizedEnabled, custom }));
1826
1839
  sanitized.categories = {
1827
1840
  enabledCategories: sanitizedEnabled,
1828
1841
  customCategories: custom
1829
1842
  };
1843
+ } else {
1844
+ warnings.push(
1845
+ "Prop 'categories' n\xE3o fornecida \u2014 o ConsentProvider requer configura\xE7\xE3o de categorias."
1846
+ );
1830
1847
  }
1831
- if (warnings.length > 0) {
1832
- logger.warn("Valida\xE7\xE3o do ConsentProvider:", ...warnings);
1833
- }
1834
- if (errors.length > 0 || issues.length > 0) {
1835
- issues.forEach((i) => errors.push(`Prop inv\xE1lida: ${i.path} \u2014 ${i.message}`));
1836
- logger.error("Erros de configura\xE7\xE3o do ConsentProvider:", ...errors);
1837
- }
1848
+ reportValidationMessages(warnings, errors, issues);
1838
1849
  return { sanitized, warnings, errors };
1839
1850
  }
1840
1851
 
1841
1852
  // src/utils/cookieDiscovery.ts
1842
1853
  function discoverRuntimeCookies() {
1843
1854
  const currentDocument = globalThis.document;
1844
- if (currentDocument === void 0 || !currentDocument.cookie) return [];
1855
+ if (!currentDocument?.cookie) return [];
1845
1856
  const names = Array.from(
1846
1857
  new Set(
1847
1858
  currentDocument.cookie.split(";").map((s) => s.trim()).filter(Boolean).map((s) => s.split("=")[0])
@@ -1873,7 +1884,7 @@ function isConsentJson(val) {
1873
1884
  }
1874
1885
  function detectConsentCookieName() {
1875
1886
  const currentDocument = globalThis.document;
1876
- if (currentDocument === void 0 || !currentDocument.cookie) return null;
1887
+ if (!currentDocument?.cookie) return null;
1877
1888
  try {
1878
1889
  const pairs = currentDocument.cookie.split(";").map((s) => s.trim()).filter(Boolean);
1879
1890
  for (const p of pairs) {
@@ -2027,19 +2038,19 @@ function createFullConsentState(consented, preferences, source, projectConfig, i
2027
2038
  }
2028
2039
  var BASE_TEXTS = {
2029
2040
  // Textos básicos
2030
- bannerMessage: "Utilizamos cookies para melhorar sua experi\xEAncia.",
2041
+ bannerMessage: "Usamos cookies necess\xE1rios para o funcionamento do site. Os demais cookies s\xE3o opcionais e voc\xEA pode aceitar, rejeitar ou ajustar suas prefer\xEAncias.",
2031
2042
  acceptAll: "Aceitar todos",
2032
- declineAll: "Recusar",
2043
+ declineAll: "Rejeitar opcionais",
2033
2044
  preferences: "Prefer\xEAncias",
2034
- policyLink: "Saiba mais",
2035
- modalTitle: "Prefer\xEAncias de Cookies",
2036
- modalIntro: "Ajuste as categorias de cookies. Cookies necess\xE1rios s\xE3o sempre utilizados para funcionalidades b\xE1sicas.",
2045
+ policyLink: "Pol\xEDtica de privacidade",
2046
+ modalTitle: "Prefer\xEAncias de cookies",
2047
+ modalIntro: "Cookies necess\xE1rios s\xE3o sempre ativos. As demais categorias s\xE3o opcionais e voc\xEA pode ativ\xE1-las ou desativ\xE1-las a qualquer momento.",
2037
2048
  save: "Salvar prefer\xEAncias",
2038
2049
  necessaryAlwaysOn: "Cookies necess\xE1rios (sempre ativos)",
2039
2050
  // Textos adicionais para UI customizada
2040
- preferencesButton: "Configurar Cookies",
2041
- preferencesTitle: "Gerenciar Prefer\xEAncias de Cookies",
2042
- preferencesDescription: "Escolha quais tipos de cookies voc\xEA permite que sejam utilizados.",
2051
+ preferencesButton: "Gerenciar cookies",
2052
+ preferencesTitle: "Gerenciar prefer\xEAncias de cookies",
2053
+ preferencesDescription: "Escolha quais categorias opcionais voc\xEA permite. Cookies necess\xE1rios permanecem sempre ativos.",
2043
2054
  close: "Fechar",
2044
2055
  accept: "Aceitar",
2045
2056
  reject: "Rejeitar",
@@ -2160,6 +2171,7 @@ function ConsentProvider({
2160
2171
  floatingPreferencesButtonProps = {},
2161
2172
  disableFloatingPreferencesButton = false,
2162
2173
  blocking = false,
2174
+ blockingMode = "soft",
2163
2175
  blockingStrategy = "auto",
2164
2176
  hideBranding: _hideBranding = false,
2165
2177
  onConsentGiven,
@@ -2437,6 +2449,7 @@ function ConsentProvider({
2437
2449
  hideBranding: incoming.hideBranding === void 0 ? _hideBranding : Boolean(incoming.hideBranding)
2438
2450
  };
2439
2451
  }, [cookieBannerProps, blocking, _hideBranding]);
2452
+ const hardBlockingActive = blocking && isHydrated && !state.consented && blockingMode === "hard";
2440
2453
  const content = /* @__PURE__ */ jsxRuntime.jsx(StateCtx.Provider, { value: state, children: /* @__PURE__ */ jsxRuntime.jsx(ActionsCtx.Provider, { value: api, children: /* @__PURE__ */ jsxRuntime.jsx(TextsCtx.Provider, { value: texts, children: /* @__PURE__ */ jsxRuntime.jsx(HydrationCtx.Provider, { value: isHydrated, children: /* @__PURE__ */ jsxRuntime.jsx(DesignProvider, { tokens: designTokens, children: /* @__PURE__ */ jsxRuntime.jsxs(
2441
2454
  CategoriesProvider,
2442
2455
  {
@@ -2444,7 +2457,16 @@ function ConsentProvider({
2444
2457
  disableDeveloperGuidance,
2445
2458
  disableDiscoveryLog,
2446
2459
  children: [
2447
- children,
2460
+ /* @__PURE__ */ jsxRuntime.jsx(
2461
+ "div",
2462
+ {
2463
+ "data-testid": "lgpd-app-content",
2464
+ "aria-hidden": hardBlockingActive ? true : void 0,
2465
+ inert: hardBlockingActive,
2466
+ style: { display: "contents" },
2467
+ children
2468
+ }
2469
+ ),
2448
2470
  PreferencesModalComponent ? /* @__PURE__ */ jsxRuntime.jsx(
2449
2471
  PreferencesModalComponent,
2450
2472
  {
package/dist/index.d.cts CHANGED
@@ -175,8 +175,7 @@ declare const GUIDANCE_PRESETS: {
175
175
  * Tipo auxiliar para variações de texto.
176
176
  *
177
177
  * Define um subconjunto opcional dos textos principais do banner e modal,
178
- * permitindo variações de t es: {
179
- bannerMessage: 'Utilizamos cookies para mejorar su experiencia y mostrar contenido personalizado.', (formal, casual, etc.) sem sobrescrever todos os textos.
178
+ * permitindo variações de tom (formal, casual, etc.) sem sobrescrever todos os textos.
180
179
  *
181
180
  * @category Types
182
181
  * @since 0.4.1
@@ -1174,6 +1173,19 @@ type BackdropConfig = boolean | string | {
1174
1173
  * @since 0.1.3
1175
1174
  * @remarks
1176
1175
  * **Histórico**: v0.4.1 - Expandido substancialmente com novos tokens
1176
+ *
1177
+ * ### Mapa de tokens (top-level)
1178
+ * - `colors`: cores base, semânticas e por componente
1179
+ * - `typography`: fonte, tamanhos, pesos e hierarquia
1180
+ * - `spacing`: espaçamentos, padding/margin e radius
1181
+ * - `layout`: posição, largura, backdrop e z-index
1182
+ * - `effects`: sombras, transições e filtros
1183
+ * - `accessibility`: contraste, motion e foco
1184
+ * - `themes`: overrides para light/dark
1185
+ *
1186
+ * ### Overrides complementares (MUI)
1187
+ * Mesmo com tokens, você pode ajustar detalhes via `sx` e `ThemeProvider`.
1188
+ * Ex.: `cookieBannerProps.PaperProps.sx` e `preferencesModalProps.DialogProps.sx`.
1177
1189
  * @public
1178
1190
  *
1179
1191
  * @example Configuração básica
@@ -1681,6 +1693,16 @@ interface ConsentProviderProps {
1681
1693
  * - `true`: Banner bloqueia interação até decisão (compliance rigorosa)
1682
1694
  */
1683
1695
  blocking?: boolean;
1696
+ /**
1697
+ * Intensidade do bloqueio quando `blocking` estiver habilitado.
1698
+ * - 'soft' (padrão): aplica apenas overlay visual (cliques bloqueados).
1699
+ * - 'hard': aplica overlay + torna o conteúdo da aplicação inerte (sem foco/teclado).
1700
+ *
1701
+ * @remarks
1702
+ * O modo `hard` utiliza o atributo `inert` para restringir navegação por teclado
1703
+ * ao banner/modal, atendendo contextos regulatórios mais estritos.
1704
+ */
1705
+ blockingMode?: 'soft' | 'hard';
1684
1706
  /**
1685
1707
  * Estratégia de bloqueio quando `blocking` estiver habilitado.
1686
1708
  * - 'auto' (padrão):
@@ -1826,6 +1848,20 @@ interface ConsentProviderProps {
1826
1848
  * @since 0.3.1
1827
1849
  * @public
1828
1850
  * Fornece acesso ao estado de consentimento e ações necessárias para o banner.
1851
+ *
1852
+ * @example
1853
+ * ```tsx
1854
+ * function MyBanner({ acceptAll, rejectAll, openPreferences, texts }: CustomCookieBannerProps) {
1855
+ * return (
1856
+ * <div>
1857
+ * <p>{texts.bannerMessage}</p>
1858
+ * <button onClick={acceptAll}>{texts.acceptAll}</button>
1859
+ * <button onClick={rejectAll}>{texts.declineAll}</button>
1860
+ * <button onClick={openPreferences}>{texts.preferences}</button>
1861
+ * </div>
1862
+ * )
1863
+ * }
1864
+ * ```
1829
1865
  */
1830
1866
  interface CustomCookieBannerProps {
1831
1867
  consented: boolean;
@@ -1851,6 +1887,28 @@ interface CustomCookieBannerProps {
1851
1887
  * Fornece acesso às preferências atuais do usuário, funções para atualizar e salvar preferências,
1852
1888
  * fechar o modal e textos customizados da interface.
1853
1889
  *
1890
+ * @example
1891
+ * ```tsx
1892
+ * function MyPreferencesModal({
1893
+ * preferences,
1894
+ * setPreferences,
1895
+ * closePreferences,
1896
+ * isModalOpen,
1897
+ * texts,
1898
+ * }: CustomPreferencesModalProps) {
1899
+ * if (!isModalOpen) return null
1900
+ * return (
1901
+ * <div role="dialog" aria-label={texts.accessibility?.modalLabel}>
1902
+ * <h2>{texts.modalTitle}</h2>
1903
+ * <button onClick={() => setPreferences({ ...preferences, analytics: true })}>
1904
+ * {texts.acceptAll}
1905
+ * </button>
1906
+ * <button onClick={closePreferences}>{texts.close ?? 'Fechar'}</button>
1907
+ * </div>
1908
+ * )
1909
+ * }
1910
+ * ```
1911
+ *
1854
1912
  * @property preferences Preferências atuais de consentimento do usuário.
1855
1913
  * @property setPreferences Função para atualizar e salvar as preferências.
1856
1914
  * @property closePreferences Função para fechar o modal de preferências.
@@ -1870,6 +1928,14 @@ interface CustomPreferencesModalProps {
1870
1928
  * @since 0.3.1
1871
1929
  * @public
1872
1930
  * Fornece acesso às ações de abertura do modal e ao estado de consentimento.
1931
+ *
1932
+ * @example
1933
+ * ```tsx
1934
+ * function MyFloatingButton({ openPreferences, consented }: CustomFloatingPreferencesButtonProps) {
1935
+ * if (!consented) return null
1936
+ * return <button onClick={openPreferences}>Gerenciar cookies</button>
1937
+ * }
1938
+ * ```
1873
1939
  */
1874
1940
  interface CustomFloatingPreferencesButtonProps {
1875
1941
  openPreferences: () => void;
@@ -2050,6 +2116,7 @@ type ConsentEvent = ConsentInitializedEvent | ConsentUpdatedEvent;
2050
2116
  * @param {ProjectCategoriesConfig} props.categories - **Obrigatório**. Define as categorias de cookies que seu projeto utiliza, em conformidade com o princípio de minimização da LGPD.
2051
2117
  * @param {Partial<AdvancedConsentTexts>} [props.texts] - Objeto para customizar todos os textos exibidos na UI.
2052
2118
  * @param {boolean} [props.blocking=false] - Se `true`, exibe um overlay que impede a interação com o site até uma decisão do usuário.
2119
+ * @param {'soft' | 'hard'} [props.blockingMode='soft'] - Intensidade do bloqueio; use `hard` para tornar o conteúdo inerte e restringir navegação por teclado.
2053
2120
  * @param {(state: ConsentState) => void} [props.onConsentGiven] - Callback executado na primeira vez que o usuário dá o consentimento.
2054
2121
  * @param {(prefs: ConsentPreferences) => void} [props.onPreferencesSaved] - Callback executado sempre que o usuário salva novas preferências.
2055
2122
  * @param {boolean} [props.disableDeveloperGuidance=false] - Desativa as mensagens de orientação no console em ambiente de desenvolvimento.
@@ -2083,7 +2150,7 @@ type ConsentEvent = ConsentInitializedEvent | ConsentUpdatedEvent;
2083
2150
  * </ConsentProvider>
2084
2151
  * ```
2085
2152
  */
2086
- declare function ConsentProvider({ initialState, categories, texts: textsProp, language, textVariant, designTokens, PreferencesModalComponent, preferencesModalProps, CookieBannerComponent, cookieBannerProps, FloatingPreferencesButtonComponent, floatingPreferencesButtonProps, disableFloatingPreferencesButton, blocking, blockingStrategy, hideBranding: _hideBranding, onConsentGiven, onPreferencesSaved, cookie: cookieOpts, storage, onConsentVersionChange, disableDeveloperGuidance, guidanceConfig, children, disableDiscoveryLog, onConsentInit, onConsentChange, onAuditLog, }: Readonly<ConsentProviderProps>): react_jsx_runtime.JSX.Element;
2153
+ declare function ConsentProvider({ initialState, categories, texts: textsProp, language, textVariant, designTokens, PreferencesModalComponent, preferencesModalProps, CookieBannerComponent, cookieBannerProps, FloatingPreferencesButtonComponent, floatingPreferencesButtonProps, disableFloatingPreferencesButton, blocking, blockingMode, blockingStrategy, hideBranding: _hideBranding, onConsentGiven, onPreferencesSaved, cookie: cookieOpts, storage, onConsentVersionChange, disableDeveloperGuidance, guidanceConfig, children, disableDiscoveryLog, onConsentInit, onConsentChange, onAuditLog, }: Readonly<ConsentProviderProps>): react_jsx_runtime.JSX.Element;
2087
2154
  declare const defaultTexts: AdvancedConsentTexts;
2088
2155
 
2089
2156
  /**
@@ -3678,6 +3745,20 @@ interface RegisteredScript {
3678
3745
  * @returns Função de cleanup para remover o script da fila.
3679
3746
  */
3680
3747
  declare function registerScript(def: RegisteredScript): () => void;
3748
+ /**
3749
+ * Props do ConsentScriptLoader.
3750
+ *
3751
+ * @category Types
3752
+ * @since 0.2.0
3753
+ *
3754
+ * @example
3755
+ * ```tsx
3756
+ * <ConsentScriptLoader
3757
+ * integrations={[COMMON_INTEGRATIONS.googleTagManager({ containerId: 'GTM-XXXX' })]}
3758
+ * reloadOnChange
3759
+ * />
3760
+ * ```
3761
+ */
3681
3762
  interface ConsentScriptLoaderProps {
3682
3763
  /** Lista de integrações de scripts para carregar baseado no consentimento */
3683
3764
  integrations: ScriptIntegration[];
@@ -3706,6 +3787,14 @@ interface ConsentScriptLoaderProps {
3706
3787
  *
3707
3788
  * <ConsentScriptLoader integrations={integrations} />
3708
3789
  * ```
3790
+ *
3791
+ * @example
3792
+ * ```tsx
3793
+ * <ConsentScriptLoader
3794
+ * integrations={[COMMON_INTEGRATIONS.googleTagManager({ containerId: 'GTM-XXXX' })]}
3795
+ * nonce="csp-nonce"
3796
+ * />
3797
+ * ```
3709
3798
  */
3710
3799
  declare function ConsentScriptLoader({ integrations, reloadOnChange, nonce, }: Readonly<ConsentScriptLoaderProps>): null;
3711
3800
  /**
package/dist/index.d.ts CHANGED
@@ -175,8 +175,7 @@ declare const GUIDANCE_PRESETS: {
175
175
  * Tipo auxiliar para variações de texto.
176
176
  *
177
177
  * Define um subconjunto opcional dos textos principais do banner e modal,
178
- * permitindo variações de t es: {
179
- bannerMessage: 'Utilizamos cookies para mejorar su experiencia y mostrar contenido personalizado.', (formal, casual, etc.) sem sobrescrever todos os textos.
178
+ * permitindo variações de tom (formal, casual, etc.) sem sobrescrever todos os textos.
180
179
  *
181
180
  * @category Types
182
181
  * @since 0.4.1
@@ -1174,6 +1173,19 @@ type BackdropConfig = boolean | string | {
1174
1173
  * @since 0.1.3
1175
1174
  * @remarks
1176
1175
  * **Histórico**: v0.4.1 - Expandido substancialmente com novos tokens
1176
+ *
1177
+ * ### Mapa de tokens (top-level)
1178
+ * - `colors`: cores base, semânticas e por componente
1179
+ * - `typography`: fonte, tamanhos, pesos e hierarquia
1180
+ * - `spacing`: espaçamentos, padding/margin e radius
1181
+ * - `layout`: posição, largura, backdrop e z-index
1182
+ * - `effects`: sombras, transições e filtros
1183
+ * - `accessibility`: contraste, motion e foco
1184
+ * - `themes`: overrides para light/dark
1185
+ *
1186
+ * ### Overrides complementares (MUI)
1187
+ * Mesmo com tokens, você pode ajustar detalhes via `sx` e `ThemeProvider`.
1188
+ * Ex.: `cookieBannerProps.PaperProps.sx` e `preferencesModalProps.DialogProps.sx`.
1177
1189
  * @public
1178
1190
  *
1179
1191
  * @example Configuração básica
@@ -1681,6 +1693,16 @@ interface ConsentProviderProps {
1681
1693
  * - `true`: Banner bloqueia interação até decisão (compliance rigorosa)
1682
1694
  */
1683
1695
  blocking?: boolean;
1696
+ /**
1697
+ * Intensidade do bloqueio quando `blocking` estiver habilitado.
1698
+ * - 'soft' (padrão): aplica apenas overlay visual (cliques bloqueados).
1699
+ * - 'hard': aplica overlay + torna o conteúdo da aplicação inerte (sem foco/teclado).
1700
+ *
1701
+ * @remarks
1702
+ * O modo `hard` utiliza o atributo `inert` para restringir navegação por teclado
1703
+ * ao banner/modal, atendendo contextos regulatórios mais estritos.
1704
+ */
1705
+ blockingMode?: 'soft' | 'hard';
1684
1706
  /**
1685
1707
  * Estratégia de bloqueio quando `blocking` estiver habilitado.
1686
1708
  * - 'auto' (padrão):
@@ -1826,6 +1848,20 @@ interface ConsentProviderProps {
1826
1848
  * @since 0.3.1
1827
1849
  * @public
1828
1850
  * Fornece acesso ao estado de consentimento e ações necessárias para o banner.
1851
+ *
1852
+ * @example
1853
+ * ```tsx
1854
+ * function MyBanner({ acceptAll, rejectAll, openPreferences, texts }: CustomCookieBannerProps) {
1855
+ * return (
1856
+ * <div>
1857
+ * <p>{texts.bannerMessage}</p>
1858
+ * <button onClick={acceptAll}>{texts.acceptAll}</button>
1859
+ * <button onClick={rejectAll}>{texts.declineAll}</button>
1860
+ * <button onClick={openPreferences}>{texts.preferences}</button>
1861
+ * </div>
1862
+ * )
1863
+ * }
1864
+ * ```
1829
1865
  */
1830
1866
  interface CustomCookieBannerProps {
1831
1867
  consented: boolean;
@@ -1851,6 +1887,28 @@ interface CustomCookieBannerProps {
1851
1887
  * Fornece acesso às preferências atuais do usuário, funções para atualizar e salvar preferências,
1852
1888
  * fechar o modal e textos customizados da interface.
1853
1889
  *
1890
+ * @example
1891
+ * ```tsx
1892
+ * function MyPreferencesModal({
1893
+ * preferences,
1894
+ * setPreferences,
1895
+ * closePreferences,
1896
+ * isModalOpen,
1897
+ * texts,
1898
+ * }: CustomPreferencesModalProps) {
1899
+ * if (!isModalOpen) return null
1900
+ * return (
1901
+ * <div role="dialog" aria-label={texts.accessibility?.modalLabel}>
1902
+ * <h2>{texts.modalTitle}</h2>
1903
+ * <button onClick={() => setPreferences({ ...preferences, analytics: true })}>
1904
+ * {texts.acceptAll}
1905
+ * </button>
1906
+ * <button onClick={closePreferences}>{texts.close ?? 'Fechar'}</button>
1907
+ * </div>
1908
+ * )
1909
+ * }
1910
+ * ```
1911
+ *
1854
1912
  * @property preferences Preferências atuais de consentimento do usuário.
1855
1913
  * @property setPreferences Função para atualizar e salvar as preferências.
1856
1914
  * @property closePreferences Função para fechar o modal de preferências.
@@ -1870,6 +1928,14 @@ interface CustomPreferencesModalProps {
1870
1928
  * @since 0.3.1
1871
1929
  * @public
1872
1930
  * Fornece acesso às ações de abertura do modal e ao estado de consentimento.
1931
+ *
1932
+ * @example
1933
+ * ```tsx
1934
+ * function MyFloatingButton({ openPreferences, consented }: CustomFloatingPreferencesButtonProps) {
1935
+ * if (!consented) return null
1936
+ * return <button onClick={openPreferences}>Gerenciar cookies</button>
1937
+ * }
1938
+ * ```
1873
1939
  */
1874
1940
  interface CustomFloatingPreferencesButtonProps {
1875
1941
  openPreferences: () => void;
@@ -2050,6 +2116,7 @@ type ConsentEvent = ConsentInitializedEvent | ConsentUpdatedEvent;
2050
2116
  * @param {ProjectCategoriesConfig} props.categories - **Obrigatório**. Define as categorias de cookies que seu projeto utiliza, em conformidade com o princípio de minimização da LGPD.
2051
2117
  * @param {Partial<AdvancedConsentTexts>} [props.texts] - Objeto para customizar todos os textos exibidos na UI.
2052
2118
  * @param {boolean} [props.blocking=false] - Se `true`, exibe um overlay que impede a interação com o site até uma decisão do usuário.
2119
+ * @param {'soft' | 'hard'} [props.blockingMode='soft'] - Intensidade do bloqueio; use `hard` para tornar o conteúdo inerte e restringir navegação por teclado.
2053
2120
  * @param {(state: ConsentState) => void} [props.onConsentGiven] - Callback executado na primeira vez que o usuário dá o consentimento.
2054
2121
  * @param {(prefs: ConsentPreferences) => void} [props.onPreferencesSaved] - Callback executado sempre que o usuário salva novas preferências.
2055
2122
  * @param {boolean} [props.disableDeveloperGuidance=false] - Desativa as mensagens de orientação no console em ambiente de desenvolvimento.
@@ -2083,7 +2150,7 @@ type ConsentEvent = ConsentInitializedEvent | ConsentUpdatedEvent;
2083
2150
  * </ConsentProvider>
2084
2151
  * ```
2085
2152
  */
2086
- declare function ConsentProvider({ initialState, categories, texts: textsProp, language, textVariant, designTokens, PreferencesModalComponent, preferencesModalProps, CookieBannerComponent, cookieBannerProps, FloatingPreferencesButtonComponent, floatingPreferencesButtonProps, disableFloatingPreferencesButton, blocking, blockingStrategy, hideBranding: _hideBranding, onConsentGiven, onPreferencesSaved, cookie: cookieOpts, storage, onConsentVersionChange, disableDeveloperGuidance, guidanceConfig, children, disableDiscoveryLog, onConsentInit, onConsentChange, onAuditLog, }: Readonly<ConsentProviderProps>): react_jsx_runtime.JSX.Element;
2153
+ declare function ConsentProvider({ initialState, categories, texts: textsProp, language, textVariant, designTokens, PreferencesModalComponent, preferencesModalProps, CookieBannerComponent, cookieBannerProps, FloatingPreferencesButtonComponent, floatingPreferencesButtonProps, disableFloatingPreferencesButton, blocking, blockingMode, blockingStrategy, hideBranding: _hideBranding, onConsentGiven, onPreferencesSaved, cookie: cookieOpts, storage, onConsentVersionChange, disableDeveloperGuidance, guidanceConfig, children, disableDiscoveryLog, onConsentInit, onConsentChange, onAuditLog, }: Readonly<ConsentProviderProps>): react_jsx_runtime.JSX.Element;
2087
2154
  declare const defaultTexts: AdvancedConsentTexts;
2088
2155
 
2089
2156
  /**
@@ -3678,6 +3745,20 @@ interface RegisteredScript {
3678
3745
  * @returns Função de cleanup para remover o script da fila.
3679
3746
  */
3680
3747
  declare function registerScript(def: RegisteredScript): () => void;
3748
+ /**
3749
+ * Props do ConsentScriptLoader.
3750
+ *
3751
+ * @category Types
3752
+ * @since 0.2.0
3753
+ *
3754
+ * @example
3755
+ * ```tsx
3756
+ * <ConsentScriptLoader
3757
+ * integrations={[COMMON_INTEGRATIONS.googleTagManager({ containerId: 'GTM-XXXX' })]}
3758
+ * reloadOnChange
3759
+ * />
3760
+ * ```
3761
+ */
3681
3762
  interface ConsentScriptLoaderProps {
3682
3763
  /** Lista de integrações de scripts para carregar baseado no consentimento */
3683
3764
  integrations: ScriptIntegration[];
@@ -3706,6 +3787,14 @@ interface ConsentScriptLoaderProps {
3706
3787
  *
3707
3788
  * <ConsentScriptLoader integrations={integrations} />
3708
3789
  * ```
3790
+ *
3791
+ * @example
3792
+ * ```tsx
3793
+ * <ConsentScriptLoader
3794
+ * integrations={[COMMON_INTEGRATIONS.googleTagManager({ containerId: 'GTM-XXXX' })]}
3795
+ * nonce="csp-nonce"
3796
+ * />
3797
+ * ```
3709
3798
  */
3710
3799
  declare function ConsentScriptLoader({ integrations, reloadOnChange, nonce, }: Readonly<ConsentScriptLoaderProps>): null;
3711
3800
  /**
package/dist/index.js CHANGED
@@ -40,27 +40,27 @@ var EXPANDED_DEFAULT_TEXTS = {
40
40
  categories: {
41
41
  necessary: {
42
42
  name: "Cookies Necess\xE1rios",
43
- description: "Essenciais para o funcionamento b\xE1sico do site",
43
+ description: "Essenciais para o funcionamento b\xE1sico do site e n\xE3o podem ser desativados",
44
44
  examples: "Sess\xE3o, seguran\xE7a, prefer\xEAncias de idioma"
45
45
  },
46
46
  analytics: {
47
47
  name: "Cookies de Analytics",
48
- description: "Ajudam a entender como os visitantes usam o site",
48
+ description: "Opcionais. Ajudam a entender como os visitantes usam o site",
49
49
  examples: "Google Analytics, contadores de p\xE1gina"
50
50
  },
51
51
  marketing: {
52
52
  name: "Cookies de Marketing",
53
- description: "Usados para personalizar an\xFAncios e ofertas",
53
+ description: "Opcionais. Usados para personalizar an\xFAncios e ofertas",
54
54
  examples: "Facebook Pixel, Google Ads, remarketing"
55
55
  },
56
56
  functional: {
57
57
  name: "Cookies Funcionais",
58
- description: "Melhoram a funcionalidade e personaliza\xE7\xE3o",
58
+ description: "Opcionais. Melhoram a funcionalidade e personaliza\xE7\xE3o",
59
59
  examples: "Chat, mapas, v\xEDdeos embarcados"
60
60
  },
61
61
  performance: {
62
62
  name: "Cookies de Performance",
63
- description: "Coletam informa\xE7\xF5es sobre velocidade e estabilidade",
63
+ description: "Opcionais. Coletam informa\xE7\xF5es sobre velocidade e estabilidade",
64
64
  examples: "Monitoramento de erro, otimiza\xE7\xE3o de velocidade"
65
65
  }
66
66
  },
@@ -533,7 +533,6 @@ function buildConsentStorageKey(options) {
533
533
  var DEFAULT_COOKIE_OPTS = {
534
534
  name: "cookieConsent",
535
535
  maxAge: DEFAULT_MAX_AGE_SECONDS,
536
- maxAgeDays: 365,
537
536
  sameSite: "Lax",
538
537
  secure: false,
539
538
  path: "/",
@@ -542,18 +541,23 @@ var DEFAULT_COOKIE_OPTS = {
542
541
  function resolveCookieOptions(opts) {
543
542
  const currentWindow = globalThis.window;
544
543
  const currentLocation = globalThis.location;
545
- const protocols = [currentWindow?.location?.protocol, currentLocation?.protocol].filter(
546
- Boolean
547
- );
544
+ const protocols = [currentWindow?.location?.protocol, currentLocation?.protocol].filter(Boolean);
548
545
  const forceHttps = globalThis.__LGPD_FORCE_HTTPS__ === true;
549
546
  const isHttps = forceHttps || protocols.includes("https:");
550
- const maxAgeSecondsFromDays = typeof opts?.maxAgeDays === "number" ? Math.max(0, opts.maxAgeDays * 24 * 60 * 60) : null;
551
- const maxAgeSeconds = typeof opts?.maxAge === "number" ? Math.max(0, opts.maxAge) : maxAgeSecondsFromDays ?? DEFAULT_MAX_AGE_SECONDS;
547
+ const maxAgeSeconds = typeof opts?.maxAge === "number" ? Math.max(0, opts.maxAge) : typeof opts?.maxAgeDays === "number" ? Math.max(0, opts.maxAgeDays) * 24 * 60 * 60 : DEFAULT_MAX_AGE_SECONDS;
548
+ let secure;
549
+ if (typeof opts?.secure === "boolean") {
550
+ secure = opts.secure;
551
+ } else if (isHttps) {
552
+ secure = true;
553
+ } else {
554
+ secure = DEFAULT_COOKIE_OPTS.secure;
555
+ }
552
556
  return {
553
557
  name: opts?.name ?? DEFAULT_COOKIE_OPTS.name,
554
558
  maxAge: maxAgeSeconds,
555
559
  sameSite: opts?.sameSite ?? DEFAULT_COOKIE_OPTS.sameSite ?? "Lax",
556
- secure: typeof opts?.secure === "boolean" ? opts.secure : isHttps ? true : DEFAULT_COOKIE_OPTS.secure,
560
+ secure,
557
561
  path: opts?.path ?? DEFAULT_COOKIE_OPTS.path ?? "/",
558
562
  domain: opts?.domain ?? DEFAULT_COOKIE_OPTS.domain ?? void 0
559
563
  };
@@ -672,7 +676,7 @@ function removeConsentCookie(opts) {
672
676
  }
673
677
 
674
678
  // src/utils/dataLayerEvents.ts
675
- var LIBRARY_VERSION = "0.7.2";
679
+ var LIBRARY_VERSION = "0.8.0";
676
680
  function ensureDataLayer() {
677
681
  const currentWindow = globalThis.window;
678
682
  if (!currentWindow) return;
@@ -1708,14 +1712,88 @@ function runPeerDepsCheck() {
1708
1712
 
1709
1713
  // src/utils/validation.ts
1710
1714
  var isDev = () => typeof process !== "undefined" && process.env.NODE_ENV !== "production";
1715
+ var sanitizeCategories = (categories) => {
1716
+ const enabled = [...new Set(categories.enabledCategories ?? [])];
1717
+ const sanitizedEnabled = enabled.filter((c) => c !== "necessary");
1718
+ return {
1719
+ enabled,
1720
+ sanitizedEnabled,
1721
+ custom: categories.customCategories ?? []
1722
+ };
1723
+ };
1724
+ var collectZodIssues = (z, categories, issues) => {
1725
+ if (!z || !categories) return;
1726
+ const CustomCategorySchema = z.object({
1727
+ id: z.string().min(1, "customCategories[].id deve ser uma string n\xE3o vazia"),
1728
+ name: z.string().min(1, "customCategories[].name deve ser uma string n\xE3o vazia"),
1729
+ description: z.string().min(1, "customCategories[].description deve ser uma string n\xE3o vazia"),
1730
+ essential: z.boolean().optional(),
1731
+ cookies: z.array(z.string().min(1)).optional()
1732
+ });
1733
+ const ProjectCategoriesConfigSchema = z.object({
1734
+ enabledCategories: z.array(z.string().min(1)).optional(),
1735
+ customCategories: z.array(CustomCategorySchema).optional()
1736
+ }).strict();
1737
+ const res = ProjectCategoriesConfigSchema.safeParse(categories);
1738
+ if (!res.success) {
1739
+ res.error.issues.forEach(
1740
+ (issue) => issues.push({ path: `categories.${issue.path.join(".")}`, message: issue.message })
1741
+ );
1742
+ }
1743
+ const customParse = z.array(CustomCategorySchema).safeParse(categories.customCategories ?? []);
1744
+ if (!customParse.success) {
1745
+ customParse.error.issues.forEach(
1746
+ (issue) => issues.push({ path: `customCategories.${issue.path.join(".")}`, message: issue.message })
1747
+ );
1748
+ }
1749
+ };
1750
+ var collectCategoryWarnings = (input) => {
1751
+ const warnings = [];
1752
+ const { enabled, sanitizedEnabled, custom } = input;
1753
+ if (enabled.includes("necessary")) {
1754
+ warnings.push("'necessary' \xE9 sempre inclu\xEDda automaticamente \u2014 remova de enabledCategories.");
1755
+ }
1756
+ const invalidEnabled = sanitizedEnabled.filter((c) => typeof c !== "string" || c.trim() === "");
1757
+ if (invalidEnabled.length > 0) {
1758
+ warnings.push(
1759
+ `enabledCategories cont\xE9m valores inv\xE1lidos: ${invalidEnabled.map(String).join(", ")} \u2014 remova ou corrija os IDs de categoria`
1760
+ );
1761
+ }
1762
+ const ids = /* @__PURE__ */ new Set();
1763
+ const dupes = [];
1764
+ ["necessary", ...sanitizedEnabled].forEach((id) => {
1765
+ if (ids.has(id)) dupes.push(id);
1766
+ ids.add(id);
1767
+ });
1768
+ custom?.forEach((c) => {
1769
+ if (ids.has(c.id)) dupes.push(c.id);
1770
+ ids.add(c.id);
1771
+ });
1772
+ if (dupes.length > 0) {
1773
+ warnings.push(
1774
+ `IDs de categoria duplicados detectados: ${Array.from(new Set(dupes)).join(
1775
+ ", "
1776
+ )} \u2014 verifique 'enabledCategories' e 'customCategories'.`
1777
+ );
1778
+ }
1779
+ return warnings;
1780
+ };
1781
+ var reportValidationMessages = (warnings, errors, issues) => {
1782
+ if (warnings.length > 0) {
1783
+ logger.warn("Valida\xE7\xE3o do ConsentProvider:", ...warnings);
1784
+ }
1785
+ if (errors.length > 0 || issues.length > 0) {
1786
+ issues.forEach((i) => errors.push(`Prop inv\xE1lida: ${i.path} \u2014 ${i.message}`));
1787
+ logger.error("Erros de configura\xE7\xE3o do ConsentProvider:", ...errors);
1788
+ }
1789
+ };
1711
1790
  function validateConsentProviderProps(props) {
1712
1791
  const warnings = [];
1713
1792
  const errors = [];
1714
1793
  const sanitized = {};
1715
1794
  if (!isDev()) {
1716
1795
  if (props.categories) {
1717
- const enabled = [...new Set(props.categories.enabledCategories ?? [])];
1718
- const sanitizedEnabled = enabled.filter((c) => c !== "necessary");
1796
+ const { sanitizedEnabled } = sanitizeCategories(props.categories);
1719
1797
  sanitized.categories = {
1720
1798
  enabledCategories: sanitizedEnabled,
1721
1799
  customCategories: props.categories.customCategories
@@ -1730,94 +1808,27 @@ function validateConsentProviderProps(props) {
1730
1808
  z = void 0;
1731
1809
  }
1732
1810
  const issues = [];
1733
- if (z) {
1734
- const CustomCategorySchema = z.object({
1735
- id: z.string().min(1, "customCategories[].id deve ser uma string n\xE3o vazia"),
1736
- name: z.string().min(1, "customCategories[].name deve ser uma string n\xE3o vazia"),
1737
- description: z.string().min(1, "customCategories[].description deve ser uma string n\xE3o vazia"),
1738
- essential: z.boolean().optional(),
1739
- cookies: z.array(z.string().min(1)).optional()
1740
- });
1741
- const ProjectCategoriesConfigSchema = z.object({
1742
- enabledCategories: z.array(z.string().min(1)).optional(),
1743
- customCategories: z.array(CustomCategorySchema).optional()
1744
- }).strict();
1745
- const res = ProjectCategoriesConfigSchema.safeParse(props.categories);
1746
- if (!res.success) {
1747
- res.error.issues.forEach(
1748
- (issue) => issues.push({ path: `categories.${issue.path.join(".")}`, message: issue.message })
1749
- );
1750
- }
1751
- }
1752
- if (!props.categories) {
1753
- warnings.push(
1754
- "Prop 'categories' n\xE3o fornecida \u2014 o ConsentProvider requer configura\xE7\xE3o de categorias."
1755
- );
1756
- } else {
1757
- const cat = props.categories;
1758
- const enabled = [...new Set(cat.enabledCategories ?? [])];
1759
- if (enabled.includes("necessary")) {
1760
- warnings.push("'necessary' \xE9 sempre inclu\xEDda automaticamente \u2014 remova de enabledCategories.");
1761
- }
1762
- const sanitizedEnabled = enabled.filter((c) => c !== "necessary");
1763
- const invalidEnabled = sanitizedEnabled.filter((c) => typeof c !== "string" || c.trim() === "");
1764
- if (invalidEnabled.length > 0) {
1765
- warnings.push(
1766
- `enabledCategories cont\xE9m valores inv\xE1lidos: ${invalidEnabled.map(String).join(", ")} \u2014 remova ou corrija os IDs de categoria`
1767
- );
1768
- }
1769
- const custom = cat.customCategories ?? [];
1770
- if (z) {
1771
- const CustomCategorySchema = z.object({
1772
- id: z.string().min(1),
1773
- name: z.string().min(1),
1774
- description: z.string().min(1),
1775
- essential: z.boolean().optional(),
1776
- cookies: z.array(z.string().min(1)).optional()
1777
- });
1778
- const customParse = z.array(CustomCategorySchema).safeParse(custom);
1779
- if (!customParse.success) {
1780
- customParse.error.issues.forEach(
1781
- (issue) => issues.push({ path: `customCategories.${issue.path.join(".")}`, message: issue.message })
1782
- );
1783
- }
1784
- }
1785
- const ids = /* @__PURE__ */ new Set();
1786
- const dupes = [];
1787
- ["necessary", ...sanitizedEnabled].forEach((id) => {
1788
- if (ids.has(id)) dupes.push(id);
1789
- ids.add(id);
1790
- });
1791
- custom.forEach((c) => {
1792
- if (ids.has(c.id)) dupes.push(c.id);
1793
- ids.add(c.id);
1794
- });
1795
- if (dupes.length > 0) {
1796
- warnings.push(
1797
- `IDs de categoria duplicados detectados: ${Array.from(new Set(dupes)).join(
1798
- ", "
1799
- )} \u2014 verifique 'enabledCategories' e 'customCategories'.`
1800
- );
1801
- }
1811
+ collectZodIssues(z, props.categories, issues);
1812
+ if (props.categories) {
1813
+ const { enabled, sanitizedEnabled, custom } = sanitizeCategories(props.categories);
1814
+ warnings.push(...collectCategoryWarnings({ enabled, sanitizedEnabled, custom }));
1802
1815
  sanitized.categories = {
1803
1816
  enabledCategories: sanitizedEnabled,
1804
1817
  customCategories: custom
1805
1818
  };
1819
+ } else {
1820
+ warnings.push(
1821
+ "Prop 'categories' n\xE3o fornecida \u2014 o ConsentProvider requer configura\xE7\xE3o de categorias."
1822
+ );
1806
1823
  }
1807
- if (warnings.length > 0) {
1808
- logger.warn("Valida\xE7\xE3o do ConsentProvider:", ...warnings);
1809
- }
1810
- if (errors.length > 0 || issues.length > 0) {
1811
- issues.forEach((i) => errors.push(`Prop inv\xE1lida: ${i.path} \u2014 ${i.message}`));
1812
- logger.error("Erros de configura\xE7\xE3o do ConsentProvider:", ...errors);
1813
- }
1824
+ reportValidationMessages(warnings, errors, issues);
1814
1825
  return { sanitized, warnings, errors };
1815
1826
  }
1816
1827
 
1817
1828
  // src/utils/cookieDiscovery.ts
1818
1829
  function discoverRuntimeCookies() {
1819
1830
  const currentDocument = globalThis.document;
1820
- if (currentDocument === void 0 || !currentDocument.cookie) return [];
1831
+ if (!currentDocument?.cookie) return [];
1821
1832
  const names = Array.from(
1822
1833
  new Set(
1823
1834
  currentDocument.cookie.split(";").map((s) => s.trim()).filter(Boolean).map((s) => s.split("=")[0])
@@ -1849,7 +1860,7 @@ function isConsentJson(val) {
1849
1860
  }
1850
1861
  function detectConsentCookieName() {
1851
1862
  const currentDocument = globalThis.document;
1852
- if (currentDocument === void 0 || !currentDocument.cookie) return null;
1863
+ if (!currentDocument?.cookie) return null;
1853
1864
  try {
1854
1865
  const pairs = currentDocument.cookie.split(";").map((s) => s.trim()).filter(Boolean);
1855
1866
  for (const p of pairs) {
@@ -2003,19 +2014,19 @@ function createFullConsentState(consented, preferences, source, projectConfig, i
2003
2014
  }
2004
2015
  var BASE_TEXTS = {
2005
2016
  // Textos básicos
2006
- bannerMessage: "Utilizamos cookies para melhorar sua experi\xEAncia.",
2017
+ bannerMessage: "Usamos cookies necess\xE1rios para o funcionamento do site. Os demais cookies s\xE3o opcionais e voc\xEA pode aceitar, rejeitar ou ajustar suas prefer\xEAncias.",
2007
2018
  acceptAll: "Aceitar todos",
2008
- declineAll: "Recusar",
2019
+ declineAll: "Rejeitar opcionais",
2009
2020
  preferences: "Prefer\xEAncias",
2010
- policyLink: "Saiba mais",
2011
- modalTitle: "Prefer\xEAncias de Cookies",
2012
- modalIntro: "Ajuste as categorias de cookies. Cookies necess\xE1rios s\xE3o sempre utilizados para funcionalidades b\xE1sicas.",
2021
+ policyLink: "Pol\xEDtica de privacidade",
2022
+ modalTitle: "Prefer\xEAncias de cookies",
2023
+ modalIntro: "Cookies necess\xE1rios s\xE3o sempre ativos. As demais categorias s\xE3o opcionais e voc\xEA pode ativ\xE1-las ou desativ\xE1-las a qualquer momento.",
2013
2024
  save: "Salvar prefer\xEAncias",
2014
2025
  necessaryAlwaysOn: "Cookies necess\xE1rios (sempre ativos)",
2015
2026
  // Textos adicionais para UI customizada
2016
- preferencesButton: "Configurar Cookies",
2017
- preferencesTitle: "Gerenciar Prefer\xEAncias de Cookies",
2018
- preferencesDescription: "Escolha quais tipos de cookies voc\xEA permite que sejam utilizados.",
2027
+ preferencesButton: "Gerenciar cookies",
2028
+ preferencesTitle: "Gerenciar prefer\xEAncias de cookies",
2029
+ preferencesDescription: "Escolha quais categorias opcionais voc\xEA permite. Cookies necess\xE1rios permanecem sempre ativos.",
2019
2030
  close: "Fechar",
2020
2031
  accept: "Aceitar",
2021
2032
  reject: "Rejeitar",
@@ -2136,6 +2147,7 @@ function ConsentProvider({
2136
2147
  floatingPreferencesButtonProps = {},
2137
2148
  disableFloatingPreferencesButton = false,
2138
2149
  blocking = false,
2150
+ blockingMode = "soft",
2139
2151
  blockingStrategy = "auto",
2140
2152
  hideBranding: _hideBranding = false,
2141
2153
  onConsentGiven,
@@ -2413,6 +2425,7 @@ function ConsentProvider({
2413
2425
  hideBranding: incoming.hideBranding === void 0 ? _hideBranding : Boolean(incoming.hideBranding)
2414
2426
  };
2415
2427
  }, [cookieBannerProps, blocking, _hideBranding]);
2428
+ const hardBlockingActive = blocking && isHydrated && !state.consented && blockingMode === "hard";
2416
2429
  const content = /* @__PURE__ */ jsx(StateCtx.Provider, { value: state, children: /* @__PURE__ */ jsx(ActionsCtx.Provider, { value: api, children: /* @__PURE__ */ jsx(TextsCtx.Provider, { value: texts, children: /* @__PURE__ */ jsx(HydrationCtx.Provider, { value: isHydrated, children: /* @__PURE__ */ jsx(DesignProvider, { tokens: designTokens, children: /* @__PURE__ */ jsxs(
2417
2430
  CategoriesProvider,
2418
2431
  {
@@ -2420,7 +2433,16 @@ function ConsentProvider({
2420
2433
  disableDeveloperGuidance,
2421
2434
  disableDiscoveryLog,
2422
2435
  children: [
2423
- children,
2436
+ /* @__PURE__ */ jsx(
2437
+ "div",
2438
+ {
2439
+ "data-testid": "lgpd-app-content",
2440
+ "aria-hidden": hardBlockingActive ? true : void 0,
2441
+ inert: hardBlockingActive,
2442
+ style: { display: "contents" },
2443
+ children
2444
+ }
2445
+ ),
2424
2446
  PreferencesModalComponent ? /* @__PURE__ */ jsx(
2425
2447
  PreferencesModalComponent,
2426
2448
  {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@react-lgpd-consent/core",
3
- "version": "0.7.2",
3
+ "version": "0.8.0",
4
4
  "description": "Núcleo da biblioteca de consentimento LGPD para React - Estado, hooks e utilitários sem dependências de UI",
5
5
  "keywords": [
6
6
  "lgpd",