@c15t/browser 3.0.0-alpha.0 → 3.0.0-alpha.2

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.
Files changed (43) hide show
  1. package/AGENTS.md +3 -0
  2. package/CHANGELOG.md +25 -0
  3. package/dist/861.js +1 -1
  4. package/dist/925.js +21 -13
  5. package/dist/c15t.css +9 -112
  6. package/dist/c15t.devtools.js +1 -1
  7. package/dist/c15t.headless.js +1 -1
  8. package/dist/c15t.iab.css +3 -109
  9. package/dist/c15t.iab.js +9 -141
  10. package/dist/c15t.js +4 -30
  11. package/dist/iab.js +1 -1
  12. package/dist-types/auto-init.d.ts +1 -1
  13. package/dist-types/client.d.ts +1 -1
  14. package/dist-types/devtools.d.ts +1 -1
  15. package/dist-types/gated-scripts.d.ts +3 -2
  16. package/dist-types/global.d.ts +4 -4
  17. package/dist-types/headless.d.ts +11 -11
  18. package/dist-types/iab/mount.d.ts +1 -1
  19. package/dist-types/iab/preferences.d.ts +1 -1
  20. package/dist-types/iab/surface.d.ts +2 -2
  21. package/dist-types/iab/vendor-disclosures.d.ts +1 -1
  22. package/dist-types/iab.d.ts +7 -7
  23. package/dist-types/index.d.ts +12 -12
  24. package/dist-types/types.d.ts +3 -1
  25. package/dist-types/ui/banner.d.ts +2 -2
  26. package/dist-types/ui/dialog.d.ts +2 -2
  27. package/dist-types/ui/mount.d.ts +2 -2
  28. package/dist-types/ui/surface.d.ts +1 -1
  29. package/dist-types/ui/trigger.d.ts +2 -2
  30. package/dist-types/ui/widget.d.ts +1 -1
  31. package/dist-types/version.d.ts +1 -1
  32. package/docs/README.md +3 -0
  33. package/docs/frameworks/javascript/script-loader.md +47 -0
  34. package/docs/frameworks/javascript/script-tag.md +30 -6
  35. package/docs/integrations/building-integrations.md +5 -0
  36. package/docs/integrations/clear-on-revocation.md +167 -0
  37. package/docs/integrations/cloudflare-zaraz.md +399 -0
  38. package/docs/integrations/google-maps.md +20 -20
  39. package/docs/integrations/granular-consent.md +208 -0
  40. package/docs/integrations/overview.md +5 -4
  41. package/docs/integrations/youtube.md +26 -21
  42. package/package.json +7 -7
  43. package/readme.json +0 -44
package/docs/README.md CHANGED
@@ -27,14 +27,17 @@ These docs describe v3. Start with Inth hosted setup, identify the framework, ro
27
27
  - [Ahrefs Analytics](./integrations/ahrefs-analytics.md): Configure Ahrefs Analytics with c15t v3, understand measurement permission and verify loading and revocation.
28
28
  - [Amplitude](./integrations/amplitude.md): Configure Amplitude with c15t v3, understand measurement permission and verify loading and revocation.
29
29
  - [Custom integrations](./integrations/building-integrations.md): Define loading, initialization and consent-change behavior for a vendor without a helper.
30
+ - [Clear on revocation](./integrations/clear-on-revocation.md): Remove configured first-party cookies and Web Storage keys when their consent category is denied.
30
31
  - [Clearbit](./integrations/clearbit.md): Configure Clearbit with c15t v3, understand marketing permission and verify loading and revocation.
31
32
  - [Cloudflare Web Analytics](./integrations/cloudflare-web-analytics.md): Configure Cloudflare Web Analytics with c15t v3, understand measurement permission and verify loading and revocation.
33
+ - [Cloudflare Zaraz](./integrations/cloudflare-zaraz.md): Synchronize c15t permissions with Zaraz purposes while Cloudflare manages your tools.
32
34
  - [Crisp](./integrations/crisp.md): Configure Crisp with c15t v3, understand functionality permission and verify loading and revocation.
33
35
  - [Databuddy](./integrations/databuddy.md): Configure Databuddy's initial and updated consent state with c15t v3.
34
36
  - [Fathom Analytics](./integrations/fathom-analytics.md): Configure Fathom Analytics with c15t v3, understand measurement permission and verify loading and revocation.
35
37
  - [Google Maps](./integrations/google-maps.md): Prevent a map iframe from mounting before the required permission.
