@nortic/citronkaka-service 0.0.2-beta.32

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.
@@ -0,0 +1,156 @@
1
+ declare class CookieCleaner {
2
+ /**
3
+ * Cleans up cookies based on a list of blacklist patterns.
4
+ * @param blacklist An array of RegExp patterns for cookies to delete.
5
+ */
6
+ static run(blacklist: RegExp[], persistentService?: typeof CookieService): void;
7
+ }
8
+
9
+ declare class CookieConsent {
10
+ private config;
11
+ private listeners;
12
+ private persistentService;
13
+ private cookieCleaner;
14
+ private cookieInterceptor;
15
+ private scriptInterceptor;
16
+ constructor(config?: CookieConsentConfig, listeners?: Record<CookieConsentEvent, CookieConsentEventListener[]>, persistentService?: typeof CookieService, cookieCleaner?: typeof CookieCleaner, cookieInterceptor?: typeof CookieInterceptor, scriptInterceptor?: typeof ScriptInterceptor);
17
+ /**
18
+ * Retrieves the current cookie consent model from persistent storage.
19
+ * If the stored value is invalid, it falls back to the default model.
20
+ */
21
+ get model(): CookieConsentModel;
22
+ /**
23
+ * Updates the cookie consent model in persistent storage after validation.
24
+ * @param value The new model to save. Must pass validation as a CookieConsentModel.
25
+ * @throws Error if the provided model is invalid.
26
+ */
27
+ private set model(value);
28
+ /**
29
+ * Update the cookie consent model and notify listeners.
30
+ * @param consent The new cookie consent values.
31
+ */
32
+ update(consent: CookieConsentValue): void;
33
+ /**
34
+ * Register an event listener for a specific event.
35
+ * @param eventName The name of the event.
36
+ * @param listener The listener function to register.
37
+ */
38
+ on(eventName: CookieConsentEvent, listener: CookieConsentEventListener): void;
39
+ /**
40
+ * Notify all listeners for a specific event.
41
+ * @param eventName The name of the event to notify.
42
+ */
43
+ private notifyListeners;
44
+ /**
45
+ * Generates a blacklist of cookies and scripts to block based on user consent.
46
+ * @returns An object containing arrays of cookies and scripts to block.
47
+ */
48
+ private get blacklist();
49
+ /**
50
+ * Validates whether a value conforms to the CookieConsentModel interface.
51
+ * @param value The value to validate.
52
+ * @returns True if the value is a valid CookieConsentModel, otherwise false.
53
+ */
54
+ private isCookieConsentModel;
55
+ }
56
+
57
+ declare interface CookieConsentConfig {
58
+ domain: string;
59
+ defaultModel: CookieConsentModel;
60
+ persistentKey: string;
61
+ persistentExpirationDays: number;
62
+ categories: string[];
63
+ services: {
64
+ name: string;
65
+ category: string;
66
+ scripts: RegExp[];
67
+ cookies: RegExp[];
68
+ }[];
69
+ }
70
+
71
+ declare type CookieConsentEvent = 'update';
72
+
73
+ declare type CookieConsentEventListener = (model: CookieConsentModel) => void;
74
+
75
+ declare interface CookieConsentModel {
76
+ useraction: boolean;
77
+ value: CookieConsentValue;
78
+ }
79
+
80
+ export declare interface CookieConsentValue {
81
+ necessary: true;
82
+ functional: boolean;
83
+ analytics: boolean;
84
+ marketing: boolean;
85
+ }
86
+
87
+ declare class CookieInterceptor {
88
+ /**
89
+ * Starts the cookie interception process to block cookies based on a blacklist.
90
+ * @param blacklist An array of RegExp patterns for cookie names to block.
91
+ */
92
+ static run(blacklist: RegExp[]): void;
93
+ }
94
+
95
+ declare class CookieService {
96
+ private static createCookieString;
97
+ /**
98
+ * Sets a cookie with the specified options.
99
+ * @param name The name of the cookie.
100
+ * @param value The value of the cookie (string or object).
101
+ * @param days Number of days until the cookie expires.
102
+ * @param domain Optional domain for the cookie.
103
+ */
104
+ static set<T extends string | object>(name: string, value: T, days: number, domain?: string): void;
105
+ /**
106
+ * Retrieves the value of a cookie by name.
107
+ * @param name The name of the cookie.
108
+ * @returns The value of the cookie or null if not found.
109
+ */
110
+ static get<T extends string | object>(name: string): T | null;
111
+ /**
112
+ * Deletes a cookie by name.
113
+ * @param name The name of the cookie.
114
+ * @param domain Optional domain for the cookie.
115
+ */
116
+ static delete(name: string, domain?: string): void;
117
+ }
118
+
119
+ declare const _default: CookieConsent;
120
+ export default _default;
121
+
122
+ declare class ScriptInterceptor {
123
+ /**
124
+ * Starts the script interception process with a list of blacklist patterns for script `src` attributes.
125
+ * @param blacklist An array of RegExp patterns for script `src` attributes to block.
126
+ */
127
+ static run(blacklist: RegExp[]): void;
128
+ /**
129
+ * Determines whether a script should be blocked based on its `src` attribute.
130
+ * @param blacklist An array of RegExp patterns to match against the script `src`.
131
+ * @param src The `src` attribute of the script to check.
132
+ * @returns True if the script should be blocked, false otherwise.
133
+ */
134
+ private static shouldBlockScript;
135
+ /**
136
+ * Intercepts the creation of `<script>` elements via `document.createElement`.
137
+ * Blocks scripts with `src` matching a blacklist pattern.
138
+ * @param blacklist An array of RegExp patterns for script `src` attributes to block.
139
+ */
140
+ private static interceptScriptCreation;
141
+ /**
142
+ * Observes dynamically added `<script>` elements in the DOM and blocks them if necessary.
143
+ * Prevents execution of blocked scripts by attaching a `beforescriptexecute` listener (Firefox specific fix).
144
+ * @param blacklist An array of RegExp patterns for script `src` attributes to block.
145
+ */
146
+ private static observeDynamicScripts;
147
+ }
148
+
149
+ export { }
150
+
151
+
152
+ declare global {
153
+ interface Window {
154
+ Citronkaka: InstanceType<typeof CookieConsent>;
155
+ }
156
+ }
@@ -0,0 +1,269 @@
1
+ const f = { BASE_URL: "/", DEV: !1, DOMAIN: "localhost", MODE: "development", NODE_ENV: "development", PROD: !0, SSR: !1 }, { DOMAIN: m } = f, k = {
2
+ domain: m,
3
+ defaultModel: {
4
+ useraction: !1,
5
+ value: { necessary: !0, functional: !1, analytics: !1, marketing: !1 }
6
+ },
7
+ persistentKey: "nortic_cookie_consent",
8
+ persistentExpirationDays: 365,
9
+ categories: ["necessary", "functional", "analytics", "marketing"],
10
+ services: [
11
+ { name: "Translations", category: "functional", scripts: [], cookies: [/^i18n_redirected/] },
12
+ { name: "Color mode", category: "functional", scripts: [], cookies: [/^color-mode/] }
13
+ ]
14
+ };
15
+ class p {
16
+ static createCookieString(e, t, o = {}) {
17
+ const { expires: i, path: r = "/", sameSite: s = "Strict", domain: n } = o, c = encodeURIComponent(t), u = i ? `; expires=${i.toUTCString()}` : "", d = n ? `; domain=${n}` : "";
18
+ return `${e}=${c}${u}; path=${r}; SameSite=${s}${d}`;
19
+ }
20
+ /**
21
+ * Sets a cookie with the specified options.
22
+ * @param name The name of the cookie.
23
+ * @param value The value of the cookie (string or object).
24
+ * @param days Number of days until the cookie expires.
25
+ * @param domain Optional domain for the cookie.
26
+ */
27
+ static set(e, t, o, i) {
28
+ const r = typeof t == "string" ? t : JSON.stringify(t), s = new Date(Date.now() + o * 24 * 60 * 60 * 1e3);
29
+ document.cookie = this.createCookieString(e, r, { expires: s, domain: i });
30
+ }
31
+ /**
32
+ * Retrieves the value of a cookie by name.
33
+ * @param name The name of the cookie.
34
+ * @returns The value of the cookie or null if not found.
35
+ */
36
+ static get(e) {
37
+ const t = document.cookie.match(new RegExp(`(^| )${e}=([^;]+)`));
38
+ if (!t)
39
+ return null;
40
+ const o = decodeURIComponent(t[2]);
41
+ try {
42
+ return JSON.parse(o);
43
+ } catch {
44
+ return o;
45
+ }
46
+ }
47
+ /**
48
+ * Deletes a cookie by name.
49
+ * @param name The name of the cookie.
50
+ * @param domain Optional domain for the cookie.
51
+ */
52
+ static delete(e, t) {
53
+ document.cookie = this.createCookieString(e, "", {
54
+ expires: /* @__PURE__ */ new Date(0),
55
+ domain: t
56
+ });
57
+ }
58
+ }
59
+ class h {
60
+ /**
61
+ * Cleans up cookies based on a list of blacklist patterns.
62
+ * @param blacklist An array of RegExp patterns for cookies to delete.
63
+ */
64
+ static run(e, t = p) {
65
+ function o(i) {
66
+ document.cookie.split(";").forEach((r) => {
67
+ const s = r.split("=")[0].trim();
68
+ i.test(s) && (t.delete(s), console.warn(`Removed cookie: ${s}`));
69
+ });
70
+ }
71
+ e.forEach((i) => {
72
+ o(i);
73
+ });
74
+ }
75
+ }
76
+ class y {
77
+ /**
78
+ * Starts the cookie interception process to block cookies based on a blacklist.
79
+ * @param blacklist An array of RegExp patterns for cookie names to block.
80
+ */
81
+ static run(e) {
82
+ const t = Object.getOwnPropertyDescriptor(Document.prototype, "cookie");
83
+ if (!t) {
84
+ console.error("Failed to retrieve the cookie property descriptor.");
85
+ return;
86
+ }
87
+ Object.defineProperty(document, "cookie", {
88
+ configurable: !0,
89
+ enumerable: !0,
90
+ /**
91
+ * Getter for `document.cookie`.
92
+ * @returns The current cookies as a string.
93
+ */
94
+ get() {
95
+ var o;
96
+ return ((o = t.get) == null ? void 0 : o.call(document)) || "";
97
+ },
98
+ /**
99
+ * Setter for `document.cookie`. Intercepts and blocks cookies based on the blacklist.
100
+ * @param cookieString The cookie string to be set.
101
+ */
102
+ set(o) {
103
+ var n;
104
+ const i = o.split("=")[0].trim(), r = o.match(/expires=([^;]+)/i);
105
+ (r ? new Date(r[1]).getTime() < Date.now() : !1) || !e.some((c) => c.test(i)) ? (n = t.set) == null || n.call(document, o) : console.warn(`Blocked cookie: ${i}`);
106
+ }
107
+ });
108
+ }
109
+ }
110
+ class l {
111
+ /**
112
+ * Starts the script interception process with a list of blacklist patterns for script `src` attributes.
113
+ * @param blacklist An array of RegExp patterns for script `src` attributes to block.
114
+ */
115
+ static run(e) {
116
+ this.interceptScriptCreation(e), this.observeDynamicScripts(e);
117
+ }
118
+ /**
119
+ * Determines whether a script should be blocked based on its `src` attribute.
120
+ * @param blacklist An array of RegExp patterns to match against the script `src`.
121
+ * @param src The `src` attribute of the script to check.
122
+ * @returns True if the script should be blocked, false otherwise.
123
+ */
124
+ static shouldBlockScript(e, t) {
125
+ return e.some((o) => o.test(t));
126
+ }
127
+ /**
128
+ * Intercepts the creation of `<script>` elements via `document.createElement`.
129
+ * Blocks scripts with `src` matching a blacklist pattern.
130
+ * @param blacklist An array of RegExp patterns for script `src` attributes to block.
131
+ */
132
+ static interceptScriptCreation(e) {
133
+ const t = document.createElement.bind(document);
134
+ document.createElement = function(o) {
135
+ const i = t(o);
136
+ if (o.toLowerCase() === "script") {
137
+ const r = i, s = Object.getOwnPropertyDescriptor(
138
+ HTMLScriptElement.prototype,
139
+ "src"
140
+ );
141
+ s && Object.defineProperty(r, "src", {
142
+ configurable: !0,
143
+ enumerable: !0,
144
+ get() {
145
+ var n;
146
+ return (n = s.get) == null ? void 0 : n.call(r);
147
+ },
148
+ set(n) {
149
+ var c;
150
+ if (l.shouldBlockScript(e, n)) {
151
+ console.warn(`Blocked script: ${n}`), r.type = "javascript/blocked";
152
+ return;
153
+ }
154
+ (c = s.set) == null || c.call(r, n);
155
+ }
156
+ });
157
+ }
158
+ return i;
159
+ };
160
+ }
161
+ /**
162
+ * Observes dynamically added `<script>` elements in the DOM and blocks them if necessary.
163
+ * Prevents execution of blocked scripts by attaching a `beforescriptexecute` listener (Firefox specific fix).
164
+ * @param blacklist An array of RegExp patterns for script `src` attributes to block.
165
+ */
166
+ static observeDynamicScripts(e) {
167
+ new MutationObserver((o) => {
168
+ o.forEach(({ addedNodes: i }) => {
169
+ i.forEach((r) => {
170
+ if (r.nodeType === Node.ELEMENT_NODE && r.tagName === "SCRIPT") {
171
+ const s = r;
172
+ if (s.src && this.shouldBlockScript(e, s.src)) {
173
+ console.warn(`Blocked dynamically added script: ${s.src}`), s.type = "javascript/blocked";
174
+ const n = (c) => {
175
+ s.getAttribute("type") === "javascript/blocked" && (c.preventDefault(), console.warn(`Prevented execution of blocked script: ${s.src}`)), s.removeEventListener("beforescriptexecute", n);
176
+ };
177
+ s.addEventListener("beforescriptexecute", n);
178
+ }
179
+ }
180
+ });
181
+ });
182
+ }).observe(document.documentElement, {
183
+ childList: !0,
184
+ subtree: !0
185
+ });
186
+ }
187
+ }
188
+ class g {
189
+ constructor(e = k, t = { update: [] }, o = p, i = h, r = y, s = l) {
190
+ this.config = e, this.listeners = t, this.persistentService = o, this.cookieCleaner = i, this.cookieInterceptor = r, this.scriptInterceptor = s, i.run(this.blacklist.cookies), r.run(this.blacklist.cookies), s.run(this.blacklist.scripts);
191
+ }
192
+ /**
193
+ * Retrieves the current cookie consent model from persistent storage.
194
+ * If the stored value is invalid, it falls back to the default model.
195
+ */
196
+ get model() {
197
+ const e = this.persistentService.get(this.config.persistentKey);
198
+ return this.isCookieConsentModel(e) ? e : this.config.defaultModel;
199
+ }
200
+ /**
201
+ * Updates the cookie consent model in persistent storage after validation.
202
+ * @param value The new model to save. Must pass validation as a CookieConsentModel.
203
+ * @throws Error if the provided model is invalid.
204
+ */
205
+ set model(e) {
206
+ if (!this.isCookieConsentModel(e))
207
+ throw new Error("Invalid model: The provided value is not a valid CookieConsentModel.");
208
+ this.persistentService.set(
209
+ this.config.persistentKey,
210
+ e,
211
+ this.config.persistentExpirationDays,
212
+ this.config.domain
213
+ );
214
+ }
215
+ /**
216
+ * Update the cookie consent model and notify listeners.
217
+ * @param consent The new cookie consent values.
218
+ */
219
+ update(e) {
220
+ this.model = { useraction: !0, value: e }, this.notifyListeners("update");
221
+ }
222
+ /**
223
+ * Register an event listener for a specific event.
224
+ * @param eventName The name of the event.
225
+ * @param listener The listener function to register.
226
+ */
227
+ on(e, t) {
228
+ this.listeners[e].push(t);
229
+ }
230
+ /**
231
+ * Notify all listeners for a specific event.
232
+ * @param eventName The name of the event to notify.
233
+ */
234
+ notifyListeners(e) {
235
+ this.listeners[e].forEach((t) => t(this.model));
236
+ }
237
+ /**
238
+ * Generates a blacklist of cookies and scripts to block based on user consent.
239
+ * @returns An object containing arrays of cookies and scripts to block.
240
+ */
241
+ get blacklist() {
242
+ return this.config.services.reduce((e, t) => {
243
+ if (this.model.value[t.category] === !1) {
244
+ const { cookies: o, scripts: i } = t;
245
+ e.cookies = e.cookies.concat(o), e.scripts = e.scripts.concat(i);
246
+ }
247
+ return e;
248
+ }, { cookies: [], scripts: [] });
249
+ }
250
+ /**
251
+ * Validates whether a value conforms to the CookieConsentModel interface.
252
+ * @param value The value to validate.
253
+ * @returns True if the value is a valid CookieConsentModel, otherwise false.
254
+ */
255
+ isCookieConsentModel(e) {
256
+ if (typeof e != "object" || e === null)
257
+ return !1;
258
+ const t = e;
259
+ if (typeof t.useraction != "boolean" || typeof t.value != "object" || t.value === null)
260
+ return !1;
261
+ const o = t.value;
262
+ return o.necessary === !0 && typeof o.functional == "boolean" && typeof o.analytics == "boolean" && typeof o.marketing == "boolean";
263
+ }
264
+ }
265
+ window.Citronkaka = window.Citronkaka || new g();
266
+ const b = window.Citronkaka;
267
+ export {
268
+ b as default
269
+ };
@@ -0,0 +1 @@
1
+ var Citronkaka=function(){"use strict";const u={BASE_URL:"/",DEV:!1,DOMAIN:"localhost",MODE:"development",NODE_ENV:"development",PROD:!0,SSR:!1},{DOMAIN:d}=u,f={domain:d,defaultModel:{useraction:!1,value:{necessary:!0,functional:!1,analytics:!1,marketing:!1}},persistentKey:"nortic_cookie_consent",persistentExpirationDays:365,categories:["necessary","functional","analytics","marketing"],services:[{name:"Translations",category:"functional",scripts:[],cookies:[/^i18n_redirected/]},{name:"Color mode",category:"functional",scripts:[],cookies:[/^color-mode/]}]};class p{static createCookieString(e,t,o={}){const{expires:s,path:r="/",sameSite:i="Strict",domain:n}=o,c=encodeURIComponent(t),y=s?`; expires=${s.toUTCString()}`:"",g=n?`; domain=${n}`:"";return`${e}=${c}${y}; path=${r}; SameSite=${i}${g}`}static set(e,t,o,s){const r=typeof t=="string"?t:JSON.stringify(t),i=new Date(Date.now()+o*24*60*60*1e3);document.cookie=this.createCookieString(e,r,{expires:i,domain:s})}static get(e){const t=document.cookie.match(new RegExp(`(^| )${e}=([^;]+)`));if(!t)return null;const o=decodeURIComponent(t[2]);try{return JSON.parse(o)}catch{return o}}static delete(e,t){document.cookie=this.createCookieString(e,"",{expires:new Date(0),domain:t})}}class m{static run(e,t=p){function o(s){document.cookie.split(";").forEach(r=>{const i=r.split("=")[0].trim();s.test(i)&&(t.delete(i),console.warn(`Removed cookie: ${i}`))})}e.forEach(s=>{o(s)})}}class k{static run(e){const t=Object.getOwnPropertyDescriptor(Document.prototype,"cookie");if(!t){console.error("Failed to retrieve the cookie property descriptor.");return}Object.defineProperty(document,"cookie",{configurable:!0,enumerable:!0,get(){var o;return((o=t.get)==null?void 0:o.call(document))||""},set(o){var n;const s=o.split("=")[0].trim(),r=o.match(/expires=([^;]+)/i);(r?new Date(r[1]).getTime()<Date.now():!1)||!e.some(c=>c.test(s))?(n=t.set)==null||n.call(document,o):console.warn(`Blocked cookie: ${s}`)}})}}class a{static run(e){this.interceptScriptCreation(e),this.observeDynamicScripts(e)}static shouldBlockScript(e,t){return e.some(o=>o.test(t))}static interceptScriptCreation(e){const t=document.createElement.bind(document);document.createElement=function(o){const s=t(o);if(o.toLowerCase()==="script"){const r=s,i=Object.getOwnPropertyDescriptor(HTMLScriptElement.prototype,"src");i&&Object.defineProperty(r,"src",{configurable:!0,enumerable:!0,get(){var n;return(n=i.get)==null?void 0:n.call(r)},set(n){var c;if(a.shouldBlockScript(e,n)){console.warn(`Blocked script: ${n}`),r.type="javascript/blocked";return}(c=i.set)==null||c.call(r,n)}})}return s}}static observeDynamicScripts(e){new MutationObserver(o=>{o.forEach(({addedNodes:s})=>{s.forEach(r=>{if(r.nodeType===Node.ELEMENT_NODE&&r.tagName==="SCRIPT"){const i=r;if(i.src&&this.shouldBlockScript(e,i.src)){console.warn(`Blocked dynamically added script: ${i.src}`),i.type="javascript/blocked";const n=c=>{i.getAttribute("type")==="javascript/blocked"&&(c.preventDefault(),console.warn(`Prevented execution of blocked script: ${i.src}`)),i.removeEventListener("beforescriptexecute",n)};i.addEventListener("beforescriptexecute",n)}}})})}).observe(document.documentElement,{childList:!0,subtree:!0})}}class h{constructor(e=f,t={update:[]},o=p,s=m,r=k,i=a){this.config=e,this.listeners=t,this.persistentService=o,this.cookieCleaner=s,this.cookieInterceptor=r,this.scriptInterceptor=i,s.run(this.blacklist.cookies),r.run(this.blacklist.cookies),i.run(this.blacklist.scripts)}get model(){const e=this.persistentService.get(this.config.persistentKey);return this.isCookieConsentModel(e)?e:this.config.defaultModel}set model(e){if(!this.isCookieConsentModel(e))throw new Error("Invalid model: The provided value is not a valid CookieConsentModel.");this.persistentService.set(this.config.persistentKey,e,this.config.persistentExpirationDays,this.config.domain)}update(e){this.model={useraction:!0,value:e},this.notifyListeners("update")}on(e,t){this.listeners[e].push(t)}notifyListeners(e){this.listeners[e].forEach(t=>t(this.model))}get blacklist(){return this.config.services.reduce((e,t)=>{if(this.model.value[t.category]===!1){const{cookies:o,scripts:s}=t;e.cookies=e.cookies.concat(o),e.scripts=e.scripts.concat(s)}return e},{cookies:[],scripts:[]})}isCookieConsentModel(e){if(typeof e!="object"||e===null)return!1;const t=e;if(typeof t.useraction!="boolean"||typeof t.value!="object"||t.value===null)return!1;const o=t.value;return o.necessary===!0&&typeof o.functional=="boolean"&&typeof o.analytics=="boolean"&&typeof o.marketing=="boolean"}}return window.Citronkaka=window.Citronkaka||new h,window.Citronkaka}();
package/package.json ADDED
@@ -0,0 +1,42 @@
1
+ {
2
+ "name": "@nortic/citronkaka-service",
3
+ "type": "module",
4
+ "version": "0.0.2-beta.32",
5
+ "private": false,
6
+ "publishConfig": {
7
+ "registry": "https://registry.npmjs.org/",
8
+ "access": "public"
9
+ },
10
+ "exports": {
11
+ ".": {
12
+ "types": "./dist/index.d.ts",
13
+ "import": "./dist/index.es.js",
14
+ "default": "./dist/index.iife.js"
15
+ }
16
+ },
17
+ "browser": "./dist/index.iife.js",
18
+ "module": "./dist/index.js",
19
+ "types": "./dist/index.d.ts",
20
+ "files": [
21
+ "dist"
22
+ ],
23
+ "devDependencies": {
24
+ "@tsconfig/node20": "^20.1.4",
25
+ "@types/jsdom": "^21.1.7",
26
+ "@types/node": "^20.14.5",
27
+ "@vue/tsconfig": "^0.7.0",
28
+ "jsdom": "^25.0.1",
29
+ "npm-run-all2": "^6.2.0",
30
+ "typescript": "~5.6.3",
31
+ "vite": "^5.4.8",
32
+ "vite-plugin-dts": "^4.3.0",
33
+ "vitest": "^2.1.8"
34
+ },
35
+ "scripts": {
36
+ "dev": "vite build --watch",
37
+ "build": "run-p type:check \"build:only {@}\" --",
38
+ "build:only": "vite build",
39
+ "type:check": "vue-tsc --build --force",
40
+ "test": "vitest run"
41
+ }
42
+ }