/** * Consentement aux témoins (cookies) — Loi 25 (Québec) / RGPD : * refus par défaut pour tout ce qui n'est pas strictement nécessaire, * choix granulaire, révocable à tout moment, conservé 180 jours. * * Le site ne pose aujourd'hui AUCUN témoin non nécessaire : ce module est la * porte d'entrée obligatoire pour tout futur outil d'analyse ou de marketing * (`hasConsent("analytics")` avant de charger un script). */ export const CONSENT_COOKIE = "phi_consent"; export const CONSENT_VERSION = 1; export const CONSENT_MAX_AGE_S = 180 * 24 * 60 * 60; export const CONSENT_EVENT = "phi-consent-change"; export const CONSENT_OPEN_EVENT = "phi-consent-open"; export const OPTIONAL_CATEGORIES = ["analytics", "marketing"] as const; export type OptionalCategory = (typeof OPTIONAL_CATEGORIES)[number]; export type Category = "necessary" | OptionalCategory; export type Consent = { version: number; date: string; // ISO necessary: true; analytics: boolean; marketing: boolean; }; export function allRejected(): Consent { return { version: CONSENT_VERSION, date: new Date().toISOString(), necessary: true, analytics: false, marketing: false }; } export function allAccepted(): Consent { return { version: CONSENT_VERSION, date: new Date().toISOString(), necessary: true, analytics: true, marketing: true }; } export function parseConsent(raw: string | null | undefined): Consent | null { if (!raw) return null; try { const v = JSON.parse(decodeURIComponent(raw)) as Partial; if (v.version !== CONSENT_VERSION) return null; // nouvelle version = redemander if (typeof v.analytics !== "boolean" || typeof v.marketing !== "boolean") return null; return { version: CONSENT_VERSION, date: typeof v.date === "string" ? v.date : new Date(0).toISOString(), necessary: true, analytics: v.analytics, marketing: v.marketing, }; } catch { return null; } } export function serializeConsent(c: Consent): string { return encodeURIComponent(JSON.stringify(c)); } export function readConsentCookie(cookieHeader: string): Consent | null { const m = cookieHeader.match(new RegExp(`(?:^|;\\s*)${CONSENT_COOKIE}=([^;]*)`)); return parseConsent(m?.[1]); } export function hasConsent(consent: Consent | null, category: Category): boolean { if (category === "necessary") return true; return consent?.[category] === true; }