@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.
- package/README.md +245 -0
- package/bin/audit-cli.js +25 -0
- package/bin/init-cli.js +126 -0
- package/dist/chunk-DDAAVRWG.js +25 -0
- package/dist/chunk-DDAAVRWG.js.map +1 -0
- package/dist/chunk-LIWFUVWY.js +1858 -0
- package/dist/chunk-LIWFUVWY.js.map +1 -0
- package/dist/consent-engine-BvsH_wqV.d.cts +191 -0
- package/dist/consent-engine-BvsH_wqV.d.ts +191 -0
- package/dist/consent.esm.cjs +2051 -0
- package/dist/consent.esm.cjs.map +1 -0
- package/dist/consent.esm.d.cts +146 -0
- package/dist/consent.esm.d.ts +146 -0
- package/dist/consent.esm.js +184 -0
- package/dist/consent.esm.js.map +1 -0
- package/dist/consent.min.js +589 -0
- package/dist/consent.min.js.map +1 -0
- package/dist/wrappers/angular.cjs +1867 -0
- package/dist/wrappers/angular.cjs.map +1 -0
- package/dist/wrappers/angular.d.cts +20 -0
- package/dist/wrappers/angular.d.ts +20 -0
- package/dist/wrappers/angular.js +42 -0
- package/dist/wrappers/angular.js.map +1 -0
- package/dist/wrappers/next.cjs +1840 -0
- package/dist/wrappers/next.cjs.map +1 -0
- package/dist/wrappers/next.d.cts +3 -0
- package/dist/wrappers/next.d.ts +3 -0
- package/dist/wrappers/next.js +15 -0
- package/dist/wrappers/next.js.map +1 -0
- package/dist/wrappers/react.cjs +1882 -0
- package/dist/wrappers/react.cjs.map +1 -0
- package/dist/wrappers/react.d.cts +4 -0
- package/dist/wrappers/react.d.ts +4 -0
- package/dist/wrappers/react.js +56 -0
- package/dist/wrappers/react.js.map +1 -0
- package/dist/wrappers/wordpress.cjs +64 -0
- package/dist/wrappers/wordpress.cjs.map +1 -0
- package/dist/wrappers/wordpress.d.cts +10 -0
- package/dist/wrappers/wordpress.d.ts +10 -0
- package/dist/wrappers/wordpress.js +41 -0
- package/dist/wrappers/wordpress.js.map +1 -0
- package/package.json +85 -0
- package/schema.json +165 -0
|
@@ -0,0 +1,1882 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __getOwnPropSymbols = Object.getOwnPropertySymbols;
|
|
6
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
7
|
+
var __propIsEnum = Object.prototype.propertyIsEnumerable;
|
|
8
|
+
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
9
|
+
var __spreadValues = (a, b) => {
|
|
10
|
+
for (var prop in b || (b = {}))
|
|
11
|
+
if (__hasOwnProp.call(b, prop))
|
|
12
|
+
__defNormalProp(a, prop, b[prop]);
|
|
13
|
+
if (__getOwnPropSymbols)
|
|
14
|
+
for (var prop of __getOwnPropSymbols(b)) {
|
|
15
|
+
if (__propIsEnum.call(b, prop))
|
|
16
|
+
__defNormalProp(a, prop, b[prop]);
|
|
17
|
+
}
|
|
18
|
+
return a;
|
|
19
|
+
};
|
|
20
|
+
var __export = (target, all) => {
|
|
21
|
+
for (var name in all)
|
|
22
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
23
|
+
};
|
|
24
|
+
var __copyProps = (to, from, except, desc) => {
|
|
25
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
26
|
+
for (let key of __getOwnPropNames(from))
|
|
27
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
28
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
29
|
+
}
|
|
30
|
+
return to;
|
|
31
|
+
};
|
|
32
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
33
|
+
|
|
34
|
+
// src/wrappers/react.ts
|
|
35
|
+
var react_exports = {};
|
|
36
|
+
__export(react_exports, {
|
|
37
|
+
useConsent: () => useConsent,
|
|
38
|
+
useConsentService: () => useConsentService
|
|
39
|
+
});
|
|
40
|
+
module.exports = __toCommonJS(react_exports);
|
|
41
|
+
var import_react = require("react");
|
|
42
|
+
|
|
43
|
+
// src/core/config-validator.ts
|
|
44
|
+
var ConfigValidationError = class extends Error {
|
|
45
|
+
constructor(message) {
|
|
46
|
+
super(`[ConsentSDK Config Error] ${message}`);
|
|
47
|
+
this.name = "ConfigValidationError";
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
function validateConfig(config) {
|
|
51
|
+
if (!config) {
|
|
52
|
+
throw new ConfigValidationError("Configuration object is null or undefined.");
|
|
53
|
+
}
|
|
54
|
+
if (typeof config.schemaVersion !== "number" || config.schemaVersion < 1) {
|
|
55
|
+
throw new ConfigValidationError("Invalid or missing 'schemaVersion'. Expected integer >= 1.");
|
|
56
|
+
}
|
|
57
|
+
if (!config.policyVersion || typeof config.policyVersion !== "string") {
|
|
58
|
+
throw new ConfigValidationError("Invalid or missing 'policyVersion'. String required.");
|
|
59
|
+
}
|
|
60
|
+
if (!config.categories || typeof config.categories !== "object") {
|
|
61
|
+
throw new ConfigValidationError("Missing 'categories' map in configuration.");
|
|
62
|
+
}
|
|
63
|
+
const categoryKeys = Object.keys(config.categories);
|
|
64
|
+
if (categoryKeys.length === 0) {
|
|
65
|
+
throw new ConfigValidationError("At least one category must be defined in 'categories'.");
|
|
66
|
+
}
|
|
67
|
+
const necessaryCategory = config.categories["necessary"];
|
|
68
|
+
if (!necessaryCategory) {
|
|
69
|
+
throw new ConfigValidationError("Category 'necessary' must be defined.");
|
|
70
|
+
}
|
|
71
|
+
if (necessaryCategory.required !== true) {
|
|
72
|
+
throw new ConfigValidationError("Category 'necessary' must have 'required: true'.");
|
|
73
|
+
}
|
|
74
|
+
for (const [catId, catConfig] of Object.entries(config.categories)) {
|
|
75
|
+
if (catId !== "necessary" && !catConfig.required) {
|
|
76
|
+
if (catConfig.default === true) {
|
|
77
|
+
throw new ConfigValidationError(
|
|
78
|
+
`Legal violation (AEPD): Optional category '${catId}' cannot have default: true. All optional categories must be opt-in (default: false).`
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
if (config.services && typeof config.services === "object") {
|
|
84
|
+
for (const [serviceId, serviceConfig] of Object.entries(config.services)) {
|
|
85
|
+
if (!serviceConfig.category) {
|
|
86
|
+
throw new ConfigValidationError(
|
|
87
|
+
`Service '${serviceId}' missing 'category' reference.`
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
if (!config.categories[serviceConfig.category]) {
|
|
91
|
+
throw new ConfigValidationError(
|
|
92
|
+
`Service '${serviceId}' references non-existent category '${serviceConfig.category}'.`
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// src/core/state.ts
|
|
100
|
+
var StateManager = class {
|
|
101
|
+
constructor() {
|
|
102
|
+
this.state = {
|
|
103
|
+
initialized: false,
|
|
104
|
+
policyVersion: "",
|
|
105
|
+
locale: "es",
|
|
106
|
+
receipt: null,
|
|
107
|
+
choices: {}
|
|
108
|
+
};
|
|
109
|
+
this.config = null;
|
|
110
|
+
}
|
|
111
|
+
init(config, choices, receipt) {
|
|
112
|
+
var _a;
|
|
113
|
+
this.config = config;
|
|
114
|
+
this.state = {
|
|
115
|
+
initialized: true,
|
|
116
|
+
policyVersion: config.policyVersion,
|
|
117
|
+
locale: ((_a = config.locale) == null ? void 0 : _a.default) || "es",
|
|
118
|
+
receipt,
|
|
119
|
+
choices
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
getConfig() {
|
|
123
|
+
return this.config;
|
|
124
|
+
}
|
|
125
|
+
getState() {
|
|
126
|
+
return __spreadValues({}, this.state);
|
|
127
|
+
}
|
|
128
|
+
getChoices() {
|
|
129
|
+
return __spreadValues({}, this.state.choices);
|
|
130
|
+
}
|
|
131
|
+
getReceipt() {
|
|
132
|
+
return this.state.receipt;
|
|
133
|
+
}
|
|
134
|
+
hasCategory(category) {
|
|
135
|
+
return this.state.choices[category] === true;
|
|
136
|
+
}
|
|
137
|
+
hasService(serviceId) {
|
|
138
|
+
if (!this.config || !this.config.services) return false;
|
|
139
|
+
const service = this.config.services[serviceId];
|
|
140
|
+
if (!service) return false;
|
|
141
|
+
return this.hasCategory(service.category);
|
|
142
|
+
}
|
|
143
|
+
updateChoices(receipt) {
|
|
144
|
+
this.state.receipt = receipt;
|
|
145
|
+
this.state.choices = __spreadValues({}, receipt.choices);
|
|
146
|
+
}
|
|
147
|
+
clearChoices() {
|
|
148
|
+
if (!this.config) return;
|
|
149
|
+
const resetChoices = {};
|
|
150
|
+
for (const [catId, catConfig] of Object.entries(this.config.categories)) {
|
|
151
|
+
resetChoices[catId] = catConfig.required === true;
|
|
152
|
+
}
|
|
153
|
+
this.state.choices = resetChoices;
|
|
154
|
+
this.state.receipt = null;
|
|
155
|
+
}
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
// src/core/events.ts
|
|
159
|
+
var EventBus = class {
|
|
160
|
+
constructor() {
|
|
161
|
+
this.handlers = /* @__PURE__ */ new Map();
|
|
162
|
+
}
|
|
163
|
+
on(event, handler) {
|
|
164
|
+
if (!this.handlers.has(event)) {
|
|
165
|
+
this.handlers.set(event, /* @__PURE__ */ new Set());
|
|
166
|
+
}
|
|
167
|
+
const set = this.handlers.get(event);
|
|
168
|
+
set.add(handler);
|
|
169
|
+
return () => {
|
|
170
|
+
set.delete(handler);
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
emit(event, detail) {
|
|
174
|
+
const set = this.handlers.get(event);
|
|
175
|
+
if (!set) return;
|
|
176
|
+
for (const handler of set) {
|
|
177
|
+
try {
|
|
178
|
+
handler(detail);
|
|
179
|
+
} catch (err) {
|
|
180
|
+
console.error(`[ConsentSDK Event Error] Handler for '${event}' failed:`, err);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
clear() {
|
|
185
|
+
this.handlers.clear();
|
|
186
|
+
}
|
|
187
|
+
};
|
|
188
|
+
|
|
189
|
+
// src/core/security.ts
|
|
190
|
+
function sha256Sync(ascii) {
|
|
191
|
+
let i, j;
|
|
192
|
+
let result = "";
|
|
193
|
+
const asciiLength = ascii.length * 8;
|
|
194
|
+
let hash = [
|
|
195
|
+
1779033703,
|
|
196
|
+
3144134277,
|
|
197
|
+
1013904242,
|
|
198
|
+
2773480762,
|
|
199
|
+
1359893119,
|
|
200
|
+
2600822924,
|
|
201
|
+
528734635,
|
|
202
|
+
1541459225
|
|
203
|
+
];
|
|
204
|
+
const k = [
|
|
205
|
+
1116352408,
|
|
206
|
+
1899447441,
|
|
207
|
+
3049323471,
|
|
208
|
+
3921009573,
|
|
209
|
+
961987163,
|
|
210
|
+
1508970993,
|
|
211
|
+
2453635748,
|
|
212
|
+
2870763221,
|
|
213
|
+
3624381080,
|
|
214
|
+
310598401,
|
|
215
|
+
607225278,
|
|
216
|
+
1426881987,
|
|
217
|
+
1925078388,
|
|
218
|
+
2162078206,
|
|
219
|
+
2614888103,
|
|
220
|
+
3248222580,
|
|
221
|
+
3835390401,
|
|
222
|
+
4022224774,
|
|
223
|
+
264347078,
|
|
224
|
+
604807628,
|
|
225
|
+
770255983,
|
|
226
|
+
1249150122,
|
|
227
|
+
1555081692,
|
|
228
|
+
1996064986,
|
|
229
|
+
2554220882,
|
|
230
|
+
2821834349,
|
|
231
|
+
2952996808,
|
|
232
|
+
3210313671,
|
|
233
|
+
3336571891,
|
|
234
|
+
3584528711,
|
|
235
|
+
113926993,
|
|
236
|
+
338241895,
|
|
237
|
+
666307205,
|
|
238
|
+
773529912,
|
|
239
|
+
1294757372,
|
|
240
|
+
1396182291,
|
|
241
|
+
1695183700,
|
|
242
|
+
1986661051,
|
|
243
|
+
2177026350,
|
|
244
|
+
2456956037,
|
|
245
|
+
2730485921,
|
|
246
|
+
2820302411,
|
|
247
|
+
3259730800,
|
|
248
|
+
3345764771,
|
|
249
|
+
3516065817,
|
|
250
|
+
3600352804,
|
|
251
|
+
4094571909,
|
|
252
|
+
275423344,
|
|
253
|
+
430227734,
|
|
254
|
+
506948616,
|
|
255
|
+
659060556,
|
|
256
|
+
883997877,
|
|
257
|
+
958139571,
|
|
258
|
+
1322822218,
|
|
259
|
+
1537002063,
|
|
260
|
+
1747873779,
|
|
261
|
+
1955562222,
|
|
262
|
+
2024104815,
|
|
263
|
+
2227730452,
|
|
264
|
+
2361852424,
|
|
265
|
+
2428436474,
|
|
266
|
+
2756734187,
|
|
267
|
+
3204031479,
|
|
268
|
+
3329325298
|
|
269
|
+
];
|
|
270
|
+
const blocks = [];
|
|
271
|
+
for (i = 0; i < ascii.length; i++) {
|
|
272
|
+
blocks[i >> 2] |= ascii.charCodeAt(i) << 24 - i % 4 * 8;
|
|
273
|
+
}
|
|
274
|
+
blocks[asciiLength >> 5] |= 128 << 24 - asciiLength % 32;
|
|
275
|
+
blocks[(asciiLength + 64 >> 9 << 4) + 15] = asciiLength;
|
|
276
|
+
for (i = 0; i < blocks.length; i += 16) {
|
|
277
|
+
const w = blocks.slice(i, i + 16);
|
|
278
|
+
const oldHash = [...hash];
|
|
279
|
+
for (j = 0; j < 64; j++) {
|
|
280
|
+
const w15 = w[j - 15], w2 = w[j - 2];
|
|
281
|
+
const s0 = (w15 >>> 7 | w15 << 25) ^ (w15 >>> 18 | w15 << 14) ^ w15 >>> 3;
|
|
282
|
+
const s1 = (w2 >>> 17 | w2 << 15) ^ (w2 >>> 19 | w2 << 13) ^ w2 >>> 10;
|
|
283
|
+
w[j] = j < 16 ? w[j] || 0 : w[j - 16] + s0 + w[j - 7] + s1 | 0;
|
|
284
|
+
const ch = hash[4] & hash[5] ^ ~hash[4] & hash[6];
|
|
285
|
+
const maj = hash[0] & hash[1] ^ hash[0] & hash[2] ^ hash[1] & hash[2];
|
|
286
|
+
const sig0 = (hash[0] >>> 2 | hash[0] << 30) ^ (hash[0] >>> 13 | hash[0] << 19) ^ (hash[0] >>> 22 | hash[0] << 10);
|
|
287
|
+
const sig1 = (hash[4] >>> 6 | hash[4] << 26) ^ (hash[4] >>> 11 | hash[4] << 21) ^ (hash[4] >>> 25 | hash[4] << 7);
|
|
288
|
+
const t1 = hash[7] + sig1 + ch + k[j] + (w[j] | 0);
|
|
289
|
+
const t2 = sig0 + maj;
|
|
290
|
+
hash[7] = hash[6];
|
|
291
|
+
hash[6] = hash[5];
|
|
292
|
+
hash[5] = hash[4];
|
|
293
|
+
hash[4] = hash[3] + t1 | 0;
|
|
294
|
+
hash[3] = hash[2];
|
|
295
|
+
hash[2] = hash[1];
|
|
296
|
+
hash[1] = hash[0];
|
|
297
|
+
hash[0] = t1 + t2 | 0;
|
|
298
|
+
}
|
|
299
|
+
for (j = 0; j < 8; j++) {
|
|
300
|
+
hash[j] = hash[j] + oldHash[j] | 0;
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
for (i = 0; i < 8; i++) {
|
|
304
|
+
for (j = 3; j >= 0; j--) {
|
|
305
|
+
const b = hash[i] >> j * 8 & 255;
|
|
306
|
+
result += (b < 16 ? "0" : "") + b.toString(16);
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
return result;
|
|
310
|
+
}
|
|
311
|
+
function computeReceiptSignature(payload, secretKey) {
|
|
312
|
+
if (secretKey) {
|
|
313
|
+
return sha256Sync(`${secretKey}:${payload}:${secretKey}`);
|
|
314
|
+
}
|
|
315
|
+
let hash = 2166136261;
|
|
316
|
+
for (let i = 0; i < payload.length; i++) {
|
|
317
|
+
hash ^= payload.charCodeAt(i);
|
|
318
|
+
hash = Math.imul(hash, 16777619);
|
|
319
|
+
}
|
|
320
|
+
return (hash >>> 0).toString(16);
|
|
321
|
+
}
|
|
322
|
+
function sanitizeHtml(str) {
|
|
323
|
+
if (!str) return "";
|
|
324
|
+
return str.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
325
|
+
}
|
|
326
|
+
function sanitizeUrl(url) {
|
|
327
|
+
if (!url) return "#";
|
|
328
|
+
const trimmed = url.trim().toLowerCase();
|
|
329
|
+
if (trimmed.startsWith("javascript:") || trimmed.startsWith("data:") || trimmed.startsWith("vbscript:")) {
|
|
330
|
+
return "#";
|
|
331
|
+
}
|
|
332
|
+
return url;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
// src/core/receipt.ts
|
|
336
|
+
function generateUUID() {
|
|
337
|
+
if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") {
|
|
338
|
+
return crypto.randomUUID();
|
|
339
|
+
}
|
|
340
|
+
return "xxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) => {
|
|
341
|
+
const r = Math.random() * 16 | 0;
|
|
342
|
+
const v = c === "x" ? r : r & 3 | 8;
|
|
343
|
+
return v.toString(16);
|
|
344
|
+
});
|
|
345
|
+
}
|
|
346
|
+
function createReceipt(policyVersion, choices, source, existingReceipt, secretKey) {
|
|
347
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
348
|
+
const receiptId = (existingReceipt == null ? void 0 : existingReceipt.receiptId) || generateUUID();
|
|
349
|
+
const decidedAt = (existingReceipt == null ? void 0 : existingReceipt.decidedAt) || now;
|
|
350
|
+
const payloadToSign = `${receiptId}:${policyVersion}:${JSON.stringify(choices)}`;
|
|
351
|
+
const signature = computeReceiptSignature(payloadToSign, secretKey);
|
|
352
|
+
return {
|
|
353
|
+
schema: 1,
|
|
354
|
+
receiptId,
|
|
355
|
+
policyVersion,
|
|
356
|
+
decidedAt,
|
|
357
|
+
updatedAt: now,
|
|
358
|
+
source,
|
|
359
|
+
choices,
|
|
360
|
+
signature
|
|
361
|
+
};
|
|
362
|
+
}
|
|
363
|
+
function parseReceipt(jsonStr) {
|
|
364
|
+
try {
|
|
365
|
+
const data = JSON.parse(jsonStr);
|
|
366
|
+
if (typeof data === "object" && data !== null && typeof data.receiptId === "string" && typeof data.policyVersion === "string" && typeof data.choices === "object") {
|
|
367
|
+
return data;
|
|
368
|
+
}
|
|
369
|
+
return null;
|
|
370
|
+
} catch (e) {
|
|
371
|
+
return null;
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
function isReceiptExpired(receipt, maxAgeDays = 365) {
|
|
375
|
+
if (!receipt.updatedAt) return true;
|
|
376
|
+
const updated = new Date(receipt.updatedAt).getTime();
|
|
377
|
+
const now = Date.now();
|
|
378
|
+
const maxAgeMs = maxAgeDays * 86400 * 1e3;
|
|
379
|
+
return now - updated > maxAgeMs;
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
// src/core/policy-engine.ts
|
|
383
|
+
function evaluatePolicy(config, receipt) {
|
|
384
|
+
var _a, _b, _c;
|
|
385
|
+
const defaultChoices = {};
|
|
386
|
+
for (const [catId, catConfig] of Object.entries(config.categories)) {
|
|
387
|
+
defaultChoices[catId] = catConfig.required === true;
|
|
388
|
+
}
|
|
389
|
+
if (!receipt) {
|
|
390
|
+
return {
|
|
391
|
+
isValid: false,
|
|
392
|
+
reason: "missing",
|
|
393
|
+
choices: defaultChoices
|
|
394
|
+
};
|
|
395
|
+
}
|
|
396
|
+
if (receipt.policyVersion !== config.policyVersion) {
|
|
397
|
+
return {
|
|
398
|
+
isValid: false,
|
|
399
|
+
reason: "policy_version_changed",
|
|
400
|
+
choices: defaultChoices
|
|
401
|
+
};
|
|
402
|
+
}
|
|
403
|
+
const maxAgeDays = (_b = (_a = config.consent) == null ? void 0 : _a.maxAgeDays) != null ? _b : 365;
|
|
404
|
+
if (isReceiptExpired(receipt, maxAgeDays)) {
|
|
405
|
+
return {
|
|
406
|
+
isValid: false,
|
|
407
|
+
reason: "expired",
|
|
408
|
+
choices: defaultChoices
|
|
409
|
+
};
|
|
410
|
+
}
|
|
411
|
+
if (receipt.signature) {
|
|
412
|
+
const payloadToSign = `${receipt.receiptId}:${receipt.policyVersion}:${JSON.stringify(receipt.choices)}`;
|
|
413
|
+
const expectedSignature = computeReceiptSignature(payloadToSign, (_c = config.security) == null ? void 0 : _c.secretKey);
|
|
414
|
+
if (receipt.signature !== expectedSignature) {
|
|
415
|
+
return {
|
|
416
|
+
isValid: false,
|
|
417
|
+
reason: "tampered",
|
|
418
|
+
choices: defaultChoices
|
|
419
|
+
};
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
const activeChoices = __spreadValues({}, defaultChoices);
|
|
423
|
+
for (const catId of Object.keys(config.categories)) {
|
|
424
|
+
if (catId === "necessary") {
|
|
425
|
+
activeChoices[catId] = true;
|
|
426
|
+
} else if (typeof receipt.choices[catId] === "boolean") {
|
|
427
|
+
activeChoices[catId] = receipt.choices[catId];
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
return {
|
|
431
|
+
isValid: true,
|
|
432
|
+
reason: "valid",
|
|
433
|
+
choices: activeChoices
|
|
434
|
+
};
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
// src/storage/cookie-store.ts
|
|
438
|
+
var CookieStore = class {
|
|
439
|
+
static get(name) {
|
|
440
|
+
if (typeof document === "undefined") return null;
|
|
441
|
+
const nameEQ = encodeURIComponent(name) + "=";
|
|
442
|
+
const ca = document.cookie.split(";");
|
|
443
|
+
for (let i = 0; i < ca.length; i++) {
|
|
444
|
+
let c = ca[i];
|
|
445
|
+
while (c.charAt(0) === " ") c = c.substring(1, c.length);
|
|
446
|
+
if (c.indexOf(nameEQ) === 0) {
|
|
447
|
+
return decodeURIComponent(c.substring(nameEQ.length, c.length));
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
return null;
|
|
451
|
+
}
|
|
452
|
+
static set(name, value, options = {}) {
|
|
453
|
+
var _a, _b;
|
|
454
|
+
if (typeof document === "undefined") return;
|
|
455
|
+
const path = options.path || "/";
|
|
456
|
+
const maxAgeDays = (_a = options.maxAgeDays) != null ? _a : 365;
|
|
457
|
+
const maxAgeSeconds = Math.floor(maxAgeDays * 86400);
|
|
458
|
+
const sameSite = options.sameSite || "Lax";
|
|
459
|
+
const isHttps = typeof window !== "undefined" && window.location.protocol === "https:";
|
|
460
|
+
const secure = (_b = options.secure) != null ? _b : isHttps;
|
|
461
|
+
let cookieStr = `${encodeURIComponent(name)}=${encodeURIComponent(value)}; Path=${path}; Max-Age=${maxAgeSeconds}; SameSite=${sameSite}`;
|
|
462
|
+
if (secure) {
|
|
463
|
+
cookieStr += "; Secure";
|
|
464
|
+
}
|
|
465
|
+
document.cookie = cookieStr;
|
|
466
|
+
}
|
|
467
|
+
static remove(name, path = "/") {
|
|
468
|
+
if (typeof document === "undefined") return;
|
|
469
|
+
document.cookie = `${encodeURIComponent(name)}=; Path=${path}; Max-Age=0; SameSite=Lax`;
|
|
470
|
+
}
|
|
471
|
+
};
|
|
472
|
+
|
|
473
|
+
// src/storage/memory-store.ts
|
|
474
|
+
var _MemoryStorageProvider = class _MemoryStorageProvider {
|
|
475
|
+
get(key) {
|
|
476
|
+
var _a;
|
|
477
|
+
return (_a = _MemoryStorageProvider.store.get(key)) != null ? _a : null;
|
|
478
|
+
}
|
|
479
|
+
set(key, value) {
|
|
480
|
+
_MemoryStorageProvider.store.set(key, value);
|
|
481
|
+
}
|
|
482
|
+
remove(key) {
|
|
483
|
+
_MemoryStorageProvider.store.delete(key);
|
|
484
|
+
}
|
|
485
|
+
clear() {
|
|
486
|
+
_MemoryStorageProvider.store.clear();
|
|
487
|
+
}
|
|
488
|
+
};
|
|
489
|
+
_MemoryStorageProvider.store = /* @__PURE__ */ new Map();
|
|
490
|
+
var MemoryStorageProvider = _MemoryStorageProvider;
|
|
491
|
+
var MemoryStore = {
|
|
492
|
+
get: (name) => new MemoryStorageProvider().get(name),
|
|
493
|
+
set: (name, val) => new MemoryStorageProvider().set(name, val),
|
|
494
|
+
remove: (name) => new MemoryStorageProvider().remove(name),
|
|
495
|
+
clear: () => new MemoryStorageProvider().clear()
|
|
496
|
+
};
|
|
497
|
+
|
|
498
|
+
// src/blocker/script-gate.ts
|
|
499
|
+
var ScriptGate = class {
|
|
500
|
+
static scanAndActivate(isCategoryAllowed, isServiceAllowed, onServiceLoaded) {
|
|
501
|
+
var _a;
|
|
502
|
+
if (typeof document === "undefined") return;
|
|
503
|
+
const blockedScripts = Array.from(
|
|
504
|
+
document.querySelectorAll(
|
|
505
|
+
'script[type="text/plain"][data-consent], script[type="text/plain"][data-service]'
|
|
506
|
+
)
|
|
507
|
+
);
|
|
508
|
+
for (const oldScript of blockedScripts) {
|
|
509
|
+
if (this.executedScripts.has(oldScript)) continue;
|
|
510
|
+
const category = oldScript.getAttribute("data-consent");
|
|
511
|
+
const serviceId = oldScript.getAttribute("data-service");
|
|
512
|
+
let isAllowed = false;
|
|
513
|
+
if (serviceId) {
|
|
514
|
+
isAllowed = isServiceAllowed(serviceId);
|
|
515
|
+
} else if (category) {
|
|
516
|
+
isAllowed = isCategoryAllowed(category);
|
|
517
|
+
}
|
|
518
|
+
if (!isAllowed) continue;
|
|
519
|
+
this.executedScripts.add(oldScript);
|
|
520
|
+
const newScript = document.createElement("script");
|
|
521
|
+
for (let i = 0; i < oldScript.attributes.length; i++) {
|
|
522
|
+
const attr = oldScript.attributes[i];
|
|
523
|
+
if (attr.name === "type") continue;
|
|
524
|
+
if (attr.name === "data-src") {
|
|
525
|
+
newScript.src = attr.value;
|
|
526
|
+
continue;
|
|
527
|
+
}
|
|
528
|
+
newScript.setAttribute(attr.name, attr.value);
|
|
529
|
+
}
|
|
530
|
+
newScript.type = "text/javascript";
|
|
531
|
+
if (!newScript.src && oldScript.textContent) {
|
|
532
|
+
newScript.textContent = oldScript.textContent;
|
|
533
|
+
}
|
|
534
|
+
(_a = oldScript.parentNode) == null ? void 0 : _a.replaceChild(newScript, oldScript);
|
|
535
|
+
if (serviceId || category) {
|
|
536
|
+
onServiceLoaded == null ? void 0 : onServiceLoaded(serviceId || category, category || "custom");
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
};
|
|
541
|
+
ScriptGate.executedScripts = /* @__PURE__ */ new Set();
|
|
542
|
+
|
|
543
|
+
// src/blocker/iframe-gate.ts
|
|
544
|
+
var IframeGate = class {
|
|
545
|
+
static processIframes(isCategoryAllowed, isServiceAllowed, options = {}) {
|
|
546
|
+
if (typeof document === "undefined") return;
|
|
547
|
+
const iframes = Array.from(
|
|
548
|
+
document.querySelectorAll(
|
|
549
|
+
"iframe[data-consent], iframe[data-service]"
|
|
550
|
+
)
|
|
551
|
+
);
|
|
552
|
+
for (const iframe of iframes) {
|
|
553
|
+
const category = iframe.getAttribute("data-consent");
|
|
554
|
+
const serviceId = iframe.getAttribute("data-service");
|
|
555
|
+
let isAllowed = false;
|
|
556
|
+
if (serviceId) {
|
|
557
|
+
isAllowed = isServiceAllowed(serviceId);
|
|
558
|
+
} else if (category) {
|
|
559
|
+
isAllowed = isCategoryAllowed(category);
|
|
560
|
+
}
|
|
561
|
+
const originalSrc = iframe.getAttribute("data-src") || iframe.getAttribute("src");
|
|
562
|
+
if (!originalSrc) continue;
|
|
563
|
+
if (!iframe.getAttribute("data-src")) {
|
|
564
|
+
iframe.setAttribute("data-src", originalSrc);
|
|
565
|
+
}
|
|
566
|
+
if (isAllowed) {
|
|
567
|
+
this.unlockIframe(iframe, originalSrc);
|
|
568
|
+
} else {
|
|
569
|
+
this.lockIframe(
|
|
570
|
+
iframe,
|
|
571
|
+
category || "marketing",
|
|
572
|
+
serviceId || void 0,
|
|
573
|
+
options
|
|
574
|
+
);
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
static lockIframe(iframe, category, serviceId, options) {
|
|
579
|
+
var _a;
|
|
580
|
+
if (iframe.hasAttribute("src")) {
|
|
581
|
+
iframe.removeAttribute("src");
|
|
582
|
+
}
|
|
583
|
+
if (this.placeholders.has(iframe)) return;
|
|
584
|
+
const container = document.createElement("div");
|
|
585
|
+
container.className = "consent-iframe-placeholder";
|
|
586
|
+
container.style.cssText = `
|
|
587
|
+
width: ${iframe.width ? iframe.width.endsWith("%") || iframe.width.endsWith("px") ? iframe.width : iframe.width + "px" : "100%"};
|
|
588
|
+
height: ${iframe.height ? iframe.height.endsWith("%") || iframe.height.endsWith("px") ? iframe.height : iframe.height + "px" : "315px"};
|
|
589
|
+
min-height: 200px;
|
|
590
|
+
background: #0f172a;
|
|
591
|
+
color: #f8fafc;
|
|
592
|
+
display: flex;
|
|
593
|
+
flex-direction: column;
|
|
594
|
+
align-items: center;
|
|
595
|
+
justify-content: center;
|
|
596
|
+
text-align: center;
|
|
597
|
+
padding: 1.5rem;
|
|
598
|
+
border-radius: 8px;
|
|
599
|
+
box-sizing: border-box;
|
|
600
|
+
font-family: system-ui, -apple-system, sans-serif;
|
|
601
|
+
`;
|
|
602
|
+
const titleText = serviceId ? `Contenido de ${serviceId} bloqueado` : "Contenido externo bloqueado";
|
|
603
|
+
const bodyText = "Este contenido est\xE1 bloqueado hasta que autorices esta finalidad de privacidad.";
|
|
604
|
+
container.innerHTML = `
|
|
605
|
+
<div style="font-weight: 600; font-size: 1.1rem; margin-bottom: 0.5rem; color: #f8fafc;">
|
|
606
|
+
${titleText}
|
|
607
|
+
</div>
|
|
608
|
+
<p style="font-size: 0.9rem; color: #94a3b8; margin: 0 0 1rem 0; max-width: 400px;">
|
|
609
|
+
${bodyText}
|
|
610
|
+
</p>
|
|
611
|
+
<button type="button" class="consent-placeholder-allow-btn" style="
|
|
612
|
+
background: #2563eb;
|
|
613
|
+
color: #ffffff;
|
|
614
|
+
border: none;
|
|
615
|
+
padding: 0.6rem 1.2rem;
|
|
616
|
+
border-radius: 6px;
|
|
617
|
+
font-weight: 500;
|
|
618
|
+
cursor: pointer;
|
|
619
|
+
font-size: 0.9rem;
|
|
620
|
+
transition: background 0.2s ease;
|
|
621
|
+
">
|
|
622
|
+
Permitir y mostrar contenido
|
|
623
|
+
</button>
|
|
624
|
+
`;
|
|
625
|
+
const button = container.querySelector(
|
|
626
|
+
".consent-placeholder-allow-btn"
|
|
627
|
+
);
|
|
628
|
+
button == null ? void 0 : button.addEventListener("click", () => {
|
|
629
|
+
var _a2;
|
|
630
|
+
(_a2 = options.onAllowClick) == null ? void 0 : _a2.call(options, category, serviceId);
|
|
631
|
+
});
|
|
632
|
+
iframe.style.display = "none";
|
|
633
|
+
(_a = iframe.parentNode) == null ? void 0 : _a.insertBefore(container, iframe);
|
|
634
|
+
this.placeholders.set(iframe, container);
|
|
635
|
+
}
|
|
636
|
+
static unlockIframe(iframe, src) {
|
|
637
|
+
var _a;
|
|
638
|
+
if (iframe.getAttribute("src") !== src) {
|
|
639
|
+
iframe.setAttribute("src", src);
|
|
640
|
+
}
|
|
641
|
+
iframe.style.display = "";
|
|
642
|
+
const placeholder = this.placeholders.get(iframe);
|
|
643
|
+
if (placeholder) {
|
|
644
|
+
(_a = placeholder.parentNode) == null ? void 0 : _a.removeChild(placeholder);
|
|
645
|
+
this.placeholders.delete(iframe);
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
};
|
|
649
|
+
IframeGate.placeholders = /* @__PURE__ */ new Map();
|
|
650
|
+
|
|
651
|
+
// src/blocker/resource-gate.ts
|
|
652
|
+
var ResourceGate = class {
|
|
653
|
+
static processImages(isCategoryAllowed, isServiceAllowed) {
|
|
654
|
+
if (typeof document === "undefined") return;
|
|
655
|
+
const images = Array.from(
|
|
656
|
+
document.querySelectorAll(
|
|
657
|
+
"img[data-consent], img[data-service]"
|
|
658
|
+
)
|
|
659
|
+
);
|
|
660
|
+
for (const img of images) {
|
|
661
|
+
const category = img.getAttribute("data-consent");
|
|
662
|
+
const serviceId = img.getAttribute("data-service");
|
|
663
|
+
let isAllowed = false;
|
|
664
|
+
if (serviceId) {
|
|
665
|
+
isAllowed = isServiceAllowed(serviceId);
|
|
666
|
+
} else if (category) {
|
|
667
|
+
isAllowed = isCategoryAllowed(category);
|
|
668
|
+
}
|
|
669
|
+
const originalSrc = img.getAttribute("data-src") || img.getAttribute("src");
|
|
670
|
+
if (!originalSrc) continue;
|
|
671
|
+
if (!img.getAttribute("data-src")) {
|
|
672
|
+
img.setAttribute("data-src", originalSrc);
|
|
673
|
+
}
|
|
674
|
+
if (isAllowed) {
|
|
675
|
+
if (img.getAttribute("src") !== originalSrc) {
|
|
676
|
+
img.setAttribute("src", originalSrc);
|
|
677
|
+
}
|
|
678
|
+
} else {
|
|
679
|
+
if (img.hasAttribute("src")) {
|
|
680
|
+
img.removeAttribute("src");
|
|
681
|
+
}
|
|
682
|
+
}
|
|
683
|
+
}
|
|
684
|
+
}
|
|
685
|
+
};
|
|
686
|
+
|
|
687
|
+
// src/blocker/registry.ts
|
|
688
|
+
var BlockerRegistry = class {
|
|
689
|
+
constructor() {
|
|
690
|
+
this.observer = null;
|
|
691
|
+
}
|
|
692
|
+
init(isCategoryAllowed, isServiceAllowed, iframeOptions, onServiceLoaded) {
|
|
693
|
+
const runBlockers = () => {
|
|
694
|
+
ScriptGate.scanAndActivate(
|
|
695
|
+
isCategoryAllowed,
|
|
696
|
+
isServiceAllowed,
|
|
697
|
+
onServiceLoaded
|
|
698
|
+
);
|
|
699
|
+
IframeGate.processIframes(
|
|
700
|
+
isCategoryAllowed,
|
|
701
|
+
isServiceAllowed,
|
|
702
|
+
iframeOptions
|
|
703
|
+
);
|
|
704
|
+
ResourceGate.processImages(isCategoryAllowed, isServiceAllowed);
|
|
705
|
+
};
|
|
706
|
+
runBlockers();
|
|
707
|
+
if (typeof MutationObserver !== "undefined" && typeof document !== "undefined") {
|
|
708
|
+
this.observer = new MutationObserver(() => {
|
|
709
|
+
runBlockers();
|
|
710
|
+
});
|
|
711
|
+
this.observer.observe(document.documentElement || document.body, {
|
|
712
|
+
childList: true,
|
|
713
|
+
subtree: true
|
|
714
|
+
});
|
|
715
|
+
}
|
|
716
|
+
}
|
|
717
|
+
destroy() {
|
|
718
|
+
if (this.observer) {
|
|
719
|
+
this.observer.disconnect();
|
|
720
|
+
this.observer = null;
|
|
721
|
+
}
|
|
722
|
+
}
|
|
723
|
+
};
|
|
724
|
+
|
|
725
|
+
// src/services/google.ts
|
|
726
|
+
var GoogleConsentAdapter = class {
|
|
727
|
+
static initDefault(_configMap) {
|
|
728
|
+
if (typeof window === "undefined") return;
|
|
729
|
+
window.dataLayer = window.dataLayer || [];
|
|
730
|
+
if (!window.gtag) {
|
|
731
|
+
window.gtag = function() {
|
|
732
|
+
var _a;
|
|
733
|
+
(_a = window.dataLayer) == null ? void 0 : _a.push(arguments);
|
|
734
|
+
};
|
|
735
|
+
}
|
|
736
|
+
if (!this.initialized) {
|
|
737
|
+
window.gtag("consent", "default", {
|
|
738
|
+
analytics_storage: "denied",
|
|
739
|
+
ad_storage: "denied",
|
|
740
|
+
ad_user_data: "denied",
|
|
741
|
+
ad_personalization: "denied",
|
|
742
|
+
wait_for_update: 500
|
|
743
|
+
});
|
|
744
|
+
this.initialized = true;
|
|
745
|
+
}
|
|
746
|
+
}
|
|
747
|
+
static update(choices, configMap) {
|
|
748
|
+
if (typeof window === "undefined") return;
|
|
749
|
+
this.initDefault(configMap);
|
|
750
|
+
const analyticsCategory = (configMap == null ? void 0 : configMap.analytics_storage) || "analytics";
|
|
751
|
+
const marketingCategory = (configMap == null ? void 0 : configMap.ad_storage) || "marketing";
|
|
752
|
+
const isAnalyticsAllowed = choices[analyticsCategory] === true;
|
|
753
|
+
const isMarketingAllowed = choices[marketingCategory] === true;
|
|
754
|
+
window.gtag("consent", "update", {
|
|
755
|
+
analytics_storage: isAnalyticsAllowed ? "granted" : "denied",
|
|
756
|
+
ad_storage: isMarketingAllowed ? "granted" : "denied",
|
|
757
|
+
ad_user_data: isMarketingAllowed ? "granted" : "denied",
|
|
758
|
+
ad_personalization: isMarketingAllowed ? "granted" : "denied"
|
|
759
|
+
});
|
|
760
|
+
}
|
|
761
|
+
};
|
|
762
|
+
GoogleConsentAdapter.initialized = false;
|
|
763
|
+
|
|
764
|
+
// src/services/custom.ts
|
|
765
|
+
var CustomServiceAdapter = class {
|
|
766
|
+
static createWhen(eventBus, isCategoryAllowed, isServiceAllowed) {
|
|
767
|
+
return function when(categoryOrService, callback) {
|
|
768
|
+
if (isCategoryAllowed(categoryOrService) || isServiceAllowed(categoryOrService)) {
|
|
769
|
+
try {
|
|
770
|
+
callback();
|
|
771
|
+
} catch (err) {
|
|
772
|
+
console.error(`[ConsentSDK when()] Callback execution failed:`, err);
|
|
773
|
+
}
|
|
774
|
+
}
|
|
775
|
+
return eventBus.on("consent:changed", ({ choices }) => {
|
|
776
|
+
if (choices[categoryOrService] === true || isServiceAllowed(categoryOrService)) {
|
|
777
|
+
try {
|
|
778
|
+
callback();
|
|
779
|
+
} catch (err) {
|
|
780
|
+
console.error(`[ConsentSDK when()] Callback execution failed:`, err);
|
|
781
|
+
}
|
|
782
|
+
}
|
|
783
|
+
});
|
|
784
|
+
};
|
|
785
|
+
}
|
|
786
|
+
};
|
|
787
|
+
|
|
788
|
+
// src/ui/styles.ts
|
|
789
|
+
function injectStyles(nonce) {
|
|
790
|
+
if (typeof document === "undefined") return;
|
|
791
|
+
const existing = document.getElementById("consent-sdk-styles");
|
|
792
|
+
if (existing) {
|
|
793
|
+
existing.remove();
|
|
794
|
+
}
|
|
795
|
+
const styleEl = document.createElement("style");
|
|
796
|
+
styleEl.id = "consent-sdk-styles";
|
|
797
|
+
if (nonce) {
|
|
798
|
+
styleEl.setAttribute("nonce", nonce);
|
|
799
|
+
}
|
|
800
|
+
styleEl.textContent = `
|
|
801
|
+
:root {
|
|
802
|
+
--consent-bg: #ffffff;
|
|
803
|
+
--consent-fg: #0f172a;
|
|
804
|
+
--consent-muted: #64748b;
|
|
805
|
+
--consent-divider: #f1f5f9;
|
|
806
|
+
--consent-border: #e2e8f0;
|
|
807
|
+
--consent-hover-bg: #f8fafc;
|
|
808
|
+
--consent-primary: #0f172a;
|
|
809
|
+
--consent-primary-hover: #1e293b;
|
|
810
|
+
--consent-primary-fg: #ffffff;
|
|
811
|
+
--consent-secondary: #f8fafc;
|
|
812
|
+
--consent-secondary-hover: #f1f5f9;
|
|
813
|
+
--consent-secondary-fg: #0f172a;
|
|
814
|
+
--consent-accent: #2563eb;
|
|
815
|
+
--consent-focus-ring: rgba(37, 99, 235, 0.3);
|
|
816
|
+
--consent-font: system-ui, -apple-system, BlinkMacSystemFont, "SF Pro Display", "SF Pro Text", "Segoe UI", Roboto, sans-serif;
|
|
817
|
+
--consent-modal-shadow: 0 32px 64px -16px rgba(15, 23, 42, 0.2), 0 0 1px rgba(15, 23, 42, 0.12);
|
|
818
|
+
}
|
|
819
|
+
|
|
820
|
+
@media (prefers-color-scheme: dark) {
|
|
821
|
+
:root {
|
|
822
|
+
--consent-bg: #0f172a;
|
|
823
|
+
--consent-fg: #f8fafc;
|
|
824
|
+
--consent-muted: #94a3b8;
|
|
825
|
+
--consent-divider: #1e293b;
|
|
826
|
+
--consent-border: #334155;
|
|
827
|
+
--consent-hover-bg: #1e293b;
|
|
828
|
+
--consent-primary: #f8fafc;
|
|
829
|
+
--consent-primary-hover: #ffffff;
|
|
830
|
+
--consent-primary-fg: #0f172a;
|
|
831
|
+
--consent-secondary: #1e293b;
|
|
832
|
+
--consent-secondary-hover: #334155;
|
|
833
|
+
--consent-secondary-fg: #f8fafc;
|
|
834
|
+
--consent-modal-shadow: 0 32px 64px -16px rgba(0, 0, 0, 0.6), 0 0 1px rgba(255, 255, 255, 0.12);
|
|
835
|
+
}
|
|
836
|
+
}
|
|
837
|
+
|
|
838
|
+
@keyframes consentSlideUp {
|
|
839
|
+
from {
|
|
840
|
+
opacity: 0;
|
|
841
|
+
transform: translateY(16px) scale(0.985);
|
|
842
|
+
}
|
|
843
|
+
to {
|
|
844
|
+
opacity: 1;
|
|
845
|
+
transform: translateY(0) scale(1);
|
|
846
|
+
}
|
|
847
|
+
}
|
|
848
|
+
|
|
849
|
+
@keyframes consentFadeIn {
|
|
850
|
+
from { opacity: 0; }
|
|
851
|
+
to { opacity: 1; }
|
|
852
|
+
}
|
|
853
|
+
|
|
854
|
+
/* 1st Layer Banner - Clean & Seamless */
|
|
855
|
+
.consent-banner-wrapper {
|
|
856
|
+
position: fixed;
|
|
857
|
+
bottom: 1.25rem;
|
|
858
|
+
left: 1.25rem;
|
|
859
|
+
right: 1.25rem;
|
|
860
|
+
max-width: 1060px;
|
|
861
|
+
margin: 0 auto;
|
|
862
|
+
z-index: 2147483645;
|
|
863
|
+
background: var(--consent-bg);
|
|
864
|
+
color: var(--consent-fg);
|
|
865
|
+
border: 1px solid var(--consent-border);
|
|
866
|
+
border-radius: 20px;
|
|
867
|
+
box-shadow: var(--consent-modal-shadow);
|
|
868
|
+
padding: 1.5rem 1.75rem;
|
|
869
|
+
font-family: var(--consent-font);
|
|
870
|
+
font-size: 0.92rem;
|
|
871
|
+
line-height: 1.5;
|
|
872
|
+
animation: consentSlideUp 0.3s cubic-bezier(0.16, 1, 0.3, 1) forwards;
|
|
873
|
+
box-sizing: border-box;
|
|
874
|
+
}
|
|
875
|
+
|
|
876
|
+
.consent-banner-container {
|
|
877
|
+
display: flex;
|
|
878
|
+
flex-direction: column;
|
|
879
|
+
gap: 1.25rem;
|
|
880
|
+
}
|
|
881
|
+
|
|
882
|
+
@media (min-width: 860px) {
|
|
883
|
+
.consent-banner-container {
|
|
884
|
+
flex-direction: row;
|
|
885
|
+
align-items: center;
|
|
886
|
+
justify-content: space-between;
|
|
887
|
+
}
|
|
888
|
+
}
|
|
889
|
+
|
|
890
|
+
.consent-banner-text {
|
|
891
|
+
flex: 1;
|
|
892
|
+
}
|
|
893
|
+
|
|
894
|
+
.consent-banner-title {
|
|
895
|
+
font-size: 1.1rem;
|
|
896
|
+
font-weight: 700;
|
|
897
|
+
letter-spacing: -0.015em;
|
|
898
|
+
margin: 0 0 0.35rem 0;
|
|
899
|
+
color: var(--consent-fg);
|
|
900
|
+
display: flex;
|
|
901
|
+
align-items: center;
|
|
902
|
+
gap: 0.5rem;
|
|
903
|
+
}
|
|
904
|
+
|
|
905
|
+
.consent-banner-title-icon {
|
|
906
|
+
display: inline-flex;
|
|
907
|
+
align-items: center;
|
|
908
|
+
justify-content: center;
|
|
909
|
+
width: 22px;
|
|
910
|
+
height: 22px;
|
|
911
|
+
border-radius: 6px;
|
|
912
|
+
background: rgba(37, 99, 235, 0.1);
|
|
913
|
+
color: var(--consent-accent);
|
|
914
|
+
}
|
|
915
|
+
|
|
916
|
+
.consent-banner-desc {
|
|
917
|
+
margin: 0;
|
|
918
|
+
color: var(--consent-muted);
|
|
919
|
+
font-size: 0.88rem;
|
|
920
|
+
max-width: 720px;
|
|
921
|
+
}
|
|
922
|
+
|
|
923
|
+
.consent-banner-links {
|
|
924
|
+
margin-top: 0.4rem;
|
|
925
|
+
}
|
|
926
|
+
|
|
927
|
+
.consent-banner-links a {
|
|
928
|
+
color: var(--consent-muted);
|
|
929
|
+
font-size: 0.83rem;
|
|
930
|
+
text-decoration: underline;
|
|
931
|
+
text-underline-offset: 3px;
|
|
932
|
+
font-weight: 500;
|
|
933
|
+
margin-right: 1.2rem;
|
|
934
|
+
transition: color 0.15s ease;
|
|
935
|
+
}
|
|
936
|
+
|
|
937
|
+
.consent-banner-links a:hover {
|
|
938
|
+
color: var(--consent-fg);
|
|
939
|
+
}
|
|
940
|
+
|
|
941
|
+
/* Equal Visual Prominence CTAs */
|
|
942
|
+
.consent-banner-actions {
|
|
943
|
+
display: flex;
|
|
944
|
+
flex-direction: column;
|
|
945
|
+
gap: 0.5rem;
|
|
946
|
+
width: 100%;
|
|
947
|
+
}
|
|
948
|
+
|
|
949
|
+
@media (min-width: 640px) {
|
|
950
|
+
.consent-banner-actions {
|
|
951
|
+
flex-direction: row;
|
|
952
|
+
width: auto;
|
|
953
|
+
align-items: center;
|
|
954
|
+
}
|
|
955
|
+
}
|
|
956
|
+
|
|
957
|
+
.consent-btn {
|
|
958
|
+
display: inline-flex;
|
|
959
|
+
align-items: center;
|
|
960
|
+
justify-content: center;
|
|
961
|
+
padding: 0.6rem 1.2rem;
|
|
962
|
+
font-size: 0.88rem;
|
|
963
|
+
font-weight: 600;
|
|
964
|
+
border-radius: 10px;
|
|
965
|
+
border: 1px solid var(--consent-border);
|
|
966
|
+
cursor: pointer;
|
|
967
|
+
font-family: inherit;
|
|
968
|
+
transition: all 0.18s cubic-bezier(0.16, 1, 0.3, 1);
|
|
969
|
+
min-height: 42px;
|
|
970
|
+
min-width: 110px;
|
|
971
|
+
box-sizing: border-box;
|
|
972
|
+
user-select: none;
|
|
973
|
+
}
|
|
974
|
+
|
|
975
|
+
.consent-btn:hover {
|
|
976
|
+
transform: translateY(-1px);
|
|
977
|
+
}
|
|
978
|
+
|
|
979
|
+
.consent-btn:active {
|
|
980
|
+
transform: translateY(0) scale(0.98);
|
|
981
|
+
}
|
|
982
|
+
|
|
983
|
+
.consent-btn:focus-visible {
|
|
984
|
+
outline: 3px solid var(--consent-focus-ring);
|
|
985
|
+
outline-offset: 2px;
|
|
986
|
+
}
|
|
987
|
+
|
|
988
|
+
.consent-btn-reject {
|
|
989
|
+
background: var(--consent-secondary);
|
|
990
|
+
color: var(--consent-secondary-fg);
|
|
991
|
+
border-color: var(--consent-border);
|
|
992
|
+
}
|
|
993
|
+
|
|
994
|
+
.consent-btn-reject:hover {
|
|
995
|
+
background: var(--consent-secondary-hover);
|
|
996
|
+
}
|
|
997
|
+
|
|
998
|
+
.consent-btn-configure {
|
|
999
|
+
background: transparent;
|
|
1000
|
+
color: var(--consent-muted);
|
|
1001
|
+
border-color: transparent;
|
|
1002
|
+
}
|
|
1003
|
+
|
|
1004
|
+
.consent-btn-configure:hover {
|
|
1005
|
+
color: var(--consent-fg);
|
|
1006
|
+
background: var(--consent-secondary);
|
|
1007
|
+
}
|
|
1008
|
+
|
|
1009
|
+
.consent-btn-accept {
|
|
1010
|
+
background: var(--consent-primary);
|
|
1011
|
+
color: var(--consent-primary-fg);
|
|
1012
|
+
border-color: var(--consent-primary);
|
|
1013
|
+
}
|
|
1014
|
+
|
|
1015
|
+
.consent-btn-accept:hover {
|
|
1016
|
+
background: var(--consent-primary-hover);
|
|
1017
|
+
}
|
|
1018
|
+
|
|
1019
|
+
/* 2nd Layer Preference Modal - Ultra Clean Linear List Style */
|
|
1020
|
+
.consent-dialog-backdrop {
|
|
1021
|
+
position: fixed;
|
|
1022
|
+
top: 0;
|
|
1023
|
+
left: 0;
|
|
1024
|
+
width: 100vw;
|
|
1025
|
+
height: 100vh;
|
|
1026
|
+
background: rgba(15, 23, 42, 0.45);
|
|
1027
|
+
backdrop-filter: blur(8px);
|
|
1028
|
+
-webkit-backdrop-filter: blur(8px);
|
|
1029
|
+
z-index: 2147483646;
|
|
1030
|
+
display: flex;
|
|
1031
|
+
align-items: center;
|
|
1032
|
+
justify-content: center;
|
|
1033
|
+
padding: 1.25rem;
|
|
1034
|
+
box-sizing: border-box;
|
|
1035
|
+
animation: consentFadeIn 0.2s ease forwards;
|
|
1036
|
+
}
|
|
1037
|
+
|
|
1038
|
+
.consent-dialog {
|
|
1039
|
+
background: var(--consent-bg);
|
|
1040
|
+
color: var(--consent-fg);
|
|
1041
|
+
border: 1px solid var(--consent-border);
|
|
1042
|
+
border-radius: 20px;
|
|
1043
|
+
width: 100%;
|
|
1044
|
+
max-width: 600px;
|
|
1045
|
+
max-height: 85vh;
|
|
1046
|
+
display: flex;
|
|
1047
|
+
flex-direction: column;
|
|
1048
|
+
box-shadow: var(--consent-modal-shadow);
|
|
1049
|
+
font-family: var(--consent-font);
|
|
1050
|
+
overflow: hidden;
|
|
1051
|
+
animation: consentSlideUp 0.25s cubic-bezier(0.16, 1, 0.3, 1) forwards;
|
|
1052
|
+
}
|
|
1053
|
+
|
|
1054
|
+
.consent-dialog-header {
|
|
1055
|
+
padding: 1.5rem 1.75rem 1.25rem 1.75rem;
|
|
1056
|
+
border-bottom: 1px solid var(--consent-divider);
|
|
1057
|
+
display: flex;
|
|
1058
|
+
justify-content: space-between;
|
|
1059
|
+
align-items: flex-start;
|
|
1060
|
+
}
|
|
1061
|
+
|
|
1062
|
+
.consent-dialog-title {
|
|
1063
|
+
font-size: 1.15rem;
|
|
1064
|
+
font-weight: 700;
|
|
1065
|
+
letter-spacing: -0.015em;
|
|
1066
|
+
margin: 0;
|
|
1067
|
+
}
|
|
1068
|
+
|
|
1069
|
+
.consent-dialog-close {
|
|
1070
|
+
background: transparent;
|
|
1071
|
+
border: none;
|
|
1072
|
+
color: var(--consent-muted);
|
|
1073
|
+
cursor: pointer;
|
|
1074
|
+
padding: 0.35rem;
|
|
1075
|
+
border-radius: 8px;
|
|
1076
|
+
display: flex;
|
|
1077
|
+
align-items: center;
|
|
1078
|
+
justify-content: center;
|
|
1079
|
+
transition: background 0.15s ease, color 0.15s ease;
|
|
1080
|
+
margin: -0.35rem -0.35rem 0 0;
|
|
1081
|
+
}
|
|
1082
|
+
|
|
1083
|
+
.consent-dialog-close:hover {
|
|
1084
|
+
background: var(--consent-secondary);
|
|
1085
|
+
color: var(--consent-fg);
|
|
1086
|
+
}
|
|
1087
|
+
|
|
1088
|
+
/* Seamless Category List Body (NO individual boxed divs) */
|
|
1089
|
+
.consent-dialog-body {
|
|
1090
|
+
padding: 0;
|
|
1091
|
+
overflow-y: auto;
|
|
1092
|
+
display: flex;
|
|
1093
|
+
flex-direction: column;
|
|
1094
|
+
}
|
|
1095
|
+
|
|
1096
|
+
.consent-category-row {
|
|
1097
|
+
padding: 1.25rem 1.75rem;
|
|
1098
|
+
border-bottom: 1px solid var(--consent-divider);
|
|
1099
|
+
transition: background 0.15s ease;
|
|
1100
|
+
}
|
|
1101
|
+
|
|
1102
|
+
.consent-category-row:last-child {
|
|
1103
|
+
border-bottom: none;
|
|
1104
|
+
}
|
|
1105
|
+
|
|
1106
|
+
.consent-category-row:hover {
|
|
1107
|
+
background: var(--consent-hover-bg);
|
|
1108
|
+
}
|
|
1109
|
+
|
|
1110
|
+
.consent-category-header {
|
|
1111
|
+
display: flex;
|
|
1112
|
+
justify-content: space-between;
|
|
1113
|
+
align-items: flex-start;
|
|
1114
|
+
gap: 1rem;
|
|
1115
|
+
}
|
|
1116
|
+
|
|
1117
|
+
.consent-category-name {
|
|
1118
|
+
font-weight: 600;
|
|
1119
|
+
font-size: 0.95rem;
|
|
1120
|
+
color: var(--consent-fg);
|
|
1121
|
+
}
|
|
1122
|
+
|
|
1123
|
+
.consent-category-desc {
|
|
1124
|
+
margin: 0.3rem 0 0 0;
|
|
1125
|
+
font-size: 0.85rem;
|
|
1126
|
+
color: var(--consent-muted);
|
|
1127
|
+
line-height: 1.45;
|
|
1128
|
+
}
|
|
1129
|
+
|
|
1130
|
+
/* Minimalist Badges */
|
|
1131
|
+
.consent-badge {
|
|
1132
|
+
display: inline-flex;
|
|
1133
|
+
align-items: center;
|
|
1134
|
+
padding: 0.15rem 0.5rem;
|
|
1135
|
+
border-radius: 6px;
|
|
1136
|
+
font-size: 0.7rem;
|
|
1137
|
+
font-weight: 600;
|
|
1138
|
+
letter-spacing: 0.01em;
|
|
1139
|
+
}
|
|
1140
|
+
|
|
1141
|
+
.consent-badge-required {
|
|
1142
|
+
background: rgba(16, 185, 129, 0.1);
|
|
1143
|
+
color: #059669;
|
|
1144
|
+
}
|
|
1145
|
+
|
|
1146
|
+
.consent-badge-optional {
|
|
1147
|
+
background: rgba(100, 116, 139, 0.08);
|
|
1148
|
+
color: var(--consent-muted);
|
|
1149
|
+
}
|
|
1150
|
+
|
|
1151
|
+
/* Inline Service Disclosure Button */
|
|
1152
|
+
.consent-toggle-services-btn {
|
|
1153
|
+
border: none;
|
|
1154
|
+
background: transparent;
|
|
1155
|
+
color: var(--consent-accent);
|
|
1156
|
+
font-size: 0.78rem;
|
|
1157
|
+
font-weight: 600;
|
|
1158
|
+
cursor: pointer;
|
|
1159
|
+
padding: 0.35rem 0 0 0;
|
|
1160
|
+
display: inline-flex;
|
|
1161
|
+
align-items: center;
|
|
1162
|
+
gap: 0.25rem;
|
|
1163
|
+
font-family: inherit;
|
|
1164
|
+
}
|
|
1165
|
+
|
|
1166
|
+
.consent-toggle-services-btn:hover {
|
|
1167
|
+
opacity: 0.85;
|
|
1168
|
+
}
|
|
1169
|
+
|
|
1170
|
+
/* Minimalist Toggle Switch */
|
|
1171
|
+
.consent-toggle {
|
|
1172
|
+
position: relative;
|
|
1173
|
+
display: inline-block;
|
|
1174
|
+
width: 40px;
|
|
1175
|
+
height: 22px;
|
|
1176
|
+
flex-shrink: 0;
|
|
1177
|
+
margin-top: 0.15rem;
|
|
1178
|
+
}
|
|
1179
|
+
|
|
1180
|
+
.consent-toggle input {
|
|
1181
|
+
opacity: 0;
|
|
1182
|
+
width: 0;
|
|
1183
|
+
height: 0;
|
|
1184
|
+
}
|
|
1185
|
+
|
|
1186
|
+
.consent-toggle-slider {
|
|
1187
|
+
position: absolute;
|
|
1188
|
+
cursor: pointer;
|
|
1189
|
+
top: 0; left: 0; right: 0; bottom: 0;
|
|
1190
|
+
background-color: #cbd5e1;
|
|
1191
|
+
transition: background-color 0.2s ease;
|
|
1192
|
+
border-radius: 22px;
|
|
1193
|
+
}
|
|
1194
|
+
|
|
1195
|
+
.consent-toggle-slider:before {
|
|
1196
|
+
position: absolute;
|
|
1197
|
+
content: "";
|
|
1198
|
+
height: 16px;
|
|
1199
|
+
width: 16px;
|
|
1200
|
+
left: 3px;
|
|
1201
|
+
bottom: 3px;
|
|
1202
|
+
background-color: white;
|
|
1203
|
+
transition: transform 0.2s cubic-bezier(0.16, 1, 0.3, 1);
|
|
1204
|
+
border-radius: 50%;
|
|
1205
|
+
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.15);
|
|
1206
|
+
}
|
|
1207
|
+
|
|
1208
|
+
.consent-toggle input:checked + .consent-toggle-slider {
|
|
1209
|
+
background-color: var(--consent-accent);
|
|
1210
|
+
}
|
|
1211
|
+
|
|
1212
|
+
.consent-toggle input:checked + .consent-toggle-slider:before {
|
|
1213
|
+
transform: translateX(18px);
|
|
1214
|
+
}
|
|
1215
|
+
|
|
1216
|
+
.consent-toggle input:disabled + .consent-toggle-slider {
|
|
1217
|
+
opacity: 0.45;
|
|
1218
|
+
cursor: not-allowed;
|
|
1219
|
+
}
|
|
1220
|
+
|
|
1221
|
+
/* Footer */
|
|
1222
|
+
.consent-dialog-footer {
|
|
1223
|
+
padding: 1.25rem 1.75rem;
|
|
1224
|
+
border-top: 1px solid var(--consent-divider);
|
|
1225
|
+
background: var(--consent-bg);
|
|
1226
|
+
display: flex;
|
|
1227
|
+
flex-direction: column;
|
|
1228
|
+
gap: 0.6rem;
|
|
1229
|
+
justify-content: space-between;
|
|
1230
|
+
align-items: center;
|
|
1231
|
+
}
|
|
1232
|
+
|
|
1233
|
+
@media (min-width: 520px) {
|
|
1234
|
+
.consent-dialog-footer {
|
|
1235
|
+
flex-direction: row;
|
|
1236
|
+
}
|
|
1237
|
+
}
|
|
1238
|
+
`;
|
|
1239
|
+
document.head.appendChild(styleEl);
|
|
1240
|
+
}
|
|
1241
|
+
|
|
1242
|
+
// src/ui/banner.ts
|
|
1243
|
+
var ConsentBanner = class {
|
|
1244
|
+
constructor() {
|
|
1245
|
+
this.element = null;
|
|
1246
|
+
}
|
|
1247
|
+
render(config, handlers) {
|
|
1248
|
+
var _a, _b, _c, _d, _e, _f, _g;
|
|
1249
|
+
if (typeof document === "undefined") return;
|
|
1250
|
+
this.remove();
|
|
1251
|
+
injectStyles((_a = config.csp) == null ? void 0 : _a.nonce);
|
|
1252
|
+
const bannerConfig = ((_b = config.ui) == null ? void 0 : _b.banner) || {};
|
|
1253
|
+
const titleText = sanitizeHtml(bannerConfig.title || "Tu privacidad, bajo tu control");
|
|
1254
|
+
const descText = sanitizeHtml(
|
|
1255
|
+
bannerConfig.description || "Usamos tecnolog\xEDas necesarias para el funcionamiento del sitio. Con tu permiso, tambi\xE9n podemos utilizar anal\xEDtica y marketing."
|
|
1256
|
+
);
|
|
1257
|
+
const acceptText = sanitizeHtml(bannerConfig.accept || "Aceptar todas");
|
|
1258
|
+
const rejectText = sanitizeHtml(bannerConfig.reject || "Rechazar todas");
|
|
1259
|
+
const configureText = sanitizeHtml(bannerConfig.configure || "Configurar");
|
|
1260
|
+
const privacyUrl = sanitizeUrl(((_c = config.policy) == null ? void 0 : _c.privacyUrl) || "/politica-privacidad");
|
|
1261
|
+
const cookiesUrl = sanitizeUrl(((_d = config.policy) == null ? void 0 : _d.cookiesUrl) || "/politica-cookies");
|
|
1262
|
+
const wrapper = document.createElement("div");
|
|
1263
|
+
wrapper.className = "consent-banner-wrapper";
|
|
1264
|
+
wrapper.setAttribute("role", "region");
|
|
1265
|
+
wrapper.setAttribute(
|
|
1266
|
+
"aria-label",
|
|
1267
|
+
"Gesti\xF3n de consentimiento de privacidad"
|
|
1268
|
+
);
|
|
1269
|
+
wrapper.innerHTML = `
|
|
1270
|
+
<div class="consent-banner-container">
|
|
1271
|
+
<div class="consent-banner-text">
|
|
1272
|
+
<h2 class="consent-banner-title">
|
|
1273
|
+
<span class="consent-banner-title-icon">
|
|
1274
|
+
<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5">
|
|
1275
|
+
<path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/>
|
|
1276
|
+
</svg>
|
|
1277
|
+
</span>
|
|
1278
|
+
${titleText}
|
|
1279
|
+
</h2>
|
|
1280
|
+
<p class="consent-banner-desc">${descText}</p>
|
|
1281
|
+
<div class="consent-banner-links">
|
|
1282
|
+
<a href="${cookiesUrl}" target="_blank" rel="noopener">Pol\xEDtica de cookies</a>
|
|
1283
|
+
<a href="${privacyUrl}" target="_blank" rel="noopener">Pol\xEDtica de privacidad</a>
|
|
1284
|
+
</div>
|
|
1285
|
+
</div>
|
|
1286
|
+
<div class="consent-banner-actions">
|
|
1287
|
+
<button type="button" class="consent-btn consent-btn-reject" id="consent-btn-reject">
|
|
1288
|
+
${rejectText}
|
|
1289
|
+
</button>
|
|
1290
|
+
<button type="button" class="consent-btn consent-btn-configure" id="consent-btn-configure">
|
|
1291
|
+
${configureText}
|
|
1292
|
+
</button>
|
|
1293
|
+
<button type="button" class="consent-btn consent-btn-accept" id="consent-btn-accept">
|
|
1294
|
+
${acceptText}
|
|
1295
|
+
</button>
|
|
1296
|
+
</div>
|
|
1297
|
+
</div>
|
|
1298
|
+
`;
|
|
1299
|
+
(_e = wrapper.querySelector("#consent-btn-reject")) == null ? void 0 : _e.addEventListener("click", () => {
|
|
1300
|
+
handlers.onRejectAll();
|
|
1301
|
+
this.remove();
|
|
1302
|
+
});
|
|
1303
|
+
(_f = wrapper.querySelector("#consent-btn-configure")) == null ? void 0 : _f.addEventListener("click", () => {
|
|
1304
|
+
handlers.onConfigure();
|
|
1305
|
+
});
|
|
1306
|
+
(_g = wrapper.querySelector("#consent-btn-accept")) == null ? void 0 : _g.addEventListener("click", () => {
|
|
1307
|
+
handlers.onAcceptAll();
|
|
1308
|
+
this.remove();
|
|
1309
|
+
});
|
|
1310
|
+
document.body.appendChild(wrapper);
|
|
1311
|
+
this.element = wrapper;
|
|
1312
|
+
}
|
|
1313
|
+
remove() {
|
|
1314
|
+
if (this.element && this.element.parentNode) {
|
|
1315
|
+
this.element.parentNode.removeChild(this.element);
|
|
1316
|
+
this.element = null;
|
|
1317
|
+
}
|
|
1318
|
+
}
|
|
1319
|
+
};
|
|
1320
|
+
|
|
1321
|
+
// src/ui/preferences.ts
|
|
1322
|
+
var PreferencesModal = class {
|
|
1323
|
+
constructor() {
|
|
1324
|
+
this.backdrop = null;
|
|
1325
|
+
this.lastFocusedElement = null;
|
|
1326
|
+
}
|
|
1327
|
+
render(config, currentChoices, handlers) {
|
|
1328
|
+
var _a, _b, _c, _d, _e, _f, _g;
|
|
1329
|
+
if (typeof document === "undefined") return;
|
|
1330
|
+
this.close();
|
|
1331
|
+
injectStyles((_a = config.csp) == null ? void 0 : _a.nonce);
|
|
1332
|
+
this.lastFocusedElement = document.activeElement;
|
|
1333
|
+
const prefConfig = ((_b = config.ui) == null ? void 0 : _b.preferences) || {};
|
|
1334
|
+
const modalTitle = sanitizeHtml(prefConfig.title || "Preferencias de privacidad");
|
|
1335
|
+
const saveText = sanitizeHtml(prefConfig.save || "Guardar selecci\xF3n");
|
|
1336
|
+
const acceptAllText = sanitizeHtml(prefConfig.acceptAll || "Permitir todas");
|
|
1337
|
+
const rejectAllText = sanitizeHtml(prefConfig.rejectAll || "Rechazar opcionales");
|
|
1338
|
+
const backdrop = document.createElement("div");
|
|
1339
|
+
backdrop.className = "consent-dialog-backdrop";
|
|
1340
|
+
const dialog = document.createElement("div");
|
|
1341
|
+
dialog.className = "consent-dialog";
|
|
1342
|
+
dialog.setAttribute("role", "dialog");
|
|
1343
|
+
dialog.setAttribute("aria-modal", "true");
|
|
1344
|
+
dialog.setAttribute("aria-labelledby", "consent-dialog-title-id");
|
|
1345
|
+
const header = document.createElement("div");
|
|
1346
|
+
header.className = "consent-dialog-header";
|
|
1347
|
+
header.innerHTML = `
|
|
1348
|
+
<div>
|
|
1349
|
+
<h2 class="consent-dialog-title" id="consent-dialog-title-id">${modalTitle}</h2>
|
|
1350
|
+
<p style="margin: 0.2rem 0 0 0; font-size: 0.84rem; color: var(--consent-muted); font-weight: 400;">
|
|
1351
|
+
Gestiona tus permisos de almacenamiento por finalidad (LSSI art. 22.2 & RGPD).
|
|
1352
|
+
</p>
|
|
1353
|
+
</div>
|
|
1354
|
+
<button type="button" class="consent-dialog-close" aria-label="Cerrar ventana">
|
|
1355
|
+
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round">
|
|
1356
|
+
<line x1="18" y1="6" x2="6" y2="18"></line>
|
|
1357
|
+
<line x1="6" y1="6" x2="18" y2="18"></line>
|
|
1358
|
+
</svg>
|
|
1359
|
+
</button>
|
|
1360
|
+
`;
|
|
1361
|
+
const body = document.createElement("div");
|
|
1362
|
+
body.className = "consent-dialog-body";
|
|
1363
|
+
const categoryChoices = __spreadValues({}, currentChoices);
|
|
1364
|
+
for (const [catId, catConfig] of Object.entries(config.categories)) {
|
|
1365
|
+
const catRow = document.createElement("div");
|
|
1366
|
+
catRow.className = "consent-category-row";
|
|
1367
|
+
const isRequired = catConfig.required === true;
|
|
1368
|
+
const isChecked = isRequired ? true : (_c = categoryChoices[catId]) != null ? _c : false;
|
|
1369
|
+
const associatedServices = config.services ? Object.entries(config.services).filter(([_, srv]) => srv.category === catId) : [];
|
|
1370
|
+
let servicesHtml = "";
|
|
1371
|
+
if (associatedServices.length > 0) {
|
|
1372
|
+
servicesHtml = `
|
|
1373
|
+
<div class="consent-category-services" id="cat-services-${catId}" style="display: none; margin-top: 0.75rem; padding-top: 0.6rem; border-top: 1px dashed var(--consent-divider); font-size: 0.82rem;">
|
|
1374
|
+
<div style="font-weight: 600; color: var(--consent-muted); margin-bottom: 0.4rem; font-size: 0.75rem; letter-spacing: 0.02em;">
|
|
1375
|
+
Servicios incluidos (${associatedServices.length})
|
|
1376
|
+
</div>
|
|
1377
|
+
<div style="display: flex; flex-direction: column; gap: 0.3rem;">
|
|
1378
|
+
${associatedServices.map(
|
|
1379
|
+
([srvId, srv]) => `
|
|
1380
|
+
<div style="display: flex; justify-content: space-between; align-items: center; padding: 0.2rem 0;">
|
|
1381
|
+
<span style="font-weight: 500;">${sanitizeHtml(srv.label || srvId)}</span>
|
|
1382
|
+
<span style="color: var(--consent-muted); font-size: 0.78rem;">${sanitizeHtml(srv.provider || "Terceros")}</span>
|
|
1383
|
+
</div>
|
|
1384
|
+
`
|
|
1385
|
+
).join("")}
|
|
1386
|
+
</div>
|
|
1387
|
+
</div>
|
|
1388
|
+
`;
|
|
1389
|
+
}
|
|
1390
|
+
catRow.innerHTML = `
|
|
1391
|
+
<div class="consent-category-header">
|
|
1392
|
+
<div style="flex: 1; padding-right: 1rem;">
|
|
1393
|
+
<div style="display: flex; align-items: center; gap: 0.4rem;">
|
|
1394
|
+
<span class="consent-category-name">${sanitizeHtml(catConfig.label)}</span>
|
|
1395
|
+
${isRequired ? '<span class="consent-badge consent-badge-required">Requerida</span>' : '<span class="consent-badge consent-badge-optional">Opcional</span>'}
|
|
1396
|
+
</div>
|
|
1397
|
+
<p class="consent-category-desc">${sanitizeHtml(catConfig.description)}</p>
|
|
1398
|
+
${associatedServices.length > 0 ? `<button type="button" class="consent-toggle-services-btn" data-target="cat-services-${catId}">
|
|
1399
|
+
Ver servicios (${associatedServices.length}) \u25BE
|
|
1400
|
+
</button>` : ""}
|
|
1401
|
+
</div>
|
|
1402
|
+
<label class="consent-toggle">
|
|
1403
|
+
<input type="checkbox" id="cat-toggle-${catId}" ${isChecked ? "checked" : ""} ${isRequired ? "disabled" : ""}>
|
|
1404
|
+
<span class="consent-toggle-slider"></span>
|
|
1405
|
+
</label>
|
|
1406
|
+
</div>
|
|
1407
|
+
${servicesHtml}
|
|
1408
|
+
`;
|
|
1409
|
+
if (!isRequired) {
|
|
1410
|
+
const checkbox = catRow.querySelector(
|
|
1411
|
+
`#cat-toggle-${catId}`
|
|
1412
|
+
);
|
|
1413
|
+
checkbox == null ? void 0 : checkbox.addEventListener("change", (e) => {
|
|
1414
|
+
categoryChoices[catId] = e.target.checked;
|
|
1415
|
+
});
|
|
1416
|
+
}
|
|
1417
|
+
const toggleServicesBtn = catRow.querySelector(
|
|
1418
|
+
".consent-toggle-services-btn"
|
|
1419
|
+
);
|
|
1420
|
+
if (toggleServicesBtn) {
|
|
1421
|
+
toggleServicesBtn.addEventListener("click", () => {
|
|
1422
|
+
const targetId = toggleServicesBtn.getAttribute("data-target");
|
|
1423
|
+
if (targetId) {
|
|
1424
|
+
const targetEl = catRow.querySelector(`#${targetId}`);
|
|
1425
|
+
if (targetEl) {
|
|
1426
|
+
const isHidden = targetEl.style.display === "none";
|
|
1427
|
+
targetEl.style.display = isHidden ? "block" : "none";
|
|
1428
|
+
toggleServicesBtn.textContent = isHidden ? `Ocultar servicios (${associatedServices.length}) \u25B4` : `Ver servicios (${associatedServices.length}) \u25BE`;
|
|
1429
|
+
}
|
|
1430
|
+
}
|
|
1431
|
+
});
|
|
1432
|
+
}
|
|
1433
|
+
body.appendChild(catRow);
|
|
1434
|
+
}
|
|
1435
|
+
const footer = document.createElement("div");
|
|
1436
|
+
footer.className = "consent-dialog-footer";
|
|
1437
|
+
footer.innerHTML = `
|
|
1438
|
+
<button type="button" class="consent-btn consent-btn-reject" id="consent-pref-reject">
|
|
1439
|
+
${rejectAllText}
|
|
1440
|
+
</button>
|
|
1441
|
+
<div style="display: flex; gap: 0.5rem; flex-wrap: wrap;">
|
|
1442
|
+
<button type="button" class="consent-btn consent-btn-configure" id="consent-pref-accept">
|
|
1443
|
+
${acceptAllText}
|
|
1444
|
+
</button>
|
|
1445
|
+
<button type="button" class="consent-btn consent-btn-accept" id="consent-pref-save">
|
|
1446
|
+
${saveText}
|
|
1447
|
+
</button>
|
|
1448
|
+
</div>
|
|
1449
|
+
`;
|
|
1450
|
+
dialog.appendChild(header);
|
|
1451
|
+
dialog.appendChild(body);
|
|
1452
|
+
dialog.appendChild(footer);
|
|
1453
|
+
backdrop.appendChild(dialog);
|
|
1454
|
+
(_d = header.querySelector(".consent-dialog-close")) == null ? void 0 : _d.addEventListener("click", () => {
|
|
1455
|
+
handlers.onClose();
|
|
1456
|
+
this.close();
|
|
1457
|
+
});
|
|
1458
|
+
(_e = footer.querySelector("#consent-pref-reject")) == null ? void 0 : _e.addEventListener("click", () => {
|
|
1459
|
+
handlers.onRejectAll();
|
|
1460
|
+
this.close();
|
|
1461
|
+
});
|
|
1462
|
+
(_f = footer.querySelector("#consent-pref-accept")) == null ? void 0 : _f.addEventListener("click", () => {
|
|
1463
|
+
handlers.onAcceptAll();
|
|
1464
|
+
this.close();
|
|
1465
|
+
});
|
|
1466
|
+
(_g = footer.querySelector("#consent-pref-save")) == null ? void 0 : _g.addEventListener("click", () => {
|
|
1467
|
+
handlers.onSave(categoryChoices);
|
|
1468
|
+
this.close();
|
|
1469
|
+
});
|
|
1470
|
+
const handleKeyDown = (e) => {
|
|
1471
|
+
if (e.key === "Escape") {
|
|
1472
|
+
handlers.onClose();
|
|
1473
|
+
this.close();
|
|
1474
|
+
return;
|
|
1475
|
+
}
|
|
1476
|
+
if (e.key === "Tab") {
|
|
1477
|
+
const focusableElements = dialog.querySelectorAll(
|
|
1478
|
+
'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
|
|
1479
|
+
);
|
|
1480
|
+
const firstElement = focusableElements[0];
|
|
1481
|
+
const lastElement = focusableElements[focusableElements.length - 1];
|
|
1482
|
+
if (e.shiftKey) {
|
|
1483
|
+
if (document.activeElement === firstElement) {
|
|
1484
|
+
lastElement == null ? void 0 : lastElement.focus();
|
|
1485
|
+
e.preventDefault();
|
|
1486
|
+
}
|
|
1487
|
+
} else {
|
|
1488
|
+
if (document.activeElement === lastElement) {
|
|
1489
|
+
firstElement == null ? void 0 : firstElement.focus();
|
|
1490
|
+
e.preventDefault();
|
|
1491
|
+
}
|
|
1492
|
+
}
|
|
1493
|
+
}
|
|
1494
|
+
};
|
|
1495
|
+
document.addEventListener("keydown", handleKeyDown);
|
|
1496
|
+
this._keyListener = handleKeyDown;
|
|
1497
|
+
document.body.appendChild(backdrop);
|
|
1498
|
+
this.backdrop = backdrop;
|
|
1499
|
+
const firstFocusable = dialog.querySelector("button");
|
|
1500
|
+
firstFocusable == null ? void 0 : firstFocusable.focus();
|
|
1501
|
+
}
|
|
1502
|
+
close() {
|
|
1503
|
+
if (this._keyListener) {
|
|
1504
|
+
document.removeEventListener("keydown", this._keyListener);
|
|
1505
|
+
delete this._keyListener;
|
|
1506
|
+
}
|
|
1507
|
+
if (this.backdrop && this.backdrop.parentNode) {
|
|
1508
|
+
this.backdrop.parentNode.removeChild(this.backdrop);
|
|
1509
|
+
this.backdrop = null;
|
|
1510
|
+
}
|
|
1511
|
+
if (this.lastFocusedElement && typeof this.lastFocusedElement.focus === "function") {
|
|
1512
|
+
this.lastFocusedElement.focus();
|
|
1513
|
+
this.lastFocusedElement = null;
|
|
1514
|
+
}
|
|
1515
|
+
}
|
|
1516
|
+
};
|
|
1517
|
+
|
|
1518
|
+
// src/diagnostics/resource-scanner.ts
|
|
1519
|
+
var ResourceScanner = class _ResourceScanner {
|
|
1520
|
+
static runDiagnostic(_config, isConsentGiven) {
|
|
1521
|
+
const thirdPartyScripts = [];
|
|
1522
|
+
if (typeof document !== "undefined") {
|
|
1523
|
+
const scripts = document.querySelectorAll("script[src]");
|
|
1524
|
+
scripts.forEach((script) => {
|
|
1525
|
+
const src = script.getAttribute("src");
|
|
1526
|
+
if (src && (src.startsWith("http://") || src.startsWith("https://"))) {
|
|
1527
|
+
thirdPartyScripts.push(src);
|
|
1528
|
+
}
|
|
1529
|
+
});
|
|
1530
|
+
}
|
|
1531
|
+
return {
|
|
1532
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1533
|
+
unblockedThirdPartyScripts: isConsentGiven ? [] : thirdPartyScripts,
|
|
1534
|
+
compliant: isConsentGiven || thirdPartyScripts.length === 0
|
|
1535
|
+
};
|
|
1536
|
+
}
|
|
1537
|
+
scanThirdPartyResources() {
|
|
1538
|
+
return _ResourceScanner.runDiagnostic({}, true).unblockedThirdPartyScripts;
|
|
1539
|
+
}
|
|
1540
|
+
};
|
|
1541
|
+
|
|
1542
|
+
// src/ui/policy-generator.ts
|
|
1543
|
+
var PolicyGenerator = class {
|
|
1544
|
+
static renderTable(config) {
|
|
1545
|
+
let html = `
|
|
1546
|
+
<div class="consent-policy-table-wrapper" style="margin: 1.5rem 0; font-family: system-ui, sans-serif;">
|
|
1547
|
+
<table style="width: 100%; border-collapse: collapse; text-align: left; border: 1px solid #e2e8f0;">
|
|
1548
|
+
<thead>
|
|
1549
|
+
<tr style="background: #f8fafc; border-bottom: 2px solid #cbd5e1;">
|
|
1550
|
+
<th style="padding: 0.75rem; border: 1px solid #e2e8f0;">Categor\xEDa / Servicio</th>
|
|
1551
|
+
<th style="padding: 0.75rem; border: 1px solid #e2e8f0;">Proveedor</th>
|
|
1552
|
+
<th style="padding: 0.75rem; border: 1px solid #e2e8f0;">Finalidad</th>
|
|
1553
|
+
<th style="padding: 0.75rem; border: 1px solid #e2e8f0;">Requerida</th>
|
|
1554
|
+
</tr>
|
|
1555
|
+
</thead>
|
|
1556
|
+
<tbody>
|
|
1557
|
+
`;
|
|
1558
|
+
for (const [catId, cat] of Object.entries(config.categories)) {
|
|
1559
|
+
const isReq = cat.required ? "S\xED (T\xE9cnica)" : "No (Opcional)";
|
|
1560
|
+
html += `
|
|
1561
|
+
<tr style="background: #ffffff; font-weight: 600;">
|
|
1562
|
+
<td style="padding: 0.75rem; border: 1px solid #e2e8f0;" colspan="3">${sanitizeHtml(cat.label)}</td>
|
|
1563
|
+
<td style="padding: 0.75rem; border: 1px solid #e2e8f0;">${isReq}</td>
|
|
1564
|
+
</tr>
|
|
1565
|
+
`;
|
|
1566
|
+
if (config.services) {
|
|
1567
|
+
for (const [srvId, srv] of Object.entries(config.services)) {
|
|
1568
|
+
if (srv.category === catId) {
|
|
1569
|
+
html += `
|
|
1570
|
+
<tr style="background: #f8fafc; font-size: 0.9rem;">
|
|
1571
|
+
<td style="padding: 0.5rem 0.75rem 0.5rem 1.5rem; border: 1px solid #e2e8f0;">\u21B3 ${sanitizeHtml(srv.label || srvId)}</td>
|
|
1572
|
+
<td style="padding: 0.5rem 0.75rem; border: 1px solid #e2e8f0;">${sanitizeHtml(srv.provider || "-")}</td>
|
|
1573
|
+
<td style="padding: 0.5rem 0.75rem; border: 1px solid #e2e8f0;">${sanitizeHtml(cat.description || "-")}</td>
|
|
1574
|
+
<td style="padding: 0.5rem 0.75rem; border: 1px solid #e2e8f0;">${isReq}</td>
|
|
1575
|
+
</tr>
|
|
1576
|
+
`;
|
|
1577
|
+
}
|
|
1578
|
+
}
|
|
1579
|
+
}
|
|
1580
|
+
}
|
|
1581
|
+
html += `
|
|
1582
|
+
</tbody>
|
|
1583
|
+
</table>
|
|
1584
|
+
</div>
|
|
1585
|
+
`;
|
|
1586
|
+
return html;
|
|
1587
|
+
}
|
|
1588
|
+
};
|
|
1589
|
+
|
|
1590
|
+
// src/core/consent-engine.ts
|
|
1591
|
+
var ConsentEngine = class {
|
|
1592
|
+
constructor() {
|
|
1593
|
+
this.stateManager = new StateManager();
|
|
1594
|
+
this.eventBus = new EventBus();
|
|
1595
|
+
this.blockerRegistry = new BlockerRegistry();
|
|
1596
|
+
this.banner = new ConsentBanner();
|
|
1597
|
+
this.preferencesModal = new PreferencesModal();
|
|
1598
|
+
this.initPromise = null;
|
|
1599
|
+
this.resolveReady = null;
|
|
1600
|
+
this.readyPromise = new Promise((resolve) => {
|
|
1601
|
+
this.resolveReady = resolve;
|
|
1602
|
+
});
|
|
1603
|
+
}
|
|
1604
|
+
async init(configInput) {
|
|
1605
|
+
if (this.initPromise) return this.initPromise;
|
|
1606
|
+
this.initPromise = (async () => {
|
|
1607
|
+
var _a, _b, _c;
|
|
1608
|
+
let config;
|
|
1609
|
+
if (typeof configInput === "string") {
|
|
1610
|
+
const response = await fetch(configInput);
|
|
1611
|
+
if (!response.ok) {
|
|
1612
|
+
throw new Error(
|
|
1613
|
+
`[ConsentSDK] Failed to fetch consent configuration from '${configInput}' (HTTP ${response.status})`
|
|
1614
|
+
);
|
|
1615
|
+
}
|
|
1616
|
+
config = await response.json();
|
|
1617
|
+
} else {
|
|
1618
|
+
config = configInput;
|
|
1619
|
+
}
|
|
1620
|
+
validateConfig(config);
|
|
1621
|
+
const cookieName = ((_a = config.storage) == null ? void 0 : _a.name) || "site_consent";
|
|
1622
|
+
const rawReceipt = ((_b = config.storage) == null ? void 0 : _b.type) === "memory" ? MemoryStore.get(cookieName) : CookieStore.get(cookieName);
|
|
1623
|
+
const savedReceipt = rawReceipt ? parseReceipt(rawReceipt) : null;
|
|
1624
|
+
const evalResult = evaluatePolicy(config, savedReceipt);
|
|
1625
|
+
const activeReceipt = evalResult.isValid ? savedReceipt : null;
|
|
1626
|
+
this.stateManager.init(config, evalResult.choices, activeReceipt);
|
|
1627
|
+
GoogleConsentAdapter.initDefault();
|
|
1628
|
+
if (evalResult.isValid) {
|
|
1629
|
+
GoogleConsentAdapter.update(evalResult.choices);
|
|
1630
|
+
}
|
|
1631
|
+
this.blockerRegistry.init(
|
|
1632
|
+
(cat) => this.has(cat),
|
|
1633
|
+
(srv) => this.hasService(srv),
|
|
1634
|
+
{
|
|
1635
|
+
onAllowClick: (category) => {
|
|
1636
|
+
const current = this.stateManager.getChoices();
|
|
1637
|
+
current[category] = true;
|
|
1638
|
+
this.setPreferences(current);
|
|
1639
|
+
}
|
|
1640
|
+
},
|
|
1641
|
+
(serviceId, category) => {
|
|
1642
|
+
this.eventBus.emit("service:loaded", { serviceId, category });
|
|
1643
|
+
}
|
|
1644
|
+
);
|
|
1645
|
+
this.setupGlobalRevocationTrigger();
|
|
1646
|
+
(_c = this.resolveReady) == null ? void 0 : _c.call(this);
|
|
1647
|
+
this.eventBus.emit("ready", { state: this.getConsent() });
|
|
1648
|
+
if (!evalResult.isValid) {
|
|
1649
|
+
this.showBanner();
|
|
1650
|
+
}
|
|
1651
|
+
})();
|
|
1652
|
+
return this.initPromise;
|
|
1653
|
+
}
|
|
1654
|
+
async ready() {
|
|
1655
|
+
return this.readyPromise;
|
|
1656
|
+
}
|
|
1657
|
+
getConsent() {
|
|
1658
|
+
return this.stateManager.getState();
|
|
1659
|
+
}
|
|
1660
|
+
has(category) {
|
|
1661
|
+
return this.stateManager.hasCategory(category);
|
|
1662
|
+
}
|
|
1663
|
+
hasService(serviceId) {
|
|
1664
|
+
return this.stateManager.hasService(serviceId);
|
|
1665
|
+
}
|
|
1666
|
+
acceptAll() {
|
|
1667
|
+
const config = this.stateManager.getConfig();
|
|
1668
|
+
if (!config) return;
|
|
1669
|
+
const choices = {};
|
|
1670
|
+
for (const catId of Object.keys(config.categories)) {
|
|
1671
|
+
choices[catId] = true;
|
|
1672
|
+
}
|
|
1673
|
+
this.saveChoices(choices, "banner");
|
|
1674
|
+
this.eventBus.emit("consent:accepted", {
|
|
1675
|
+
choices,
|
|
1676
|
+
receipt: this.getReceipt()
|
|
1677
|
+
});
|
|
1678
|
+
}
|
|
1679
|
+
rejectAll() {
|
|
1680
|
+
const config = this.stateManager.getConfig();
|
|
1681
|
+
if (!config) return;
|
|
1682
|
+
const choices = {};
|
|
1683
|
+
for (const [catId, catConfig] of Object.entries(config.categories)) {
|
|
1684
|
+
choices[catId] = catConfig.required === true;
|
|
1685
|
+
}
|
|
1686
|
+
this.saveChoices(choices, "banner");
|
|
1687
|
+
this.eventBus.emit("consent:rejected", {
|
|
1688
|
+
choices,
|
|
1689
|
+
receipt: this.getReceipt()
|
|
1690
|
+
});
|
|
1691
|
+
}
|
|
1692
|
+
setPreferences(choices) {
|
|
1693
|
+
const config = this.stateManager.getConfig();
|
|
1694
|
+
if (!config) return;
|
|
1695
|
+
const sanitizedChoices = __spreadValues({}, choices);
|
|
1696
|
+
sanitizedChoices["necessary"] = true;
|
|
1697
|
+
this.saveChoices(sanitizedChoices, "preferences");
|
|
1698
|
+
}
|
|
1699
|
+
openPreferences() {
|
|
1700
|
+
const config = this.stateManager.getConfig();
|
|
1701
|
+
if (!config) return;
|
|
1702
|
+
this.preferencesModal.render(config, this.stateManager.getChoices(), {
|
|
1703
|
+
onSave: (choices) => this.setPreferences(choices),
|
|
1704
|
+
onAcceptAll: () => this.acceptAll(),
|
|
1705
|
+
onRejectAll: () => this.rejectAll(),
|
|
1706
|
+
onClose: () => {
|
|
1707
|
+
this.eventBus.emit("preferences:closed", void 0);
|
|
1708
|
+
}
|
|
1709
|
+
});
|
|
1710
|
+
this.eventBus.emit("preferences:opened", void 0);
|
|
1711
|
+
}
|
|
1712
|
+
closePreferences() {
|
|
1713
|
+
this.preferencesModal.close();
|
|
1714
|
+
}
|
|
1715
|
+
withdraw() {
|
|
1716
|
+
var _a, _b;
|
|
1717
|
+
const previousChoices = this.stateManager.getChoices();
|
|
1718
|
+
const config = this.stateManager.getConfig();
|
|
1719
|
+
if (!config) return;
|
|
1720
|
+
const cookieName = ((_a = config.storage) == null ? void 0 : _a.name) || "site_consent";
|
|
1721
|
+
if (((_b = config.storage) == null ? void 0 : _b.type) === "memory") {
|
|
1722
|
+
MemoryStore.remove(cookieName);
|
|
1723
|
+
} else {
|
|
1724
|
+
CookieStore.remove(cookieName);
|
|
1725
|
+
}
|
|
1726
|
+
this.stateManager.clearChoices();
|
|
1727
|
+
GoogleConsentAdapter.update(this.stateManager.getChoices());
|
|
1728
|
+
this.eventBus.emit("consent:withdrawn", { previousChoices });
|
|
1729
|
+
this.showBanner();
|
|
1730
|
+
}
|
|
1731
|
+
when(categoryOrService, callback) {
|
|
1732
|
+
return CustomServiceAdapter.createWhen(
|
|
1733
|
+
this.eventBus,
|
|
1734
|
+
(cat) => this.has(cat),
|
|
1735
|
+
(srv) => this.hasService(srv)
|
|
1736
|
+
)(categoryOrService, callback);
|
|
1737
|
+
}
|
|
1738
|
+
on(event, handler) {
|
|
1739
|
+
return this.eventBus.on(event, handler);
|
|
1740
|
+
}
|
|
1741
|
+
getReceipt() {
|
|
1742
|
+
return this.stateManager.getReceipt();
|
|
1743
|
+
}
|
|
1744
|
+
rescan() {
|
|
1745
|
+
const config = this.stateManager.getConfig();
|
|
1746
|
+
if (!config) {
|
|
1747
|
+
throw new Error("[ConsentSDK] SDK not initialized.");
|
|
1748
|
+
}
|
|
1749
|
+
const isGiven = !!this.getReceipt();
|
|
1750
|
+
return ResourceScanner.runDiagnostic(config, isGiven);
|
|
1751
|
+
}
|
|
1752
|
+
mountPolicy(targetContainer) {
|
|
1753
|
+
const config = this.stateManager.getConfig();
|
|
1754
|
+
if (!config) {
|
|
1755
|
+
throw new Error("[ConsentSDK] SDK not initialized.");
|
|
1756
|
+
}
|
|
1757
|
+
const container = typeof targetContainer === "string" ? document.querySelector(targetContainer) : targetContainer;
|
|
1758
|
+
if (container) {
|
|
1759
|
+
container.innerHTML = PolicyGenerator.renderTable(config);
|
|
1760
|
+
}
|
|
1761
|
+
}
|
|
1762
|
+
saveChoices(choices, source) {
|
|
1763
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _i;
|
|
1764
|
+
const config = this.stateManager.getConfig();
|
|
1765
|
+
if (!config) return;
|
|
1766
|
+
const receipt = createReceipt(
|
|
1767
|
+
config.policyVersion,
|
|
1768
|
+
choices,
|
|
1769
|
+
source,
|
|
1770
|
+
this.stateManager.getReceipt(),
|
|
1771
|
+
(_a = config.security) == null ? void 0 : _a.secretKey
|
|
1772
|
+
);
|
|
1773
|
+
this.stateManager.updateChoices(receipt);
|
|
1774
|
+
const cookieName = ((_b = config.storage) == null ? void 0 : _b.name) || "site_consent";
|
|
1775
|
+
const receiptJson = JSON.stringify(receipt);
|
|
1776
|
+
if (((_c = config.storage) == null ? void 0 : _c.type) === "memory") {
|
|
1777
|
+
MemoryStore.set(cookieName, receiptJson);
|
|
1778
|
+
} else {
|
|
1779
|
+
CookieStore.set(cookieName, receiptJson, {
|
|
1780
|
+
path: ((_d = config.storage) == null ? void 0 : _d.path) || "/",
|
|
1781
|
+
maxAgeDays: (_f = (_e = config.consent) == null ? void 0 : _e.maxAgeDays) != null ? _f : 365,
|
|
1782
|
+
sameSite: ((_g = config.storage) == null ? void 0 : _g.sameSite) || "Lax",
|
|
1783
|
+
secure: (_h = config.storage) == null ? void 0 : _h.secure
|
|
1784
|
+
});
|
|
1785
|
+
}
|
|
1786
|
+
GoogleConsentAdapter.update(choices);
|
|
1787
|
+
this.blockerRegistry.init(
|
|
1788
|
+
(cat) => this.has(cat),
|
|
1789
|
+
(srv) => this.hasService(srv)
|
|
1790
|
+
);
|
|
1791
|
+
if (((_i = config.logging) == null ? void 0 : _i.enabled) && config.logging.endpoint) {
|
|
1792
|
+
try {
|
|
1793
|
+
fetch(config.logging.endpoint, {
|
|
1794
|
+
method: "POST",
|
|
1795
|
+
headers: { "Content-Type": "application/json" },
|
|
1796
|
+
body: receiptJson
|
|
1797
|
+
}).catch(() => {
|
|
1798
|
+
});
|
|
1799
|
+
} catch (e) {
|
|
1800
|
+
}
|
|
1801
|
+
}
|
|
1802
|
+
this.banner.remove();
|
|
1803
|
+
this.eventBus.emit("consent:changed", { choices, receipt });
|
|
1804
|
+
}
|
|
1805
|
+
showBanner() {
|
|
1806
|
+
const config = this.stateManager.getConfig();
|
|
1807
|
+
if (!config) return;
|
|
1808
|
+
this.banner.render(config, {
|
|
1809
|
+
onAcceptAll: () => this.acceptAll(),
|
|
1810
|
+
onRejectAll: () => this.rejectAll(),
|
|
1811
|
+
onConfigure: () => this.openPreferences()
|
|
1812
|
+
});
|
|
1813
|
+
this.eventBus.emit("banner:shown", void 0);
|
|
1814
|
+
}
|
|
1815
|
+
setupGlobalRevocationTrigger() {
|
|
1816
|
+
if (typeof document === "undefined") return;
|
|
1817
|
+
document.addEventListener("click", (e) => {
|
|
1818
|
+
const target = e.target;
|
|
1819
|
+
if (target == null ? void 0 : target.closest("[data-consent-open]")) {
|
|
1820
|
+
e.preventDefault();
|
|
1821
|
+
this.openPreferences();
|
|
1822
|
+
}
|
|
1823
|
+
});
|
|
1824
|
+
}
|
|
1825
|
+
};
|
|
1826
|
+
var globalScope = typeof window !== "undefined" ? window : globalThis;
|
|
1827
|
+
if (!globalScope.__ConsentSDK_Instance__) {
|
|
1828
|
+
globalScope.__ConsentSDK_Instance__ = new ConsentEngine();
|
|
1829
|
+
}
|
|
1830
|
+
var Consent = globalScope.__ConsentSDK_Instance__;
|
|
1831
|
+
|
|
1832
|
+
// src/wrappers/react.ts
|
|
1833
|
+
function useConsent(category) {
|
|
1834
|
+
const [allowed, setAllowed] = (0, import_react.useState)(() => Consent.has(category));
|
|
1835
|
+
(0, import_react.useEffect)(() => {
|
|
1836
|
+
const update = () => {
|
|
1837
|
+
setAllowed(Consent.has(category));
|
|
1838
|
+
};
|
|
1839
|
+
update();
|
|
1840
|
+
const unsub1 = Consent.on("consent:changed", update);
|
|
1841
|
+
const unsub2 = Consent.on("consent:accepted", update);
|
|
1842
|
+
const unsub3 = Consent.on("consent:rejected", update);
|
|
1843
|
+
const unsub4 = Consent.on("consent:withdrawn", update);
|
|
1844
|
+
const unsub5 = Consent.on("ready", update);
|
|
1845
|
+
return () => {
|
|
1846
|
+
unsub1();
|
|
1847
|
+
unsub2();
|
|
1848
|
+
unsub3();
|
|
1849
|
+
unsub4();
|
|
1850
|
+
unsub5();
|
|
1851
|
+
};
|
|
1852
|
+
}, [category]);
|
|
1853
|
+
return allowed;
|
|
1854
|
+
}
|
|
1855
|
+
function useConsentService(serviceId) {
|
|
1856
|
+
const [allowed, setAllowed] = (0, import_react.useState)(() => Consent.hasService(serviceId));
|
|
1857
|
+
(0, import_react.useEffect)(() => {
|
|
1858
|
+
const update = () => {
|
|
1859
|
+
setAllowed(Consent.hasService(serviceId));
|
|
1860
|
+
};
|
|
1861
|
+
update();
|
|
1862
|
+
const unsub1 = Consent.on("consent:changed", update);
|
|
1863
|
+
const unsub2 = Consent.on("consent:accepted", update);
|
|
1864
|
+
const unsub3 = Consent.on("consent:rejected", update);
|
|
1865
|
+
const unsub4 = Consent.on("consent:withdrawn", update);
|
|
1866
|
+
const unsub5 = Consent.on("ready", update);
|
|
1867
|
+
return () => {
|
|
1868
|
+
unsub1();
|
|
1869
|
+
unsub2();
|
|
1870
|
+
unsub3();
|
|
1871
|
+
unsub4();
|
|
1872
|
+
unsub5();
|
|
1873
|
+
};
|
|
1874
|
+
}, [serviceId]);
|
|
1875
|
+
return allowed;
|
|
1876
|
+
}
|
|
1877
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
1878
|
+
0 && (module.exports = {
|
|
1879
|
+
useConsent,
|
|
1880
|
+
useConsentService
|
|
1881
|
+
});
|
|
1882
|
+
//# sourceMappingURL=react.cjs.map
|