@el7ven/cookie-kit 0.2.15

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/package.json ADDED
@@ -0,0 +1,64 @@
1
+ {
2
+ "name": "@el7ven/cookie-kit",
3
+ "version": "0.2.15",
4
+ "type": "module",
5
+ "main": "./src/index.js",
6
+ "module": "./src/index.js",
7
+ "exports": {
8
+ ".": "./src/index.js",
9
+ "./styles": "./dist/styles/index.css",
10
+ "./src/styles": "./dist/styles/index.css",
11
+ "./dist/styles": "./dist/styles/index.css",
12
+ "./react": "./src/react/index.js",
13
+ "./vue": "./src/vue/index.js",
14
+ "./src/composables/useCookieConsent": "./src/vue/composables/useCookieConsent.js",
15
+ "./composables/useCookieConsent": "./src/vue/composables/useCookieConsent.js",
16
+ "./package.json": "./package.json"
17
+ },
18
+ "files": [
19
+ "src",
20
+ "dist",
21
+ "README.md"
22
+ ],
23
+ "scripts": {
24
+ "build": "echo \"@el7ven/cookie-consent: source-only package\"",
25
+ "test": "node --test src/**/*.test.js"
26
+ },
27
+ "keywords": [
28
+ "cookie",
29
+ "consent",
30
+ "gdpr",
31
+ "privacy",
32
+ "compliance",
33
+ "react",
34
+ "vue",
35
+ "javascript",
36
+ "cookie-consent"
37
+ ],
38
+ "description": "Unified cookie consent management kit for GDPR compliance with React, Vue, and Vanilla JS support",
39
+ "author": {
40
+ "name": "Igor Semionov",
41
+ "email": "igsemionov@gmail.com"
42
+ },
43
+ "license": "MIT",
44
+ "repository": {
45
+ "type": "git",
46
+ "url": "https://github.com/El7ven/cookie-kit.git"
47
+ },
48
+ "homepage": "https://github.com/El7ven/cookie-kit#readme",
49
+ "peerDependencies": {
50
+ "react": ">=18.0.0",
51
+ "vue": "^3.0.0"
52
+ },
53
+ "peerDependenciesMeta": {
54
+ "react": {
55
+ "optional": true
56
+ },
57
+ "vue": {
58
+ "optional": true
59
+ }
60
+ },
61
+ "engines": {
62
+ "node": ">=16.0.0"
63
+ }
64
+ }
@@ -0,0 +1,232 @@
1
+ import { VERSION, getVersion, getVersionInfo, logVersion } from './version.js'
2
+
3
+ const DEFAULT_CONFIG = {
4
+ storageKey: 'cookie_consent',
5
+ consentExpireDays: 365,
6
+ categories: {
7
+ necessary: { required: true, enabled: true },
8
+ analytics: { required: false, enabled: false },
9
+ marketing: { required: false, enabled: false },
10
+ preferences: { required: false, enabled: false }
11
+ }
12
+ }
13
+
14
+ function isPromiseLike(value) {
15
+ return !!value && typeof value.then === 'function'
16
+ }
17
+
18
+ function createWebStorageAdapter(storageKey) {
19
+ if (typeof window === 'undefined' || !window.localStorage) {
20
+ const memory = new Map()
21
+ return {
22
+ getItem: key => memory.get(key) ?? null,
23
+ setItem: (key, value) => memory.set(key, value),
24
+ removeItem: key => memory.delete(key),
25
+ defaultKey: storageKey
26
+ }
27
+ }
28
+
29
+ return {
30
+ getItem: key => window.localStorage.getItem(key),
31
+ setItem: (key, value) => window.localStorage.setItem(key, value),
32
+ removeItem: key => window.localStorage.removeItem(key),
33
+ defaultKey: storageKey
34
+ }
35
+ }
36
+
37
+ function mergeConfig(base, extra) {
38
+ return {
39
+ ...base,
40
+ ...extra,
41
+ categories: {
42
+ ...base.categories,
43
+ ...(extra?.categories || {})
44
+ }
45
+ }
46
+ }
47
+
48
+ function normalizeCategories(categories) {
49
+ if (Array.isArray(categories)) {
50
+ return categories.reduce((acc, cat) => {
51
+ acc[cat.id] = cat
52
+ return acc
53
+ }, {})
54
+ }
55
+ return categories || {}
56
+ }
57
+
58
+ export class CookieKitCore {
59
+ constructor(userConfig = {}) {
60
+ this.config = mergeConfig(DEFAULT_CONFIG, userConfig)
61
+ this.config.categories = normalizeCategories(this.config.categories)
62
+ this.listeners = new Map()
63
+ this.storage = this.config.storageAdapter || createWebStorageAdapter(this.config.storageKey)
64
+ }
65
+
66
+ getStorageKey() {
67
+ return this.config.storageKey || this.storage.defaultKey || 'cookie_consent'
68
+ }
69
+
70
+ getConsent() {
71
+ try {
72
+ const raw = this.storage.getItem(this.getStorageKey())
73
+ if (isPromiseLike(raw)) return null
74
+ if (!raw) return null
75
+ const data = JSON.parse(raw)
76
+ if (!data || typeof data !== 'object') return null
77
+ if (data.expiresAt && Date.now() > new Date(data.expiresAt).getTime()) return null
78
+ return data
79
+ } catch {
80
+ return null
81
+ }
82
+ }
83
+
84
+ async getConsentAsync() {
85
+ try {
86
+ const raw = await this.storage.getItem(this.getStorageKey())
87
+ if (!raw) return null
88
+ const data = JSON.parse(raw)
89
+ if (!data || typeof data !== 'object') return null
90
+ if (data.expiresAt && Date.now() > new Date(data.expiresAt).getTime()) return null
91
+ return data
92
+ } catch {
93
+ return null
94
+ }
95
+ }
96
+
97
+ hasConsent() {
98
+ const consent = this.getConsent()
99
+ return !!consent?.hasConsented
100
+ }
101
+
102
+ async hasConsentAsync() {
103
+ const consent = await this.getConsentAsync()
104
+ return !!consent?.hasConsented
105
+ }
106
+
107
+ hasCategoryConsent(categoryId) {
108
+ const consent = this.getConsent()
109
+ return consent?.categories?.[categoryId] === true
110
+ }
111
+
112
+ async hasCategoryConsentAsync(categoryId) {
113
+ const consent = await this.getConsentAsync()
114
+ return consent?.categories?.[categoryId] === true
115
+ }
116
+
117
+ getDefaultCategoriesState() {
118
+ const next = {}
119
+ Object.keys(this.config.categories).forEach(id => {
120
+ const category = this.config.categories[id]
121
+ if (category?.enabled !== false) {
122
+ next[id] = !!category.required
123
+ }
124
+ })
125
+ return next
126
+ }
127
+
128
+ async saveConsent(categories, source = 'api') {
129
+ const now = new Date()
130
+ const expiresAt = new Date(now.getTime() + this.config.consentExpireDays * 24 * 60 * 60 * 1000)
131
+
132
+ const normalized = this.getDefaultCategoriesState()
133
+ Object.keys(categories || {}).forEach(id => {
134
+ if (Object.prototype.hasOwnProperty.call(normalized, id)) {
135
+ normalized[id] = !!categories[id] || !!this.config.categories[id]?.required
136
+ }
137
+ })
138
+
139
+ const optionalIds = Object.keys(normalized).filter(id => !this.config.categories[id]?.required)
140
+ const allOptionalAccepted = optionalIds.length > 0 && optionalIds.every(id => normalized[id] === true)
141
+ const allOptionalRejected = optionalIds.length > 0 && optionalIds.every(id => normalized[id] === false)
142
+
143
+ let status = 'custom'
144
+ if (optionalIds.length === 0 || allOptionalAccepted) status = 'accepted'
145
+ if (allOptionalRejected) status = 'rejected'
146
+
147
+ const consent = {
148
+ consentId: typeof crypto !== 'undefined' && crypto.randomUUID ? crypto.randomUUID() : `${Date.now()}-${Math.random()}`,
149
+ timestamp: now.toISOString(),
150
+ expiresAt: expiresAt.toISOString(),
151
+ status,
152
+ source,
153
+ hasConsented: true,
154
+ categories: normalized,
155
+ version: this.config.version || '0.1.0'
156
+ }
157
+
158
+ await this.storage.setItem(this.getStorageKey(), JSON.stringify(consent))
159
+ this.emit('consentChanged', { consent, categories: normalized })
160
+ return consent
161
+ }
162
+
163
+ acceptAll(source = 'banner') {
164
+ const categories = {}
165
+ Object.keys(this.config.categories).forEach(id => {
166
+ if (this.config.categories[id]?.enabled !== false) {
167
+ categories[id] = true
168
+ }
169
+ })
170
+ return this.saveConsent(categories, source)
171
+ }
172
+
173
+ rejectAll(source = 'banner') {
174
+ const categories = {}
175
+ Object.keys(this.config.categories).forEach(id => {
176
+ const category = this.config.categories[id]
177
+ if (category?.enabled !== false) {
178
+ categories[id] = !!category.required
179
+ }
180
+ })
181
+ return this.saveConsent(categories, source)
182
+ }
183
+
184
+ acceptSelected(selectedIds = [], source = 'settings') {
185
+ const selectedSet = new Set(selectedIds)
186
+ const categories = {}
187
+ Object.keys(this.config.categories).forEach(id => {
188
+ const category = this.config.categories[id]
189
+ if (category?.enabled !== false) {
190
+ categories[id] = selectedSet.has(id) || !!category.required
191
+ }
192
+ })
193
+ return this.saveConsent(categories, source)
194
+ }
195
+
196
+ clearConsent() {
197
+ this.storage.removeItem(this.getStorageKey())
198
+ this.emit('consentCleared', {})
199
+ }
200
+
201
+ async clearConsentAsync() {
202
+ await this.storage.removeItem(this.getStorageKey())
203
+ this.emit('consentCleared', {})
204
+ }
205
+
206
+ on(event, callback) {
207
+ const list = this.listeners.get(event) || []
208
+ list.push(callback)
209
+ this.listeners.set(event, list)
210
+ return () => this.off(event, callback)
211
+ }
212
+
213
+ off(event, callback) {
214
+ const list = this.listeners.get(event) || []
215
+ this.listeners.set(event, list.filter(cb => cb !== callback))
216
+ }
217
+
218
+ emit(event, payload) {
219
+ const list = this.listeners.get(event) || []
220
+ list.forEach(cb => cb(payload))
221
+
222
+ if (typeof window !== 'undefined') {
223
+ window.dispatchEvent(new CustomEvent(`cookie-consent:${event}`, { detail: payload }))
224
+ }
225
+ }
226
+ }
227
+
228
+ export function createCookieKit(config = {}) {
229
+ return new CookieKitCore(config)
230
+ }
231
+
232
+ export { DEFAULT_CONFIG, VERSION, getVersion, getVersionInfo, logVersion }
@@ -0,0 +1,90 @@
1
+ import test from 'node:test'
2
+ import assert from 'node:assert/strict'
3
+
4
+ import { createCookieKit } from './index.js'
5
+
6
+ function createMemoryStorageAdapter() {
7
+ const memory = new Map()
8
+
9
+ return {
10
+ getItem: key => memory.get(key) ?? null,
11
+ setItem: (key, value) => memory.set(key, value),
12
+ removeItem: key => memory.delete(key),
13
+ defaultKey: 'cookie_consent'
14
+ }
15
+ }
16
+
17
+ function createAsyncMemoryStorageAdapter() {
18
+ const memory = new Map()
19
+
20
+ return {
21
+ getItem: async key => memory.get(key) ?? null,
22
+ setItem: async (key, value) => memory.set(key, value),
23
+ removeItem: async key => memory.delete(key),
24
+ defaultKey: 'cookie_consent'
25
+ }
26
+ }
27
+
28
+ test('core: acceptSelected stores consent and required categories', async () => {
29
+ const kit = createCookieKit({
30
+ storageAdapter: createMemoryStorageAdapter(),
31
+ categories: {
32
+ necessary: { required: true, enabled: true },
33
+ analytics: { required: false, enabled: true },
34
+ marketing: { required: false, enabled: true }
35
+ }
36
+ })
37
+
38
+ const consent = await kit.acceptSelected(['analytics'])
39
+
40
+ assert.equal(consent.hasConsented, true)
41
+ assert.equal(consent.categories.necessary, true)
42
+ assert.equal(consent.categories.analytics, true)
43
+ assert.equal(consent.categories.marketing, false)
44
+ assert.equal(kit.hasCategoryConsent('analytics'), true)
45
+ assert.equal(kit.hasCategoryConsent('marketing'), false)
46
+ })
47
+
48
+ test('core: rejectAll and clearConsent flow', async () => {
49
+ const kit = createCookieKit({
50
+ storageAdapter: createMemoryStorageAdapter(),
51
+ categories: {
52
+ necessary: { required: true, enabled: true },
53
+ analytics: { required: false, enabled: true }
54
+ }
55
+ })
56
+
57
+ await kit.rejectAll()
58
+
59
+ assert.equal(kit.hasConsent(), true)
60
+ assert.equal(kit.hasCategoryConsent('necessary'), true)
61
+ assert.equal(kit.hasCategoryConsent('analytics'), false)
62
+
63
+ kit.clearConsent()
64
+
65
+ assert.equal(kit.getConsent(), null)
66
+ assert.equal(kit.hasConsent(), false)
67
+ })
68
+
69
+ test('core: async storage adapter works with async API', async () => {
70
+ const kit = createCookieKit({
71
+ storageAdapter: createAsyncMemoryStorageAdapter(),
72
+ categories: {
73
+ necessary: { required: true, enabled: true },
74
+ analytics: { required: false, enabled: true }
75
+ }
76
+ })
77
+
78
+ await kit.acceptSelected(['analytics'])
79
+
80
+ assert.equal(kit.getConsent(), null)
81
+ assert.equal(await kit.hasConsentAsync(), true)
82
+ assert.equal(await kit.hasCategoryConsentAsync('necessary'), true)
83
+ assert.equal(await kit.hasCategoryConsentAsync('analytics'), true)
84
+
85
+ const consent = await kit.getConsentAsync()
86
+ assert.equal(consent?.hasConsented, true)
87
+
88
+ await kit.clearConsentAsync()
89
+ assert.equal(await kit.getConsentAsync(), null)
90
+ })
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Version metadata for package runtime.
3
+ */
4
+
5
+ export const VERSION = '1.0.0-beta.0'
6
+ export const PACKAGE_NAME = '@el7ven/cookie-consent/core'
7
+
8
+ // Core version metadata.
9
+ export const VERSION_INFO = {
10
+ version: VERSION,
11
+ name: PACKAGE_NAME,
12
+ buildDate: new Date().toISOString(),
13
+ environment: 'production'
14
+ }
15
+
16
+ // Return package version.
17
+ export function getVersion() {
18
+ return VERSION
19
+ }
20
+
21
+ // Return full version metadata.
22
+ export function getVersionInfo() {
23
+ return VERSION_INFO
24
+ }
25
+
26
+ // Print runtime version in debug-friendly format.
27
+ export function logVersion() {
28
+ console.log(`🍪 ${PACKAGE_NAME} v${VERSION}`)
29
+ }
package/src/index.js ADDED
@@ -0,0 +1,67 @@
1
+ /**
2
+ * @el7ven/cookie-consent - Unified Cookie Consent Management
3
+ * GDPR compliant cookie consent with React and Vue support
4
+ * @version 1.0.0-beta.0
5
+ * @author Igor Semionov
6
+ */
7
+
8
+ // Core functionality
9
+ export { createCookieKit, CookieKitCore, DEFAULT_CONFIG } from './core/index.js'
10
+
11
+ // Import for default export
12
+ import { createCookieKit, CookieKitCore, DEFAULT_CONFIG } from './core/index.js'
13
+ import { initCookieKit } from './js/index.js'
14
+ import { VERSION } from './core/version.js'
15
+
16
+ // Vanilla JS components
17
+ export { initCookieKit, mountCookieConsent } from './js/index.js'
18
+
19
+ // React components
20
+ export * from './react/index.js'
21
+
22
+ // Vue components
23
+ export * from './vue/index.js'
24
+
25
+ // Version info
26
+ export { VERSION, getVersion, getVersionInfo, logVersion } from './core/version.js'
27
+
28
+ // Unified factory function
29
+ export function createCookieConsent(config = {}) {
30
+ const { framework = 'js', ...restConfig } = config
31
+
32
+ switch (framework) {
33
+ case 'react':
34
+ return import('./react/index.js').then(m => m.createCookieKit(restConfig))
35
+ case 'vue':
36
+ return import('./vue/index.js').then(m => m.createCookieKit(restConfig))
37
+ default:
38
+ return import('./js/index.js').then(m => m.initCookieKit(restConfig))
39
+ }
40
+ }
41
+
42
+ // Auto-detect framework and create appropriate instance
43
+ export function autoCookieConsent(config = {}) {
44
+ // Detect React
45
+ if (typeof window !== 'undefined' && window.React) {
46
+ return createCookieConsent({ ...config, framework: 'react' })
47
+ }
48
+
49
+ // Detect Vue
50
+ if (typeof window !== 'undefined' && window.Vue) {
51
+ return createCookieConsent({ ...config, framework: 'vue' })
52
+ }
53
+
54
+ // Default to vanilla JS
55
+ return createCookieConsent({ ...config, framework: 'js' })
56
+ }
57
+
58
+ // Default export for convenience
59
+ export default {
60
+ createCookieKit,
61
+ createCookieConsent,
62
+ autoCookieConsent,
63
+ initCookieKit,
64
+ CookieKitCore,
65
+ DEFAULT_CONFIG,
66
+ VERSION
67
+ }