@easy-cookie/core 0.0.0-stage → 0.1.1
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/LICENSE +21 -0
- package/LICENSE.upstream +21 -0
- package/README.md +213 -2
- package/UPSTREAM.md +18 -0
- package/dist/catalog.cjs +674 -0
- package/dist/catalog.cjs.map +1 -0
- package/dist/catalog.d.cts +46 -0
- package/dist/catalog.d.ts +46 -0
- package/dist/catalog.js +645 -0
- package/dist/catalog.js.map +1 -0
- package/dist/chunk-LUBO6KZ2.js +417 -0
- package/dist/chunk-LUBO6KZ2.js.map +1 -0
- package/dist/easy-cookie.css +1 -0
- package/dist/easy-cookie.iife.js +23 -0
- package/dist/easy-cookie.iife.js.map +1 -0
- package/dist/index.cjs +3346 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +11 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.js +2928 -0
- package/dist/index.js.map +1 -0
- package/dist/shared.cjs +460 -0
- package/dist/shared.cjs.map +1 -0
- package/dist/shared.d.cts +126 -0
- package/dist/shared.d.ts +126 -0
- package/dist/shared.js +44 -0
- package/dist/shared.js.map +1 -0
- package/dist/types-B6QEQkj-.d.cts +239 -0
- package/dist/types-B6QEQkj-.d.ts +239 -0
- package/package.json +83 -3
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 easy-cookie contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/LICENSE.upstream
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2020-2025 Orest Bida
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,214 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @easy-cookie/core
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
A self-hosted, GDPR/ePrivacy-compliant cookie consent library. It covers:
|
|
4
|
+
- the banner and preferences panel;
|
|
5
|
+
- blocking scripts and embeds until consent;
|
|
6
|
+
- Google Consent Mode v2;
|
|
7
|
+
- proof of consent sent to your own server.
|
|
8
|
+
|
|
9
|
+
Everything is driven by **one config**.
|
|
10
|
+
|
|
11
|
+
It is built on a source fork of [vanilla-cookieconsent](https://github.com/orestbida/cookieconsent) v3.3.0 (MIT, © Orest Bida); see [UPSTREAM.md](UPSTREAM.md).
|
|
12
|
+
|
|
13
|
+
## What you get by default (Italian Garante 2021 / EDPB)
|
|
14
|
+
|
|
15
|
+
- "Accept all" and "Reject all" have **identical weight**: two equal halves of one row.
|
|
16
|
+
- The **X closes the banner and counts as a rejection**, recorded as `close`.
|
|
17
|
+
- Categories are **off by default**, and scrolling never counts as consent.
|
|
18
|
+
- **Re-prompt** when you change `revision`, or after `expiryDays` (default 182 days ≈ 6 months).
|
|
19
|
+
- A **floating preferences button** stays available after the first choice. Withdrawing consent clears the category's cookies and reloads the page.
|
|
20
|
+
- **Proof of consent**: every choice is POSTed to your consent server (`log`). If the server is unreachable, the record is queued and retried on the next page load.
|
|
21
|
+
|
|
22
|
+
## Install
|
|
23
|
+
|
|
24
|
+
### 1. Script tag via your consent server (Iubenda-style)
|
|
25
|
+
|
|
26
|
+
```html
|
|
27
|
+
<head>
|
|
28
|
+
<script src="https://consent.example.com/v1/sites/pk_live_xxx/embed.js"></script>
|
|
29
|
+
<!-- then GTM / gtag -->
|
|
30
|
+
</head>
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
`embed.js` contains:
|
|
34
|
+
- the Consent Mode default for the visitor's stored state;
|
|
35
|
+
- the published config;
|
|
36
|
+
- a loader for the runtime and the stylesheet.
|
|
37
|
+
|
|
38
|
+
The config is edited in the admin console and published as revisions.
|
|
39
|
+
|
|
40
|
+
### 2. npm
|
|
41
|
+
|
|
42
|
+
```sh
|
|
43
|
+
pnpm add @easy-cookie/core
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
```ts
|
|
47
|
+
import EasyCookie, { defineConfig } from '@easy-cookie/core';
|
|
48
|
+
import '@easy-cookie/core/style.css';
|
|
49
|
+
|
|
50
|
+
const config = defineConfig({
|
|
51
|
+
revision: '2026-11-13',
|
|
52
|
+
policyUrl: '/cookie-policy',
|
|
53
|
+
language: { default: 'it', available: ['it', 'en'] },
|
|
54
|
+
googleConsentMode: { enabled: true },
|
|
55
|
+
log: { endpoint: 'https://consent.example.com', siteKey: 'pk_live_xxx' },
|
|
56
|
+
categories: {
|
|
57
|
+
necessary: { readOnly: true },
|
|
58
|
+
measurement: {},
|
|
59
|
+
marketing: {},
|
|
60
|
+
},
|
|
61
|
+
services: {
|
|
62
|
+
ga4: {
|
|
63
|
+
category: 'measurement',
|
|
64
|
+
label: 'Google Analytics 4',
|
|
65
|
+
provider: 'Google Ireland Ltd',
|
|
66
|
+
privacyUrl: 'https://policies.google.com/privacy',
|
|
67
|
+
cookies: [{ name: '^_ga', pattern: true, duration: { it: '2 anni', en: '2 years' } }],
|
|
68
|
+
},
|
|
69
|
+
},
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
EasyCookie.run(config);
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
Put the Consent Mode head snippet **before** GTM. In plain HTML, use the `head.js` served by the consent server. For server-rendered apps, generate it yourself:
|
|
76
|
+
|
|
77
|
+
```ts
|
|
78
|
+
import { renderHeadScript } from '@easy-cookie/core/shared';
|
|
79
|
+
const inline = renderHeadScript(config); // <script>{inline}</script> in <head>
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
React and Next.js are covered by `@easy-cookie/react` and `@easy-cookie/next`.
|
|
83
|
+
|
|
84
|
+
## Categories follow the services
|
|
85
|
+
|
|
86
|
+
As in Iubenda, the banner only asks about what the site actually uses. Once `services` declares at least one service, an optional category with no services is left out of the banner, the preferences, the cookie policy and Consent Mode, and its signals stay `denied`. Read-only categories (`necessary`) are always shown. A config without any `services` keeps every category, for setups that only use blocked script tags.
|
|
87
|
+
|
|
88
|
+
## Blocking scripts and embeds
|
|
89
|
+
|
|
90
|
+
```html
|
|
91
|
+
<script type="text/plain" data-category="measurement" data-service="ga4"
|
|
92
|
+
data-src="https://www.googletagmanager.com/gtag/js?id=G-XXXX"></script>
|
|
93
|
+
|
|
94
|
+
<iframe data-category="experience" data-service="youtube"
|
|
95
|
+
data-src="https://www.youtube-nocookie.com/embed/ID" width="560" height="315"
|
|
96
|
+
title="Video"></iframe>
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
- **Scripts** run as soon as their category, or their service, is granted.
|
|
100
|
+
- **Iframes** stay empty and show a placeholder with two options: show this content once, or always enable the category.
|
|
101
|
+
|
|
102
|
+
## Google Consent Mode v2 and GTM
|
|
103
|
+
|
|
104
|
+
Category → signal defaults:
|
|
105
|
+
|
|
106
|
+
| Category id | Consent Mode signals |
|
|
107
|
+
|---|---|
|
|
108
|
+
| `measurement` | `analytics_storage` |
|
|
109
|
+
| `marketing` | `ad_storage`, `ad_user_data`, `ad_personalization` |
|
|
110
|
+
| `functionality` | `functionality_storage` |
|
|
111
|
+
| `experience` | `personalization_storage` |
|
|
112
|
+
|
|
113
|
+
`security_storage` is always granted. Override the mapping per category with `gcm: [...]`.
|
|
114
|
+
|
|
115
|
+
When the visitor makes a first choice or changes it, the runtime calls `gtag('consent','update', …)` and pushes this event:
|
|
116
|
+
|
|
117
|
+
```js
|
|
118
|
+
{ event: 'easy_cookie_update', easyCookie: { categories: { measurement: true, … }, method, first, revision } }
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
> **GTM setup:** a tag blocked by consent on `gtm.init` / `All Pages` is **not** fired again by a later consent update. Add a *Custom Event* trigger `easy_cookie_update` to your GA4 / Ads / Meta base tags, next to their page-view trigger.
|
|
122
|
+
|
|
123
|
+
The event name is `googleConsentMode.eventName` (default `easy_cookie_update`). When migrating, set it to the event your GTM triggers already listen to (e.g. `cookie_consent_update`) and nothing changes in GTM. The payload is always `easyCookie.categories.<id>`. If a GTM variable read other keys (`analytics`, `marketing`…), point it there.
|
|
124
|
+
|
|
125
|
+
Category ids are yours too: `measurement`, `marketing`, `functionality` and `experience` only come with default titles and Consent Mode signals. A category such as `analytics` works the same with `gcm: ['analytics_storage']`.
|
|
126
|
+
|
|
127
|
+
Page loads with a stored choice do **not** push the event (the head snippet already set the right default), so tags never fire twice.
|
|
128
|
+
|
|
129
|
+
## API
|
|
130
|
+
|
|
131
|
+
```ts
|
|
132
|
+
EasyCookie.get() // ConsentState | null
|
|
133
|
+
EasyCookie.isGranted('measurement')
|
|
134
|
+
EasyCookie.isServiceGranted('ga4')
|
|
135
|
+
EasyCookie.on('consent' | 'change' | 'replay' | 'ready', cb) // returns an unsubscribe function
|
|
136
|
+
EasyCookie.onGranted('marketing', () => loadPixel())
|
|
137
|
+
EasyCookie.openPreferences() // e.g. from a footer link
|
|
138
|
+
EasyCookie.acceptAll() / rejectAll() / accept(['measurement'])
|
|
139
|
+
EasyCookie.identify(userId) // link the anonymous consent id to a user (after login)
|
|
140
|
+
EasyCookie.reset() // forget the choice (logged as withdraw) and re-prompt
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
The same events are dispatched on `window` as `easycookie:consent`, `easycookie:change`, `easycookie:replay` and `easycookie:ready`.
|
|
144
|
+
|
|
145
|
+
No-JS footer links: any element with the `data-ec-open-preferences` attribute opens the preferences when clicked (the default action is prevented). The listener is delegated on `document`, so elements added after `run()` work too, for example the `{{preferences-link}}` of a legal document.
|
|
146
|
+
|
|
147
|
+
```html
|
|
148
|
+
<a href="#" data-ec-open-preferences>Preferenze cookie</a>
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
Server side (middleware, RSC, route handlers):
|
|
152
|
+
|
|
153
|
+
```ts
|
|
154
|
+
import { consentFromCookieHeader } from '@easy-cookie/core/shared';
|
|
155
|
+
const consent = consentFromCookieHeader(request.headers.get('cookie'), { revision: config.revision });
|
|
156
|
+
if (consent?.categories.includes('measurement')) sendServerSideAnalytics();
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
## Service catalog
|
|
160
|
+
|
|
161
|
+
`@easy-cookie/core/catalog` ships about 30 ready-made service declarations (GA4, GTM, PostHog, Matomo, Plausible, Hotjar, Clarity, Mixpanel, Amplitude, Google Ads, Meta Pixel, LinkedIn, TikTok, Pinterest, Microsoft UET, X, YouTube, Vimeo, Google Maps, Spotify, Calendly, Intercom, Crisp, HubSpot, Zendesk, Stripe, PayPal, reCAPTCHA, hCaptcha, Cloudflare, Sentry, easy-cookie itself). Each entry has the EU-facing provider, the privacy link, the default consent category, cookies with IT/EN durations and descriptions, data transfers and a note for the owner. It is plain data: the console, the CLI and npm users share it, and nothing reads it at runtime.
|
|
162
|
+
|
|
163
|
+
```ts
|
|
164
|
+
import { CATALOG, findCatalogEntry, catalogToService } from '@easy-cookie/core/catalog';
|
|
165
|
+
|
|
166
|
+
const config = defineConfig({
|
|
167
|
+
// …
|
|
168
|
+
services: {
|
|
169
|
+
ga4: catalogToService(findCatalogEntry('ga4')!), // { category: 'measurement', …, from: 'ga4' }
|
|
170
|
+
youtube: catalogToService(findCatalogEntry('youtube')!),
|
|
171
|
+
},
|
|
172
|
+
});
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
`catalogToService` returns a deep copy with `from: entry.id`. Edit the result freely: `from` is informational. Default categories follow the Italian Garante 2021 guidelines: third-party analytics → `measurement`, advertising → `marketing`, embeds → `experience`, support chats → `functionality`, payments, security and infrastructure → `necessary`. Read each entry's `note`, and check the data against your own setup and contracts: providers change their cookies over time.
|
|
176
|
+
|
|
177
|
+
## Theming
|
|
178
|
+
|
|
179
|
+
Every visual value is a CSS custom property. Set them on `:root`, or on `html.ec--dark` for the dark scheme (or `ui.colorScheme: 'dark' | 'auto'`).
|
|
180
|
+
|
|
181
|
+
| Variable | Default | Role |
|
|
182
|
+
|---|---|---|
|
|
183
|
+
| `--ec-font` | system UI stack | Banner font (set it to your site's font) |
|
|
184
|
+
| `--ec-bg` / `--ec-ink` / `--ec-ink-2` | `#fff` / `#1c1c1a` / `#4a4a46` | Ground, text, secondary text |
|
|
185
|
+
| `--ec-hair` / `--ec-wash` | `#d9d9d3` / `#f4f4f1` | Hairlines, hover wash |
|
|
186
|
+
| `--ec-border` | `1px solid var(--ec-ink)` | Banner and button border |
|
|
187
|
+
| `--ec-radius` | `0px` | Corner radius everywhere |
|
|
188
|
+
| `--ec-width` / `--ec-pad` | `560px` / `28px` | Banner width and padding |
|
|
189
|
+
| `--ec-text` / `--ec-title` / `--ec-small` | `15px` / `20px` / `13px` | Type sizes |
|
|
190
|
+
| `--ec-module` | `40px` | Size of the category modules |
|
|
191
|
+
| `--ec-c-necessary` … `--ec-c-marketing` | Olivetti palette | Category module colours |
|
|
192
|
+
| `--ec-shadow` | `none` | Banner shadow |
|
|
193
|
+
| `--ec-focus` | 2px ink ring | Focus indicator |
|
|
194
|
+
|
|
195
|
+
Example, for a brand with rounded corners and an amber accent:
|
|
196
|
+
|
|
197
|
+
```css
|
|
198
|
+
:root {
|
|
199
|
+
--ec-font: "Inter", system-ui, sans-serif;
|
|
200
|
+
--ec-radius: 10px;
|
|
201
|
+
--ec-ink: #1b1530;
|
|
202
|
+
--ec-c-measurement: #f2a516;
|
|
203
|
+
}
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
## The consent cookie
|
|
207
|
+
|
|
208
|
+
- `ec_consent` is first-party, `SameSite=Lax`, `Secure` on HTTPS, and lasts `expiryDays`.
|
|
209
|
+
- Its value is JSON (engine format). easy-cookie's revision and method live in `data.ec`.
|
|
210
|
+
- Read it with `parseConsent` / `consentFromCookieHeader`; never parse it by hand.
|
|
211
|
+
|
|
212
|
+
## License
|
|
213
|
+
|
|
214
|
+
MIT. The bundled engine is MIT © Orest Bida ([LICENSE.upstream](LICENSE.upstream)).
|
package/UPSTREAM.md
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# Upstream
|
|
2
|
+
|
|
3
|
+
`src/engine/` and `test/engine/` started as a source copy of
|
|
4
|
+
[orestbida/cookieconsent](https://github.com/orestbida/cookieconsent) **v3.3.0**
|
|
5
|
+
(commit `944bb270df5be3732d5384d8d5e12c224468f1d1`), MIT License,
|
|
6
|
+
Copyright (c) 2020-2025 Orest Bida. The original license text is kept in
|
|
7
|
+
[`LICENSE.upstream`](LICENSE.upstream) and must ship with this package.
|
|
8
|
+
|
|
9
|
+
The engine is not an npm dependency: easy-cookie owns and modifies this code.
|
|
10
|
+
Upstream fixes are reviewed and cherry-picked by hand. When you do, record it below.
|
|
11
|
+
|
|
12
|
+
## Local changes to the engine
|
|
13
|
+
|
|
14
|
+
- Engine tests stay on Jest (paths adjusted to `test/engine`); the GUI-less "core build" test was removed because easy-cookie ships a single build.
|
|
15
|
+
|
|
16
|
+
## Cherry-picked upstream changes
|
|
17
|
+
|
|
18
|
+
_None yet._
|