non-spooky-react-cookie 0.1.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/CHANGELOG.md +1 -0
- package/LICENSE +21 -0
- package/README.md +577 -0
- package/dist/index.cjs +856 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +137 -0
- package/dist/index.d.ts +137 -0
- package/dist/index.js +837 -0
- package/dist/index.js.map +1 -0
- package/dist/resolve-texts-yAIbWOUX.d.cts +456 -0
- package/dist/resolve-texts-yAIbWOUX.d.ts +456 -0
- package/dist/server.cjs +26 -0
- package/dist/server.cjs.map +1 -0
- package/dist/server.d.cts +28 -0
- package/dist/server.d.ts +28 -0
- package/dist/server.js +23 -0
- package/dist/server.js.map +1 -0
- package/dist/storage-Cqed-yMR.cjs +373 -0
- package/dist/storage-Cqed-yMR.cjs.map +1 -0
- package/dist/storage-WH-kuxkg.js +296 -0
- package/dist/storage-WH-kuxkg.js.map +1 -0
- package/dist/styles.css +624 -0
- package/package.json +119 -0
- package/server/README.md +25 -0
- package/server/package.json +5 -0
|
@@ -0,0 +1,373 @@
|
|
|
1
|
+
//#region src/languages/de.ts
|
|
2
|
+
const de = {
|
|
3
|
+
banner: {
|
|
4
|
+
title: "Datenschutzeinstellungen",
|
|
5
|
+
description: "Wir verwenden notwendige Technologien für den Betrieb der Website. Optionale Dienste für Analyse, Marketing und Präferenzen werden erst nach Ihrer Einwilligung geladen.",
|
|
6
|
+
acceptAll: "Alle akzeptieren",
|
|
7
|
+
rejectAll: "Optionale ablehnen",
|
|
8
|
+
settings: "Einstellungen",
|
|
9
|
+
policyLink: "Datenschutzerklärung"
|
|
10
|
+
},
|
|
11
|
+
dialog: {
|
|
12
|
+
title: "Cookie-Einstellungen",
|
|
13
|
+
description: "Wählen Sie aus, welche optionalen Kategorien Sie erlauben möchten. Notwendige Technologien sind für den Betrieb der Website erforderlich.",
|
|
14
|
+
save: "Auswahl speichern",
|
|
15
|
+
close: "Schließen",
|
|
16
|
+
itemsLabel: "Dienste anzeigen"
|
|
17
|
+
},
|
|
18
|
+
footerLink: "Cookie-Einstellungen",
|
|
19
|
+
categories: {
|
|
20
|
+
necessary: {
|
|
21
|
+
title: "Notwendig",
|
|
22
|
+
description: "Erforderlich für den Betrieb der Website und das Speichern Ihrer Datenschutzeinstellungen. Diese Kategorie kann nicht deaktiviert werden."
|
|
23
|
+
},
|
|
24
|
+
preferences: {
|
|
25
|
+
title: "Präferenzen",
|
|
26
|
+
description: "Speichert optionale Einstellungen wie Darstellung oder Sprache, wenn die Website solche Funktionen nutzt."
|
|
27
|
+
},
|
|
28
|
+
analytics: {
|
|
29
|
+
title: "Analyse",
|
|
30
|
+
description: "Hilft uns zu verstehen, wie die Website genutzt wird, damit wir Inhalte und Performance verbessern können."
|
|
31
|
+
},
|
|
32
|
+
marketing: {
|
|
33
|
+
title: "Marketing",
|
|
34
|
+
description: "Erlaubt Marketing- und Werbedienste wie Pixel oder Conversion-Tracking. Derzeit nur aktiv, wenn entsprechende Integrationen eingebunden werden."
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
//#endregion
|
|
39
|
+
//#region src/languages/en.ts
|
|
40
|
+
const en = {
|
|
41
|
+
banner: {
|
|
42
|
+
title: "Privacy preferences",
|
|
43
|
+
description: "We use necessary technologies to run this website. Optional analytics, marketing, and preference services are loaded only after your consent.",
|
|
44
|
+
acceptAll: "Accept all",
|
|
45
|
+
rejectAll: "Reject optional",
|
|
46
|
+
settings: "Settings",
|
|
47
|
+
policyLink: "Privacy policy"
|
|
48
|
+
},
|
|
49
|
+
dialog: {
|
|
50
|
+
title: "Cookie settings",
|
|
51
|
+
description: "Choose which optional categories you want to allow. Necessary technologies are required for the website to work.",
|
|
52
|
+
save: "Save selection",
|
|
53
|
+
close: "Close",
|
|
54
|
+
itemsLabel: "Show services"
|
|
55
|
+
},
|
|
56
|
+
footerLink: "Cookie settings",
|
|
57
|
+
categories: {
|
|
58
|
+
necessary: {
|
|
59
|
+
title: "Necessary",
|
|
60
|
+
description: "Required for website operation and for saving your privacy settings. This category cannot be disabled."
|
|
61
|
+
},
|
|
62
|
+
preferences: {
|
|
63
|
+
title: "Preferences",
|
|
64
|
+
description: "Stores optional settings such as appearance or language when the website provides those features."
|
|
65
|
+
},
|
|
66
|
+
analytics: {
|
|
67
|
+
title: "Analytics",
|
|
68
|
+
description: "Helps us understand how the website is used so we can improve content and performance."
|
|
69
|
+
},
|
|
70
|
+
marketing: {
|
|
71
|
+
title: "Marketing",
|
|
72
|
+
description: "Allows marketing and advertising services such as pixels or conversion tracking. Active only when such integrations are added."
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
};
|
|
76
|
+
//#endregion
|
|
77
|
+
//#region src/languages/pl.ts
|
|
78
|
+
const pl = {
|
|
79
|
+
banner: {
|
|
80
|
+
title: "Ustawienia prywatności",
|
|
81
|
+
description: "Używamy technologii niezbędnych do działania strony. Opcjonalne usługi analityczne, marketingowe i preferencje uruchamiamy dopiero po Twojej zgodzie.",
|
|
82
|
+
acceptAll: "Akceptuj wszystkie",
|
|
83
|
+
rejectAll: "Odrzuć opcjonalne",
|
|
84
|
+
settings: "Ustawienia",
|
|
85
|
+
policyLink: "Polityka prywatności"
|
|
86
|
+
},
|
|
87
|
+
dialog: {
|
|
88
|
+
title: "Ustawienia cookies",
|
|
89
|
+
description: "Wybierz, które opcjonalne kategorie chcesz włączyć. Technologie niezbędne są wymagane do działania strony.",
|
|
90
|
+
save: "Zapisz wybór",
|
|
91
|
+
close: "Zamknij",
|
|
92
|
+
itemsLabel: "Pokaż usługi"
|
|
93
|
+
},
|
|
94
|
+
footerLink: "Ustawienia cookies",
|
|
95
|
+
categories: {
|
|
96
|
+
necessary: {
|
|
97
|
+
title: "Niezbędne",
|
|
98
|
+
description: "Wymagane do działania strony i zapisania ustawień prywatności. Tej kategorii nie można wyłączyć."
|
|
99
|
+
},
|
|
100
|
+
preferences: {
|
|
101
|
+
title: "Preferencje",
|
|
102
|
+
description: "Zapisuje opcjonalne ustawienia, takie jak wygląd lub język, jeśli strona korzysta z takich funkcji."
|
|
103
|
+
},
|
|
104
|
+
analytics: {
|
|
105
|
+
title: "Analityka",
|
|
106
|
+
description: "Pomaga zrozumieć, jak używana jest strona, aby poprawiać treści i wydajność."
|
|
107
|
+
},
|
|
108
|
+
marketing: {
|
|
109
|
+
title: "Marketing",
|
|
110
|
+
description: "Pozwala na usługi marketingowe i reklamowe, takie jak piksele lub śledzenie konwersji. Aktywne tylko po dodaniu takich integracji."
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
};
|
|
114
|
+
//#endregion
|
|
115
|
+
//#region src/resolve-texts.ts
|
|
116
|
+
/** The languages that ship with built-in texts. Anything else gets English. */
|
|
117
|
+
const BUILT_IN_LANGUAGES = [
|
|
118
|
+
"en",
|
|
119
|
+
"de",
|
|
120
|
+
"pl"
|
|
121
|
+
];
|
|
122
|
+
const builtInTexts = {
|
|
123
|
+
de,
|
|
124
|
+
en,
|
|
125
|
+
pl
|
|
126
|
+
};
|
|
127
|
+
function isRecord(value) {
|
|
128
|
+
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
129
|
+
}
|
|
130
|
+
function mergeDeep(target, source) {
|
|
131
|
+
if (!source) return target;
|
|
132
|
+
const result = { ...target };
|
|
133
|
+
Object.entries(source).forEach(([key, value]) => {
|
|
134
|
+
if (value === void 0 || value === null) return;
|
|
135
|
+
const current = result[key];
|
|
136
|
+
if (isRecord(current) && isRecord(value)) {
|
|
137
|
+
result[key] = mergeDeep(current, value);
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
result[key] = value;
|
|
141
|
+
});
|
|
142
|
+
return result;
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* The built-in texts for a language code. Matching ignores case, and a
|
|
146
|
+
* region or script suffix falls back to the base language ("pl-PL" and
|
|
147
|
+
* "de_AT" resolve to "pl" and "de"). Unknown languages get English.
|
|
148
|
+
*
|
|
149
|
+
* The result is plain JSON, so it is safe to use on the server, e.g. as the
|
|
150
|
+
* default values of CMS fields.
|
|
151
|
+
*/
|
|
152
|
+
function getBuiltInTexts(language = "en") {
|
|
153
|
+
const code = language.toLowerCase();
|
|
154
|
+
const base = code.split(/[-_]/)[0] ?? code;
|
|
155
|
+
return builtInTexts[code] ?? builtInTexts[base] ?? en;
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* Resolves the final texts: built-in texts for the chosen language,
|
|
159
|
+
* merged with any user-provided overrides.
|
|
160
|
+
*/
|
|
161
|
+
function resolveTexts(language = "en", texts) {
|
|
162
|
+
return mergeDeep(getBuiltInTexts(language), texts);
|
|
163
|
+
}
|
|
164
|
+
//#endregion
|
|
165
|
+
//#region src/storage/adapters.ts
|
|
166
|
+
const DEFAULT_COOKIE_MAX_AGE = 31536e3;
|
|
167
|
+
/**
|
|
168
|
+
* Finds `key` in a `Cookie` header / `document.cookie` string and returns its
|
|
169
|
+
* decoded value, or `null` when absent. Shared by the browser adapter and the
|
|
170
|
+
* server-side reader so both parse cookies the same way.
|
|
171
|
+
*/
|
|
172
|
+
function findCookie(cookieString, key) {
|
|
173
|
+
const name = encodeURIComponent(key);
|
|
174
|
+
for (const part of cookieString.split(";")) {
|
|
175
|
+
const trimmed = part.trim();
|
|
176
|
+
if (!trimmed.startsWith(`${name}=`)) continue;
|
|
177
|
+
try {
|
|
178
|
+
return decodeURIComponent(trimmed.slice(name.length + 1));
|
|
179
|
+
} catch {
|
|
180
|
+
return null;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
return null;
|
|
184
|
+
}
|
|
185
|
+
/** The default: `window.localStorage`, keyed by `storageKey`. */
|
|
186
|
+
const localStorageAdapter = {
|
|
187
|
+
get: (key) => window.localStorage.getItem(key),
|
|
188
|
+
set: (key, value) => window.localStorage.setItem(key, value),
|
|
189
|
+
remove: (key) => window.localStorage.removeItem(key)
|
|
190
|
+
};
|
|
191
|
+
/**
|
|
192
|
+
* Stores the decision in a cookie named `storageKey`, so a server can read it
|
|
193
|
+
* (see `readPreferencesFromCookies`). The value is the url-encoded JSON state,
|
|
194
|
+
* a few hundred bytes for typical configs — far below the 4 KB cookie limit.
|
|
195
|
+
*/
|
|
196
|
+
function createCookieStorage(options = {}) {
|
|
197
|
+
const { maxAge = DEFAULT_COOKIE_MAX_AGE, domain, path = "/", sameSite = "lax" } = options;
|
|
198
|
+
const isSecure = () => sameSite === "none" || (options.secure ?? window.location.protocol === "https:");
|
|
199
|
+
const attributes = (age) => [
|
|
200
|
+
`Path=${path}`,
|
|
201
|
+
`Max-Age=${age}`,
|
|
202
|
+
`SameSite=${sameSite === "none" ? "None" : sameSite === "strict" ? "Strict" : "Lax"}`,
|
|
203
|
+
domain ? `Domain=${domain}` : "",
|
|
204
|
+
isSecure() ? "Secure" : ""
|
|
205
|
+
].filter(Boolean).join("; ");
|
|
206
|
+
return {
|
|
207
|
+
get: (key) => findCookie(document.cookie, key),
|
|
208
|
+
set: (key, value) => {
|
|
209
|
+
document.cookie = `${encodeURIComponent(key)}=${encodeURIComponent(value)}; ${attributes(maxAge)}`;
|
|
210
|
+
},
|
|
211
|
+
remove: (key) => {
|
|
212
|
+
document.cookie = `${encodeURIComponent(key)}=; ${attributes(0)}`;
|
|
213
|
+
}
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
/**
|
|
217
|
+
* Writes to the cookie **and** localStorage; reads the cookie first and falls
|
|
218
|
+
* back to localStorage. The cookie wins because it is the copy a server can
|
|
219
|
+
* see; the fallback keeps visitors who decided before a site switched from
|
|
220
|
+
* localStorage to cookies from being asked again.
|
|
221
|
+
*/
|
|
222
|
+
function createBothStorage(options) {
|
|
223
|
+
const cookie = createCookieStorage(options);
|
|
224
|
+
const local = localStorageAdapter;
|
|
225
|
+
return {
|
|
226
|
+
get: (key) => cookie.get(key) ?? local.get(key),
|
|
227
|
+
set: (key, value) => {
|
|
228
|
+
cookie.set(key, value);
|
|
229
|
+
local.set(key, value);
|
|
230
|
+
},
|
|
231
|
+
remove: (key) => {
|
|
232
|
+
cookie.remove(key);
|
|
233
|
+
local.remove(key);
|
|
234
|
+
}
|
|
235
|
+
};
|
|
236
|
+
}
|
|
237
|
+
/** Maps the `storage` prop (a kind or a custom adapter) to a storage object. */
|
|
238
|
+
function resolveStorage(storage, cookieOptions) {
|
|
239
|
+
if (typeof storage !== "string") return storage;
|
|
240
|
+
switch (storage) {
|
|
241
|
+
case "cookie": return createCookieStorage(cookieOptions);
|
|
242
|
+
case "both": return createBothStorage(cookieOptions);
|
|
243
|
+
default: return localStorageAdapter;
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
//#endregion
|
|
247
|
+
//#region src/storage/index.ts
|
|
248
|
+
const DEFAULT_STORAGE_KEY = "non-spooky-react-cookie";
|
|
249
|
+
function isPreferencesState(value) {
|
|
250
|
+
if (typeof value !== "object" || value === null) return false;
|
|
251
|
+
const candidate = value;
|
|
252
|
+
return typeof candidate.version === "string" && typeof candidate.updatedAt === "string" && typeof candidate.accepted === "object" && candidate.accepted !== null;
|
|
253
|
+
}
|
|
254
|
+
/**
|
|
255
|
+
* Parses a raw stored value into a `PreferencesState`; `null` when missing,
|
|
256
|
+
* unreadable, or malformed. Shared by the client read and the server read.
|
|
257
|
+
*/
|
|
258
|
+
function parsePreferences(raw) {
|
|
259
|
+
if (!raw) return null;
|
|
260
|
+
try {
|
|
261
|
+
const parsed = JSON.parse(raw);
|
|
262
|
+
if (!isPreferencesState(parsed)) return null;
|
|
263
|
+
return {
|
|
264
|
+
version: parsed.version,
|
|
265
|
+
updatedAt: parsed.updatedAt,
|
|
266
|
+
accepted: { ...parsed.accepted }
|
|
267
|
+
};
|
|
268
|
+
} catch {
|
|
269
|
+
return null;
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
/** Reads the stored preferences; `null` when missing, unreadable, or malformed. */
|
|
273
|
+
function readPreferences(storage, storageKey) {
|
|
274
|
+
if (typeof window === "undefined") return null;
|
|
275
|
+
try {
|
|
276
|
+
return parsePreferences(storage.get(storageKey));
|
|
277
|
+
} catch {
|
|
278
|
+
return null;
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
function writePreferences(storage, storageKey, state) {
|
|
282
|
+
if (typeof window === "undefined") return;
|
|
283
|
+
try {
|
|
284
|
+
storage.set(storageKey, JSON.stringify(state));
|
|
285
|
+
} catch {}
|
|
286
|
+
}
|
|
287
|
+
function removePreferences(storage, storageKey) {
|
|
288
|
+
if (typeof window === "undefined") return;
|
|
289
|
+
try {
|
|
290
|
+
storage.remove(storageKey);
|
|
291
|
+
} catch {}
|
|
292
|
+
}
|
|
293
|
+
//#endregion
|
|
294
|
+
Object.defineProperty(exports, "BUILT_IN_LANGUAGES", {
|
|
295
|
+
enumerable: true,
|
|
296
|
+
get: function() {
|
|
297
|
+
return BUILT_IN_LANGUAGES;
|
|
298
|
+
}
|
|
299
|
+
});
|
|
300
|
+
Object.defineProperty(exports, "DEFAULT_STORAGE_KEY", {
|
|
301
|
+
enumerable: true,
|
|
302
|
+
get: function() {
|
|
303
|
+
return DEFAULT_STORAGE_KEY;
|
|
304
|
+
}
|
|
305
|
+
});
|
|
306
|
+
Object.defineProperty(exports, "createBothStorage", {
|
|
307
|
+
enumerable: true,
|
|
308
|
+
get: function() {
|
|
309
|
+
return createBothStorage;
|
|
310
|
+
}
|
|
311
|
+
});
|
|
312
|
+
Object.defineProperty(exports, "createCookieStorage", {
|
|
313
|
+
enumerable: true,
|
|
314
|
+
get: function() {
|
|
315
|
+
return createCookieStorage;
|
|
316
|
+
}
|
|
317
|
+
});
|
|
318
|
+
Object.defineProperty(exports, "findCookie", {
|
|
319
|
+
enumerable: true,
|
|
320
|
+
get: function() {
|
|
321
|
+
return findCookie;
|
|
322
|
+
}
|
|
323
|
+
});
|
|
324
|
+
Object.defineProperty(exports, "getBuiltInTexts", {
|
|
325
|
+
enumerable: true,
|
|
326
|
+
get: function() {
|
|
327
|
+
return getBuiltInTexts;
|
|
328
|
+
}
|
|
329
|
+
});
|
|
330
|
+
Object.defineProperty(exports, "localStorageAdapter", {
|
|
331
|
+
enumerable: true,
|
|
332
|
+
get: function() {
|
|
333
|
+
return localStorageAdapter;
|
|
334
|
+
}
|
|
335
|
+
});
|
|
336
|
+
Object.defineProperty(exports, "parsePreferences", {
|
|
337
|
+
enumerable: true,
|
|
338
|
+
get: function() {
|
|
339
|
+
return parsePreferences;
|
|
340
|
+
}
|
|
341
|
+
});
|
|
342
|
+
Object.defineProperty(exports, "readPreferences", {
|
|
343
|
+
enumerable: true,
|
|
344
|
+
get: function() {
|
|
345
|
+
return readPreferences;
|
|
346
|
+
}
|
|
347
|
+
});
|
|
348
|
+
Object.defineProperty(exports, "removePreferences", {
|
|
349
|
+
enumerable: true,
|
|
350
|
+
get: function() {
|
|
351
|
+
return removePreferences;
|
|
352
|
+
}
|
|
353
|
+
});
|
|
354
|
+
Object.defineProperty(exports, "resolveStorage", {
|
|
355
|
+
enumerable: true,
|
|
356
|
+
get: function() {
|
|
357
|
+
return resolveStorage;
|
|
358
|
+
}
|
|
359
|
+
});
|
|
360
|
+
Object.defineProperty(exports, "resolveTexts", {
|
|
361
|
+
enumerable: true,
|
|
362
|
+
get: function() {
|
|
363
|
+
return resolveTexts;
|
|
364
|
+
}
|
|
365
|
+
});
|
|
366
|
+
Object.defineProperty(exports, "writePreferences", {
|
|
367
|
+
enumerable: true,
|
|
368
|
+
get: function() {
|
|
369
|
+
return writePreferences;
|
|
370
|
+
}
|
|
371
|
+
});
|
|
372
|
+
|
|
373
|
+
//# sourceMappingURL=storage-Cqed-yMR.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"storage-Cqed-yMR.cjs","names":[],"sources":["../src/languages/de.ts","../src/languages/en.ts","../src/languages/pl.ts","../src/resolve-texts.ts","../src/storage/adapters.ts","../src/storage/index.ts"],"sourcesContent":["import type { Texts } from \"../types\";\n\nexport const de: Texts = {\n banner: {\n title: \"Datenschutzeinstellungen\",\n description:\n \"Wir verwenden notwendige Technologien für den Betrieb der Website. Optionale Dienste für Analyse, Marketing und Präferenzen werden erst nach Ihrer Einwilligung geladen.\",\n acceptAll: \"Alle akzeptieren\",\n rejectAll: \"Optionale ablehnen\",\n settings: \"Einstellungen\",\n policyLink: \"Datenschutzerklärung\",\n },\n dialog: {\n title: \"Cookie-Einstellungen\",\n description:\n \"Wählen Sie aus, welche optionalen Kategorien Sie erlauben möchten. Notwendige Technologien sind für den Betrieb der Website erforderlich.\",\n save: \"Auswahl speichern\",\n close: \"Schließen\",\n itemsLabel: \"Dienste anzeigen\",\n },\n footerLink: \"Cookie-Einstellungen\",\n categories: {\n necessary: {\n title: \"Notwendig\",\n description:\n \"Erforderlich für den Betrieb der Website und das Speichern Ihrer Datenschutzeinstellungen. Diese Kategorie kann nicht deaktiviert werden.\",\n },\n preferences: {\n title: \"Präferenzen\",\n description:\n \"Speichert optionale Einstellungen wie Darstellung oder Sprache, wenn die Website solche Funktionen nutzt.\",\n },\n analytics: {\n title: \"Analyse\",\n description:\n \"Hilft uns zu verstehen, wie die Website genutzt wird, damit wir Inhalte und Performance verbessern können.\",\n },\n marketing: {\n title: \"Marketing\",\n description:\n \"Erlaubt Marketing- und Werbedienste wie Pixel oder Conversion-Tracking. Derzeit nur aktiv, wenn entsprechende Integrationen eingebunden werden.\",\n },\n },\n};\n","import type { Texts } from \"../types\";\n\nexport const en: Texts = {\n banner: {\n title: \"Privacy preferences\",\n description:\n \"We use necessary technologies to run this website. Optional analytics, marketing, and preference services are loaded only after your consent.\",\n acceptAll: \"Accept all\",\n rejectAll: \"Reject optional\",\n settings: \"Settings\",\n policyLink: \"Privacy policy\",\n },\n dialog: {\n title: \"Cookie settings\",\n description:\n \"Choose which optional categories you want to allow. Necessary technologies are required for the website to work.\",\n save: \"Save selection\",\n close: \"Close\",\n itemsLabel: \"Show services\",\n },\n footerLink: \"Cookie settings\",\n categories: {\n necessary: {\n title: \"Necessary\",\n description:\n \"Required for website operation and for saving your privacy settings. This category cannot be disabled.\",\n },\n preferences: {\n title: \"Preferences\",\n description:\n \"Stores optional settings such as appearance or language when the website provides those features.\",\n },\n analytics: {\n title: \"Analytics\",\n description:\n \"Helps us understand how the website is used so we can improve content and performance.\",\n },\n marketing: {\n title: \"Marketing\",\n description:\n \"Allows marketing and advertising services such as pixels or conversion tracking. Active only when such integrations are added.\",\n },\n },\n};\n","import type { Texts } from \"../types\";\n\nexport const pl: Texts = {\n banner: {\n title: \"Ustawienia prywatności\",\n description:\n \"Używamy technologii niezbędnych do działania strony. Opcjonalne usługi analityczne, marketingowe i preferencje uruchamiamy dopiero po Twojej zgodzie.\",\n acceptAll: \"Akceptuj wszystkie\",\n rejectAll: \"Odrzuć opcjonalne\",\n settings: \"Ustawienia\",\n policyLink: \"Polityka prywatności\",\n },\n dialog: {\n title: \"Ustawienia cookies\",\n description:\n \"Wybierz, które opcjonalne kategorie chcesz włączyć. Technologie niezbędne są wymagane do działania strony.\",\n save: \"Zapisz wybór\",\n close: \"Zamknij\",\n itemsLabel: \"Pokaż usługi\",\n },\n footerLink: \"Ustawienia cookies\",\n categories: {\n necessary: {\n title: \"Niezbędne\",\n description:\n \"Wymagane do działania strony i zapisania ustawień prywatności. Tej kategorii nie można wyłączyć.\",\n },\n preferences: {\n title: \"Preferencje\",\n description:\n \"Zapisuje opcjonalne ustawienia, takie jak wygląd lub język, jeśli strona korzysta z takich funkcji.\",\n },\n analytics: {\n title: \"Analityka\",\n description:\n \"Pomaga zrozumieć, jak używana jest strona, aby poprawiać treści i wydajność.\",\n },\n marketing: {\n title: \"Marketing\",\n description:\n \"Pozwala na usługi marketingowe i reklamowe, takie jak piksele lub śledzenie konwersji. Aktywne tylko po dodaniu takich integracji.\",\n },\n },\n};\n","import { de } from \"./languages/de\";\nimport { en } from \"./languages/en\";\nimport { pl } from \"./languages/pl\";\nimport type { DeepPartialNullable, Texts } from \"./types\";\n\n/** The languages that ship with built-in texts. Anything else gets English. */\nexport const BUILT_IN_LANGUAGES = [\"en\", \"de\", \"pl\"] as const;\n\nexport type BuiltInLanguage = (typeof BUILT_IN_LANGUAGES)[number];\n\nconst builtInTexts: Record<BuiltInLanguage, Texts> = {\n de,\n en,\n pl,\n};\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return Boolean(value) && typeof value === \"object\" && !Array.isArray(value);\n}\n\nfunction mergeDeep<T extends Record<string, unknown>>(\n target: T,\n source?: DeepPartialNullable<T> | null,\n): T {\n if (!source) return target;\n\n const result: Record<string, unknown> = { ...target };\n\n Object.entries(source).forEach(([key, value]) => {\n // `null` is how a CMS says \"empty\", so it keeps the built-in text just\n // like a missing key. An empty string is a deliberate value and wins.\n if (value === undefined || value === null) return;\n\n const current = result[key];\n if (isRecord(current) && isRecord(value)) {\n result[key] = mergeDeep(current, value as DeepPartialNullable<typeof current>);\n return;\n }\n\n result[key] = value;\n });\n\n return result as T;\n}\n\n/**\n * The built-in texts for a language code. Matching ignores case, and a\n * region or script suffix falls back to the base language (\"pl-PL\" and\n * \"de_AT\" resolve to \"pl\" and \"de\"). Unknown languages get English.\n *\n * The result is plain JSON, so it is safe to use on the server, e.g. as the\n * default values of CMS fields.\n */\nexport function getBuiltInTexts(language: string = \"en\"): Texts {\n const code = language.toLowerCase();\n const base = code.split(/[-_]/)[0] ?? code;\n\n return (\n builtInTexts[code as BuiltInLanguage] ?? builtInTexts[base as BuiltInLanguage] ?? en\n );\n}\n\n/**\n * Resolves the final texts: built-in texts for the chosen language,\n * merged with any user-provided overrides.\n */\nexport function resolveTexts(\n language: string = \"en\",\n texts?: DeepPartialNullable<Texts> | null,\n): Texts {\n return mergeDeep(getBuiltInTexts(language), texts);\n}\n","import type { CookieStorageOptions, PreferencesStorage, StorageKind } from \"../types\";\n\nconst DEFAULT_COOKIE_MAX_AGE = 60 * 60 * 24 * 365; // 365 days, in seconds\n\n/**\n * Finds `key` in a `Cookie` header / `document.cookie` string and returns its\n * decoded value, or `null` when absent. Shared by the browser adapter and the\n * server-side reader so both parse cookies the same way.\n */\nexport function findCookie(cookieString: string, key: string): string | null {\n const name = encodeURIComponent(key);\n\n for (const part of cookieString.split(\";\")) {\n const trimmed = part.trim();\n if (!trimmed.startsWith(`${name}=`)) continue;\n\n try {\n return decodeURIComponent(trimmed.slice(name.length + 1));\n } catch {\n return null;\n }\n }\n\n return null;\n}\n\n/** The default: `window.localStorage`, keyed by `storageKey`. */\nexport const localStorageAdapter: PreferencesStorage = {\n get: (key) => window.localStorage.getItem(key),\n set: (key, value) => window.localStorage.setItem(key, value),\n remove: (key) => window.localStorage.removeItem(key),\n};\n\n/**\n * Stores the decision in a cookie named `storageKey`, so a server can read it\n * (see `readPreferencesFromCookies`). The value is the url-encoded JSON state,\n * a few hundred bytes for typical configs — far below the 4 KB cookie limit.\n */\nexport function createCookieStorage(\n options: CookieStorageOptions = {},\n): PreferencesStorage {\n const {\n maxAge = DEFAULT_COOKIE_MAX_AGE,\n domain,\n path = \"/\",\n sameSite = \"lax\",\n } = options;\n\n // `SameSite=None` is rejected by browsers without `Secure`.\n const isSecure = () =>\n sameSite === \"none\" || (options.secure ?? window.location.protocol === \"https:\");\n\n // The remove call must repeat Path/Domain, otherwise the browser treats it\n // as a different cookie and keeps the original.\n const attributes = (age: number) =>\n [\n `Path=${path}`,\n `Max-Age=${age}`,\n `SameSite=${sameSite === \"none\" ? \"None\" : sameSite === \"strict\" ? \"Strict\" : \"Lax\"}`,\n domain ? `Domain=${domain}` : \"\",\n isSecure() ? \"Secure\" : \"\",\n ]\n .filter(Boolean)\n .join(\"; \");\n\n return {\n get: (key) => findCookie(document.cookie, key),\n // The Cookie Store API is async and still missing in Safari/Firefox; the\n // synchronous `document.cookie` write keeps the adapter contract simple.\n set: (key, value) => {\n // biome-ignore lint/suspicious/noDocumentCookie: sync write on purpose\n document.cookie = `${encodeURIComponent(key)}=${encodeURIComponent(value)}; ${attributes(maxAge)}`;\n },\n remove: (key) => {\n // biome-ignore lint/suspicious/noDocumentCookie: sync write on purpose\n document.cookie = `${encodeURIComponent(key)}=; ${attributes(0)}`;\n },\n };\n}\n\n/**\n * Writes to the cookie **and** localStorage; reads the cookie first and falls\n * back to localStorage. The cookie wins because it is the copy a server can\n * see; the fallback keeps visitors who decided before a site switched from\n * localStorage to cookies from being asked again.\n */\nexport function createBothStorage(options?: CookieStorageOptions): PreferencesStorage {\n const cookie = createCookieStorage(options);\n const local = localStorageAdapter;\n\n return {\n get: (key) => cookie.get(key) ?? local.get(key),\n set: (key, value) => {\n cookie.set(key, value);\n local.set(key, value);\n },\n remove: (key) => {\n cookie.remove(key);\n local.remove(key);\n },\n };\n}\n\n/** Maps the `storage` prop (a kind or a custom adapter) to a storage object. */\nexport function resolveStorage(\n storage: StorageKind | PreferencesStorage,\n cookieOptions?: CookieStorageOptions,\n): PreferencesStorage {\n if (typeof storage !== \"string\") return storage;\n\n switch (storage) {\n case \"cookie\":\n return createCookieStorage(cookieOptions);\n case \"both\":\n return createBothStorage(cookieOptions);\n default:\n return localStorageAdapter;\n }\n}\n","import type { PreferencesState, PreferencesStorage } from \"../types\";\n\nexport {\n createBothStorage,\n createCookieStorage,\n findCookie,\n localStorageAdapter,\n resolveStorage,\n} from \"./adapters\";\n\nexport const DEFAULT_STORAGE_KEY = \"non-spooky-react-cookie\";\n\nfunction isPreferencesState(value: unknown): value is PreferencesState {\n if (typeof value !== \"object\" || value === null) return false;\n const candidate = value as Record<string, unknown>;\n return (\n typeof candidate.version === \"string\" &&\n typeof candidate.updatedAt === \"string\" &&\n typeof candidate.accepted === \"object\" &&\n candidate.accepted !== null\n );\n}\n\n/**\n * Parses a raw stored value into a `PreferencesState`; `null` when missing,\n * unreadable, or malformed. Shared by the client read and the server read.\n */\nexport function parsePreferences(\n raw: string | null | undefined,\n): PreferencesState | null {\n if (!raw) return null;\n\n try {\n const parsed: unknown = JSON.parse(raw);\n if (!isPreferencesState(parsed)) return null;\n\n return {\n version: parsed.version,\n updatedAt: parsed.updatedAt,\n accepted: { ...parsed.accepted },\n };\n } catch {\n return null;\n }\n}\n\n/** Reads the stored preferences; `null` when missing, unreadable, or malformed. */\nexport function readPreferences(\n storage: PreferencesStorage,\n storageKey: string,\n): PreferencesState | null {\n if (typeof window === \"undefined\") return null;\n\n try {\n return parsePreferences(storage.get(storageKey));\n } catch {\n return null;\n }\n}\n\nexport function writePreferences(\n storage: PreferencesStorage,\n storageKey: string,\n state: PreferencesState,\n): void {\n if (typeof window === \"undefined\") return;\n\n try {\n storage.set(storageKey, JSON.stringify(state));\n } catch {\n // Storage can be unavailable or full (private mode, quota, blocked\n // cookies); the in-memory state is set by the caller, so a failed\n // persist must never crash the decision flow.\n }\n}\n\nexport function removePreferences(storage: PreferencesStorage, storageKey: string): void {\n if (typeof window === \"undefined\") return;\n\n try {\n storage.remove(storageKey);\n } catch {\n // Same as writePreferences: storage errors must never break a reset.\n }\n}\n"],"mappings":";AAEA,MAAa,KAAY;CACvB,QAAQ;EACN,OAAO;EACP,aACE;EACF,WAAW;EACX,WAAW;EACX,UAAU;EACV,YAAY;CACd;CACA,QAAQ;EACN,OAAO;EACP,aACE;EACF,MAAM;EACN,OAAO;EACP,YAAY;CACd;CACA,YAAY;CACZ,YAAY;EACV,WAAW;GACT,OAAO;GACP,aACE;EACJ;EACA,aAAa;GACX,OAAO;GACP,aACE;EACJ;EACA,WAAW;GACT,OAAO;GACP,aACE;EACJ;EACA,WAAW;GACT,OAAO;GACP,aACE;EACJ;CACF;AACF;;;ACzCA,MAAa,KAAY;CACvB,QAAQ;EACN,OAAO;EACP,aACE;EACF,WAAW;EACX,WAAW;EACX,UAAU;EACV,YAAY;CACd;CACA,QAAQ;EACN,OAAO;EACP,aACE;EACF,MAAM;EACN,OAAO;EACP,YAAY;CACd;CACA,YAAY;CACZ,YAAY;EACV,WAAW;GACT,OAAO;GACP,aACE;EACJ;EACA,aAAa;GACX,OAAO;GACP,aACE;EACJ;EACA,WAAW;GACT,OAAO;GACP,aACE;EACJ;EACA,WAAW;GACT,OAAO;GACP,aACE;EACJ;CACF;AACF;;;ACzCA,MAAa,KAAY;CACvB,QAAQ;EACN,OAAO;EACP,aACE;EACF,WAAW;EACX,WAAW;EACX,UAAU;EACV,YAAY;CACd;CACA,QAAQ;EACN,OAAO;EACP,aACE;EACF,MAAM;EACN,OAAO;EACP,YAAY;CACd;CACA,YAAY;CACZ,YAAY;EACV,WAAW;GACT,OAAO;GACP,aACE;EACJ;EACA,aAAa;GACX,OAAO;GACP,aACE;EACJ;EACA,WAAW;GACT,OAAO;GACP,aACE;EACJ;EACA,WAAW;GACT,OAAO;GACP,aACE;EACJ;CACF;AACF;;;;ACrCA,MAAa,qBAAqB;CAAC;CAAM;CAAM;AAAI;AAInD,MAAM,eAA+C;CACnD;CACA;CACA;AACF;AAEA,SAAS,SAAS,OAAkD;CAClE,OAAO,QAAQ,KAAK,KAAK,OAAO,UAAU,YAAY,CAAC,MAAM,QAAQ,KAAK;AAC5E;AAEA,SAAS,UACP,QACA,QACG;CACH,IAAI,CAAC,QAAQ,OAAO;CAEpB,MAAM,SAAkC,EAAE,GAAG,OAAO;CAEpD,OAAO,QAAQ,MAAM,CAAC,CAAC,SAAS,CAAC,KAAK,WAAW;EAG/C,IAAI,UAAU,KAAA,KAAa,UAAU,MAAM;EAE3C,MAAM,UAAU,OAAO;EACvB,IAAI,SAAS,OAAO,KAAK,SAAS,KAAK,GAAG;GACxC,OAAO,OAAO,UAAU,SAAS,KAA4C;GAC7E;EACF;EAEA,OAAO,OAAO;CAChB,CAAC;CAED,OAAO;AACT;;;;;;;;;AAUA,SAAgB,gBAAgB,WAAmB,MAAa;CAC9D,MAAM,OAAO,SAAS,YAAY;CAClC,MAAM,OAAO,KAAK,MAAM,MAAM,CAAC,CAAC,MAAM;CAEtC,OACE,aAAa,SAA4B,aAAa,SAA4B;AAEtF;;;;;AAMA,SAAgB,aACd,WAAmB,MACnB,OACO;CACP,OAAO,UAAU,gBAAgB,QAAQ,GAAG,KAAK;AACnD;;;ACrEA,MAAM,yBAAyB;;;;;;AAO/B,SAAgB,WAAW,cAAsB,KAA4B;CAC3E,MAAM,OAAO,mBAAmB,GAAG;CAEnC,KAAK,MAAM,QAAQ,aAAa,MAAM,GAAG,GAAG;EAC1C,MAAM,UAAU,KAAK,KAAK;EAC1B,IAAI,CAAC,QAAQ,WAAW,GAAG,KAAK,EAAE,GAAG;EAErC,IAAI;GACF,OAAO,mBAAmB,QAAQ,MAAM,KAAK,SAAS,CAAC,CAAC;EAC1D,QAAQ;GACN,OAAO;EACT;CACF;CAEA,OAAO;AACT;;AAGA,MAAa,sBAA0C;CACrD,MAAM,QAAQ,OAAO,aAAa,QAAQ,GAAG;CAC7C,MAAM,KAAK,UAAU,OAAO,aAAa,QAAQ,KAAK,KAAK;CAC3D,SAAS,QAAQ,OAAO,aAAa,WAAW,GAAG;AACrD;;;;;;AAOA,SAAgB,oBACd,UAAgC,CAAC,GACb;CACpB,MAAM,EACJ,SAAS,wBACT,QACA,OAAO,KACP,WAAW,UACT;CAGJ,MAAM,iBACJ,aAAa,WAAW,QAAQ,UAAU,OAAO,SAAS,aAAa;CAIzE,MAAM,cAAc,QAClB;EACE,QAAQ;EACR,WAAW;EACX,YAAY,aAAa,SAAS,SAAS,aAAa,WAAW,WAAW;EAC9E,SAAS,UAAU,WAAW;EAC9B,SAAS,IAAI,WAAW;CAC1B,CAAC,CACE,OAAO,OAAO,CAAC,CACf,KAAK,IAAI;CAEd,OAAO;EACL,MAAM,QAAQ,WAAW,SAAS,QAAQ,GAAG;EAG7C,MAAM,KAAK,UAAU;GAEnB,SAAS,SAAS,GAAG,mBAAmB,GAAG,EAAE,GAAG,mBAAmB,KAAK,EAAE,IAAI,WAAW,MAAM;EACjG;EACA,SAAS,QAAQ;GAEf,SAAS,SAAS,GAAG,mBAAmB,GAAG,EAAE,KAAK,WAAW,CAAC;EAChE;CACF;AACF;;;;;;;AAQA,SAAgB,kBAAkB,SAAoD;CACpF,MAAM,SAAS,oBAAoB,OAAO;CAC1C,MAAM,QAAQ;CAEd,OAAO;EACL,MAAM,QAAQ,OAAO,IAAI,GAAG,KAAK,MAAM,IAAI,GAAG;EAC9C,MAAM,KAAK,UAAU;GACnB,OAAO,IAAI,KAAK,KAAK;GACrB,MAAM,IAAI,KAAK,KAAK;EACtB;EACA,SAAS,QAAQ;GACf,OAAO,OAAO,GAAG;GACjB,MAAM,OAAO,GAAG;EAClB;CACF;AACF;;AAGA,SAAgB,eACd,SACA,eACoB;CACpB,IAAI,OAAO,YAAY,UAAU,OAAO;CAExC,QAAQ,SAAR;EACE,KAAK,UACH,OAAO,oBAAoB,aAAa;EAC1C,KAAK,QACH,OAAO,kBAAkB,aAAa;EACxC,SACE,OAAO;CACX;AACF;;;AC5GA,MAAa,sBAAsB;AAEnC,SAAS,mBAAmB,OAA2C;CACrE,IAAI,OAAO,UAAU,YAAY,UAAU,MAAM,OAAO;CACxD,MAAM,YAAY;CAClB,OACE,OAAO,UAAU,YAAY,YAC7B,OAAO,UAAU,cAAc,YAC/B,OAAO,UAAU,aAAa,YAC9B,UAAU,aAAa;AAE3B;;;;;AAMA,SAAgB,iBACd,KACyB;CACzB,IAAI,CAAC,KAAK,OAAO;CAEjB,IAAI;EACF,MAAM,SAAkB,KAAK,MAAM,GAAG;EACtC,IAAI,CAAC,mBAAmB,MAAM,GAAG,OAAO;EAExC,OAAO;GACL,SAAS,OAAO;GAChB,WAAW,OAAO;GAClB,UAAU,EAAE,GAAG,OAAO,SAAS;EACjC;CACF,QAAQ;EACN,OAAO;CACT;AACF;;AAGA,SAAgB,gBACd,SACA,YACyB;CACzB,IAAI,OAAO,WAAW,aAAa,OAAO;CAE1C,IAAI;EACF,OAAO,iBAAiB,QAAQ,IAAI,UAAU,CAAC;CACjD,QAAQ;EACN,OAAO;CACT;AACF;AAEA,SAAgB,iBACd,SACA,YACA,OACM;CACN,IAAI,OAAO,WAAW,aAAa;CAEnC,IAAI;EACF,QAAQ,IAAI,YAAY,KAAK,UAAU,KAAK,CAAC;CAC/C,QAAQ,CAIR;AACF;AAEA,SAAgB,kBAAkB,SAA6B,YAA0B;CACvF,IAAI,OAAO,WAAW,aAAa;CAEnC,IAAI;EACF,QAAQ,OAAO,UAAU;CAC3B,QAAQ,CAER;AACF"}
|