@c15t/react 3.0.0-alpha.4 → 3.0.0-alpha.6

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 (111) hide show
  1. package/AGENTS.md +8 -5
  2. package/dist/component-hooks/use-headless-consent-ui.js +1 -1
  3. package/dist/component-hooks/use-headless-iab-consent-ui.js +1 -1
  4. package/dist/components/iab-panel/atoms/vendor-list.js +1 -1
  5. package/dist/components/iab-panel/hooks/use-gvl-data.js +1 -1
  6. package/dist/components/iab-panel/index.js +1 -1
  7. package/dist/components/panel/index.js +1 -1
  8. package/dist/components/panel-trigger/atoms/toolbar.js +1 -1
  9. package/dist/components/panel-trigger/atoms/trigger-icons.js +1 -1
  10. package/dist/components/panel-trigger/panel-trigger.js +1 -1
  11. package/dist/components/preferences/atoms/accordion.js +1 -1
  12. package/dist/components/preferences/index.js +1 -1
  13. package/dist/components/shared/primitives/button.js +1 -1
  14. package/dist/components/shared/ui/accordion/accordion.js +1 -1
  15. package/dist/components/shared/ui/collapsible/collapsible.js +1 -1
  16. package/dist/components/shared/ui/preference-item/preference-item.js +1 -1
  17. package/dist/components/shared/ui/switch/switch.js +1 -1
  18. package/dist/components/shared/ui/tabs/tabs.js +1 -1
  19. package/dist/devtools-launcher.js +2 -0
  20. package/dist/devtools.js +1 -1
  21. package/dist/draft-context.js +2 -0
  22. package/dist/draft.js +1 -1
  23. package/dist/external-iab-context.js +1 -1
  24. package/dist/gpp.js +2 -0
  25. package/dist/hooks/use-color-scheme.js +1 -1
  26. package/dist/hooks/use-headless-iab-consent-ui.js +1 -1
  27. package/dist/hooks.js +1 -1
  28. package/dist/index-parts/consent-banner.js +1 -0
  29. package/dist/index-parts/consent-dialog-link.js +1 -0
  30. package/dist/index-parts/consent-dialog-trigger.js +1 -0
  31. package/dist/index-parts/consent-dialog.js +1 -0
  32. package/dist/index-parts/consent-gate.js +1 -0
  33. package/dist/index-parts/core.js +1 -0
  34. package/dist/index-parts/draft.js +1 -0
  35. package/dist/index-parts/hooks.js +1 -0
  36. package/dist/index-parts/module-hooks.js +1 -0
  37. package/dist/index-parts/provider.js +1 -0
  38. package/dist/index-parts/theme.js +1 -0
  39. package/dist/index-parts/translations.js +1 -0
  40. package/dist/index.js +1 -1
  41. package/dist/kernel-selector.js +1 -1
  42. package/dist/module-hooks/iframe-blocker.js +1 -1
  43. package/dist/provider.js +1 -1
  44. package/dist/styles.tw3.css +27 -3
  45. package/dist/version.js +1 -1
  46. package/dist-types/component-hooks/use-headless-consent-ui.d.ts +4 -4
  47. package/dist-types/components/iab-panel/index.d.ts +0 -1
  48. package/dist-types/components/panel/index.d.ts +0 -1
  49. package/dist-types/components/panel-trigger/atoms/trigger-icons.d.ts +5 -0
  50. package/dist-types/components/panel-trigger/panel-trigger.d.ts +0 -8
  51. package/dist-types/components/preferences/index.d.ts +0 -1
  52. package/dist-types/components/shared/ui/accordion/accordion.d.ts +0 -1
  53. package/dist-types/components/shared/ui/collapsible/collapsible.d.ts +0 -1
  54. package/dist-types/components/shared/ui/preference-item/preference-item.d.ts +0 -1
  55. package/dist-types/components/shared/ui/switch/switch.d.ts +0 -1
  56. package/dist-types/components/shared/ui/tabs/tabs.d.ts +0 -1
  57. package/dist-types/devtools-launcher.d.ts +29 -0
  58. package/dist-types/draft-context.d.ts +31 -0
  59. package/dist-types/draft.d.ts +13 -36
  60. package/dist-types/gpp.d.ts +27 -0
  61. package/dist-types/hooks/use-iab-manager.d.ts +4 -4
  62. package/dist-types/hooks.d.ts +30 -5
  63. package/dist-types/index-parts/consent-banner.d.ts +7 -0
  64. package/dist-types/index-parts/consent-dialog-link.d.ts +7 -0
  65. package/dist-types/index-parts/consent-dialog-trigger.d.ts +7 -0
  66. package/dist-types/index-parts/consent-dialog.d.ts +7 -0
  67. package/dist-types/index-parts/consent-gate.d.ts +7 -0
  68. package/dist-types/index-parts/core.d.ts +8 -0
  69. package/dist-types/index-parts/draft.d.ts +7 -0
  70. package/dist-types/index-parts/hooks.d.ts +7 -0
  71. package/dist-types/index-parts/module-hooks.d.ts +7 -0
  72. package/dist-types/index-parts/provider.d.ts +7 -0
  73. package/dist-types/index-parts/theme.d.ts +8 -0
  74. package/dist-types/index-parts/translations.d.ts +7 -0
  75. package/dist-types/index.d.ts +15 -16
  76. package/dist-types/kernel-selector.d.ts +0 -8
  77. package/dist-types/module-hooks/iframe-blocker.d.ts +12 -7
  78. package/dist-types/provider.d.ts +47 -13
  79. package/dist-types/version.d.ts +1 -1
  80. package/docs/README.md +8 -5
  81. package/docs/concepts/choose-your-setup.md +22 -5
  82. package/docs/concepts/consent-state.md +60 -5
  83. package/docs/concepts/data-fetching.md +34 -11
  84. package/docs/customization/recipes.md +4 -5
  85. package/docs/customization/slots.md +3 -2
  86. package/docs/customization/stylesheets.md +29 -39
  87. package/docs/customization/tailwind.md +5 -5
  88. package/docs/customization/tokens.md +1 -1
  89. package/docs/customization/translations.md +33 -7
  90. package/docs/frameworks/react/components/consent-dialog-trigger.md +5 -0
  91. package/docs/frameworks/react/components/consent-dialog.md +2 -1
  92. package/docs/frameworks/react/components/consent-provider.md +6 -2
  93. package/docs/frameworks/react/components/dev-tools.md +24 -1
  94. package/docs/frameworks/react/gpp.md +163 -0
  95. package/docs/frameworks/react/headless.md +4 -4
  96. package/docs/frameworks/react/hooks.md +10 -5
  97. package/docs/frameworks/react/iab.md +24 -18
  98. package/docs/frameworks/react/quickstart.md +77 -16
  99. package/docs/frameworks/react/rendering.md +82 -11
  100. package/docs/frameworks/react/scripts.md +8 -3
  101. package/docs/frameworks/react/upgrade-v3.md +596 -0
  102. package/docs/frameworks/react/vendor-consent.md +6 -0
  103. package/docs/guides/banner-experiments.md +26 -5
  104. package/docs/guides/troubleshooting.md +3 -2
  105. package/docs/integrations/overview.md +28 -27
  106. package/docs/integrations/posthog.md +99 -11
  107. package/docs/integrations/sentry.md +416 -0
  108. package/docs/upgrade-v3.md +222 -314
  109. package/package.json +13 -8
  110. package/dist/ui-save.js +0 -1
  111. package/dist-types/ui-save.d.ts +0 -25
