@el7ven/cookie-kit 0.3.1 → 0.3.3

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,179 @@
1
+ /**
2
+ * Safe Storage Adapter — handles localStorage failures (Safari private mode, quota exceeded)
3
+ * @module StorageAdapter
4
+ */
5
+
6
+ export class StorageAdapter {
7
+ constructor(config = {}) {
8
+ this.storageType = config.storageType || 'localStorage'
9
+ this.storageKey = config.storageKey || 'cookie_consent'
10
+ this.cookieExpireDays = config.consentExpireDays || 365
11
+ this._testStorage()
12
+ }
13
+
14
+ /**
15
+ * Test if storage is available (Safari private mode blocks localStorage)
16
+ */
17
+ _testStorage() {
18
+ this.isLocalStorageAvailable = false
19
+ this.isCookieAvailable = false
20
+
21
+ // Test localStorage
22
+ if (typeof window !== 'undefined' && window.localStorage) {
23
+ try {
24
+ const testKey = '__storage_test__'
25
+ localStorage.setItem(testKey, 'test')
26
+ localStorage.removeItem(testKey)
27
+ this.isLocalStorageAvailable = true
28
+ } catch (e) {
29
+ console.warn('[StorageAdapter] localStorage not available:', e.message)
30
+ }
31
+ }
32
+
33
+ // Test cookies
34
+ if (typeof document !== 'undefined') {
35
+ try {
36
+ document.cookie = '__cookie_test__=test; path=/; SameSite=Lax'
37
+ this.isCookieAvailable = document.cookie.includes('__cookie_test__')
38
+ document.cookie = '__cookie_test__=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;'
39
+ } catch (e) {
40
+ console.warn('[StorageAdapter] Cookies not available:', e.message)
41
+ }
42
+ }
43
+ }
44
+
45
+ /**
46
+ * Get data from storage
47
+ */
48
+ get(key = null) {
49
+ const storageKey = key || this.storageKey
50
+
51
+ try {
52
+ // Try localStorage first
53
+ if (this.storageType === 'localStorage' || this.storageType === 'both') {
54
+ if (this.isLocalStorageAvailable) {
55
+ const value = localStorage.getItem(storageKey)
56
+ if (value) return value
57
+ }
58
+ }
59
+
60
+ // Fallback to cookies
61
+ if (this.storageType === 'cookie' || this.storageType === 'both') {
62
+ if (this.isCookieAvailable) {
63
+ return this._readCookie(storageKey)
64
+ }
65
+ }
66
+
67
+ return null
68
+ } catch (e) {
69
+ console.error('[StorageAdapter] Error reading storage:', e)
70
+ return null
71
+ }
72
+ }
73
+
74
+ /**
75
+ * Set data to storage
76
+ */
77
+ set(key, value) {
78
+ const storageKey = typeof key === 'string' ? key : this.storageKey
79
+ const data = typeof key === 'string' ? value : key
80
+
81
+ // Validate data before saving
82
+ if (data === null || data === undefined) {
83
+ console.warn('[StorageAdapter] Attempted to save null/undefined data, skipping')
84
+ return false
85
+ }
86
+
87
+ if (typeof data === 'string' && (data === 'null' || data === 'undefined')) {
88
+ console.warn('[StorageAdapter] Attempted to save literal "null"/"undefined" string, skipping')
89
+ return false
90
+ }
91
+
92
+ try {
93
+ // Try localStorage
94
+ if (this.storageType === 'localStorage' || this.storageType === 'both') {
95
+ if (this.isLocalStorageAvailable) {
96
+ try {
97
+ localStorage.setItem(storageKey, data)
98
+ } catch (e) {
99
+ // Quota exceeded — try to clear old data
100
+ if (e.name === 'QuotaExceededError') {
101
+ console.warn('[StorageAdapter] localStorage quota exceeded, falling back to cookies')
102
+ this.isLocalStorageAvailable = false
103
+ }
104
+ }
105
+ }
106
+ }
107
+
108
+ // Fallback to cookies
109
+ if (this.storageType === 'cookie' || this.storageType === 'both') {
110
+ if (this.isCookieAvailable) {
111
+ this._writeCookie(storageKey, data, this.cookieExpireDays)
112
+ }
113
+ }
114
+
115
+ return true
116
+ } catch (e) {
117
+ console.error('[StorageAdapter] Error writing storage:', e)
118
+ return false
119
+ }
120
+ }
121
+
122
+ /**
123
+ * Remove data from storage
124
+ */
125
+ remove(key = null) {
126
+ const storageKey = key || this.storageKey
127
+
128
+ try {
129
+ // Remove from localStorage
130
+ if (this.storageType === 'localStorage' || this.storageType === 'both') {
131
+ if (this.isLocalStorageAvailable) {
132
+ localStorage.removeItem(storageKey)
133
+ }
134
+ }
135
+
136
+ // Remove from cookies
137
+ if (this.storageType === 'cookie' || this.storageType === 'both') {
138
+ if (this.isCookieAvailable) {
139
+ this._writeCookie(storageKey, '', -1)
140
+ }
141
+ }
142
+
143
+ return true
144
+ } catch (e) {
145
+ console.error('[StorageAdapter] Error removing storage:', e)
146
+ return false
147
+ }
148
+ }
149
+
150
+ /**
151
+ * Check if storage is available
152
+ */
153
+ isAvailable() {
154
+ return this.isLocalStorageAvailable || this.isCookieAvailable
155
+ }
156
+
157
+ /**
158
+ * Get storage type being used
159
+ */
160
+ getActiveStorageType() {
161
+ if (this.isLocalStorageAvailable) return 'localStorage'
162
+ if (this.isCookieAvailable) return 'cookie'
163
+ return 'none'
164
+ }
165
+
166
+ // --- Cookie helpers ---
167
+
168
+ _readCookie(name) {
169
+ if (typeof document === 'undefined') return null
170
+ const match = document.cookie.match(new RegExp('(^| )' + name + '=([^;]+)'))
171
+ return match ? decodeURIComponent(match[2]) : null
172
+ }
173
+
174
+ _writeCookie(name, value, days) {
175
+ if (typeof document === 'undefined') return
176
+ const expires = new Date(Date.now() + days * 864e5).toUTCString()
177
+ document.cookie = `${name}=${encodeURIComponent(value)}; expires=${expires}; path=/; SameSite=Lax`
178
+ }
179
+ }
package/src/core/index.js CHANGED
@@ -225,7 +225,7 @@ export class CookieKitCore {
225
225
  const next = {}
226
226
  Object.keys(this.config.categories).forEach(id => {
227
227
  const category = this.config.categories[id]
228
- if (category?.enabled !== false) {
228
+ if (category?.enabled === true) {
229
229
  next[id] = !!category.required
230
230
  }
231
231
  })
@@ -270,7 +270,7 @@ export class CookieKitCore {
270
270
  acceptAll(source = 'banner') {
271
271
  const categories = {}
272
272
  Object.keys(this.config.categories).forEach(id => {
273
- if (this.config.categories[id]?.enabled !== false) {
273
+ if (this.config.categories[id]?.enabled === true) {
274
274
  categories[id] = true
275
275
  }
276
276
  })
@@ -281,7 +281,7 @@ export class CookieKitCore {
281
281
  const categories = {}
282
282
  Object.keys(this.config.categories).forEach(id => {
283
283
  const category = this.config.categories[id]
284
- if (category?.enabled !== false) {
284
+ if (category?.enabled === true) {
285
285
  categories[id] = !!category.required
286
286
  }
287
287
  })
@@ -293,7 +293,7 @@ export class CookieKitCore {
293
293
  const categories = {}
294
294
  Object.keys(this.config.categories).forEach(id => {
295
295
  const category = this.config.categories[id]
296
- if (category?.enabled !== false) {
296
+ if (category?.enabled === true) {
297
297
  categories[id] = selectedSet.has(id) || !!category.required
298
298
  }
299
299
  })