36
38
  - [Google Tag](./integrations/google-tag.md): Configure gtag with c15t Consent Mode signals and understand its loading behavior.
37
39
  - [Google Tag Manager](./integrations/google-tag-manager.md): Load GTM with c15t consent signals and verify the tags inside your container.
40
+ - [Granular consent](./integrations/granular-consent.md): Let visitors grant a category and still turn one vendor off, without adopting IAB TCF.
38
41
  - [Heap](./integrations/heap.md): Configure Heap with c15t v3, understand measurement permission and verify loading and revocation.
39
42
  - [Hightouch](./integrations/hightouch.md): Configure Hightouch with c15t v3, understand measurement permission and verify loading and revocation.
40
43
  - [Hotjar](./integrations/hotjar.md): Configure Hotjar with c15t v3, understand measurement permission and verify loading and revocation.
@@ -45,3 +45,50 @@ assuming all helpers have the same network behavior.
45
45
  The loader exposes `updateScripts`, `getLoadedScriptIds` and `dispose`.
46
46
  Unloading an element cannot reverse requests or code that already ran. Test
47
47
  revocation and vendor cleanup with [verification](https://c15t.com/docs/guides/verify-consent).
48
+
49
+ ## Dispose integration resources
50
+
51
+ Ordinary scripts keep their mounted resource when `updateScripts` receives a
52
+ fresh object with the same ID and unchanged element configuration. New callback
53
+ functions alone do not reload the vendor or send a temporary denial. Changing
54
+ the source, inline code or element attributes starts a new loading lifecycle.
55
+ Consent conditions are reevaluated on every configuration update. Pending
56
+ load and error events use the latest registered callbacks and consent state.
57
+ Setting `persistAfterConsentRevoked` to `false` removes an owned retained element
58
+ when the script no longer has consent.
59
+
60
+ Custom script configurations can use `onDispose(info)` to release event
61
+ listeners or other resources. Adding this hook opts the configuration into an
62
+ object-owned lifecycle: replacing the object disposes its resources and starts
63
+ again, even with the same ID. Keep these objects stable across framework
64
+ rerenders. The loader also calls the hook when the configuration is removed or
65
+ the loader is disposed, including configurations that never loaded.
66
+ `info.element` contains the last loaded or retained element when available,
67
+ even when configuration removal has already detached it.
68
+
69
+ Duplicate references receive one cleanup per registration. Re-registering a
70
+ removed object starts a new lifecycle. Updates requested from lifecycle
71
+ callbacks run after the current pass; if several are requested, the latest
72
+ configuration wins. Disposal stops further reconciliation. A callback feedback
73
+ loop exceeding 100 consecutive passes disposes the loader and reports an
74
+ `error` debug event. Avoid callbacks that keep changing consent or replacing
75
+ their own configuration.
76
+
77
+ `onBeforeLoad` prepares a loading attempt. If a callback changes consent or
78
+ replaces configurations, the loader cancels that attempt before loading and
79
+ reevaluates the latest state. A still-eligible script can retry preparation
80
+ with a new element and updated consent. Make `onBeforeLoad` safe to repeat;
81
+ use `onLoad` for initialization that requires a completed load. This also
82
+ applies to callback-only scripts, whose `onLoad` is skipped when preparation
83
+ invalidates the current pass.
84
+
85
+ Consent revocation alone does not call `onDispose`. Use `onConsentChange` for
86
+ vendor opt-out commands. Cleanup errors are reported through the loader's debug
87
+ events and do not prevent other configurations from being cleaned up.
88
+
89
+ ## Clear stored tracking data
90
+
91
+ Script gating does not remove cookies or Web Storage entries that a script
92
+ already wrote. Configure [clear on revocation](../../integrations/clear-on-revocation.md)
93
+ on your runtime, or attach its module to your existing kernel, to remove
94
+ declared data when its category is denied.
@@ -15,7 +15,7 @@ Paste this before `</head>` (Framer: Site Settings → Custom Code; Webflow: Pro
15
15
 
16
16
  ```html
17
17
  <script
18
- src="https://cdn.jsdelivr.net/npm/@c15t/browser@0.2.0/dist/c15t.js"
18
+ src="https://cdn.jsdelivr.net/npm/@c15t/browser@alpha/dist/c15t.js"
19
19
  data-backend-url="https://your-instance.c15t.dev"
20
20
  defer
21
21
  ></script>
@@ -72,7 +72,7 @@ For anything the attributes cannot express, queue a `config` call on `window.c15
72
72
  },
73
73
  }]);
74
74
  </script>
