@c15t/react 3.0.0-alpha.9 → 3.0.0

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 (147) hide show
  1. package/AGENTS.md +7 -9
  2. package/README.md +41 -70
  3. package/SKILL.md +1 -1
  4. package/dist/aggregate-components.js +1 -1
  5. package/dist/components/consent-gate/atoms.js +1 -1
  6. package/dist/components/consent-gate/frame.js +2 -0
  7. package/dist/components/consent-gate/index.js +1 -1
  8. package/dist/components/iab-panel/atoms/root.js +1 -1
  9. package/dist/components/iab-panel/iab-panel.js +1 -1
  10. package/dist/components/iab-prompt/atoms/root.js +1 -1
  11. package/dist/components/iab-prompt/iab-prompt.js +1 -1
  12. package/dist/components/panel/atoms/root.js +1 -1
  13. package/dist/components/panel-trigger/atoms/root.js +1 -1
  14. package/dist/components/panel-trigger/panel-trigger.js +1 -1
  15. package/dist/components/panel-trigger/toolbar-loader.js +2 -0
  16. package/dist/components/preferences/atoms/root.js +1 -1
  17. package/dist/components/prompt/atoms/early-tap-script.js +2 -0
  18. package/dist/components/prompt/atoms/root.js +1 -1
  19. package/dist/components/shared/primitives/button.js +1 -1
  20. package/dist/components/shared/surface-styles.js +1 -1
  21. package/dist/consent-gate.js +1 -1
  22. package/dist/context/index.js +1 -1
  23. package/dist/devtools.js +1 -1
  24. package/dist/iab/styles.tw3.css +2 -2
  25. package/dist/iab-dialog-deferred.js +2 -0
  26. package/dist/iab.js +1 -1
  27. package/dist/index-parts/consent-gate.js +1 -1
  28. package/dist/index-parts/core.js +1 -1
  29. package/dist/index-parts/frame.js +1 -0
  30. package/dist/index-parts/hosted.js +1 -0
  31. package/dist/index-parts/offline.js +1 -0
  32. package/dist/index-parts/presets.js +1 -0
  33. package/dist/index.js +1 -1
  34. package/dist/modes.js +1 -0
  35. package/dist/panel-link.js +1 -1
  36. package/dist/panel-trigger.js +1 -1
  37. package/dist/provider.js +1 -1
  38. package/dist/styles.tw3.css +2 -2
  39. package/dist/transports/hosted.js +1 -0
  40. package/dist/transports/manifest.js +1 -0
  41. package/dist/utils/deferred-module.js +1 -1
  42. package/dist/version.js +1 -1
  43. package/dist-types/chunk-warming.d.ts +7 -6
  44. package/dist-types/components/consent-gate/frame.d.ts +19 -0
  45. package/dist-types/components/consent-gate/index.d.ts +3 -19
  46. package/dist-types/components/iab-prompt/iab-prompt.d.ts +1 -1
  47. package/dist-types/components/panel/index.d.ts +1 -1
  48. package/dist-types/components/panel-trigger/toolbar-loader.d.ts +34 -0
  49. package/dist-types/components/preferences/index.d.ts +1 -1
  50. package/dist-types/components/prompt/atoms/early-tap-script.d.ts +17 -0
  51. package/dist-types/components/prompt/index.d.ts +1 -1
  52. package/dist-types/components/shared/surface-styles.d.ts +26 -2
  53. package/dist-types/consent-gate.d.ts +1 -1
  54. package/dist-types/context/index.d.ts +3 -3
  55. package/dist-types/context.d.ts +2 -0
  56. package/dist-types/gpp.d.ts +1 -1
  57. package/dist-types/iab-dialog-deferred.d.ts +36 -0
  58. package/dist-types/iab.d.ts +3 -2
  59. package/dist-types/index-parts/consent-gate.d.ts +1 -1
  60. package/dist-types/index-parts/core.d.ts +1 -2
  61. package/dist-types/index-parts/frame.d.ts +7 -0
  62. package/dist-types/index-parts/hosted.d.ts +7 -0
  63. package/dist-types/index-parts/offline.d.ts +7 -0
  64. package/dist-types/index-parts/presets.d.ts +7 -0
  65. package/dist-types/index.d.ts +5 -1
  66. package/dist-types/modes.d.ts +26 -0
  67. package/dist-types/panel-link.d.ts +1 -1
  68. package/dist-types/panel-trigger.d.ts +1 -1
  69. package/dist-types/provider.d.ts +3 -3
  70. package/dist-types/transports/hosted.d.ts +36 -0
  71. package/dist-types/transports/manifest.d.ts +48 -0
  72. package/dist-types/version.d.ts +1 -1
  73. package/docs/README.md +7 -9
  74. package/docs/concepts/choose-your-setup.md +45 -10
  75. package/docs/concepts/consent-categories.md +1 -1
  76. package/docs/concepts/consent-state.md +5 -2
  77. package/docs/concepts/data-fetching.md +100 -42
  78. package/docs/concepts/modes.md +281 -0
  79. package/docs/customization/dark-mode.md +1 -1
  80. package/docs/customization/motion.md +2 -2
  81. package/docs/customization/recipes.md +4 -4
  82. package/docs/customization/slots.md +25 -25
  83. package/docs/customization/stylesheets.md +12 -14
  84. package/docs/customization/tailwind.md +17 -11
  85. package/docs/customization/tokens.md +36 -7
  86. package/docs/frameworks/react/components/consent-banner.md +14 -10
  87. package/docs/frameworks/react/components/consent-dialog.md +14 -10
  88. package/docs/frameworks/react/components/consent-provider.md +51 -44
  89. package/docs/frameworks/react/components.md +14 -10
  90. package/docs/frameworks/react/content-security-policy.md +21 -12
  91. package/docs/frameworks/react/hooks.md +2 -2
  92. package/docs/frameworks/react/iab.md +20 -2
  93. package/docs/frameworks/react/quickstart.md +63 -97
  94. package/docs/frameworks/react/rendering.md +168 -94
  95. package/docs/frameworks/react/scripts.md +19 -28
  96. package/docs/frameworks/react/troubleshooting.md +57 -25
  97. package/docs/frameworks/react/upgrade-v3.md +110 -62
  98. package/docs/guides/banner-experiments.md +21 -6
  99. package/docs/guides/troubleshooting.md +89 -25
  100. package/docs/guides/verify-consent.md +23 -8
  101. package/docs/integrations/adobe-analytics.md +60 -64
  102. package/docs/integrations/ahrefs-analytics.md +58 -63
  103. package/docs/integrations/amplitude.md +60 -64
  104. package/docs/integrations/clearbit.md +59 -63
  105. package/docs/integrations/cloudflare-web-analytics.md +56 -60
  106. package/docs/integrations/cloudflare-zaraz.md +55 -57
  107. package/docs/integrations/crisp.md +64 -69
  108. package/docs/integrations/databuddy.md +62 -64
  109. package/docs/integrations/fathom-analytics.md +56 -60
  110. package/docs/integrations/front-chat.md +56 -60
  111. package/docs/integrations/google-maps.md +25 -13
  112. package/docs/integrations/google-tag-manager.md +153 -77
  113. package/docs/integrations/google-tag.md +134 -78
  114. package/docs/integrations/heap.md +56 -60
  115. package/docs/integrations/hightouch.md +55 -60
  116. package/docs/integrations/hotjar.md +60 -64
  117. package/docs/integrations/intercom.md +55 -60
  118. package/docs/integrations/klaviyo.md +65 -66
  119. package/docs/integrations/linkedin-insights.md +58 -63
  120. package/docs/integrations/logrocket.md +55 -60
  121. package/docs/integrations/matomo-analytics.md +58 -60
  122. package/docs/integrations/meta-pixel.md +56 -60
  123. package/docs/integrations/microsoft-clarity.md +57 -60
  124. package/docs/integrations/microsoft-uet.md +58 -61
  125. package/docs/integrations/mixpanel-analytics.md +73 -75
  126. package/docs/integrations/one-dollar-stats.md +53 -59
  127. package/docs/integrations/openai-pixel.md +58 -60
  128. package/docs/integrations/overview.md +59 -42
  129. package/docs/integrations/pinterest-tag.md +56 -60
  130. package/docs/integrations/pirsch.md +54 -60
  131. package/docs/integrations/plausible-analytics.md +55 -60
  132. package/docs/integrations/posthog.md +56 -60
  133. package/docs/integrations/promptwatch.md +57 -63
  134. package/docs/integrations/reddit-pixel.md +57 -60
  135. package/docs/integrations/rudderstack.md +65 -67
  136. package/docs/integrations/rybbit-analytics.md +70 -75
  137. package/docs/integrations/segment.md +56 -60
  138. package/docs/integrations/sentry.md +65 -60
  139. package/docs/integrations/snapchat-pixel.md +54 -60
  140. package/docs/integrations/tiktok-pixel.md +59 -63
  141. package/docs/integrations/umami-analytics.md +54 -60
  142. package/docs/integrations/vercel-analytics.md +53 -59
  143. package/docs/integrations/vercel-speed-insights.md +317 -0
  144. package/docs/integrations/x-pixel.md +57 -63
  145. package/docs/integrations/youtube.md +25 -13
  146. package/package.json +18 -8
  147. package/docs/upgrade-v3.md +0 -429
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@c15t/react",
3
- "version": "3.0.0-alpha.9",
3
+ "version": "3.0.0",
4
4
  "description": "React cookie banner, consent manager, preference center, and headless CMP components with IAB TCF and Consent Mode support.",