@@ -0,0 +1,416 @@
1
+ ---
2
+ title: Sentry
3
+ description: Configure Sentry's CDN or your own SDK with consent for error
4
+ monitoring, Session Replay and user data.
5
+ icon: sentry
6
+ group: integrations
7
+ ---
8
+
9
+ ## Configure Sentry
10
+
11
+ Remove any existing Sentry Loader Script or CDN tags, then add this configuration.
12
+ Errors run before consent by default; Replay and SDK data collection wait for
13
+ `measurement`.
14
+
15
+ | Package manager | Command |
16
+ | :-------------- | :------------------------------------- |
17
+ | npm | `npm install @c15t/integrations@alpha` |
18
+ | pnpm | `pnpm add @c15t/integrations@alpha` |
19
+ | yarn | `yarn add @c15t/integrations@alpha` |
20
+ | bun | `bun add @c15t/integrations@alpha` |
21
+
22
+ ```ts title="src/consent-scripts.ts"
23
+ import { sentry } from '@c15t/integrations/sentry';
24
+
25
+ export const scripts = [
26
+ sentry({
27
+ dsn: 'https://your-key@o0.ingest.sentry.io/0',
28
+ initOptions: {
29
+ release: 'my-app@1.0.0',
30
+ replaysOnErrorSampleRate: 1,
31
+ replaysSessionSampleRate: 0.1,
32
+ tracesSampleRate: 0.1,
33
+ },
34
+ }),
35
+ ];
36
+ ```
37
+
38
+ The default SDK is 11.4.0 with subresource integrity: `bundle.min.js`, or
39
+ `bundle.tracing.min.js` when traces are sampled. `replay.min.js` loads after
40
+ consent when a Replay sample rate is above 0.
41
+
42
+ ## Register the scripts
43
+
44
+ Complete your [framework quickstart](https://c15t.com/docs/frameworks) first. Keep its Inth
45
+ endpoint, policy, styles and consent UI. Remove the vendor's original script,
46
+ SDK initializer or tag-manager entry, so the vendor loads only through c15t.
47
+
48
+ The vendor pages put the helper in `src/consent-scripts.ts`. If your framework
49
+ quickstart already created a scripts file, such as `lib/scripts.ts` in the
50
+ Next.js guide, add the helper to that array instead of creating a second file.
51
+ The `scripts` export is a configuration, not an initializer. Add it to the c15t provider you already have, at the registration
52
+ point for your framework below. These are edits to that provider, not a second
53
+ provider.
54
+
55
+ **Next.js**
56
+
57
+ Import the configuration into the client boundary from your router guide:
58
+
59
+ ```ts
60
+ import { ConsentRoot } from 'c15t/next';
61
+ import { scripts } from './consent-scripts';
62
+ ```
63
+
64
+ Keep the server-resolved `state` and shared `consentConfig` from your
65
+ router guide. Its manifest, init and save URLs stay in effect. Add
66
+ `scripts` as a top-level prop on the existing root:
67
+
68
+ ```tsx
69
+ <ConsentRoot state={state} config={consentConfig} scripts={scripts}>
70
+ {children}
71
+ </ConsentRoot>
72
+ ```
73
+
74
+ App Router, Pages Router and static export all use this `ConsentRoot` in
75
+ the `'use client'` wrapper `components/consent.tsx`. Keep `scripts` there,
76
+ because a Server Component cannot pass script callbacks to it. See
77
+ [Next.js scripts and embeds](https://c15t.com/docs/frameworks/next/scripts).
78
+
79
+ **TanStack Start**
80
+
81
+ Import the configuration into your root route and pass it to the existing
82
+ `ConsentRoot` as a top-level prop. Keep the loader, `backendURL` and
83
+ `initRoute` from the [TanStack Start quickstart](https://c15t.com/docs/frameworks/tanstack-start/quickstart):
84
+
85
+ ```tsx title="src/routes/__root.tsx"
86
+ import { scripts } from '../consent-scripts';
87
+
88
+ <ConsentRoot
89
+ state={consent}
90
+ backendURL={backendURL}
91
+ initRoute={false}
92
+ scripts={scripts}
93
+ >
94
+ ```
95
+
96
+ Import vendor helpers in the root route module, not in a server function.
97
+ A server function's return value must be serializable, and script
98
+ configurations carry callbacks. See
99
+ [TanStack Start scripts](https://c15t.com/docs/frameworks/tanstack-start/scripts).
100
+
101
+ **React**
102
+
103
+ Add the configuration to the existing `ConsentProvider` options, next to
104
+ `mode`:
105
+
106
+ ```tsx title="src/consent.tsx"
107
+ import { scripts } from './consent-scripts';
108
+
109
+ <ConsentProvider options={{ mode, scripts }}>
110
+ ```
111
+
112
+ `mode` is the `hosted({ url: 'https://your-project.inth.app' })` value
113
+ from the [React quickstart](../frameworks/react/quickstart.md). Keep the banner,
114
+ dialog and preferences link inside the provider. See
115
+ [React scripts and embeds](../frameworks/react/scripts.md).
116
+
117
+ **Nuxt**
118
+
119
+ Register the scripts under the `c15t` key in `app/app.config.ts`. Adjust the
120
+ relative import to where you created `consent-scripts.ts`:
121
+
122
+ ```ts title="app/app.config.ts"
123
+ import { scripts } from '../src/consent-scripts';
124
+
125
+ export default defineAppConfig({
126
+ c15t: { scripts },
127
+ });
128
+ ```
129
+
130
+ The Nuxt module merges this over its options in `nuxt.config.ts` and starts
131
+ one script loader in the browser after hydration, once it has applied the
132
+ visitor's stored choice and privacy signals. Keep `scripts` out of
133
+ `nuxt.config.ts`, which reaches the browser as JSON and drops the vendor
134
+ callbacks. Write the vendor IDs into `consent-scripts.ts`. See
135
+ [Nuxt scripts and embeds](https://c15t.com/docs/frameworks/nuxt/scripts).
136
+
137
+ **Vue**
138
+
139
+ Pass the scripts to the existing `c15tVue` plugin call in `src/main.ts`:
140
+
141
+ ```ts title="src/main.ts"
142
+ import { scripts } from './consent-scripts';
143
+
144
+ app.use(c15tVue, {
145
+ backendURL: 'https://your-project.inth.app',
146
+ scripts,
147
+ });
148
+ ```
149
+
150
+ Keep your existing backend URL and other options. The plugin starts one
151
+ script loader when the app mounts, after it has applied the visitor's stored
152
+ choice. Do not also call `createScriptLoader` from a component. See
153
+ [Vue scripts and embeds](https://c15t.com/docs/frameworks/vue/scripts).
154
+
155
+ **Astro**
156
+
157
+ Add the scripts to the client entrypoint from the
158
+ [Astro quickstart](https://c15t.com/docs/frameworks/astro/quickstart), the module that the
159
+ integration's `clientEntrypoint` option names. Keep `mode`, `ui` and the
160
+ framework integration in `astro.config.mjs` as they are. If the module
161
+ already exports scripts, combine the two arrays.
162
+
163
+ ```ts title="src/consent-client.ts"
164
+ import type { C15tClientOptionsExtension } from 'c15t/astro';
165
+ import { scripts } from './consent-scripts';
166
+
167
+ export default { scripts } satisfies C15tClientOptionsExtension;
168
+ ```
169
+
170
+ Vendor helpers contain callbacks, and the integration options in
171
+ `astro.config.mjs` are serialized into the page, so do not put helpers in
172
+ the integration's `scripts` option. The integration passes the client
173
+ entrypoint to the one runtime every page shares, including across
174
+ `ClientRouter` navigation.
175
+
176
+ **Svelte**
177
+
178
+ Import the scripts in the component that owns your existing provider and
179
+ pass them as a top-level prop:
180
+
181
+ ```svelte title="src/App.svelte"
182
+ <script lang="ts">
183
+ import { ConsentManagerProvider, hosted } from '@c15t/svelte';
184
+ import { scripts } from './consent-scripts';
185
+
186
+ const mode = hosted({ url: 'https://your-project.inth.app' });
187
+ </script>
188
+
189
+ <ConsentManagerProvider {mode} {scripts}>
190
+ <!-- Keep your application, consent UI and preferences link here. -->
191
+ </ConsentManagerProvider>
192
+ ```
193
+
194
+ Retain the styles and consent UI from the [Svelte quickstart](https://c15t.com/docs/frameworks/svelte/quickstart).
195
+ The provider owns the loader and disposes it on unmount.
196
+
197
+ **SvelteKit**
198
+
199
+ Add the scripts to the existing root layout provider. Keep the server load
200
+ and its serializable prefetch data from the [SvelteKit quickstart](https://c15t.com/docs/frameworks/sveltekit/quickstart).
201
+
202
+ ```svelte title="src/routes/+layout.svelte"
203
+ <script lang="ts">
204
+ import { ConsentManagerProvider, hosted } from '@c15t/svelte';
205
+ import { scripts } from '../consent-scripts';
206
+
207
+ let { children, data } = $props();
208
+ const mode = hosted({ url: 'https://your-project.inth.app' });
209
+ </script>
210
+
211
+ <ConsentManagerProvider {mode} {scripts} prefetch={data.prefetch}>
212
+ {@render children()}
213
+ <!-- Keep your consent UI and preferences link here. -->
214
+ </ConsentManagerProvider>
215
+ ```
216
+
217
+ Import vendor helpers in the layout component, not in `+layout.server.ts`:
218
+ a server load cannot send functions to the browser. Prerendered, static and
219
+ SPA-mode pages use the same `scripts` prop. If you pass an externally owned
220
+ `runtime` to the provider, register scripts when creating that runtime instead.
221
+
222
+ **HTML**
223
+
224
+ The helpers in `@c15t/integrations` are ES modules that need a bundler. On a
225
+ page that loads the c15t script tag, paste the vendor's own snippet instead
226
+ and keep it inert until its category is allowed:
227
+
228
+ ```html
229
+ <script type="text/plain" data-c15t-category="measurement">
230
+ // The vendor's snippet, unchanged
231
+ </script>
232
+ ```
233
+
234
+ Use the category this guide names for the vendor. c15t runs the snippet
235
+ once that category is allowed, and reloads the page when the visitor
236
+ withdraws it. Helper options on this page, such as `loadMode`, do not apply
237
+ to a pasted snippet. See [HTML scripts](https://c15t.com/docs/frameworks/html/scripts).
238
+
239
+ **JavaScript**
240
+
241
+ Pass the scripts to `init()` from `@c15t/browser`, next to your backend
242
+ URL:
243
+
244
+ ```ts
245
+ import { init } from '@c15t/browser';
246
+ import { scripts } from './consent-scripts';
247
+
248
+ const consent = init({
249
+ backendURL: 'https://your-project.inth.app',
250
+ scripts,
251
+ });
252
+ ```
253
+
254
+ Keep the backend URL from your quickstart. With
255
+ `createConsentRuntime` from `c15t/runtime`, pass `scripts` to it instead.
256
+ A kernel you create yourself needs a loader from
257
+ `c15t/modules/script-loader`. Attach one loader per kernel. See
258
+ [JavaScript scripts](https://c15t.com/docs/frameworks/javascript/scripts).
259
+
260
+ ## Use your own Sentry SDK
261
+
262
+ For `@sentry/*` 10.67.0 or later, pass your SDK's functions instead of `dsn`.
263
+ Keep Replay in a separate module so it can load after consent:
264
+
265
+ ```ts title="src/sentry-replay.ts"
266
+ import { replayIntegration } from '@sentry/browser';
267
+
268
+ export const createReplay = () =>
269
+ replayIntegration({ maskAllText: true, blockAllMedia: true });
270
+ ```
271
+
272
+ In `src/consent-scripts.ts`, replace the CDN configuration with:
273
+
274
+ ```ts
275
+ import { getClient, init, setUser } from '@sentry/browser';
276
+
277
+ sentry({
278
+ getClient,
279
+ init,
280
+ setUser,
281
+ initOptions: {
282
+ dsn: 'https://your-key@o0.ingest.sentry.io/0',
283
+ replaysSessionSampleRate: 0.1,
284
+ replaysOnErrorSampleRate: 1,
285
+ },
286
+ replay: {
287
+ load: () => import('./sentry-replay').then((m) => m.createReplay()),
288
+ },
289
+ });
290
+ ```
291
+
292
+ Passing `init` protects startup captures before other SDK integrations run.
293
+ Custom initializers must forward the supplied options to `Sentry.init`, including
294
+ after a dynamic import. Avoid initialization as an import side effect.
295
+
296
+ Use named imports from the same Sentry package throughout your app. Next.js and
297
+ Vite split dynamically imported Replay into its own chunk; esbuild and Bun keep
298
+ it in the main bundle. A main-bundle `Sentry.replayIntegration` reference through
299
+ `import * as Sentry` also prevents the split.
300
+
301
+ ### If Sentry already initializes independently
302
+
303
+ c15t applies filters once it finds the client; it cannot retract earlier data.
304
+ For startup protection, disable collection and remove event users in your own
305
+ `Sentry.init` call. With SDK 10.67, omit `queues`:
306
+
307
+ ```ts
308
+ // Include these options in your app's Sentry.init call.
309
+ maxBreadcrumbs: 0,
310
+ dataCollection: {
311
+ userInfo: false,
312
+ cookies: false,
313
+ httpHeaders: { request: false, response: false },
314
+ httpBodies: [],
315
+ urlQueryParams: false,
316
+ graphQL: { document: false, variables: false },
317
+ genAI: { inputs: false, outputs: false },
318
+ databaseQueryData: false,
319
+ queues: false,
320
+ stackFrameVariables: false,
321
+ frameContextLines: 0,
322
+ },
323
+ beforeSend(event) {
324
+ delete event.user;
325
+ delete event.request;
326
+ return event;
327
+ },
328
+ ```
329
+
330
+ These settings keep collection and event users disabled after consent too.
331
+ To restore event users, check effective c15t permission in `beforeSend`. Wait
332
+ until the adapter is registered before assigning scope users or capturing logs
333
+ and metrics.
334
+
335
+ ## Options
336
+
337
+ | Option | Default | Behavior |
338
+ | ----------------- | ------------------------------------ | ----------------------------------------------------------------------------------------------------------------------- |
339
+ | `dsn` | Required to load Sentry | Your project's DSN. c15t loads the SDK and calls `Sentry.init`. |
340
+ | `initOptions` | `{}` | Passed to `Sentry.init` with c15t's consent integration first. CDN `integrations` callbacks can access `window.Sentry`. |
341
+ | `version` | `'11.4.0'` | SDK version to load. Another version loads without subresource integrity. |
342
+ | `tracing` | `true` when traces are sampled | Loads the tracing bundle and adds `browserTracingIntegration()`. |
343
+ | `replay` | Loads CDN Replay when sampled | CDN: `{ category, options }` or `false`. SDK: `{ category, load }`. |
344
+ | `getClient` | Required for your own SDK | Your SDK's `getClient`. Supports initialization after c15t starts. |
345
+ | `setUser` | None | `setUser` from your Sentry SDK. Called with `null` while user data is not allowed. Required with `pii.user`. |
346
+ | `init` | None | Your SDK's `init`, called once when `loadMode` allows. Required for SDK mode with `'after-consent'`. |
347
+ | `replay.load` | None | With your own SDK, returns a new `replayIntegration()`. Called once, the first time Replay is allowed. |
348
+ | `loadMode` | `'always'` | When error monitoring runs. See the table below. |
349
+ | `replay.category` | `'measurement'` | Consent condition for Replay. `pii.category` must also be allowed. |
350
+ | `pii.category` | `'measurement'` | Consent condition for SDK data collection and user data. `'necessary'` permits it while the Sentry vendor is enabled. |
351
+ | `pii.user` | None | Returns the user to set when user data becomes allowed. |
352
+ | `onError` | Report to an available Sentry client | Receives CDN and Replay lifecycle failures. Set it to report CDN download failures before Sentry exists. |
353
+
354
+ Replay's category is registered only with an SDK `replay.load` function or
355
+ enabled CDN Replay with a positive sample rate.
356
+
357
+ ## Loading and revocation
358
+
359
+ | `loadMode` | When Sentry runs | On withdrawal without a reload |
360
+ | ----------------- | ----------------------------------- | ----------------------------------------------------------- |
361
+ | `'always'` | On every page, before consent | Replay stops; errors keep reaching Sentry without user data |
362
+ | `'after-consent'` | Only while `measurement` is allowed | Sentry stops sending anything |
363
+
364
+ Replay needs both `replay.category` and `pii.category`, because recordings
365
+ include page URLs with queries and fragments. Keep Replay out of `Sentry.init`:
366
+ `replaysOnErrorSampleRate` can buffer recordings before consent. On denial or
367
+ final removal, c15t stops Replay without flushing, discards queued uploads and
368
+ blocks restarts. Data already sent stays in Sentry; a later grant preserves
369
+ the original sampling decision.
370
+
371
+ While PII is denied, c15t disables supported `dataCollection` settings and IP
372
+ inference. Before sending, it removes user fields, feedback contact details,
373
+ visitor identifiers in sessions, request data and URL queries/fragments from
374
+ supported events, spans, logs, metrics and breadcrumbs, including retained
375
+ breadcrumbs. A grant restores your resolved collection settings and allowlists.
376
+ Tracing itself is not gated. Custom messages, tags, extras, attachments and
377
+ URL paths remain diagnostics, so keep sensitive values out of them.
378
+
379
+ The vendor slug is `sentry`. Turning it off denies Replay and PII, plus errors
380
+ in `'after-consent'` mode. See [vendor consent](./overview.md#vendor-switches-and-cookie-cleanup).
381
+
382
+ ### Shared clients and configuration changes
383
+
384
+ Keep callback functions stable across renders. Equal options and callbacks
385
+ reuse the SDK bundle and active recording. Changed CDN options initialize a
386
+ client with the new DSN and settings, while reusing the page's single Replay
387
+ recorder. Its original Replay options and sampling decision last until reload.
388
+
389
+ Every active configuration targeting one client must allow each feature;
390
+ removing a loader releases its restriction and preserves surviving loaders and
391
+ downloads. Final removal denies PII and stops Replay, and also disables errors
392
+ in `'after-consent'` mode. An app-initiated `Sentry.close()` stays closed after
393
+ a grant. Separate clients have separate permissions.
394
+
395
+ ## Content security policy
396
+
397
+ Allow `worker-src blob:` for Replay and your ingest or tunnel endpoint in
398
+ `connect-src`. CDN mode also needs `https://browser.sentry-cdn.com` in
399
+ `script-src`. The loader or provider's `nonce` applies to both CDN bundles;
400
+ a per-script nonce overrides it.
401
+
402
+ ## Verify Sentry
403
+
404
+ Test in a private window with an opt-in policy and
405
+ `replaysSessionSampleRate: 1`. Filter DevTools Network by `sentry`.
406
+
407
+ 1. Load the page. `bundle.min.js` loads and errors reach Sentry, without `user`.
408
+ No `replay.min.js` or `replay_recording` request is sent. With
409
+ `loadMode: 'after-consent'`, nothing loads.
410
+ 2. Allow measurement. Without a reload, Replay loads and `replay_recording`
411
+ requests start.
412
+ 3. Turn measurement off and save. c15t reloads the page, and no replay request
413
+ follows.
414
+
415
+ See the [consent verification guide](../guides/verify-consent.md) for
416
+ navigation, expiry and hosting checks.