75
- <script src="https://cdn.jsdelivr.net/npm/@c15t/browser@0.2.0/dist/c15t.js" defer></script>
75
+ <script src="https://cdn.jsdelivr.net/npm/@c15t/browser@alpha/dist/c15t.js" defer></script>
76
76
  ```
77
77
 
78
78
  With `data-manual`, a later `c15t.init()` retains the original tag's attributes. Queued `config` calls override those attributes, and options passed directly to `init()` win over both. Changing or removing the tag after it loads does not change the captured configuration. After `dispose()`, a new `init()` reuses the captured attributes and queued defaults, including backend-injected settings. Pass new options to `init()` to override them.
@@ -93,6 +93,30 @@ An activated inline or external script cannot be undone. Supply cleanup through
93
93
 
94
94
  See [script gating](./script-loader.md) for the full `scripts` option, and the [integrations](../../integrations/overview.md) for ready-made GTM, GA4, and pixel loaders.
95
95
 
96
+ ## Clear data when consent is withdrawn
97
+
98
+ Queue `clearOnRevocation` before the bundle loads, or pass it to `c15t.init()`:
99
+
100
+ ```html
101
+ <script>
102
+ window.c15t = window.c15t || [];
103
+ c15t.push(['config', {
104
+ clearOnRevocation: {
105
+ measurement: {
106
+ cookies: ['_ga', '_ga_*'],
107
+ localStorage: ['analytics:*'],
108
+ sessionStorage: ['analytics:session'],
109
+ },
110
+ },
111
+ }]);
112
+ </script>
113
+ <script src="https://cdn.jsdelivr.net/npm/@c15t/browser@alpha/dist/c15t.js" defer></script>
114
+ ```
115
+
116
+ The client removes these entries when measurement permission becomes denied. It also clears them at startup after the policy resolves if measurement is already denied. Other categories and c15t's own consent records stay intact.
117
+
118
+ Cleanup is disabled unless configured. The configuration is initial-only; call `dispose()` and `init()` to replace it. Deleting data does not stop an already running tracker, so use the vendor's consent or shutdown API too. See [clear on revocation](../../integrations/clear-on-revocation.md) for prefix matching, cookie scopes, and browser limits.
119
+
96
120
  ## Buttons and links
97
121
 
98
122
  Any element on the page can drive the banner. Add `data-c15t-action` and the client wires it:
@@ -148,7 +172,7 @@ Load `c15t.headless.js` instead and no banner or CSS ships. The runtime still re
148
172
  <button data-c15t-action="reject">Reject</button>
149
173
  <button data-c15t-action="accept">Accept</button>
150
174
  </div>
151
- <script src="https://cdn.jsdelivr.net/npm/@c15t/browser@0.2.0/dist/c15t.headless.js" data-backend-url="https://your-instance.c15t.dev" defer></script>
175
+ <script src="https://cdn.jsdelivr.net/npm/@c15t/browser@alpha/dist/c15t.headless.js" data-backend-url="https://your-instance.c15t.dev" defer></script>
152
176
  <script>
153
177
  window.c15t = [['on', 'ui', (surface) => { document.getElementById('banner').hidden = surface !== 'banner'; }]];
154
178
  </script>
@@ -216,7 +240,7 @@ Or fetch a cached manifest with `data-manifest-url`. When you host the manifest
216
240
 
217
241
  ```html
218
242
  <script
219
- src="https://cdn.jsdelivr.net/npm/@c15t/browser@0.2.0/dist/c15t.js"
243
+ src="https://cdn.jsdelivr.net/npm/@c15t/browser@alpha/dist/c15t.js"
220
244
  data-manifest-url="https://cdn.example.com/consent-policy.json"
221
245
  data-backend-url="https://your-instance.c15t.dev"
222
246
  defer
@@ -260,7 +284,7 @@ Anything the page queues with `c15t.push(['config', ...])` before the tag still
260
284
  Add a second tag and the c15t DevTools panel attaches to the page's client. It shows consents, gated scripts, the resolved policy and every kernel event, and its Location tab changes country, region, language and GPC and re-runs init, so you can watch a geo-keyed policy switch without spoofing headers or a VPN.
261
285
 
262
286
  ```html
263
- <script src="https://cdn.jsdelivr.net/npm/@c15t/browser@0.2.0/dist/c15t.devtools.js" data-open data-tab="location" defer></script>
287
+ <script src="https://cdn.jsdelivr.net/npm/@c15t/browser@alpha/dist/c15t.devtools.js" data-open data-tab="location" defer></script>
264
288
  ```