5
5
  "keywords": [
6
6
  "ccpa",
@@ -67,6 +67,11 @@
67
67
  "import": "./dist/index.js",
68
68
  "default": "./dist/index.js"
69
69
  },
70
+ "./modes": {
71
+ "types": "./dist-types/modes.d.ts",
72
+ "import": "./dist/modes.js",
73
+ "default": "./dist/modes.js"
74
+ },
70
75
  "./provider": {
71
76
  "types": "./dist-types/provider.d.ts",
72
77
  "import": "./dist/provider.js",
@@ -82,6 +87,11 @@
82
87
  "import": "./dist/draft.js",
83
88
  "default": "./dist/draft.js"
84
89
  },
90
+ "./theme": {
91
+ "types": "./dist-types/index-parts/theme.d.ts",
92
+ "import": "./dist/index-parts/theme.js",
93
+ "default": "./dist/index-parts/theme.js"
94
+ },
85
95
  "./module-hooks": {
86
96
  "types": "./dist-types/module-hooks.d.ts",
87
97
  "import": "./dist/module-hooks.js",
@@ -254,16 +264,16 @@
254
264
  "test:watch": "bun prebuild && vitest"
255
265
  },
256
266
  "dependencies": {
257
- "@c15t/core": "3.0.0-alpha.9",
258
- "@c15t/dev-tools": "3.0.0-alpha.9",
259
- "@c15t/iab": "3.0.0-alpha.9",
260
- "@c15t/schema": "3.0.0-alpha.7",
261
- "@c15t/translations": "3.0.0-alpha.6",
262
- "@c15t/ui": "3.0.0-alpha.9",
267
+ "@c15t/core": "3.0.0",
268
+ "@c15t/dev-tools": "3.0.0",
269
+ "@c15t/iab": "3.0.0",
270
+ "@c15t/schema": "3.0.0",
271
+ "@c15t/translations": "3.0.0",
272
+ "@c15t/ui": "3.0.0",
263
273
  "@types/google.maps": "3.65.3"
264
274
  },
