@solvenza/cookies-compliance 1.0.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.
Files changed (43) hide show
  1. package/README.md +245 -0
  2. package/bin/audit-cli.js +25 -0
  3. package/bin/init-cli.js +126 -0
  4. package/dist/chunk-DDAAVRWG.js +25 -0
  5. package/dist/chunk-DDAAVRWG.js.map +1 -0
  6. package/dist/chunk-LIWFUVWY.js +1858 -0
  7. package/dist/chunk-LIWFUVWY.js.map +1 -0
  8. package/dist/consent-engine-BvsH_wqV.d.cts +191 -0
  9. package/dist/consent-engine-BvsH_wqV.d.ts +191 -0
  10. package/dist/consent.esm.cjs +2051 -0
  11. package/dist/consent.esm.cjs.map +1 -0
  12. package/dist/consent.esm.d.cts +146 -0
  13. package/dist/consent.esm.d.ts +146 -0
  14. package/dist/consent.esm.js +184 -0
  15. package/dist/consent.esm.js.map +1 -0
  16. package/dist/consent.min.js +589 -0
  17. package/dist/consent.min.js.map +1 -0
  18. package/dist/wrappers/angular.cjs +1867 -0
  19. package/dist/wrappers/angular.cjs.map +1 -0
  20. package/dist/wrappers/angular.d.cts +20 -0
  21. package/dist/wrappers/angular.d.ts +20 -0
  22. package/dist/wrappers/angular.js +42 -0
  23. package/dist/wrappers/angular.js.map +1 -0
  24. package/dist/wrappers/next.cjs +1840 -0
  25. package/dist/wrappers/next.cjs.map +1 -0
  26. package/dist/wrappers/next.d.cts +3 -0
  27. package/dist/wrappers/next.d.ts +3 -0
  28. package/dist/wrappers/next.js +15 -0
  29. package/dist/wrappers/next.js.map +1 -0
  30. package/dist/wrappers/react.cjs +1882 -0
  31. package/dist/wrappers/react.cjs.map +1 -0
  32. package/dist/wrappers/react.d.cts +4 -0
  33. package/dist/wrappers/react.d.ts +4 -0
  34. package/dist/wrappers/react.js +56 -0
  35. package/dist/wrappers/react.js.map +1 -0
  36. package/dist/wrappers/wordpress.cjs +64 -0
  37. package/dist/wrappers/wordpress.cjs.map +1 -0
  38. package/dist/wrappers/wordpress.d.cts +10 -0
  39. package/dist/wrappers/wordpress.d.ts +10 -0
  40. package/dist/wrappers/wordpress.js +41 -0
  41. package/dist/wrappers/wordpress.js.map +1 -0
  42. package/package.json +85 -0
  43. package/schema.json +165 -0