265
289
 
266
290
  Tag order does not matter; the panel waits for the client. It is also on `window.c15t.devtools`, so `c15t.devtools.actions.setOverrides({ country: 'US' })` followed by `c15t.devtools.actions.init()` does the same from the console. Leave the tag out of production pages; it shows internal state. Bundler users import `mountDevTools` from `@c15t/browser/devtools`.
@@ -307,7 +331,7 @@ Load `c15t.iab.js` instead of `c15t.js` when your policy requires IAB TCF. This
307
331
  },
308
332
  }]];
309
333
  </script>
310
- <script src="https://cdn.jsdelivr.net/npm/@c15t/browser@0.2.0/dist/c15t.iab.js" defer></script>
334
+ <script src="https://cdn.jsdelivr.net/npm/@c15t/browser@alpha/dist/c15t.iab.js" defer></script>
311
335
  <button type="button" data-c15t-action="customize">Privacy settings</button>
312
336
  ```
313
337
 
@@ -31,6 +31,11 @@ Replace the example URL and implement the vendor's initialization. This is a
31
31
  loader template, not a functioning analytics SDK. The script stays blocked
32
32
  while measurement permission is denied.
33
33
 
34
+ Add `vendor: 'example-analytics'` and declare the vendor in the runtime's
35
+ `vendors` option or in the backend manifest when visitors should be able to
36
+ turn this vendor off inside a granted category. See
37
+ [granular consent](./granular-consent.md).
38
+
34
39
  ## Define revocation deliberately
35
40
 
36
41
  `onConsentChange` receives current permission information. Use it to update the
@@ -0,0 +1,167 @@
1
+ ---
2
+ title: Clear on revocation
3
+ description: Remove configured first-party cookies and Web Storage keys when
4
+ their consent category is denied.
5
+ group: integrations
6
+ ---
7
+
8
+ ## Configure cleanup
9
+
10
+ Add `clearOnRevocation` to your provider or runtime options. Declare only the
11
+ data owned by each optional category:
12
+
13
+ ```ts
14
+ import { hosted, type ClearOnRevocationConfig } from 'c15t';
15
+ import { createConsentRuntime } from 'c15t/runtime';
16
+
17
+ const clearOnRevocation = {
18
+ measurement: {
19
+ cookies: ['_ga', '_ga_*'],
20
+ localStorage: ['analytics:*'],
21
+ },
22
+ marketing: {
23
+ cookies: ['_fbp'],
24
+ sessionStorage: ['campaign-id'],
25
+ },
26
+ } satisfies ClearOnRevocationConfig;
27
+
28
+ const runtime = createConsentRuntime({
29
+ mode: hosted({ url: '/api/c15t' }),
30
+ clearOnRevocation,
31
+ });
32
+
33
+ // Start in the browser after mount. The endpoint must serve your c15t backend.
34
+ runtime.start();
35
+
36
+ // Call runtime.dispose() when the app no longer needs consent management.
37
+ ```
38
+
39
+ For React, pass the same configuration through `ConsentProvider.options`:
40
+
41
+ ```tsx
42
+ import type { ReactNode } from 'react';
43
+ import { ConsentProvider, hosted } from 'c15t/react';
44
+
45
+ const mode = hosted({ url: '/api/c15t' });
46
+
47
+ export function Consent({ children }: { children: ReactNode }) {
48
+ return (
49
+ <ConsentProvider
50
+ options={{
51
+ mode,
52
+ clearOnRevocation: {
53
+ measurement: { cookies: ['_ga', '_ga_*'] },
54
+ },
55
+ }}
56
+ >
57
+ {children}
58
+ </ConsentProvider>
59
+ );
60
+ }
61
+ ```
62
+
63
+ The same option is available in Next.js and TanStack Start `ConsentRoot` props, Vue and
64
+ Nuxt configuration, Svelte providers, and Astro integration options. Solid and
65
+ other headless integrations can use `createConsentRuntime` as shown above.
66
+ Every adapter uses the same cleanup module.
67
+
68
+ Omitting `clearOnRevocation` leaves cleanup disabled. The provider option is
69
+ initial-only. Remount the provider to replace its cleanup configuration. For
70
+ a shared runtime, configure the runtime owner rather than a borrowing provider.
71
+
72
+ ## Matching names and cookie scopes
73
+
74
+ Use an exact string or a nonempty prefix followed by `*`. `_ga_*` matches
75
+ `_ga_ABC123`; it does not match `_ga`. Regular expressions, wildcards in other
76
+ positions, and a bare `*` are unsupported. Web Storage keys may contain spaces,
77
+ Unicode, and punctuation. Cookie names use their raw spelling, without URL
78
+ decoding.
79
+
80
+ Cookies can share a name while having different domains or paths. Cleanup
81
+ tries the current host and its parent domains, and the current path and its
82
+ ancestors. To target a specific scope, use an object:
83
+
84
+ ```ts
85
+ const clearOnRevocation = {
86
+ measurement: {
87
+ cookies: [
88
+ { name: 'analytics-id', domain: 'example.com', path: '/' },
89
+ { name: 'checkout-metrics', domain: '', path: '/checkout' },
90
+ { name: 'partitioned-metrics', partitioned: true },
91
+ ],
92
+ },
93
+ };
94
+ ```
95
+
96
+ An empty `domain` means host-only. Explicit domains and paths replace the
97
+ automatic attempts for that field. Exact names can be deleted at a configured
98
+ path even when the current page cannot read that cookie. Prefix matching can
99
+ only discover cookie names visible to the current page, so use an exact name
100
+ for a cookie on another path.
101
+
102
+ Partitioned cookies require `partitioned: true`; ordinary targets remove
103
+ unpartitioned cookies. Cookie deletion preserves the browser's `__Secure-`
104
+ and `__Host-` prefix requirements. c15t protects its own consent, notice,
105
+ privacy, pending-save, and IAB consent records in cookies and localStorage,
106
+ including configured custom storage keys, even if your patterns match them.
107
+ c15t does not store consent in sessionStorage, so targeted entries there are
108
+ removed even when their names match consent storage keys.
109
+
110
+ ## When cleanup runs
111
+
112
+ The runtime attaches cleanup after persistence and the script loader. Cleanup
113
+ waits while the policy is pending. On the first settled snapshot, it removes
114
+ configured data for every denied category. This includes a new opt-in visitor
115
+ who has not made a choice and a returning visitor whose permission expired.
116
+
117
+ After that first pass, cleanup runs when a category changes from allowed to
118
+ denied. Saving a refusal, expiry, a policy change, Global Privacy Control,
119
+ or synchronized records can cause that transition. Under an opt-out policy,
120
+ an expired grant that remains effectively allowed does not trigger deletion.
121
+ The `necessary` category cannot be configured for cleanup.
122
+
123
+ Cleanup keeps waiting if a failed initial request leaves the policy pending.
124
+ If a previously settled policy falls back to denial after an initialization
125
+ failure, cleanup removes its configured data. A later successful retry cannot
126
+ restore deleted data.
127
+
128
+ Runtime construction, server rendering, draft checkbox edits, opening the
129
+ dialog, and disposal do not clear data. Cleanup does not poll storage or repeat
130
+ on unrelated UI updates.
131
+
132
+ ## Use an existing kernel
133
+
134
+ For a manually assembled integration, attach the module in the browser after
135
+ persistence hydration and script-loader setup:
136
+
137
+ ```ts
138
+ import { createClearOnRevocation } from 'c15t/modules/clear-on-revocation';
139
+
140
+ const cleanup = createClearOnRevocation({
141
+ kernel,
142
+ config: { measurement: { cookies: ['_ga', '_ga_*'] } },
143
+ storageConfig,
144
+ });
145
+
146
+ // Stop observing consent when this integration is torn down.
147
+ cleanup.dispose();
148
+ ```
149
+
150
+ Here `kernel` is your existing consent kernel. Pass the same `storageConfig`
151
+ used by persistence so cleanup protects custom record keys. Attaching the
152
+ module can immediately clear denied categories if the policy is already
153
+ settled. Do not also attach it when your provider or runtime owns cleanup.
154
+
155
+ ## Browser limits
156
+
157
+ Cleanup can remove JavaScript-accessible first-party cookies and keys in the
158
+ current origin's `localStorage` and `sessionStorage`. It cannot remove
159
+ `HttpOnly` cookies or another origin's data. Keep `HttpOnly` protections and
160
+ use your server to expire cookies that require server access. Cookie deletion
161
+ must match the cookie's scope. See the
162
+ [browser cookie documentation](https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/Cookies).
163
+
164
+ Browser restrictions can prevent reads or deletions. Cleanup failures do not
165
+ block consent updates. A running SDK may write data again after a sweep, so
166
+ keep script gating and the integration's consent-change or teardown behavior
167
+ configured. Deleting a script element cannot undo JavaScript it already ran.