265
275
  "devDependencies": {
266
- "@c15t/backend": "3.0.0-alpha.9",
276
+ "@c15t/backend": "3.0.0",
267
277
  "@c15t/conformance": "0.0.1",
268
278
  "@c15t/typescript-config": "0.0.1",
269
279
  "@c15t/vitest-config": "1.0.0",
@@ -1,429 +0,0 @@
1
- ---
2
- title: Migrate to v3
3
- description: Upgrade from c15t v2 to v3. Pick the guide for the v2 package you
4
- use (@c15t/nextjs, @c15t/react or the c15t store), rename @c15t/scripts, keep
5
- visitors' choices, and upgrade a self-hosted backend and the Node.js SDK.
6
- group: reference
7
- ---
8
-
9
- ## Hand the upgrade to a coding agent
10
-
11
- **Upgrade this app to c15t v3**
12
-
13
- Paste into Claude Code, Codex, Cursor or another coding agent at the root of your app.
14
-
15
- ```prompt
16
- Upgrade this app from c15t v2 to c15t v3.
17
-
18
- 1. Check `package.json` for c15t packages. `@c15t/nextjs` means Next.js, `@c15t/react` without it means React, and `c15t` alone means the JavaScript store.
19
- 2. Read the matching guide before you change anything: <https://c15t.com/docs/frameworks/next/upgrade-v3.md>, <https://c15t.com/docs/frameworks/react/upgrade-v3.md> or <https://c15t.com/docs/frameworks/javascript/upgrade-v3.md>. Follow it in order and do not guess v3 APIs from memory.
20
- 3. If the app also runs its own c15t backend with `@c15t/backend`, read <https://c15t.com/docs/upgrade-v3.md> and upgrade the backend in the same change. Start with `npx @c15t/cli@alpha codemods backend-config-to-v3 node-sdk-to-v3 --dry-run --json`, then run it without `--dry-run`.
21
- 4. Keep the existing backend URL.
22
- 5. Run the typecheck and build when you finish.
23
-
24
- List the files you changed, anything you could not migrate, and how you checked that vendor requests still wait for consent.
25
- ```
26
-
27
- ## What changes in v3
28
-
29
- * One package, `c15t`, replaces `@c15t/nextjs`, `@c15t/react` and the v2
30
- `c15t` store. Import from its subpaths: `c15t/next`, `c15t/react`, and `c15t`
31
- for the headless engine.
32
- * The vendor helpers move from `@c15t/scripts` to `@c15t/integrations`, with
33
- the same subpaths and helper names.
34
- * `ConsentManagerProvider` becomes `ConsentProvider` in React. Next.js apps
35
- that resolve consent on the server use `ConsentRoot`.
36
- * `mode` and `backendURL` become one transport, `hosted({ url })` or
37
- `offline()`.
38
- * Policy packs become policy rules. You set them in your Inth project, in a
39
- self-hosted backend's `manifest.policyRules`, or in `offline({ policyRules })`.
40
- * `useConsentManager()` is gone. Each field has its own hook, and a codemod
41
- rewrites most calls.
42
- * Callbacks have new names, and each reports either a recorded choice or a
43
- permission change.
44
- * `theme` tokens no longer generate CSS on their own. Render `ConsentTheme` or
45
- add `generateThemeCSS()` output to your stylesheet.
46
- * IAB moves from the `iab` provider option to an `IABProvider` component in
47
- React and Next.js.
48
- * Every package ships ESM only. `require()` of a c15t package no longer
49
- resolves.
50
- * Visitors' existing choices keep working. v3 reads the v2 cookie.
51
- * A self-hosted backend takes `database` instead of `adapter`, and needs a
52
- schema migration.
53
- * `@c15t/node-sdk` has a new client, `createC15tClient()`. Its methods return
54
- results instead of throwing, and it reads no environment variables.
55
-
56
- ## Pick your guide
57
-
58
- Each guide covers one v2 package from start to finish.
59
-
60
- | You use in v2 | Follow |
61
- | ----------------------------------------------------- | -------------------------------------------------------------------- |
62
- | `@c15t/nextjs` | [Next.js](https://c15t.com/docs/frameworks/next/upgrade-v3) |
63
- | `@c15t/react` | [React](./frameworks/react/upgrade-v3.md) |
64
- | `c15t`, with `getOrCreateConsentRuntime` or the store | [JavaScript](https://c15t.com/docs/frameworks/javascript/upgrade-v3) |
65
-
66
- v3 also adds TanStack Start, Nuxt, Vue, Astro, Svelte, SvelteKit and a script
67
- tag. If you want to move to one of them, start with its quickstart from
68
- [Choose your setup](./concepts/choose-your-setup.md).
69
-
70
- If you run your own c15t backend, upgrade it in the same release as the
71
- clients, following [Upgrade a self-hosted backend](#upgrade-a-self-hosted-backend).
72
- Server code that calls the API with `@c15t/node-sdk` follows
73
- [Update the Node.js SDK](#update-the-nodejs-sdk).
74
-
75
- The sections below apply to every app.
76
-
77
- ## Rename the integrations dependency
78
-
79
- v3 renames `@c15t/scripts` to `@c15t/integrations`. Remove `@c15t/scripts`
80
- from your dependencies and install `@c15t/integrations` from the `alpha`
81
- dist-tag:
82
-
83
- | Package manager | Command |
84
- | :-------------- | :------------------------------------- |
85
- | npm | `npm install @c15t/integrations@alpha` |
86
- | pnpm | `pnpm add @c15t/integrations@alpha` |
87
- | yarn | `yarn add @c15t/integrations@alpha` |
88
- | bun | `bun add @c15t/integrations@alpha` |
89
-
90
- Replace the package name in each import. Vendor subpaths and helper names stay
91
- the same.
92
-
93
- Before, in v2:
94
-
95
- ```ts
96
- import { googleTagManager } from '@c15t/scripts/google-tag-manager';
97
- import { posthog } from '@c15t/scripts/posthog';
98
- ```
99
-
100
- After:
101
-
102
- ```ts
103
- import { googleTagManager } from '@c15t/integrations/google-tag-manager';
104
- import { posthog } from '@c15t/integrations/posthog';
105
- ```
106
-
107
- The `scripts` configuration option and the `Script` type keep their names.
108
-
109
- Two helper behaviors change. A helper that needs an ID, such as `metaPixel`,
110
- `googleTagManager` or `posthog`, now throws when the ID is empty or only
111
- whitespace, where v2 loaded a broken script. And if your code reads
112
- `@c15t/integrations/registry`, an entry's `consentCategory` can now be a
113
- compound condition such as `{ and: ['marketing', 'measurement'] }`, not only a
114
- category name.
115
-
116
- `@c15t/scripts` stays available as a deprecated compatibility package for all
117
- of v3. It re-exports the implementation and types of `@c15t/integrations`, so
118
- you can migrate imports separately from the rest of the upgrade. Compatibility
119
- ends in v4; versions already published stay on npm.
120
-
121
- To preview the import changes in JavaScript and TypeScript files:
122
-
123
- ```bash
124
- npx @c15t/cli@alpha codemods scripts-to-integrations --dry-run --json
125
- ```
126
-
127
- Review the output, then run it again without `--dry-run`. The codemod runs
128
- only when you name it. It does not change `package.json`, lockfiles, or
129
- imports inside `.vue`, `.svelte` or `.astro` files; update those by hand, then
130
- reinstall dependencies and build the app.
131
-
132
- ## Replace `useConsentManager()`
133
-
134
- `useConsentManager()` returned the whole consent state, so every component
135
- that called it re-rendered on every change. v3 has one hook per field. Run the
136
- codemod first:
137
-
138
- ```bash
139
- npx @c15t/cli@alpha codemods use-consent-manager-to-hooks --dry-run --json
140
- ```
141
-
142
- Review the proposed files, then run it again without `--dry-run`. For this v2
143
- component:
144
-
145
- ```tsx title="Before (v2): components/accept-button.tsx"
146
- import { useConsentManager } from '@c15t/react';
147
-
148
- export function AcceptButton() {
149
- const { activeUI, has, saveConsents } = useConsentManager();
150
- if (activeUI !== 'banner' || has('marketing')) return null;
151
- return (
152
- <button type="button" onClick={() => void saveConsents('all')}>
153
- Accept
154
- </button>
155
- );
156
- }
157
- ```
158
-
159
- the codemod writes:
160
-
161
- ```tsx title="After (v3): components/accept-button.tsx"
162
- import { useActiveUI, useConsent } from '@c15t/react';
163
- import { useHeadlessConsentUI } from '@c15t/react/headless';
164
-
165
- export function AcceptButton() {
166
- const activeUI = useActiveUI() ?? 'none';
167
- const hasMarketing = useConsent('marketing');
168
- const { saveCustomPreferences: saveConsents } = useHeadlessConsentUI();
169
- if (activeUI !== 'banner' || hasMarketing) return null;
170
- return (
171
- <button type="button" onClick={() => void saveConsents('all')}>
172
- Accept
173
- </button>
174
- );
175
- }
176
- ```
177
-
178
- The codemod keeps the entry you imported from. Change `@c15t/react` to
179
- `c15t/react` afterwards if you moved to the umbrella package. Fields it cannot
180
- rewrite stay on a `useConsentManager()` call under a `TODO(c15t v3)` comment,
181
- which fails the build until you replace them. The table lists the v2 fields
182
- components read. The other fields held provider configuration, such as
183
- `legalLinks`, `scripts` and `storageConfig`, or store internals; set
184
- configuration through `ConsentProvider` props.
185
- Import hooks from `c15t/react`, `c15t/next` or `c15t/tanstack-start`, and
186
- `useHeadlessConsentUI` from the matching `/headless` entry.
187
-
188
- | v2 field | v3 replacement |
189
- | ------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------- |
190
- | `activeUI` | `useActiveUI()`. It returns `null` until c15t picks a surface, where v2 returned `'none'`. |
191
- | `setActiveUI(ui)` | `useSetActiveUI()` |
192
- | `has(category)` | `useConsent(category)`, one call per category at the top of the component |
193
- | `consents` | `useConsents()` |
194
- | `saveConsents('all')` | `useHeadlessConsentUI().saveCustomPreferences('all')` |
195
- | `saveConsents('necessary')` | `useHeadlessConsentUI().saveCustomPreferences('none')` |
196
- | `saveConsents('custom')` | `useConsentDraft().save()`, or `useHeadlessConsentUI().saveCustomPreferences()` inside a `ConsentDraftProvider` |
197
- | `selectedConsents` | `useConsentDraft().values` |
198
- | `setSelectedConsent(name, value)` | `useConsentDraft().set(name, value)` |
199
- | `setConsent(name, value)` | `useSaveConsents()`, called with `{ [name]: value }` |
200
- | `consentInfo` | `useExplicitChoice()` |
201
- | `hasConsented()` | `useExplicitChoice() !== null` |
202
- | `consentCategories`, `consentTypes`, `getDisplayedConsents()` | `useConsentDraft().displayedCategories`, with labels from `useTranslations().consentTypes` |
203
- | `policyCategories` | `usePolicyCategories()`. The v2 list started with `'necessary'`; this one does not. |
204
- | `policyScopeMode` | `usePolicyScopeMode()` |
205
- | `policyBanner` | `usePromptPresentation()` |
206
- | `policyDialog` | `usePreferencesPresentation()` |
207
- | `model` | `useModel()`. It returns `null` while no policy matches, where v2 returned `'opt-in'`. |
208
- | `branding` | `useBranding()`. It returns `null` when unset, where v2 returned `'c15t'`. |
209
- | `iab` | `useIABSnapshot()` |
210
- | `locationInfo` | `useLocation()` |
211
- | `overrides`, `setOverrides()` | `useOverrides()`, `useSetOverrides()` |
212
- | `setLanguage()` | `useSetLanguage()` |
213
- | `user`, `identifyUser()` | `useUser()`, `useIdentify()` |
214
- | `translationConfig` | `useTranslations()` for the active strings |
215
- | `subscribeToConsentChanges(listener)` | `useSubscribeToConsentChanges()`, or the `onPermissionsChanged` callback |
216
- | `updateConsentCategories(categories)` | `useRegisterConsentCategories()` |
217
- | `manager` | Nothing. Use the hooks above. |
218
-
219
- `saveCustomPreferences()` saves the way the stock buttons do. It closes the
220
- banner or dialog after a successful save. `useSaveConsents()` calls the engine
221
- directly and does not close anything.
222
-
223
- In v2, `useConsentManager()` kept one draft per component. In v3,
224
- `useConsentDraft()` and `useHeadlessConsentUI()` share a draft only inside a
225
- `ConsentDraftProvider`. Without one, `saveCustomPreferences()` saves a fresh
226
- draft and ignores what `useConsentDraft().set()` staged, even in the same
227
- component. Save with `useConsentDraft().save()`, or render the code that stages
228
- and the code that saves inside one `ConsentDraftProvider`. `ConsentWidget`
229
- already includes one. The codemod
230
- adds a `TODO(c15t v3)` comment where this applies.
231
-
232
- ## Keep visitors' existing choices
233
-
234
- v3 uses the same `c15t` cookie and storage key as v2 and reads v2 records. It
235
- does not rewrite them on page load; the next accept, reject or save writes a
236
- v3 record.
237
-
238
- A v2 denial keeps restricting its category. A v2 grant keeps applying until
239
- the policy's validity period runs out, and then c15t asks again. If the rule
240
- comes from a preset and the v2 record noted the policy it was made under, c15t
241
- also asks again when that policy has changed. Under custom rules, a v2 grant
242
- applies until it expires. Do not copy permissions into new
243
- records yourself; c15t decides which v2 choices still count.
244
-
245
- ## Choose the HTML bundle
246
-
247
- For HTML script tags, `c15t.js` is now the hosted bundle. Keep that filename
248
- for Inth or a self-hosted backend and supply `data-backend-url`, a queued
249
- `backendURL`, or a hosted factory. Loading it without a backend no longer
250
- starts offline mode.
251
-
252
- If an earlier v3 script tag used local policy rules, switch its URL to
253
- `c15t.offline.js`. For manifest mode, custom transports or a page that selects
254
- its mode at runtime, use the root ES module `@c15t/browser`, which still
255
- accepts every mode. The headless script supports those modes with your own
256
- UI, and the IAB script supports them for IAB policies. The self-hosted
257
- backend's `/c15t.js` route now resolves policies through `/init`; its headless
258
- and IAB routes still preload the manifest. See the
259
- [script file reference](https://c15t.com/docs/frameworks/html/attributes-and-api#script-files).
260
-
261
- ## Upgrade a self-hosted backend
262
-
263
- Deploy the v3 backend and the v3 clients in the same release. A v3 client
264
- cannot read a v2 backend's `/init` response. Policy resolution fails, optional
265
- categories stay denied and no banner appears.
266
-
267
- 1. Install `@c15t/backend@alpha` and the SQL driver for your database.
268
- 2. Replace `adapter` with `database` in your config. The Drizzle, Prisma,
269
- TypeORM and Kysely adapters are gone; point `database` at the same SQL
270
- database instead. MongoDB has no migration path.
271
- 3. Move `policyPacks`, `branding`, `customTranslations`, `i18n` and `appName`
272
- under `manifest`, and rename `policyPacks` to `policyRules`. Split `iab`
273
- between `manifest.iab` and `gvl`, as the table below shows.
274
- 4. Back up the database, then plan and apply the schema migration:
275
-
276
- ```bash
277
- npx @c15t/cli@alpha self-host migrate --config ./c15t-backend.config.ts --plan
278
- npx @c15t/cli@alpha self-host migrate --config ./c15t-backend.config.ts --apply
279
- ```
280
-
281
- The migrator recognizes the v2 schema, adopts it, and adds the v3 tables and
282
- columns. Apply it before the v3 backend serves traffic: v3 records policy
283
- decisions without the v2 `jurisdiction` label, which the migration makes
284
- nullable. [Database setup](https://c15t.com/docs/self-host/guides/database-setup#upgrade-a-v2-backend)
285
- covers the details, and the [backend quickstart](https://c15t.com/docs/self-host/quickstart)
286
- shows a complete v3 config and route.
287
-
288
- The `backend-config-to-v3` codemod does step 3 for the keys that move as they
289
- are, and leaves a `TODO(c15t v3)` comment on `adapter`, `iab`,
290
- `disableGeoLocation` and the other options in the table below:
291
-
292
- ```bash
293
- npx @c15t/cli@alpha codemods backend-config-to-v3 policy-packs-to-policy-rules --dry-run --json
294
- ```
295
-
296
- Preset calls such as `policyRulePresets.europeOptIn()` need no other change.
297
- A hand-written v2 pack uses `consent` and `ui` keys, which v3 rules replace
298
- with flat keys such as `model` and `prompt`. See
299
- [policy configuration](https://c15t.com/docs/self-host/guides/policy-packs).
300
-
301
- The v3 backend stores timestamps in UTC. Convert rows that a v2 backend wrote
302
- in another time zone before you deploy. Database setup lists the checks.
303
-
304
- Other backend options and entries change too:
305
-
306
- | v2 | v3 |
307
- | ----------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
308
- | `tablePrefix` | Removed. The migrator refuses a database with prefixed c15t tables, so rename them first. On PostgreSQL, `database.schema` keeps c15t's tables in their own schema. |
309
- | `iab.vendorIds`, `iab.endpoint`, `cache.adapter` | `gvl: { vendorIds, endpoint, cache }` |
310
- | `iab.enabled`, `iab.cmpId`, `iab.customVendors` | `manifest.iab` |
311
- | `logger`, `telemetry` | `observability`, passed to `c15tInstance()` |
312
- | `background`, `iab.bundled` | Removed |
313
- | `openapi.docsPath`, `openapi.options`, `openapi.customUiTemplate` and the `/docs` page | Removed. The spec is still at `/spec.json`. |
314
- | `policySnapshot.onValidationFailure` | Removed |
315
- | `GET /` as an alias of `/status` | Removed. Call `/status`. |
316
- | `@c15t/backend/define-config`, `/router`, `/types`, `/db/schema`, `/db/adapters/*`, `/db/migrator`, `/edge` | Removed. Import `defineConfig`, `createMigrator` and `policyRulePresets` from `@c15t/backend`. |
317
-
318
- ### Remove `disableGeoLocation`
319
-
320
- v3 removes the top-level `disableGeoLocation` option. Policy rules decide by
321
- country and region. To show every visitor the same banner, configure one rule
322
- with `match: { isDefault: true }`. It applies wherever the visitor is, so the
323
- browser can resolve it without asking the backend for a location.
324
-
325
- To test one region's rule from anywhere, set the country in the client's
326
- `overrides`, for example `overrides: { country: 'US' }`. See
327
- [runtime options](https://c15t.com/docs/frameworks/javascript/api/runtime).
328
-
329
- ### Stop reading `jurisdiction`
330
-
331
- `/init` responses, session reports and `sessions.onReport` no longer carry a
332
- `jurisdiction` label such as `GDPR`. Read the matched policy from
333
- `policyResolution` instead, or the report's `policy`, `country` and `region`.
334
- The backend still accepts `jurisdiction` in a v2 client's save request and
335
- ignores it.
336
-
337
- ## Update the Node.js SDK
338
-
339
- Install `@c15t/node-sdk@alpha`. The v2 client is gone: create one with
340
- `createC15tClient()` and pass the backend URL and API key yourself.
341
-
342
- | Package manager | Command |
343
- | :-------------- | :--------------------------------- |
344
- | npm | `npm install @c15t/node-sdk@alpha` |
345
- | pnpm | `pnpm add @c15t/node-sdk@alpha` |
346
- | yarn | `yarn add @c15t/node-sdk@alpha` |
347
- | bun | `bun add @c15t/node-sdk@alpha` |
348
-
349
- ```ts
350
- import { createC15tClient } from '@c15t/node-sdk';
351
-
352
- const apiKey = process.env.C15T_API_KEY;
353
- if (!apiKey) throw new Error('Set C15T_API_KEY');
354
-
355
- const c15t = createC15tClient({
356
- baseUrl: 'https://app.example.com/api/c15t',
357
- apiKey,
358
- });
359
-
360
- const result = await c15t.consents.check({
361
- externalId: 'user_123',
362
- types: ['privacy_policy', 'marketing_communications'],
363
- });
364
- if (result.ok) {
365
- console.log(result.data.results.privacy_policy.hasConsent);
366
- }
367
- ```
368
-
369
- | v2 | v3 |
370
- | ----------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
371
- | `c15tClient(options)`, `new C15TClient(options)` | `createC15tClient(options)` |
372
- | `C15T_API_URL`, `C15T_API_TOKEN` read from the environment | Not read. Pass `baseUrl` and `apiKey`. |
373
- | `token` | `apiKey` |
374
- | `prefix` | Part of `baseUrl`. v2 replaced the base URL's path with `prefix`, so use the origin plus the prefix, such as `https://app.example.com/api/c15t`. |
375
- | `timeout` | `timeoutMs`, per attempt. The default drops from 30 to 10 seconds. |
376
- | `retryConfig` | `retry: { maxRetries, initialDelayMs, maxDelayMs }`, or `false`. `backoffFactor`, `retryableStatusCodes`, `nonRetryableStatusCodes` and `retryOnNetworkError` are gone. |
377
- | `debug`, `C15T_DEBUG` | `onEvent` |
378
- | `client.meta.status()`, `client.status()` | `c15t.status()` |
379
- | `client.meta.init(options)`, `client.init(options)` | `c15t.init({ language, country, region, gpc }, options)`. Per-call options move to the second argument. |
380
- | `getSubject(id, { type: 'a,b' })`, `subjects.get(id, { type: 'a,b' })` | `subjects.get(id, { types: ['a', 'b'] })` |
381
- | `createSubject(input)` | `subjects.create(input)`. `givenAt` also takes a `Date`. |
382
- | `patchSubject(id, input)`, `subjects.patch(id, input)` | `subjects.identify(id, { externalId, identityProvider })` |
383
- | `listSubjects({ externalId })`, `subjects.list({ externalId })` | `subjects.list({ externalId })`, on a client with `apiKey` |
384
- | `checkConsent(query)`, `consent.check({ externalId, type: 'a,b' })` | `consents.check({ externalId, types: ['a', 'b'] })`, on a client with `apiKey` |
385
- | `ResponseContext` with `data: T \|null` | `{ ok: true, data } \|{ ok: false, error }`. Check `ok` first. |
386
- | `unwrap()`, `expect()` on the response | `unwrap(result)`, imported from `@c15t/node-sdk` |
387
- | `unwrapOr()`, `map()` on the response | Check `result.ok` |
388
- | `throw: true` | `unwrap(result)` |
389
- | `onSuccess`, `onError` | Check `result.ok` after the call |
390
- | `C15TError`, `isC15TError()` | `C15tError`, `isC15tError(error, ...codes)` |
391
- | `C15TClient`, `C15TClientOptions`, `RetryConfig`, `FetchOptions`, `ResponseContext` types | `C15tClient`, `C15tClientOptions`, `C15tRetryOptions`, `C15tCallOptions`, `C15tResult` |
392
- | Per-call `timeout`, `retryConfig` | `timeoutMs`, `retry` |
393
- | Per-call `body`, `query`, `method` | Removed. Pass the body and query as the method's arguments. |
394
- | `$fetch`, `fetcher`, `resolveUrl`, `createResponseContext` | Removed. Pass a custom `fetch` to `createC15tClient` if you need one. |
395
- | `createMockClient`, `createMockResponse`, `createMockErrorResponse` | `createMockC15tClient`, `ok`, `err` from `@c15t/node-sdk/testing` |
396
-
397
- `patchSubject` returned `{ success, subject }`. `subjects.identify` returns
398
- `{ subject }`, and `subject.identityProvider` is always set. Dates in
399
- responses, such as `givenAt` and `createdAt`, are now `Date` objects rather
400
- than strings.
401
-
402
- `consents.check` and `subjects.list` count only verified links. Links made by
403
- v2 don't count until you relink them with `subjects.identify` on a keyed
404
- client, or [with an identity token](https://c15t.com/docs/self-host/api/node-sdk#verify-a-link-made-in-the-browser) on the user's next sign-in.
405
-
406
- The `node-sdk-to-v3` codemod renames the factory, client options, methods,
407
- types and errors in files that create a client. It turns `type` strings into
408
- `types` arrays, and leaves a `TODO(c15t v3)` comment at each call, because
409
- results are now `{ ok, data }` or `{ ok, error }`. It renames per-call
410
- `timeout` and `retryConfig` too. It also marks `prefix`, `debug`, per-call
411
- `throw`, `onSuccess`, `onError`, `body`, `query` and `method`, and a client
412
- built from environment variables:
413
-
414
- ```bash
415
- npx @c15t/cli@alpha codemods node-sdk-to-v3 --dry-run --json
416
- ```
417
-
418
- The new client also adds `manifest()`, `legalDocuments.publish()` and
419
- `experiments.summary()`. The last two need a client with `apiKey`. See the
420
- [Node.js SDK reference](https://c15t.com/docs/self-host/api/node-sdk).
421
-
422
- ## Update the CLI
423
-
424
- * Run `c15t login` again. The CLI signs in through Inth now and no longer
425
- reads sessions saved by v2.
426
- * `c15t setup` no longer writes `.env.local` or `.env.example` and no longer
427
- accepts `--env`. It writes the backend URL into the files it generates.
428
- * `c15t setup` no longer detects Remix or Gatsby. Pick a guide from
429
- [Choose your setup](./concepts/choose-your-setup.md) for those apps.