Initial reliable release of consent engine

This commit is contained in:
Deyson Thomé
2026-09-02 14:19:05 -03:00
commit 9e1082d47b
22 changed files with 3377 additions and 0 deletions
+216
View File
@@ -0,0 +1,216 @@
/**
* Cookie Consent Core
* storage.js
*
* Cookie-based persistence for consent state.
* This module deliberately contains no consent/business operations.
*/
const DEFAULT_COOKIE_NAME = "cc_consent";
const DEFAULT_DURATION_DAYS = 180;
const DEFAULT_PATH = "/";
const DEFAULT_SAME_SITE = "Lax";
function isBrowser() {
return typeof document !== "undefined";
}
function encode(value) {
return encodeURIComponent(value);
}
function decode(value) {
try {
return decodeURIComponent(value);
} catch {
return null;
}
}
function buildAttributes(options = {}) {
const {
duration,
path = DEFAULT_PATH,
domain,
sameSite = DEFAULT_SAME_SITE,
secure,
partitioned = false
} = options;
const attributes = [];
if (typeof duration === "number" && Number.isFinite(duration)) {
if (duration <= 0) {
attributes.push("Max-Age=0");
} else {
const maxAge = Math.floor(duration * 24 * 60 * 60);
attributes.push(`Max-Age=${maxAge}`);
}
}
if (path) {
attributes.push(`Path=${path}`);
}
if (domain) {
attributes.push(`Domain=${domain}`);
}
if (sameSite) {
attributes.push(`SameSite=${sameSite}`);
}
const shouldSecure =
secure === true ||
(secure === undefined &&
typeof location !== "undefined" &&
location.protocol === "https:");
if (shouldSecure) {
attributes.push("Secure");
}
if (partitioned) {
attributes.push("Partitioned");
}
return attributes.join("; ");
}
export function createCookieStorage(options = {}) {
const config = {
name: options.name || DEFAULT_COOKIE_NAME,
duration:
typeof options.duration === "number"
? options.duration
: DEFAULT_DURATION_DAYS,
path: options.path || DEFAULT_PATH,
domain: options.domain,
sameSite: options.sameSite || DEFAULT_SAME_SITE,
secure: options.secure,
partitioned: options.partitioned === true
};
return Object.freeze({
name: config.name,
getRaw() {
if (!isBrowser()) {
return null;
}
const prefix = `${encode(config.name)}=`;
const cookies = document.cookie ? document.cookie.split("; ") : [];
for (const item of cookies) {
if (item.startsWith(prefix)) {
return decode(item.slice(prefix.length));
}
}
return null;
},
get() {
const raw = this.getRaw();
if (!raw) {
return null;
}
try {
return JSON.parse(raw);
} catch {
return null;
}
},
set(payload) {
if (!isBrowser()) {
return false;
}
let serialized;
try {
serialized = JSON.stringify(payload);
} catch {
return false;
}
const attributes = buildAttributes(config);
document.cookie =
`${encode(config.name)}=${encode(serialized)}` +
(attributes ? `; ${attributes}` : "");
return this.getRaw() !== null;
},
remove() {
if (!isBrowser()) {
return false;
}
const attributes = buildAttributes({
...config,
duration: 0
});
document.cookie =
`${encode(config.name)}=` +
(attributes ? `; ${attributes}` : "");
return this.getRaw() === null;
},
getConfig() {
return { ...config };
}
});
}
/**
* Validates the persisted envelope before the consent engine attempts to
* interpret its contents. Category semantics are validated separately.
*/
export function isStoragePayload(value) {
if (!value || typeof value !== "object" || Array.isArray(value)) {
return false;
}
if (typeof value.engineVersion !== "string" || !value.engineVersion) {
return false;
}
if (typeof value.policyVersion !== "string" || !value.policyVersion) {
return false;
}
if (!Number.isFinite(value.timestamp)) {
return false;
}
if (!value.categories ||
typeof value.categories !== "object" ||
Array.isArray(value.categories)) {
return false;
}
if (typeof value.jurisdiction !== "string" || !value.jurisdiction) {
return false;
}
if (typeof value.gpc !== "boolean") {
return false;
}
return true;
}
export const STORAGE_DEFAULTS = Object.freeze({
name: DEFAULT_COOKIE_NAME,
duration: DEFAULT_DURATION_DAYS,
path: DEFAULT_PATH,
sameSite: DEFAULT_SAME_SITE
});