@c15t/scripts 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.
- package/AGENTS.md +7 -0
- package/README.md +4 -3
- package/dist/e2e-test-utils.js +5 -3
- package/dist/engine/runtime.js +14 -3
- package/dist/events.js +218 -0
- package/dist/registry.js +40 -0
- package/dist/vendors/ads-and-pixels/pinterest-tag.js +123 -0
- package/dist/vendors/analytics/google-tag.js +14 -2
- package/dist/vendors/analytics/microsoft-clarity.js +4 -1
- package/dist/vendors/analytics/one-dollar-stats.js +30 -0
- package/dist/vendors/analytics/segment.js +10 -1
- package/dist/vendors/functional/front-chat.js +64 -0
- package/dist/vendors/tag-managers/cloudflare-zaraz.js +98 -0
- package/dist/vendors/tag-managers/google-tag-manager.js +17 -3
- package/dist-types/__tests__/helpers.d.ts +2 -2
- package/dist-types/engine/compile.d.ts +1 -1
- package/dist-types/engine/runtime.d.ts +1 -1
- package/dist-types/events.d.ts +46 -0
- package/dist-types/registry.d.ts +36 -0
- package/dist-types/resolve.d.ts +1 -1
- package/dist-types/vendors/_shared/install-builders.d.ts +1 -1
- package/dist-types/vendors/ads-and-pixels/pinterest-tag.d.ts +295 -0
- package/dist-types/vendors/analytics/adobe-analytics.d.ts +1 -1
- package/dist-types/vendors/analytics/google-tag.d.ts +3 -1
- package/dist-types/vendors/analytics/matomo-analytics.d.ts +1 -1
- package/dist-types/vendors/analytics/one-dollar-stats.d.ts +39 -0
- package/dist-types/vendors/analytics/segment.d.ts +7 -1
- package/dist-types/vendors/functional/front-chat.d.ts +62 -0
- package/dist-types/vendors/tag-managers/cloudflare-zaraz.d.ts +39 -0
- package/dist-types/vendors/tag-managers/google-tag-manager.d.ts +3 -1
- package/docs/README.md +7 -0
- package/docs/customization/overview.md +4 -3
- package/docs/customization/recipes.md +4 -2
- package/docs/customization/tokens.md +66 -3
- package/docs/frameworks/javascript/script-loader.md +53 -0
- package/docs/frameworks/next/script-loader.md +60 -12
- package/docs/frameworks/react/script-loader.md +14 -0
- package/docs/guides/consent-state.md +327 -0
- package/docs/guides/deployment-modes.md +12 -0
- package/docs/guides/shared-consent-controls.md +158 -0
- package/docs/integrations/adobe-analytics.md +1 -1
- package/docs/integrations/ahrefs-analytics.md +1 -1
- package/docs/integrations/amplitude.md +1 -1
- package/docs/integrations/building-integrations.md +5 -0
- package/docs/integrations/clear-on-revocation.md +167 -0
- package/docs/integrations/clearbit.md +1 -1
- package/docs/integrations/cloudflare-web-analytics.md +1 -1
- package/docs/integrations/cloudflare-zaraz.md +399 -0
- package/docs/integrations/crisp.md +1 -1
- package/docs/integrations/databuddy.md +1 -1
- package/docs/integrations/fathom-analytics.md +1 -1
- package/docs/integrations/front-chat.md +322 -0
- package/docs/integrations/google-maps.md +21 -21
- package/docs/integrations/google-tag-manager.md +1 -1
- package/docs/integrations/google-tag.md +1 -1
- package/docs/integrations/granular-consent.md +210 -0
- package/docs/integrations/heap.md +1 -1
- package/docs/integrations/hightouch.md +1 -1
- package/docs/integrations/hotjar.md +1 -1
- package/docs/integrations/intercom.md +1 -1
- package/docs/integrations/linkedin-insights.md +1 -1
- package/docs/integrations/logrocket.md +1 -1
- package/docs/integrations/matomo-analytics.md +1 -1
- package/docs/integrations/meta-pixel.md +1 -1
- package/docs/integrations/microsoft-clarity.md +1 -1
- package/docs/integrations/microsoft-uet.md +1 -1
- package/docs/integrations/mixpanel-analytics.md +1 -1
- package/docs/integrations/one-dollar-stats.md +305 -0
- package/docs/integrations/openai-pixel.md +1 -1
- package/docs/integrations/overview.md +22 -18
- package/docs/integrations/pinterest-tag.md +321 -0
- package/docs/integrations/pirsch.md +1 -1
- package/docs/integrations/plausible-analytics.md +1 -1
- package/docs/integrations/posthog.md +1 -1
- package/docs/integrations/promptwatch.md +1 -1
- package/docs/integrations/reddit-pixel.md +1 -1
- package/docs/integrations/rudderstack.md +1 -1
- package/docs/integrations/rybbit-analytics.md +1 -1
- package/docs/integrations/segment.md +1 -1
- package/docs/integrations/snapchat-pixel.md +1 -1
- package/docs/integrations/tiktok-pixel.md +1 -1
- package/docs/integrations/umami-analytics.md +1 -1
- package/docs/integrations/vercel-analytics.md +1 -1
- package/docs/integrations/x-pixel.md +1 -1
- package/docs/integrations/youtube.md +27 -22
- package/docs/upgrade-v3.md +176 -1
- package/package.json +30 -2
- package/readme.json +0 -19
|
@@ -33,12 +33,12 @@ Render this component inside your existing consent boundary or provider.
|
|
|
33
33
|
```tsx title="src/consent-embed.tsx"
|
|
34
34
|
'use client';
|
|
35
35
|
|
|
36
|
-
import {
|
|
36
|
+
import { ConsentGate } from 'c15t/next';
|
|
37
37
|
import { embedCategory, embedURL, embedTitle, embedAspectRatio } from './embed-config';
|
|
38
38
|
|
|
39
39
|
export function ConsentEmbed() {
|
|
40
40
|
return (
|
|
41
|
-
<
|
|
41
|
+
<ConsentGate category={embedCategory}>
|
|
42
42
|
<iframe
|
|
43
43
|
src={embedURL}
|
|
44
44
|
title={embedTitle}
|
|
@@ -46,12 +46,12 @@ export function ConsentEmbed() {
|
|
|
46
46
|
allowFullScreen
|
|
47
47
|
style={{ width: '100%', aspectRatio: embedAspectRatio, minHeight: 200, border: 0 }}
|
|
48
48
|
/>
|
|
49
|
-
</
|
|
49
|
+
</ConsentGate>
|
|
50
50
|
);
|
|
51
51
|
}
|
|
52
52
|
```
|
|
53
53
|
|
|
54
|
-
`
|
|
54
|
+
`ConsentGate` keeps the iframe absent while permission is denied and removes it
|
|
55
55
|
on revocation. Keep your existing consent styles and preferences dialog.
|
|
56
56
|
|
|
57
57
|
**TanStack Start**
|
|
@@ -59,12 +59,12 @@ on revocation. Keep your existing consent styles and preferences dialog.
|
|
|
59
59
|
Render this component inside your existing consent boundary or provider.
|
|
60
60
|
|
|
61
61
|
```tsx title="src/consent-embed.tsx"
|
|
62
|
-
import {
|
|
62
|
+
import { ConsentGate } from 'c15t/tanstack-start';
|
|
63
63
|
import { embedCategory, embedURL, embedTitle, embedAspectRatio } from './embed-config';
|
|
64
64
|
|
|
65
65
|
export function ConsentEmbed() {
|
|
66
66
|
return (
|
|
67
|
-
<
|
|
67
|
+
<ConsentGate category={embedCategory}>
|
|
68
68
|
<iframe
|
|
69
69
|
src={embedURL}
|
|
70
70
|
title={embedTitle}
|
|
@@ -72,12 +72,12 @@ export function ConsentEmbed() {
|
|
|
72
72
|
allowFullScreen
|
|
73
73
|
style={{ width: '100%', aspectRatio: embedAspectRatio, minHeight: 200, border: 0 }}
|
|
74
74
|
/>
|
|
75
|
-
</
|
|
75
|
+
</ConsentGate>
|
|
76
76
|
);
|
|
77
77
|
}
|
|
78
78
|
```
|
|
79
79
|
|
|
80
|
-
`
|
|
80
|
+
`ConsentGate` keeps the iframe absent while permission is denied and removes it
|
|
81
81
|
on revocation. Keep your existing consent styles and preferences dialog.
|
|
82
82
|
|
|
83
83
|
**React**
|
|
@@ -85,12 +85,12 @@ on revocation. Keep your existing consent styles and preferences dialog.
|
|
|
85
85
|
Render this component inside your existing consent boundary or provider.
|
|
86
86
|
|
|
87
87
|
```tsx title="src/consent-embed.tsx"
|
|
88
|
-
import {
|
|
88
|
+
import { ConsentGate } from 'c15t/react';
|
|
89
89
|
import { embedCategory, embedURL, embedTitle, embedAspectRatio } from './embed-config';
|
|
90
90
|
|
|
91
91
|
export function ConsentEmbed() {
|
|
92
92
|
return (
|
|
93
|
-
<
|
|
93
|
+
<ConsentGate category={embedCategory}>
|
|
94
94
|
<iframe
|
|
95
95
|
src={embedURL}
|
|
96
96
|
title={embedTitle}
|
|
@@ -98,12 +98,12 @@ export function ConsentEmbed() {
|
|
|
98
98
|
allowFullScreen
|
|
99
99
|
style={{ width: '100%', aspectRatio: embedAspectRatio, minHeight: 200, border: 0 }}
|
|
100
100
|
/>
|
|
101
|
-
</
|
|
101
|
+
</ConsentGate>
|
|
102
102
|
);
|
|
103
103
|
}
|
|
104
104
|
```
|
|
105
105
|
|
|
106
|
-
`
|
|
106
|
+
`ConsentGate` keeps the iframe absent while permission is denied and removes it
|
|
107
107
|
on revocation. Keep your existing consent styles and preferences dialog.
|
|
108
108
|
|
|
109
109
|
**Nuxt**
|
|
@@ -176,7 +176,7 @@ Add this component to pages using your consent-enabled base layout:
|
|
|
176
176
|
<c15t-consent-embed style="display: block"></c15t-consent-embed>
|
|
177
177
|
|
|
178
178
|
<script>
|
|
179
|
-
import { getConsentClient } from '
|
|
179
|
+
import { getConsentClient } from 'c15t/astro/client';
|
|
180
180
|
import { mountConsentEmbed } from '../consent-embed';
|
|
181
181
|
|
|
182
182
|
class ConsentEmbed extends HTMLElement {
|
|
@@ -223,11 +223,11 @@ The provider from your quickstart supplies its consent state.
|
|
|
223
223
|
|
|
224
224
|
```svelte title="src/ConsentEmbed.svelte"
|
|
225
225
|
<script lang="ts">
|
|
226
|
-
import {
|
|
226
|
+
import { ConsentGate } from '@c15t/svelte';
|
|
227
227
|
import { embedCategory, embedURL, embedTitle, embedAspectRatio } from './embed-config';
|
|
228
228
|
</script>
|
|
229
229
|
|
|
230
|
-
<
|
|
230
|
+
<ConsentGate category={embedCategory}>
|
|
231
231
|
<iframe
|
|
232
232
|
src={embedURL}
|
|
233
233
|
title={embedTitle}
|
|
@@ -238,10 +238,10 @@ The provider from your quickstart supplies its consent state.
|
|
|
238
238
|
style:min-height="200px"
|
|
239
239
|
style:border="0"
|
|
240
240
|
></iframe>
|
|
241
|
-
</
|
|
241
|
+
</ConsentGate>
|
|
242
242
|
```
|
|
243
243
|
|
|
244
|
-
The Svelte `
|
|
244
|
+
The Svelte `ConsentGate` waits until the browser is mounted and the category is
|
|
245
245
|
allowed. Its default placeholder opens preferences. Revocation removes the
|
|
246
246
|
iframe.
|
|
247
247
|
|
|
@@ -252,11 +252,11 @@ Keep the SvelteKit root provider and its server prefetch unchanged.
|
|
|
252
252
|
|
|
253
253
|
```svelte title="src/lib/ConsentEmbed.svelte"
|
|
254
254
|
<script lang="ts">
|
|
255
|
-
import {
|
|
255
|
+
import { ConsentGate } from '@c15t/svelte';
|
|
256
256
|
import { embedCategory, embedURL, embedTitle, embedAspectRatio } from '../embed-config';
|
|
257
257
|
</script>
|
|
258
258
|
|
|
259
|
-
<
|
|
259
|
+
<ConsentGate category={embedCategory}>
|
|
260
260
|
<iframe
|
|
261
261
|
src={embedURL}
|
|
262
262
|
title={embedTitle}
|
|
@@ -267,10 +267,10 @@ Keep the SvelteKit root provider and its server prefetch unchanged.
|
|
|
267
267
|
style:min-height="200px"
|
|
268
268
|
style:border="0"
|
|
269
269
|
></iframe>
|
|
270
|
-
</
|
|
270
|
+
</ConsentGate>
|
|
271
271
|
```
|
|
272
272
|
|
|
273
|
-
The Svelte `
|
|
273
|
+
The Svelte `ConsentGate` waits until the browser is mounted and the category is
|
|
274
274
|
allowed. Its default placeholder opens preferences. Revocation removes the
|
|
275
275
|
iframe.
|
|
276
276
|
|
|
@@ -380,7 +380,7 @@ Set playback options in the iframe URL. For example, append `start=36` to begin
|
|
|
380
380
|
[YouTube player parameters](https://developers.google.com/youtube/player_parameters)
|
|
381
381
|
and reserve at least a 200-by-200-pixel player area.
|
|
382
382
|
|
|
383
|
-
React and Svelte `
|
|
383
|
+
React and Svelte `ConsentGate` components provide a blocked-state placeholder and
|
|
384
384
|
consent action. Their `placeholder` APIs differ: React takes a node, Svelte
|
|
385
385
|
takes a snippet. Keep a clear way to reopen preferences when customizing them.
|
|
386
386
|
The Vue and browser examples show that action explicitly. `loading="lazy"` delays an
|
|
@@ -388,6 +388,11 @@ allowed iframe for performance; it does not implement consent gating.
|
|
|
388
388
|
|
|
389
389
|
## Consent behavior
|
|
390
390
|
|
|
391
|
+
Add `data-vendor="youtube"` next to `data-category` and declare a `youtube`
|
|
392
|
+
vendor in the runtime's `vendors` option or the backend manifest to let visitors
|
|
393
|
+
turn YouTube off while keeping the rest of the category on. See
|
|
394
|
+
[granular consent](./granular-consent.md).
|
|
395
|
+
|
|
391
396
|
Each example keeps the iframe absent until the category is allowed. After
|
|
392
397
|
permission, the iframe can load. Revoking permission removes the iframe and its
|
|
393
398
|
embedded document. Requests already sent cannot be recalled.
|
package/docs/upgrade-v3.md
CHANGED
|
@@ -5,6 +5,13 @@ description: Migrate policy configuration, consent records, callbacks, and
|
|
|
5
5
|
group: reference
|
|
6
6
|
---
|
|
7
7
|
|
|
8
|
+
## React version requirement
|
|
9
|
+
|
|
10
|
+
The v3 React and Next.js adapters require React and React DOM 18 or newer.
|
|
11
|
+
Upgrade both packages together before installing v3. Earlier alpha peer ranges
|
|
12
|
+
incorrectly allowed React 16 and 17 even though v3 already uses `useId` and
|
|
13
|
+
`useSyncExternalStore`, which those versions do not provide.
|
|
14
|
+
|
|
8
15
|
## Start with a policy rule
|
|
9
16
|
|
|
10
17
|
```ts
|
|
@@ -80,6 +87,115 @@ be true before a visitor acts. A notice dismissal updates `noticeDismissal` and
|
|
|
80
87
|
does not record consent. Global Privacy Control updates privacy signals and
|
|
81
88
|
configured opt-out directives without turning a browser signal into a choice.
|
|
82
89
|
|
|
90
|
+
## Replace `useConsentManager()`
|
|
91
|
+
|
|
92
|
+
`useConsentManager()` is gone from `c15t/react`, `c15t/next`,
|
|
93
|
+
`c15t/tanstack-start` and their `/headless` entries. It subscribed to the whole
|
|
94
|
+
consent snapshot, so every component that called it re-rendered on every
|
|
95
|
+
change, including changes to categories it never read. Call one hook per field
|
|
96
|
+
instead; each re-renders only when its own value changes.
|
|
97
|
+
|
|
98
|
+
Before, in v2 and earlier v3 alphas:
|
|
99
|
+
|
|
100
|
+
```tsx title="components/marketing-banner.tsx"
|
|
101
|
+
import { useConsentManager } from 'c15t/react';
|
|
102
|
+
|
|
103
|
+
export function MarketingBanner() {
|
|
104
|
+
const { activeUI, has, saveConsents } = useConsentManager();
|
|
105
|
+
if (activeUI !== 'banner' || has('marketing')) {
|
|
106
|
+
return null;
|
|
107
|
+
}
|
|
108
|
+
return <button type="button" onClick={() => void saveConsents('all')}>Accept</button>;
|
|
109
|
+
}
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
After:
|
|
113
|
+
|
|
114
|
+
```tsx title="components/marketing-banner.tsx"
|
|
115
|
+
import { useActiveUI, useConsent } from 'c15t/react';
|
|
116
|
+
import { useHeadlessConsentUI } from 'c15t/react/headless';
|
|
117
|
+
|
|
118
|
+
export function MarketingBanner() {
|
|
119
|
+
const activeUI = useActiveUI();
|
|
120
|
+
const marketing = useConsent('marketing');
|
|
121
|
+
const { performAction } = useHeadlessConsentUI();
|
|
122
|
+
if (activeUI !== 'banner' || marketing) {
|
|
123
|
+
return null;
|
|
124
|
+
}
|
|
125
|
+
return <button type="button" onClick={() => void performAction('accept')}>Accept</button>;
|
|
126
|
+
}
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
Import hooks from the same entry you used for `useConsentManager()`:
|
|
130
|
+
`c15t/next` and `c15t/next/headless` in Next.js, `c15t/tanstack-start` and
|
|
131
|
+
`c15t/tanstack-start/headless` in TanStack Start.
|
|
132
|
+
|
|
133
|
+
| `useConsentManager()` field | v3 replacement |
|
|
134
|
+
| ------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------ |
|
|
135
|
+
| `activeUI` | `useActiveUI()`. It returns `null` before the kernel picks a surface; the old field reported `'none'`. |
|
|
136
|
+
| `setActiveUI(ui)` | `useSetActiveUI()` |
|
|
137
|
+
| `has(category)` | `useConsent(category)`, one call per category at the top of the component |
|
|
138
|
+
| `consents` | `useConsents()` |
|
|
139
|
+
| `effectivePermissions` | `useEffectivePermissions()` |
|
|
140
|
+
| `explicitChoice` | `useExplicitChoice()` |
|
|
141
|
+
| `promptRequirement` | `usePromptRequirement()` |
|
|
142
|
+
| `noticeDismissal` | `useNoticeDismissal()` |
|
|
143
|
+
| `privacySignals` | `usePrivacySignals()` |
|
|
144
|
+
| `optOutDirectives` | `useOptOutDirectives()` |
|
|
145
|
+
| `restrictions` | `useRestrictions()` |
|
|
146
|
+
| `resolution` | `usePolicyResolution()` |
|
|
147
|
+
| `policyRule` | `usePolicyRule()` |
|
|
148
|
+
| `policyCategories` | `usePolicyCategories()`. The old list started with `'necessary'`; this one does not. |
|
|
149
|
+
| `policyScopeMode` | `usePolicyScopeMode()` |
|
|
150
|
+
| `policyBanner` | `usePromptPresentation()` |
|
|
151
|
+
| `policyDialog` | `usePreferencesPresentation()` |
|
|
152
|
+
| `model` | `useModel()`. It returns `null` while no policy matches; the old field reported `'opt-in'`. |
|
|
153
|
+
| `branding` | `useBranding()`. It returns `null` when unset; the old field reported `'c15t'`. |
|
|
154
|
+
| `iab` | `useIABSnapshot()` |
|
|
155
|
+
| `vendors` | `useDeclaredVendors()` |
|
|
156
|
+
| `vendorChoice` | `useVendorChoice()` |
|
|
157
|
+
| `getDisplayedVendors(category)` | `useDeclaredVendors()`, filtered to vendors whose category condition names the category |
|
|
158
|
+
| `subscribeToConsentChanges(listener)` | `useSubscribeToConsentChanges()`, or the provider's `onPermissionsChanged` callback |
|
|
159
|
+
| `updateConsentCategories(categories)` | `useRegisterConsentCategories()` |
|
|
160
|
+
| `translationConfig` | `useTranslations()` for the active strings |
|
|
161
|
+
| `selectedConsents`, `selectedConsentTypes` | `useConsentDraft().values` |
|
|
162
|
+
| `setSelectedConsent(name, value)` | `useConsentDraft().set(name, value)` |
|
|
163
|
+
| `selectedVendors` | `useConsentDraft().vendors`, or `useVendorDraft().vendors` |
|
|
164
|
+
| `setSelectedVendor(id, granted)` | `useConsentDraft().setVendor(id, granted)` |
|
|
165
|
+
| `resetDraft()` | `useConsentDraft().reset()` |
|
|
166
|
+
| `draftIsStale` | `useConsentDraft().isStale` |
|
|
167
|
+
| `consentCategories`, `consentTypes`, `getDisplayedConsents()` | `useConsentDraft().displayedCategories`, with labels from `useTranslations().consentTypes` |
|
|
168
|
+
| `saveConsents('all')` | `useHeadlessConsentUI().performAction('accept')` |
|
|
169
|
+
| `saveConsents('necessary')` | `useHeadlessConsentUI().performAction('reject')` |
|
|
170
|
+
| `saveConsents('custom')` | `useHeadlessConsentUI().performAction('save')` |
|
|
171
|
+
| `manager` | Nothing. It was always `null` in v3. |
|
|
172
|
+
|
|
173
|
+
The old hook kept one private draft per component, shared by its selection and
|
|
174
|
+
its save. `useConsentDraft()` and `useHeadlessConsentUI()` read the nearest
|
|
175
|
+
`ConsentDraftProvider` instead. When one component stages choices and another
|
|
176
|
+
saves them, or one component calls both hooks, render them inside the same
|
|
177
|
+
`ConsentDraftProvider`; without one, each hook keeps its own draft and a custom
|
|
178
|
+
save commits nothing you staged. `ConsentWidget` already provides one.
|
|
179
|
+
|
|
180
|
+
`useHeadlessConsentUI()` saves the way the stock buttons do: it closes the
|
|
181
|
+
surface after a successful save and refuses a draft that the policy made stale.
|
|
182
|
+
`useSaveConsents()` calls the kernel directly and does neither.
|
|
183
|
+
|
|
184
|
+
The CLI rewrites the common destructuring forms for you:
|
|
185
|
+
|
|
186
|
+
```bash
|
|
187
|
+
c15t codemods use-consent-manager-to-hooks --dry-run --json
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
It replaces each field that has a one-hook equivalent, turns `has('marketing')`
|
|
191
|
+
calls with a literal category into `useConsent('marketing')`, and maps
|
|
192
|
+
`saveConsents('all' | 'necessary' | 'custom')` calls to
|
|
193
|
+
`useHeadlessConsentUI().saveCustomPreferences()`. Fields it cannot rewrite stay
|
|
194
|
+
on a `useConsentManager()` call under a `TODO(c15t v3)` comment that names the
|
|
195
|
+
replacement, so the build fails at each place that needs manual work. It also
|
|
196
|
+
marks components that now need a shared `ConsentDraftProvider`. Review the
|
|
197
|
+
result, then run it without `--dry-run`.
|
|
198
|
+
|
|
83
199
|
## Replace choice callbacks
|
|
84
200
|
|
|
85
201
|
```tsx
|
|
@@ -184,7 +300,7 @@ Notice acknowledgement uses `common.acknowledge`, falling back to
|
|
|
184
300
|
|
|
185
301
|
```astro
|
|
186
302
|
---
|
|
187
|
-
import ConsentBanner from '
|
|
303
|
+
import ConsentBanner from 'c15t/astro/components/consent-banner.astro';
|
|
188
304
|
---
|
|
189
305
|
|
|
190
306
|
<ConsentBanner dismissButtonText="Got it" />
|
|
@@ -252,6 +368,43 @@ TanStack Start (`c15t/tanstack-start`, `c15t/tanstack-start/server`):
|
|
|
252
368
|
`consentLoaderOptions`, `backendURL`, `initRoute` and `DEFAULT_INIT_ROUTE` are
|
|
253
369
|
unchanged.
|
|
254
370
|
|
|
371
|
+
## `Frame` is now `ConsentGate`
|
|
372
|
+
|
|
373
|
+
The component that mounts an embed only while its category is allowed is now
|
|
374
|
+
`ConsentGate` in React, Next.js, TanStack Start, Svelte and Vue. The old names
|
|
375
|
+
still work as deprecated aliases and will be removed in a later major release.
|
|
376
|
+
|
|
377
|
+
| v2 | v3 |
|
|
378
|
+
| -------------------------------------------------------------------- | ---------------------------------------------------------------------------- |
|
|
379
|
+
| `Frame`, `Frame.Root`, `Frame.Title`, `Frame.Button` | `ConsentGate`, `ConsentGate.Root`, `ConsentGate.Title`, `ConsentGate.Button` |
|
|
380
|
+
| `FrameProps`, `FrameCompoundComponent` | `ConsentGateProps`, `ConsentGateCompoundComponent` |
|
|
381
|
+
| `c15t/react/frame`, `c15t/react/components/frame` | `c15t/react/consent-gate`, `c15t/react/components/consent-gate` |
|
|
382
|
+
| Vue `ConsentFrame`, `@c15t/vue/runtime/components/consent-frame.vue` | `ConsentGate`, `@c15t/vue/runtime/components/consent-gate.vue` |
|
|
383
|
+
|
|
384
|
+
Props and behavior are unchanged. The placeholder keeps its `frame.*`
|
|
385
|
+
translation keys, `--frame-*` CSS custom properties and
|
|
386
|
+
`data-testid="frame-placeholder"`, so custom copy, styles and tests continue
|
|
387
|
+
to apply.
|
|
388
|
+
|
|
389
|
+
## Split dialog and widget entries are deferred
|
|
390
|
+
|
|
391
|
+
`c15t/react/consent-dialog` and `c15t/react/consent-widget` (and their
|
|
392
|
+
`@c15t/react/*` equivalents) now export the same deferred `ConsentDialog` and
|
|
393
|
+
`ConsentWidget` as `c15t/react`. The dialog's code loads when it first opens
|
|
394
|
+
instead of with every page. `<ConsentDialog />`, `<ConsentWidget />` and the
|
|
395
|
+
`ConsentDialog.<Part>` properties need no change.
|
|
396
|
+
|
|
397
|
+
The two entries no longer export the individual parts. Import those from the
|
|
398
|
+
component entries, which keep the old, eagerly bundled exports:
|
|
399
|
+
|
|
400
|
+
| Before | After |
|
|
401
|
+
| --------------------------------------------------------------- | -------------------------------------------------------------------------- |
|
|
402
|
+
| `import { Card, Header } from 'c15t/react/consent-dialog'` | `import { Card, Header } from 'c15t/react/components/consent-dialog'` |
|
|
403
|
+
| `import { Accordion, Switch } from 'c15t/react/consent-widget'` | `import { Accordion, Switch } from 'c15t/react/components/consent-widget'` |
|
|
404
|
+
|
|
405
|
+
To keep the dialog in the first load so its first open needs no download,
|
|
406
|
+
import `ConsentDialog` from `c15t/react/components/consent-dialog`.
|
|
407
|
+
|
|
255
408
|
## Next.js RSC banner removal
|
|
256
409
|
|
|
257
410
|
The `@c15t/nextjs/rsc` entry (`c15t/next/rsc`) and its `RscConsentBanner`,
|
|
@@ -332,3 +485,25 @@ export function Banner() {
|
|
|
332
485
|
);
|
|
333
486
|
}
|
|
334
487
|
```
|
|
488
|
+
|
|
489
|
+
## Session reports in manifest mode
|
|
490
|
+
|
|
491
|
+
A host that resolves init from a cached manifest never calls `/init`, so a v3
|
|
492
|
+
backend could not count the visitors it served. From this release every
|
|
493
|
+
server-side resolution sends `POST /sessions` to the backend after the fact,
|
|
494
|
+
server-to-server and detached from the response: the Next.js, TanStack Start,
|
|
495
|
+
SvelteKit, Nuxt and Astro init routes, and the Next.js, TanStack Start and
|
|
496
|
+
Astro render-time prefetches. The browser makes no request.
|
|
497
|
+
|
|
498
|
+
This is a new outbound data flow from your server to the consent backend. The
|
|
499
|
+
report carries the manifest revision, the matched policy, the jurisdiction,
|
|
500
|
+
country, region, language and GPC signal, plus the visitor's user agent and
|
|
501
|
+
address as request headers. It carries no cookies and no identifier. Nothing is
|
|
502
|
+
stored on the backend; the report reaches a `sessions.onReport` sink and the
|
|
503
|
+
request log.
|
|
504
|
+
|
|
505
|
+
Reporting is on by default and needs an absolute backend URL. Set
|
|
506
|
+
`reportSessions: false` on the adapter to send none. Each report is one
|
|
507
|
+
resolution, not one visitor: a page view can produce a `render` report and a
|
|
508
|
+
`route` report, and nothing in them links the two. A consumer counting sessions
|
|
509
|
+
groups reports by the forwarded address and user agent within a window.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@c15t/scripts",
|
|
3
|
-
"version": "3.0.0-alpha.
|
|
3
|
+
"version": "3.0.0-alpha.2",
|
|
4
4
|
"description": "Consent-aware script integrations for Google Tag Manager, Google Consent Mode v2, GA4, Google Ads, Meta Pixel, and more.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"adobe-analytics",
|
|
@@ -26,6 +26,7 @@
|
|
|
26
26
|
"logrocket",
|
|
27
27
|
"meta-pixel",
|
|
28
28
|
"microsoft-uet",
|
|
29
|
+
"pinterest-tag",
|
|
29
30
|
"pirsch",
|
|
30
31
|
"posthog",
|
|
31
32
|
"privacy",
|
|
@@ -133,6 +134,11 @@
|
|
|
133
134
|
"import": "./dist/vendors/analytics/mixpanel-analytics.js",
|
|
134
135
|
"default": "./dist/vendors/analytics/mixpanel-analytics.js"
|
|
135
136
|
},
|
|
137
|
+
"./one-dollar-stats": {
|
|
138
|
+
"types": "./dist-types/vendors/analytics/one-dollar-stats.d.ts",
|
|
139
|
+
"import": "./dist/vendors/analytics/one-dollar-stats.js",
|
|
140
|
+
"default": "./dist/vendors/analytics/one-dollar-stats.js"
|
|
141
|
+
},
|
|
136
142
|
"./pirsch": {
|
|
137
143
|
"types": "./dist-types/vendors/analytics/pirsch.d.ts",
|
|
138
144
|
"import": "./dist/vendors/analytics/pirsch.js",
|
|
@@ -178,11 +184,21 @@
|
|
|
178
184
|
"import": "./dist/vendors/functional/crisp.js",
|
|
179
185
|
"default": "./dist/vendors/functional/crisp.js"
|
|
180
186
|
},
|
|
187
|
+
"./front-chat": {
|
|
188
|
+
"types": "./dist-types/vendors/functional/front-chat.d.ts",
|
|
189
|
+
"import": "./dist/vendors/functional/front-chat.js",
|
|
190
|
+
"default": "./dist/vendors/functional/front-chat.js"
|
|
191
|
+
},
|
|
181
192
|
"./intercom": {
|
|
182
193
|
"types": "./dist-types/vendors/functional/intercom.d.ts",
|
|
183
194
|
"import": "./dist/vendors/functional/intercom.js",
|
|
184
195
|
"default": "./dist/vendors/functional/intercom.js"
|
|
185
196
|
},
|
|
197
|
+
"./cloudflare-zaraz": {
|
|
198
|
+
"types": "./dist-types/vendors/tag-managers/cloudflare-zaraz.d.ts",
|
|
199
|
+
"import": "./dist/vendors/tag-managers/cloudflare-zaraz.js",
|
|
200
|
+
"default": "./dist/vendors/tag-managers/cloudflare-zaraz.js"
|
|
201
|
+
},
|
|
186
202
|
"./google-tag-manager": {
|
|
187
203
|
"types": "./dist-types/vendors/tag-managers/google-tag-manager.d.ts",
|
|
188
204
|
"import": "./dist/vendors/tag-managers/google-tag-manager.js",
|
|
@@ -203,6 +219,11 @@
|
|
|
203
219
|
"import": "./dist/vendors/ads-and-pixels/openai-pixel.js",
|
|
204
220
|
"default": "./dist/vendors/ads-and-pixels/openai-pixel.js"
|
|
205
221
|
},
|
|
222
|
+
"./pinterest-tag": {
|
|
223
|
+
"types": "./dist-types/vendors/ads-and-pixels/pinterest-tag.d.ts",
|
|
224
|
+
"import": "./dist/vendors/ads-and-pixels/pinterest-tag.js",
|
|
225
|
+
"default": "./dist/vendors/ads-and-pixels/pinterest-tag.js"
|
|
226
|
+
},
|
|
206
227
|
"./reddit-pixel": {
|
|
207
228
|
"types": "./dist-types/vendors/ads-and-pixels/reddit-pixel.d.ts",
|
|
208
229
|
"import": "./dist/vendors/ads-and-pixels/reddit-pixel.js",
|
|
@@ -237,6 +258,11 @@
|
|
|
237
258
|
"types": "./dist-types/*.d.ts",
|
|
238
259
|
"import": "./dist/*.js",
|
|
239
260
|
"default": "./dist/*.js"
|
|
261
|
+
},
|
|
262
|
+
"./events": {
|
|
263
|
+
"types": "./dist-types/events.d.ts",
|
|
264
|
+
"import": "./dist/events.js",
|
|
265
|
+
"default": "./dist/events.js"
|
|
240
266
|
}
|
|
241
267
|
},
|
|
242
268
|
"publishConfig": {
|
|
@@ -254,8 +280,10 @@
|
|
|
254
280
|
"test:live-vendors": "bun --tsconfig-override live-vendors/tsconfig.json live-vendors/runner.ts",
|
|
255
281
|
"test:watch": "vitest"
|
|
256
282
|
},
|
|
283
|
+
"dependencies": {
|
|
284
|
+
"@c15t/core": "3.0.0-alpha.3"
|
|
285
|
+
},
|
|
257
286
|
"devDependencies": {
|
|
258
|
-
"@c15t/core": "3.0.0-alpha.0",
|
|
259
287
|
"@c15t/typescript-config": "0.0.1",
|
|
260
288
|
"@c15t/vitest-config": "1.0.0",
|
|
261
289
|
"playwright": "1.58.2",
|
package/readme.json
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"title": "@c15t/scripts: Prebuilt Consent Scripts",
|
|
3
|
-
"description": "Consent-aware script integrations for Google Tag Manager, Google Consent Mode v2, GA4, Google Ads, Meta Pixel, analytics tools, pixels, tag managers, and widgets.",
|
|
4
|
-
"features": [
|
|
5
|
-
"Prebuilt script loaders for popular analytics, advertising, marketing, and functional tools",
|
|
6
|
-
"Google Tag Manager support with Google Consent Mode v2 defaults and consent updates",
|
|
7
|
-
"Google Analytics 4 and Google Ads support through gtag.js",
|
|
8
|
-
"Consent-gated Meta Pixel and conversion pixel loading",
|
|
9
|
-
"Easy integration with c15t's script loader",
|
|
10
|
-
"Configuration options for each supported vendor",
|
|
11
|
-
"Supported vendors include Google Tag Manager, Meta Pixel, OpenAI Pixel, Amplitude, Heap, PostHog, TikTok Pixel, LinkedIn Insights, Microsoft UET, X Pixel, Reddit Pixel, Snapchat Pixel, Intercom, Crisp, and more"
|
|
12
|
-
],
|
|
13
|
-
"docsLink": "https://c15t.com/docs/integrations",
|
|
14
|
-
"showCLIGeneration": false,
|
|
15
|
-
"customSections": {
|
|
16
|
-
"Integrations": "- **Google Tag Manager**: Loads with Google Consent Mode v2 defaults set to denied; GTM-managed tags fire only once matching consent is granted ([guide](https://c15t.com/docs/integrations/google-tag-manager))\n- **Google Analytics 4 + Google Ads (gtag.js)**: Consent Mode v2 defaults and consent updates when users make a choice ([guide](https://c15t.com/docs/integrations/google-tag))\n- **Conversion pixels**: Meta Pixel, OpenAI Pixel, TikTok Pixel, LinkedIn Insights, Microsoft UET (Microsoft Ads), X Pixel, Reddit Pixel, Snapchat Pixel\n- **Analytics**: PostHog, Amplitude, Heap, Segment, RudderStack, Hightouch, Mixpanel, Microsoft Clarity, Hotjar, Plausible, Fathom, Matomo, Umami, Vercel Analytics\n- **Chat widgets**: Intercom, Crisp",
|
|
17
|
-
"Example": "```ts\nimport { googleTagManager } from '@c15t/scripts/google-tag-manager'\nimport { metaPixel } from '@c15t/scripts/meta-pixel'\n\nconst scripts = [\n googleTagManager({ id: 'GTM-XXXXXX' }),\n metaPixel({ pixelId: '000000000000000' }),\n]\n```"
|
|
18
|
-
}
|
|
19
|
-
}
|