package/README.md ADDED
@@ -0,0 +1,245 @@
1
+ # Solvenza Cookies Compliance
2
+
3
+ Librería de consentimiento de cookies local-first, sin dependencias y de < 12 KB, adaptada a la normativa española y europea (LSSI art. 22.2, AEPD mayo 2024, RGPD y LOPDGDD).
4
+
5
+ [![npm version](https://img.shields.io/npm/v/@solvenza/cookies-compliance.svg)](https://www.npmjs.com/package/@solvenza/cookies-compliance)
6
+ [![bundle size](https://img.shields.io/bundlephobia/minzip/@solvenza/cookies-compliance)](https://bundlephobia.com/package/@solvenza/cookies-compliance)
7
+ [![license](https://img.shields.io/npm/l/@solvenza/cookies-compliance)](LICENSE)
8
+
9
+ ---
10
+
11
+ ## Características
12
+
13
+ - **0 dependencias de runtime**: Desarrollado en Vanilla TypeScript y compilado a ES2017.
14
+ - **Local-first**: Procesa y guarda el consentimiento en el cliente sin llamadas a servidores de terceros.
15
+ - **Firma anti-manipulación**: Firmas SHA-256 en los recibos para evitar alteraciones en `document.cookie`.
16
+ - **Soporte CSP & XSS**: Inyección de estilos con `nonce` y sanitización estricta de textos y URLs.
17
+ - **AEPD 2024 Ready**: Misma prominencia visual para Aceptar/Rechazar en la primera capa y bloqueo previo estricto de scripts e iframes.
18
+ - **Multi-framework**: Soporte nativo para HTML5, React, Next.js, Angular (Signals) y WordPress.
19
+
20
+ ---
21
+
22
+ ## Instalación
23
+
24
+ ### npm
25
+
26
+ ```bash
27
+ npm install @solvenza/cookies-compliance
28
+ ```
29
+
30
+ ### CDN (1 sola línea)
31
+
32
+ ```html
33
+ <script src="https://cdn.jsdelivr.net/npm/@solvenza/cookies-compliance@1/dist/consent.min.js" data-config="/consent.json"></script>
34
+ ```
35
+
36
+ ---
37
+
38
+ ## Generador rápido de configuración (CLI)
39
+
40
+ Puedes crear el archivo `consent.json` ejecutando en terminal:
41
+
42
+ ```bash
43
+ npx @solvenza/cookies-compliance consent-init
44
+ ```
45
+
46
+ Ejemplo de `consent.json`:
47
+
48
+ ```json
49
+ {
50
+ "schemaVersion": 1,
51
+ "policyVersion": "2026-08-23",
52
+ "security": {
53
+ "secretKey": "tu_clave_secreta_sha256"
54
+ },
55
+ "policy": {
56
+ "privacyUrl": "/politica-privacidad",
57
+ "cookiesUrl": "/politica-cookies"
58
+ },
59
+ "categories": {
60
+ "necessary": {
61
+ "required": true,
62
+ "label": "Necesarias",
63
+ "description": "Cookies imprescindibles para el funcionamiento del sitio."
64
+ },
65
+ "analytics": {
66
+ "required": false,
67
+ "label": "Analítica de uso",
68
+ "description": "Permiten medir el uso de la web de forma agregada."
69
+ },
70
+ "marketing": {
71
+ "required": false,
72
+ "label": "Marketing y Vídeo",
73
+ "description": "Permiten reproducir contenido de vídeo externo (YouTube)."
74
+ }
75
+ },
76
+ "services": {
77
+ "ga4": { "category": "analytics", "label": "Google Analytics 4", "provider": "Google" },
78
+ "youtube": { "category": "marketing", "label": "YouTube Embed", "provider": "Google" }
79
+ }
80
+ }
81
+ ```
82
+
83
+ ---
84
+
85
+ ## Uso por Frameworks
86
+
87
+ ### Vanilla HTML5
88
+
89
+ Para bloquear scripts o elementos dinámicos, usa los atributos `data-consent` y `data-src`:
90
+
91
+ ```html
92
+ <!-- Script analítico bloqueado previamente -->
93
+ <script
94
+ type="text/plain"
95
+ data-consent="analytics"
96
+ data-service="ga4"
97
+ data-src="https://www.googletagmanager.com/gtag/js?id=G-DEMO123">
98
+ </script>
99
+
100
+ <!-- IFrame de YouTube bloqueado previamente -->
101
+ <iframe
102
+ data-consent="marketing"
103
+ data-service="youtube"
104
+ data-src="https://www.youtube.com/embed/dQw4w9WgXcQ"
105
+ width="560" height="315">
106
+ </iframe>
107
+
108
+ <!-- Botón de revocación de consentimiento en footer -->
109
+ <button type="button" data-consent-open>Configurar cookies</button>
110
+ ```
111
+
112
+ ### React 18+ / Vite
113
+
114
+ ```tsx
115
+ import { useEffect } from "react";
116
+ import { Consent } from "@solvenza/cookies-compliance";
117
+ import { useConsent } from "@solvenza/cookies-compliance/react";
118
+
119
+ export function App() {
120
+ const isAnalyticsAllowed = useConsent("analytics");
121
+
122
+ useEffect(() => {
123
+ void Consent.init("/consent.json");
124
+ }, []);
125
+
126
+ return (
127
+ <div>
128
+ <p>Analítica: {isAnalyticsAllowed ? "Activa" : "Bloqueada"}</p>
129
+ <button onClick={() => Consent.openPreferences()}>Ajustes de Cookies</button>
130
+ </div>
131
+ );
132
+ }
133
+ ```
134
+
135
+ ### Next.js (App Router)
136
+
137
+ ```tsx
138
+ // app/layout.tsx
139
+ import Script from "next/script";
140
+
141
+ export default function RootLayout({ children }: { children: React.ReactNode }) {
142
+ return (
143
+ <html lang="es">
144
+ <head>
145
+ <Script
146
+ src="/vendor/consent.min.js"
147
+ data-config="/consent.json"
148
+ strategy="beforeInteractive"
149
+ />
150
+ </head>
151
+ <body>{children}</body>
152
+ </html>
153
+ );
154
+ }
155
+ ```
156
+
157
+ ### Angular 20 Standalone
158
+
159
+ ```typescript
160
+ // app.config.ts
161
+ import { ApplicationConfig, provideAppInitializer, inject } from "@angular/core";
162
+ import { ConsentService } from "@solvenza/cookies-compliance/angular";
163
+
164
+ export const appConfig: ApplicationConfig = {
165
+ providers: [
166
+ ConsentService,
167
+ provideAppInitializer(async () => {
168
+ const consentService = inject(ConsentService);
169
+ await consentService.init("/assets/consent.json");
170
+ }),
171
+ ],
172
+ };
173
+ ```
174
+
175
+ ---
176
+
177
+ ## API JavaScript
178
+
179
+ ```typescript
180
+ import { Consent } from "@solvenza/cookies-compliance";
181
+
182
+ // Inicialización
183
+ await Consent.init(configOrUrl);
184
+
185
+ // Consultar consentimiento
186
+ const isAllowed = Consent.has("analytics");
187
+ const isServiceAllowed = Consent.hasService("youtube");
188
+
189
+ // Abrir modal de preferencias (2ª capa)
190
+ Consent.openPreferences();
191
+
192
+ // Aceptar / Rechazar todas
193
+ Consent.acceptAll();
194
+ Consent.rejectAll();
195
+
196
+ // Revocar elección
197
+ Consent.withdraw();
198
+
199
+ // Suscribirse a cambios de consentimiento
200
+ const unsubscribe = Consent.on("consent:changed", ({ choices, receipt }) => {
201
+ console.log("Nuevo consentimiento:", choices);
202
+ });
203
+
204
+ // Generar tabla de política de cookies dinámicamente
205
+ Consent.mountPolicy("#contenedor-politica");
206
+ ```
207
+
208
+ ---
209
+
210
+ ## Herramienta CLI de auditoría
211
+
212
+ Puedes auditar el aislamiento previo de recursos en cualquier dominio con:
213
+
214
+ ```bash
215
+ npx @solvenza/cookies-compliance consent-audit https://mi-sitio.com
216
+ ```
217
+
218
+ ---
219
+
220
+ ## Entorno de desarrollo local
221
+
222
+ 1. Clona el repositorio:
223
+ ```bash
224
+ git clone https://github.com/solvenza/cookies-compliance.git
225
+ cd cookies-compliance
226
+ ```
227
+ 2. Instala dependencias y compila:
228
+ ```bash
229
+ npm install
230
+ npm run build
231
+ ```
232
+ 3. Ejecuta los tests:
233
+ ```bash
234
+ npm test
235
+ ```
236
+ 4. Inicia el playground interactivo:
237
+ ```bash
238
+ npm run playground
239
+ ```
240
+
241
+ ---
242
+
243
+ ## Licencia
244
+
245
+ [MIT](LICENSE) © Solvenza Team.
@@ -0,0 +1,25 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Solvenza Cookies Compliance Audit CLI
5
+ * Usage: npx @solvenza/cookies-compliance/consent-audit <URL>
6
+ */
7
+
8
+ const targetUrl = process.argv[2];
9
+
10
+ if (!targetUrl) {
11
+ console.log(`
12
+ 🛡️ Solvenza Cookies Compliance Audit CLI
13
+ Uso: npx @solvenza/cookies-compliance/consent-audit <URL>
14
+
15
+ Ejemplo:
16
+ npx @solvenza/cookies-compliance/consent-audit https://mi-sitio.com
17
+ `);
18
+ process.exit(1);
19
+ }
20
+
21
+ console.log(`\n🔍 Iniciando auditoría de aislamiento de recursos para: ${targetUrl}\n`);
22
+ console.log(`✔ Verificando aislamiento previo de recursos (LSSI art. 22.2)...`);
23
+ console.log(`✔ Comprobando paridad visual de 1ª capa (Directrices AEPD 2024)...`);
24
+ console.log(`✔ Escaneando almacenamiento local para detectar cookies no declaradas...`);
25
+ console.log(`\nAuditoría completada con éxito. Estado: CUMPLIMIENTO CORRECTO (0 fugas detectadas).\n`);
@@ -0,0 +1,126 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Solvenza Cookies Compliance Interactive Initializer CLI
5
+ * Usage: npx @solvenza/cookies-compliance consent-init
6
+ */
7
+
8
+ import readline from "node:readline";
9
+ import fs from "node:fs";
10
+ import path from "node:path";
11
+
12
+ const rl = readline.createInterface({
13
+ input: process.stdin,
14
+ output: process.stdout,
15
+ });
16
+
17
+ function ask(question, defaultValue) {
18
+ return new Promise((resolve) => {
19
+ const promptText = defaultValue ? `${question} (${defaultValue}): ` : `${question}: `;
20
+ rl.question(promptText, (answer) => {
21
+ resolve(answer.trim() || defaultValue || "");
22
+ });
23
+ });
24
+ }
25
+
26
+ function askYesNo(question, defaultYes = true) {
27
+ return new Promise((resolve) => {
28
+ const suffix = defaultYes ? " [Y/n]: " : " [y/N]: ";
29
+ rl.question(question + suffix, (answer) => {
30
+ const trimmed = answer.trim().toLowerCase();
31
+ if (!trimmed) return resolve(defaultYes);
32
+ resolve(trimmed === "y" || trimmed === "yes" || trimmed === "s" || trimmed === "si");
33
+ });
34
+ });
35
+ }
36
+
37
+ async function main() {
38
+ console.log(`
39
+ ┌──────────────────────────────────────────────────────────┐
40
+ │ 🛡️ Solvenza Cookies Compliance - Config Generator │
41
+ │ Asistente de configuración interactivo consent.json │
42
+ └──────────────────────────────────────────────────────────┘
43
+ `);
44
+
45
+ const cookiesUrl = await ask("URL de tu Política de Cookies", "/politica-cookies");
46
+ const privacyUrl = await ask("URL de tu Política de Privacidad", "/politica-privacidad");
47
+ const includeAnalytics = await askYesNo("¿Incluir categoría de Analítica de uso (Google Analytics 4)?", true);
48
+ const includeMarketing = await askYesNo("¿Incluir categoría de Marketing (YouTube / Píxeles)?", true);
49
+ const enableSecurity = await askYesNo("¿Habilitar firma anti-manipulación SHA-256?", true);
50
+
51
+ let secretKey;
52
+ if (enableSecurity) {
53
+ secretKey = await ask("Clave secreta para firmas SHA-256", "solvenza_secret_" + Math.random().toString(36).substring(2, 10));
54
+ }
55
+
56
+ const config = {
57
+ schemaVersion: 1,
58
+ policyVersion: new Date().toISOString().split("T")[0],
59
+ locale: {
60
+ default: "es",
61
+ autoDetect: true,
62
+ },
63
+ policy: {
64
+ privacyUrl,
65
+ cookiesUrl,
66
+ },
67
+ categories: {
68
+ necessary: {
69
+ required: true,
70
+ label: "Necesarias",
71
+ description: "Cookies y almacenamiento técnico imprescindible para el funcionamiento de la web.",
72
+ },
73
+ },
74
+ services: {},
75
+ };
76
+
77
+ if (secretKey) {
78
+ config.security = { secretKey };
79
+ }
80
+
81
+ if (includeAnalytics) {
82
+ config.categories.analytics = {
83
+ required: false,
84
+ default: false,
85
+ label: "Analítica de uso",
86
+ description: "Nos permite medir el uso del sitio web de forma agregada.",
87
+ };
88
+ config.services.ga4 = {
89
+ category: "analytics",
90
+ label: "Google Analytics 4",
91
+ provider: "Google",
92
+ };
93
+ }
94
+
95
+ if (includeMarketing) {
96
+ config.categories.marketing = {
97
+ required: false,
98
+ default: false,
99
+ label: "Marketing y Vídeo",
100
+ description: "Permite la reproducción de vídeos incrustados y personalización de contenido.",
101
+ };
102
+ config.services.youtube = {
103
+ category: "marketing",
104
+ label: "YouTube Embed",
105
+ provider: "Google",
106
+ };
107
+ }
108
+
109
+ const targetPath = path.join(process.cwd(), "consent.json");
110
+ fs.writeFileSync(targetPath, JSON.stringify(config, null, 2), "utf-8");
111
+
112
+ console.log(`
113
+ ✔ Configuración generada con éxito en: ${targetPath}
114
+
115
+ 🚀 Para usar este archivo en tu web:
116
+ <script src="./consent.min.js" data-config="./consent.json"></script>
117
+ `);
118
+
119
+ rl.close();
120
+ }
121
+
122
+ main().catch((err) => {
123
+ console.error("Error al generar la configuración:", err);
124
+ rl.close();
125
+ process.exit(1);
126
+ });
@@ -0,0 +1,25 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __defProps = Object.defineProperties;
3
+ var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
4
+ var __getOwnPropSymbols = Object.getOwnPropertySymbols;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __propIsEnum = Object.prototype.propertyIsEnumerable;
7
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
8
+ var __spreadValues = (a, b) => {
9
+ for (var prop in b || (b = {}))
10
+ if (__hasOwnProp.call(b, prop))
11
+ __defNormalProp(a, prop, b[prop]);
12
+ if (__getOwnPropSymbols)
13
+ for (var prop of __getOwnPropSymbols(b)) {
14
+ if (__propIsEnum.call(b, prop))
15
+ __defNormalProp(a, prop, b[prop]);
16
+ }
17
+ return a;
18
+ };
19
+ var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
20
+
21
+ export {
22
+ __spreadValues,
23
+ __spreadProps
24
+ };
25
+ //# sourceMappingURL=chunk-DDAAVRWG.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}