@c15t/react 3.0.0-alpha.3 → 3.0.0-alpha.5
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 +92 -72
- package/README.md +21 -17
- package/SKILL.md +34 -0
- package/dist/component-hooks/use-headless-consent-ui.js +1 -1
- package/dist/component-hooks/use-headless-iab-consent-ui.js +1 -1
- package/dist/components/consent-gate/atoms.js +1 -1
- package/dist/components/consent-gate/consent-gate.js +1 -1
- package/dist/components/iab-panel/atoms/card.js +1 -1
- package/dist/components/iab-panel/atoms/feature-item.js +2 -0
- package/dist/components/iab-panel/atoms/header.js +1 -1
- package/dist/components/iab-panel/iab-panel.js +1 -1
- package/dist/components/iab-panel/index.js +1 -1
- package/dist/components/iab-prompt/atoms/overlay.js +1 -1
- package/dist/components/iab-prompt/atoms/root.js +1 -1
- package/dist/components/panel/atoms/card.js +1 -1
- package/dist/components/panel/atoms/root.js +1 -1
- package/dist/components/panel-trigger/atoms/button.js +1 -1
- package/dist/components/panel-trigger/atoms/toolbar.js +1 -1
- package/dist/components/preferences/atoms/accordion.js +1 -1
- package/dist/components/prompt/atoms/overlay.js +1 -1
- package/dist/components/prompt/atoms/root.js +1 -1
- package/dist/components/prompt/components.js +1 -1
- package/dist/components/shared/libs/compose-refs.js +1 -0
- package/dist/components/shared/libs/slot.js +1 -1
- package/dist/components/shared/primitives/button.js +1 -1
- package/dist/components/shared/ui/dialog/dialog.js +1 -1
- package/dist/components/shared/ui/icon.js +1 -1
- package/dist/context/consent-tracking-context.js +1 -1
- package/dist/devtools.js +1 -1
- package/dist/draft-context.js +2 -0
- package/dist/draft.js +1 -1
- package/dist/external-iab-context.js +1 -1
- package/dist/gpp.js +2 -0
- package/dist/hooks/use-color-scheme.js +1 -1
- package/dist/hooks/use-focus-trap.js +1 -1
- package/dist/hooks.js +1 -1
- package/dist/iab/styles.tw3.css +4 -4
- package/dist/index-parts/consent-banner.js +1 -0
- package/dist/index-parts/consent-dialog-link.js +1 -0
- package/dist/index-parts/consent-dialog-trigger.js +1 -0
- package/dist/index-parts/consent-dialog.js +1 -0
- package/dist/index-parts/consent-gate.js +1 -0
- package/dist/index-parts/core.js +1 -0
- package/dist/index-parts/draft.js +1 -0
- package/dist/index-parts/hooks.js +1 -0
- package/dist/index-parts/module-hooks.js +1 -0
- package/dist/index-parts/provider.js +1 -0
- package/dist/index-parts/theme.js +1 -0
- package/dist/index-parts/translations.js +1 -0
- package/dist/index.js +1 -1
- package/dist/kernel-selector.js +1 -1
- package/dist/module-hooks/iframe-blocker.js +1 -1
- package/dist/postcss-tailwind3.js +1 -0
- package/dist/provider.js +1 -1
- package/dist/server/fetch-ssr-data.js +1 -1
- package/dist/server/headers.js +1 -1
- package/dist/server/normalize-url.js +1 -1
- package/dist/styles.tw3.css +16 -16
- package/dist/transports/offline.js +1 -1
- package/dist/utils/merge-slot-props.js +1 -1
- package/dist/utils/theme-utils.js +1 -1
- package/dist/version.js +1 -1
- package/dist-types/component-hooks/use-headless-consent-ui.d.ts +1 -1
- package/dist-types/components/iab-panel/atoms/feature-item.d.ts +28 -0
- package/dist-types/components/iab-panel/iab-panel.d.ts +2 -2
- package/dist-types/components/iab-panel/index.d.ts +5 -2
- package/dist-types/components/iab-panel/use-iab-translations.d.ts +4 -0
- package/dist-types/components/iab-prompt/iab-prompt.d.ts +2 -2
- package/dist-types/components/iab-prompt/index.d.ts +2 -2
- package/dist-types/components/panel/atoms/card.d.ts +1 -0
- package/dist-types/components/prompt/components.d.ts +6 -0
- package/dist-types/components/shared/libs/compose-refs.d.ts +20 -0
- package/dist-types/components/shared/ui/icon.d.ts +2 -1
- package/dist-types/context/consent-tracking-context.d.ts +10 -0
- package/dist-types/context/theme-context.d.ts +2 -2
- package/dist-types/context.d.ts +6 -0
- package/dist-types/devtools.d.ts +13 -2
- package/dist-types/draft-context.d.ts +31 -0
- package/dist-types/draft.d.ts +13 -30
- package/dist-types/gpp.d.ts +27 -0
- package/dist-types/hooks/use-focus-trap.d.ts +3 -1
- package/dist-types/hooks.d.ts +57 -13
- package/dist-types/iab.d.ts +1 -1
- package/dist-types/index-parts/consent-banner.d.ts +7 -0
- package/dist-types/index-parts/consent-dialog-link.d.ts +7 -0
- package/dist-types/index-parts/consent-dialog-trigger.d.ts +7 -0
- package/dist-types/index-parts/consent-dialog.d.ts +7 -0
- package/dist-types/index-parts/consent-gate.d.ts +7 -0
- package/dist-types/index-parts/core.d.ts +8 -0
- package/dist-types/index-parts/draft.d.ts +7 -0
- package/dist-types/index-parts/hooks.d.ts +7 -0
- package/dist-types/index-parts/module-hooks.d.ts +7 -0
- package/dist-types/index-parts/provider.d.ts +7 -0
- package/dist-types/index-parts/theme.d.ts +8 -0
- package/dist-types/index-parts/translations.d.ts +7 -0
- package/dist-types/index.d.ts +14 -15
- package/dist-types/kernel-selector.d.ts +0 -8
- package/dist-types/module-hooks/iframe-blocker.d.ts +12 -7
- package/dist-types/postcss-tailwind3.d.ts +21 -0
- package/dist-types/provider.d.ts +57 -17
- package/dist-types/server/headers.d.ts +14 -4
- package/dist-types/server/normalize-url.d.ts +14 -7
- package/dist-types/server/types.d.ts +12 -1
- package/dist-types/transports/offline.d.ts +7 -16
- package/dist-types/ui-config-context.d.ts +7 -1
- package/dist-types/utils/merge-slot-props.d.ts +15 -1
- package/dist-types/utils/theme-utils.d.ts +17 -2
- package/dist-types/version.d.ts +1 -1
- package/docs/README.md +92 -72
- package/docs/assets/v3/bottom-bar.png +0 -0
- package/docs/assets/v3/brand-card.png +0 -0
- package/docs/assets/v3/brand-preferences.png +0 -0
- package/docs/assets/v3/choice-wall.png +0 -0
- package/docs/assets/v3/headless-bar-html.png +0 -0
- package/docs/assets/v3/headless-bar-mobile.png +0 -0
- package/docs/assets/v3/headless-bar.png +0 -0
- package/docs/assets/v3/slim-bar.png +0 -0
- package/docs/concepts/choose-your-setup.md +87 -0
- package/docs/{frameworks/react/concepts → concepts}/consent-categories.md +22 -7
- package/docs/{guides → concepts}/consent-state.md +89 -105
- package/docs/{guides → concepts}/data-fetching.md +31 -27
- package/docs/concepts/how-consent-works.md +123 -0
- package/docs/{frameworks/react/concepts/policy-presets.md → concepts/policies.md} +7 -59
- package/docs/customization/class-names.md +202 -0
- package/docs/customization/dark-mode.md +157 -0
- package/docs/customization/motion.md +119 -0
- package/docs/customization/overview.md +67 -34
- package/docs/customization/recipes.md +839 -49
- package/docs/customization/slots.md +216 -35
- package/docs/customization/stylesheets.md +147 -0
- package/docs/customization/tailwind.md +842 -0
- package/docs/customization/tokens.md +163 -96
- package/docs/customization/translations.md +60 -3
- package/docs/frameworks/react/callbacks.md +118 -0
- package/docs/frameworks/react/clear-on-revocation.md +106 -0
- package/docs/frameworks/react/components/consent-banner.md +42 -28
- package/docs/frameworks/react/components/consent-dialog-link.md +13 -11
- package/docs/frameworks/react/components/consent-dialog-trigger.md +81 -76
- package/docs/frameworks/react/components/consent-dialog.md +56 -50
- package/docs/frameworks/react/components/consent-gate.md +37 -20
- package/docs/frameworks/react/components/consent-provider.md +130 -0
- package/docs/frameworks/react/components/consent-widget.md +23 -20
- package/docs/frameworks/react/components/dev-tools.md +53 -85
- package/docs/frameworks/react/components.md +105 -0
- package/docs/frameworks/react/compose.md +354 -0
- package/docs/frameworks/react/content-security-policy.md +126 -0
- package/docs/frameworks/react/customize.md +250 -0
- package/docs/frameworks/react/embeds.md +89 -0
- package/docs/frameworks/react/gpp.md +163 -0
- package/docs/frameworks/react/{hooks/overview.md → hooks.md} +21 -16
- package/docs/frameworks/react/{iab/overview.md → iab.md} +80 -31
- package/docs/frameworks/react/network-blocker.md +35 -41
- package/docs/frameworks/react/quickstart.md +139 -101
- package/docs/frameworks/react/rendering.md +128 -0
- package/docs/frameworks/react/scripts.md +115 -0
- package/docs/frameworks/react/translations.md +155 -0
- package/docs/frameworks/react/troubleshooting.md +92 -47
- package/docs/frameworks/react/vendor-consent.md +170 -0
- package/docs/guides/banner-experiments.md +654 -0
- package/docs/guides/troubleshooting.md +120 -47
- package/docs/guides/verify-consent.md +81 -49
- package/docs/integrations/adobe-analytics.md +167 -159
- package/docs/integrations/ahrefs-analytics.md +152 -154
- package/docs/integrations/amplitude.md +162 -156
- package/docs/integrations/building-integrations.md +136 -37
- package/docs/integrations/clearbit.md +154 -154
- package/docs/integrations/cloudflare-web-analytics.md +156 -156
- package/docs/integrations/cloudflare-zaraz.md +209 -261
- package/docs/integrations/crisp.md +164 -158
- package/docs/integrations/databuddy.md +157 -173
- package/docs/integrations/fathom-analytics.md +158 -156
- package/docs/integrations/front-chat.md +167 -167
- package/docs/integrations/google-maps.md +118 -83
- package/docs/integrations/google-tag-manager.md +178 -163
- package/docs/integrations/google-tag.md +163 -160
- package/docs/integrations/heap.md +163 -155
- package/docs/integrations/hightouch.md +161 -157
- package/docs/integrations/hotjar.md +159 -155
- package/docs/integrations/intercom.md +183 -153
- package/docs/integrations/klaviyo.md +486 -0
- package/docs/integrations/linkedin-insights.md +174 -150
- package/docs/integrations/logrocket.md +160 -156
- package/docs/integrations/matomo-analytics.md +188 -178
- package/docs/integrations/meta-pixel.md +188 -150
- package/docs/integrations/microsoft-clarity.md +163 -155
- package/docs/integrations/microsoft-uet.md +148 -154
- package/docs/integrations/mixpanel-analytics.md +155 -160
- package/docs/integrations/one-dollar-stats.md +166 -165
- package/docs/integrations/openai-pixel.md +204 -301
- package/docs/integrations/overview.md +137 -80
- package/docs/integrations/pinterest-tag.md +191 -183
- package/docs/integrations/pirsch.md +169 -159
- package/docs/integrations/plausible-analytics.md +172 -158
- package/docs/integrations/posthog.md +313 -240
- package/docs/integrations/promptwatch.md +154 -154
- package/docs/integrations/reddit-pixel.md +185 -157
- package/docs/integrations/rudderstack.md +201 -186
- package/docs/integrations/rybbit-analytics.md +171 -160
- package/docs/integrations/segment.md +182 -154
- package/docs/integrations/snapchat-pixel.md +186 -156
- package/docs/integrations/tiktok-pixel.md +171 -150
- package/docs/integrations/umami-analytics.md +163 -158
- package/docs/integrations/vercel-analytics.md +166 -157
- package/docs/integrations/x-pixel.md +176 -150
- package/docs/integrations/youtube.md +121 -86
- package/docs/upgrade-v3.md +496 -467
- package/package.json +20 -9
- package/dist/ui-save.js +0 -1
- package/dist-types/ui-save.d.ts +0 -25
- package/docs/assets/v3/brand-bar.png +0 -0
- package/docs/assets/v3/mobile-card.png +0 -0
- package/docs/assets/v3/preferences.png +0 -0
- package/docs/frameworks/react/components/consent-manager-provider.md +0 -95
- package/docs/frameworks/react/script-loader.md +0 -69
- package/docs/frameworks/react/styling/overview.md +0 -266
- package/docs/guides/deployment-modes.md +0 -75
- package/docs/guides/shared-consent-controls.md +0 -158
- package/docs/integrations/clear-on-revocation.md +0 -167
- package/docs/integrations/granular-consent.md +0 -210
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Choose your setup
|
|
3
|
+
description: Pick the c15t setup for your framework, rendering mode and hosting,
|
|
4
|
+
and decide who runs the consent backend.
|
|
5
|
+
group: concepts
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Find your app in this table
|
|
9
|
+
|
|
10
|
+
Most apps need one row. Follow its guide from start to finish; each guide links
|
|
11
|
+
to the alternatives it rules out.
|
|
12
|
+
|
|
13
|
+
| Your app | Setup | Guide |
|
|
14
|
+
| -------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------- |
|
|
15
|
+
| Next.js App Router with a Next.js server | Server resolves consent from a cached manifest and streams it to the page | [App Router](https://c15t.com/docs/frameworks/next/rendering#pick-your-rendering-path) |
|
|
16
|
+
| Next.js App Router, banner must be in the first HTML | Await consent inside `Suspense` | [Stream or await](https://c15t.com/docs/frameworks/next/rendering#stream-or-await-consent-in-the-app-router) |
|
|
17
|
+
| Next.js with static, ISR or `'use cache'` pages | Browser resolves consent on those pages | [Static, ISR and cached pages](https://c15t.com/docs/frameworks/next/rendering#render-static-isr-and-cached-pages) |
|
|
18
|
+
| Next.js Pages Router | Resolve consent in `getServerSideProps` | [Pages Router](https://c15t.com/docs/frameworks/next/rendering#pick-your-rendering-path) |
|
|
19
|
+
| Next.js with `output: 'export'` | Browser resolves consent | [Static export](https://c15t.com/docs/frameworks/next/static-export) |
|
|
20
|
+
| TanStack Start with a server | Root loader resolves consent on the server | [TanStack Start](https://c15t.com/docs/frameworks/tanstack-start/quickstart) |
|
|
21
|
+
| TanStack Start on a serverless host, banner may follow the page | Root loader streams consent | [TanStack Start rendering](https://c15t.com/docs/frameworks/tanstack-start/rendering#stream-the-page-while-consent-resolves) |
|
|
22
|
+
| TanStack Start in SPA mode or prerendered | Browser resolves consent | [TanStack Start rendering](https://c15t.com/docs/frameworks/tanstack-start/rendering#spa-mode-prerendered-pages-and-static-hosts) |
|
|
23
|
+
| React with Vite, or another single-page app | Browser resolves consent | [React](../frameworks/react/quickstart.md) |
|
|
24
|
+
| React Router framework mode, Remix, or another server-rendered React app | Browser resolves consent after hydration | [React rendering](../frameworks/react/rendering.md#react-router-remix-and-other-server-rendered-apps) |
|
|
25
|
+
| Nuxt with a Nuxt server | Nuxt module resolves consent on the server from a cached manifest | [Nuxt](https://c15t.com/docs/frameworks/nuxt/quickstart) |
|
|
26
|
+
| Nuxt with prerendered or cached routes | Nuxt server for most pages, the browser on prerendered and cached routes | [Nuxt prerendered and cached routes](https://c15t.com/docs/frameworks/nuxt/rendering#prerendered-and-cached-routes) |
|
|
27
|
+
| Nuxt with `nuxt generate` on static hosting | Nuxt module, browser resolves consent from the manifest or `/init` | [Nuxt static hosting](https://c15t.com/docs/frameworks/nuxt/rendering#deploy-to-static-hosting) |
|
|
28
|
+
| Nuxt with `ssr: false` | Nuxt module, browser resolves consent from the manifest | [Nuxt single-page app](https://c15t.com/docs/frameworks/nuxt/rendering#build-a-single-page-app-with-ssr-false) |
|
|
29
|
+
| Vue with Vite | Vue plugin, browser resolves consent | [Vue](https://c15t.com/docs/frameworks/vue/quickstart) |
|
|
30
|
+
| Vue rendered on the server without Nuxt | Vue plugin; the banner mounts after hydration. Use Nuxt for the banner in server HTML. | [Vue rendering](https://c15t.com/docs/frameworks/vue/rendering#server-rendering-without-nuxt) |
|
|
31
|
+
| Astro with static output | Browser resolves consent through the hosted backend | [Astro static output](https://c15t.com/docs/frameworks/astro/rendering#static-output-with-hosted) |
|
|
32
|
+
| Astro with server output and an adapter | Middleware resolves consent from a cached manifest | [Astro server output](https://c15t.com/docs/frameworks/astro/rendering#server-output-with-manifest) |
|
|
33
|
+
| Svelte with Vite | Svelte provider, browser resolves consent | [Svelte](https://c15t.com/docs/frameworks/svelte/quickstart) |
|
|
34
|
+
| SvelteKit with a server | Root layout load resolves consent on the server | [SvelteKit](https://c15t.com/docs/frameworks/sveltekit/quickstart) |
|
|
35
|
+
| SvelteKit with `adapter-static`, prerendered pages or SPA mode | Browser resolves consent | [SvelteKit rendering](https://c15t.com/docs/frameworks/sveltekit/rendering#build-a-static-site) |
|
|
36
|
+
| Plain HTML, a CMS, a page builder, or a static site generator such as Hugo or Eleventy | One script tag, stock banner included | [HTML](https://c15t.com/docs/frameworks/html/quickstart) |
|
|
37
|
+
| JavaScript with a bundler and no UI framework | `@c15t/browser` module, stock banner included | [JavaScript](https://c15t.com/docs/frameworks/javascript/quickstart) |
|
|
38
|
+
| Your own UI, or a framework without an adapter such as Solid | Headless runtime | [JavaScript headless](https://c15t.com/docs/frameworks/javascript/headless) |
|
|
39
|
+
|
|
40
|
+
Solid has no c15t adapter. Use the [headless runtime](https://c15t.com/docs/frameworks/javascript/headless#use-c15t-with-solid)
|
|
41
|
+
and subscribe to it from your components.
|
|
42
|
+
|
|
43
|
+
## Decide who runs the consent backend
|
|
44
|
+
|
|
45
|
+
The backend supplies policies and stores consent records. Every setup above
|
|
46
|
+
works with each option; only the URL changes.
|
|
47
|
+
|
|
48
|
+
| Option | Use it when |
|
|
49
|
+
| -------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
50
|
+
| [Inth](https://inth.com) | You want hosted policies and consent records. The guides use this. |
|
|
51
|
+
| [Self-hosted c15t backend](https://c15t.com/docs/self-host/overview) | You need to run the service and its database yourself. |
|
|
52
|
+
| Offline mode | Local development and tests. Policies live in your code and choices stay in the browser, with no consent records. Not recommended for production environments. |
|
|
53
|
+
|
|
54
|
+
To use Inth, create a project, set its policy rules, add your site's origin to
|
|
55
|
+
its trusted origins, and copy the backend URL. The guides use the placeholder
|
|
56
|
+
`https://your-project.inth.app`; replace it with your project's backend URL,
|
|
57
|
+
including any path prefix.
|
|
58
|
+
|
|
59
|
+
## Server rendering or browser rendering
|
|
60
|
+
|
|
61
|
+
Where consent resolves decides what the visitor's first HTML contains.
|
|
62
|
+
|
|
63
|
+
| Rendering | What the visitor gets |
|
|
64
|
+
| -------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
65
|
+
| The server resolves consent for each request | The server reads the visitor's cookie and location headers, so the browser starts with the answer. Gated scripts can run right after hydration, and the banner can be part of the HTML. |
|
|
66
|
+
| The browser resolves consent | The HTML is the same for everyone. After the page loads, the browser requests the policy, then shows the banner or runs gated scripts. |
|
|
67
|
+
|
|
68
|
+
A static or prerendered page cannot contain one visitor's consent, because the
|
|
69
|
+
same HTML goes to everyone. Static sites still use Inth; the browser talks to it
|
|
70
|
+
directly. A same-origin `/api/c15t` route only works where a server runs it.
|
|
71
|
+
|
|
72
|
+
Pages that are cached and shared between visitors, such as Next.js ISR or
|
|
73
|
+
`'use cache'` routes, need browser resolution for the same reason. Cache the
|
|
74
|
+
public policy manifest, never a response that contains a visitor's consent.
|
|
75
|
+
|
|
76
|
+
## How the server gets the policy
|
|
77
|
+
|
|
78
|
+
Server setups fetch policy one of two ways:
|
|
79
|
+
|
|
80
|
+
* **Manifest**, used by the Next.js, Nuxt and Astro server guides.
|
|
81
|
+
Your server downloads the project's public policy file, caches it, and
|
|
82
|
+
resolves each visitor locally. Consent saves still go to the backend.
|
|
83
|
+
* **`/init`**. Each visit asks the backend to resolve the visitor. Simpler, but
|
|
84
|
+
every page load makes a request to the backend.
|
|
85
|
+
|
|
86
|
+
[Data fetching](./data-fetching.md) compares them in detail,
|
|
87
|
+
including proxies and custom transports.
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
---
|
|
2
2
|
title: Consent categories
|
|
3
|
-
description: Assign
|
|
4
|
-
|
|
5
|
-
group:
|
|
3
|
+
description: Assign scripts, embeds and features to c15t consent categories, and
|
|
4
|
+
understand which categories the preference dialog shows.
|
|
5
|
+
group: concepts
|
|
6
6
|
---
|
|
7
7
|
|
|
8
8
|
## Choose categories by purpose
|
|
@@ -27,8 +27,23 @@ Compound script and network conditions contribute every category they reference.
|
|
|
27
27
|
Discovered categories are added to `consentCategories`, when supplied. The dialog
|
|
28
28
|
always includes Necessary and offers the optional categories in that combined
|
|
29
29
|
set that are also in the resolved policy scope. Choice completion uses the same
|
|
30
|
-
set.
|
|
31
|
-
|
|
30
|
+
set.
|
|
31
|
+
|
|
32
|
+
If neither configuration nor integrations supply categories, the result depends
|
|
33
|
+
on the policy's `scopeMode`:
|
|
34
|
+
|
|
35
|
+
* Under a permissive policy, the dialog lists only Necessary. The banner still
|
|
36
|
+
appears when the policy prompts for a choice. Accept All, Reject All and Save
|
|
37
|
+
each record that the visitor saw it, send a consent receipt for Necessary
|
|
38
|
+
alone in hosted and manifest modes, and keep the banner dismissed after
|
|
39
|
+
reload. The acknowledgement expires with the policy's choice validity and
|
|
40
|
+
after a policy change, like a choice would. A stored choice that is still
|
|
41
|
+
valid under the current policy also counts as an acknowledgement.
|
|
42
|
+
* Under a strict policy, or an IAB TCF policy, the dialog uses the full policy
|
|
43
|
+
scope. TCF consent is given per purpose and recorded in the TC string.
|
|
44
|
+
|
|
45
|
+
A category declared after that acknowledgement, such as a newly registered
|
|
46
|
+
script or a discovered iframe, needs a choice, so the banner asks again.
|
|
32
47
|
|
|
33
48
|
Categories discovered later are added immediately and retained until the provider
|
|
34
49
|
or runtime is recreated. Adding a category can require a new choice; removing a
|
|
@@ -65,5 +80,5 @@ client render. Set
|
|
|
65
80
|
`useIframeBlocker({ disableAutomaticBlocking: true }).processAllIframes()`.
|
|
66
81
|
|
|
67
82
|
Use effective permissions to gate work and explicit choices to inspect what the
|
|
68
|
-
visitor confirmed. Read [consent
|
|
69
|
-
distinction and [migration](
|
|
83
|
+
visitor confirmed. Read [how consent works](./how-consent-works.md#a-permission-is-not-a-recorded-choice) for that
|
|
84
|
+
distinction and [migration](../upgrade-v3.md) for v3 policy configuration.
|
|
@@ -1,24 +1,14 @@
|
|
|
1
1
|
---
|
|
2
|
-
title:
|
|
3
|
-
description:
|
|
4
|
-
|
|
5
|
-
group:
|
|
2
|
+
title: Consent state reference
|
|
3
|
+
description: How c15t saves choices, gates IAB vendors, hydrates server records
|
|
4
|
+
and keeps browser tabs and storage in step.
|
|
5
|
+
group: concepts
|
|
6
6
|
---
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
permission can be true before the visitor acts. It is not evidence of a recorded
|
|
13
|
-
grant.
|
|
14
|
-
|
|
15
|
-
| Task | State or API |
|
|
16
|
-
| ------------------------------------------- | ---------------------------------------------------- |
|
|
17
|
-
| Load a script or render an optional feature | `effectivePermissions`, React `useConsent(category)` |
|
|
18
|
-
| Inspect what the visitor confirmed | `explicitChoice` |
|
|
19
|
-
| Decide whether to show a prompt | `promptRequirement` |
|
|
20
|
-
| Explain regional behavior | `policyRule` |
|
|
21
|
-
| Diagnose initialization | `resolution` |
|
|
8
|
+
This reference covers the edge cases behind the model in
|
|
9
|
+
[how consent works](./how-consent-works.md). You need it when you
|
|
10
|
+
build custom consent UI, run c15t across subdomains, or debug a choice that
|
|
11
|
+
changes between tabs.
|
|
22
12
|
|
|
23
13
|
## Gate IAB vendors on the TC string
|
|
24
14
|
|
|
@@ -27,7 +17,7 @@ or IAB purposes is an IAB target. It runs only while a confirmed TC string
|
|
|
27
17
|
grants what it declares: purpose and vendor consent for `iabPurposes`, purpose
|
|
28
18
|
and vendor legitimate interest for `iabLegIntPurposes`, and opt-ins for
|
|
29
19
|
`iabSpecialFeatures`. Every category it names must also be free of
|
|
30
|
-
restrictions, so GPC
|
|
20
|
+
restrictions, so GPC or strict scope blocks it.
|
|
31
21
|
|
|
32
22
|
A refused category is the one exception. It does not block an IAB target that
|
|
33
23
|
processes only on legitimate interest, after publisher restrictions, because
|
|
@@ -37,29 +27,13 @@ itself stays refused: its `effectivePermissions` entry is `false`, and targets
|
|
|
37
27
|
that name only the category, or that also declare a consent purpose or special
|
|
38
28
|
feature, stay blocked.
|
|
39
29
|
|
|
40
|
-
## Record only explicit visitor actions
|
|
41
|
-
|
|
42
|
-
```ts
|
|
43
|
-
// Run in the corresponding click or form-submit handler.
|
|
44
|
-
await kernel.commands.save('all');
|
|
45
|
-
await kernel.commands.save('none');
|
|
46
|
-
await kernel.commands.save({ marketing: false });
|
|
47
|
-
```
|
|
48
|
-
|
|
49
|
-
These are three separate examples: accept, reject and a partial save. A partial
|
|
50
|
-
save confirms only the supplied categories and keeps the other categories'
|
|
51
|
-
confirmation times. Do not call all three in one handler.
|
|
52
|
-
|
|
53
|
-
`onChoiceRecorded` reports an explicit choice. `onPermissionsChanged` reports
|
|
54
|
-
changes in effective permissions, including changes caused by expiry or privacy
|
|
55
|
-
signals. Hydration must not be counted as another visitor choice.
|
|
56
|
-
|
|
57
30
|
## When a choice is saved
|
|
58
31
|
|
|
59
32
|
A save records the choice in the browser first and sends it to the backend
|
|
60
33
|
afterwards. The stock banner, preference dialog and IAB surfaces in every
|
|
61
|
-
framework adapter, and the
|
|
62
|
-
and
|
|
34
|
+
framework adapter, and the `acceptAll()`, `rejectAll()` and `save()` methods
|
|
35
|
+
of the browser and Astro clients, close without waiting for the backend. In
|
|
36
|
+
order:
|
|
63
37
|
|
|
64
38
|
1. In the click task, the explicit choice and effective permissions change,
|
|
65
39
|
`onChoiceRecorded` and `onPermissionsChanged` run, gated scripts, iframes
|
|
@@ -79,9 +53,10 @@ and `save()`, close without waiting for the backend. In order:
|
|
|
79
53
|
|
|
80
54
|
A failed request does not reopen the surface or roll the choice back. The
|
|
81
55
|
kernel emits `command:error`, which reaches the `onError` callback in adapters
|
|
82
|
-
that accept one, and queues the payload in localStorage.
|
|
83
|
-
|
|
84
|
-
|
|
56
|
+
that accept one, and queues the payload in localStorage. Where localStorage is
|
|
57
|
+
unavailable, the queue lives in memory until the page unloads. The queue is
|
|
58
|
+
replayed after the next successful initialization and when the browser comes
|
|
59
|
+
back online, up to 10 attempts over 7 days. A replay carries the original action
|
|
85
60
|
time and policy snapshot token, so the backend records when the visitor
|
|
86
61
|
decided, and a duplicate submission resolves to the same consent record. A
|
|
87
62
|
backend that signs policy snapshot tokens rejects a replay made after the
|
|
@@ -93,17 +68,13 @@ after its TC string is encoded, which can wait for the TCF library to load.
|
|
|
93
68
|
If that local step records nothing, for example because the vendor list
|
|
94
69
|
failed to load, the surface comes back so the visitor can try again.
|
|
95
70
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
A rule with `prompt: 'none'` can still require a persistent preferences entry
|
|
104
|
-
point. Check the policy's rights instead of hiding preferences merely because
|
|
105
|
-
the banner is absent. An unresolved rule is another distinct state; optional
|
|
106
|
-
permissions stay denied until resolution succeeds.
|
|
71
|
+
Every adapter leaves the same surface after a save: the banner while the
|
|
72
|
+
policy still owes a choice or a notice, and nothing once no prompt is owed,
|
|
73
|
+
while the policy is still loading, or after it failed to resolve. A banner
|
|
74
|
+
the visitor reopened closes too. A save that records nothing new, such as an
|
|
75
|
+
unchanged selection, closes the surface when it resolves successfully. Opening
|
|
76
|
+
or closing a surface, or starting another save, before then leaves the surface
|
|
77
|
+
as the visitor set it.
|
|
107
78
|
|
|
108
79
|
## Preserve records during hydration
|
|
109
80
|
|
|
@@ -128,7 +99,8 @@ shows the prompt again.
|
|
|
128
99
|
Tabs on another subdomain that shares the consent cookie do not share
|
|
129
100
|
localStorage, so the browser sends them no `storage` event. They pick up the
|
|
130
101
|
change on their next focus or visibility change, or when you reconcile
|
|
131
|
-
yourself
|
|
102
|
+
yourself as described in
|
|
103
|
+
[Reconcile yourself or turn it off](#reconcile-yourself-or-turn-it-off).
|
|
132
104
|
|
|
133
105
|
The `storage` event only exists for localStorage. When localStorage is
|
|
134
106
|
unavailable (blocked by the browser, a sandboxed frame or a privacy mode) and
|
|
@@ -150,8 +122,8 @@ Several triggers in quick succession run one reconciliation in a later task.
|
|
|
150
122
|
Reconnecting does not read storage, because going online changes nothing in
|
|
151
123
|
browser storage. On reconnect the kernel retries saves the backend did not
|
|
152
124
|
accept and a failed initialization. Save retries do not write the cookie or
|
|
153
|
-
localStorage. A write that follows initialization
|
|
154
|
-
|
|
125
|
+
localStorage. A write that follows initialization obeys the rules in
|
|
126
|
+
[Ordering with pending writes](#ordering-with-pending-writes).
|
|
155
127
|
|
|
156
128
|
Every adapter that mounts browser persistence does this: the React, Next.js and
|
|
157
129
|
TanStack Start providers, Vue, Svelte, Astro, the script tag and
|
|
@@ -164,8 +136,6 @@ Stored records are merged into the ones in memory:
|
|
|
164
136
|
* Category decisions merge per category. Each category keeps the decision with
|
|
165
137
|
the newer confirmation time, so a tab that only changed marketing never
|
|
166
138
|
reverts another tab's newer measurement decision.
|
|
167
|
-
* Privacy directives, such as a Global Privacy Control opt-out, merge as a
|
|
168
|
-
union. A directive only restricts, so none is dropped.
|
|
169
139
|
* The notice dismissal and the vendor record are single decisions. A stored one
|
|
170
140
|
at least as new as the one in memory replaces it; an older one is ignored.
|
|
171
141
|
* When two tabs record decisions in the same millisecond, the one stored first
|
|
@@ -177,38 +147,8 @@ Stored records are merged into the ones in memory:
|
|
|
177
147
|
resolved (at init, from a prefetch or in a save response) is replaced only by
|
|
178
148
|
a strictly newer stored choice, and an identity set with `identify()` is
|
|
179
149
|
never replaced.
|
|
180
|
-
* Under an IAB policy, the
|
|
181
|
-
|
|
182
|
-
so `__tcfapi` and the preference controls show the choice now in force.
|
|
183
|
-
Selections the visitor changed in this tab without saving are kept. A missing
|
|
184
|
-
or older stored TC string leaves the current one in place, unless it conflicts
|
|
185
|
-
with the reconciled choice: then it is withdrawn and `__tcfapi` reports no
|
|
186
|
-
consent until the next save. A category denied after the TC string was saved
|
|
187
|
-
conflicts if the TC string grants any of its purposes. A partial selection
|
|
188
|
-
saved through IAB, which records its category as denied because not every
|
|
189
|
-
purpose is granted, keeps its TC string. A TC string confirmed before the
|
|
190
|
-
choice's newest decision no longer describes it and is withdrawn too, unless
|
|
191
|
-
a newer receipt replaces it. That receipt is adopted even when its TC string
|
|
192
|
-
is identical, since TC strings round their time to the day and custom-vendor
|
|
193
|
-
selections live only in the receipt; its expiry then applies. The TC
|
|
194
|
-
string and its receipt (`euconsent-v2`, `c15t-iab-authority-v1`) belong to one
|
|
195
|
-
origin, so after a save on a sibling subdomain that shares the consent cookie,
|
|
196
|
-
this subdomain reports no consent through `__tcfapi` until it saves again.
|
|
197
|
-
Vendors never receive the stale TC string in between: it is withdrawn, or
|
|
198
|
-
held back, before `__tcfapi` publishes. A tab also reloads the TC string
|
|
199
|
-
when another tab on the same origin stores a new one, which covers a save in
|
|
200
|
-
the same millisecond or one that changed only vendors. When two tabs save in
|
|
201
|
-
the same millisecond with different selections, the more restrictive TC
|
|
202
|
-
string wins in both, so a revoked vendor is never advertised again. If each
|
|
203
|
-
grants something the other denies, neither is published until the next save:
|
|
204
|
-
the stored receipt is removed, so a page opened later does not restore it.
|
|
205
|
-
When another tab removes the receipt, or clears localStorage, the TC string
|
|
206
|
-
is withdrawn here too, since a page opened now would find none, and a
|
|
207
|
-
receipt this tab was still decoding is not installed. The removal after a
|
|
208
|
-
tie checks that the stored receipt is the one it read, but localStorage has
|
|
209
|
-
no conditional removal, so a receipt another tab stored a moment earlier can
|
|
210
|
-
still be removed. Every tab then withdraws its TC string until the next save,
|
|
211
|
-
so the race only ever withholds consent.
|
|
150
|
+
* Under an IAB policy, the TC string follows the reconciled choice. See
|
|
151
|
+
[How the TC string is reconciled](#how-the-tc-string-is-reconciled).
|
|
212
152
|
* A record removed from readable storage since this tab last saw it present is
|
|
213
153
|
cleared, and the active policy decides again. A record this tab never saw in
|
|
214
154
|
storage, such as a receipt merged from the server after `identify()` or a
|
|
@@ -219,8 +159,55 @@ Stored records are merged into the ones in memory:
|
|
|
219
159
|
Expiry is not decided here. The applied records are evaluated at the time of the
|
|
220
160
|
read, the same way as at startup.
|
|
221
161
|
|
|
162
|
+
### How the TC string is reconciled
|
|
163
|
+
|
|
164
|
+
Under an IAB policy, a tab adopts the TC string another tab stored, unless that
|
|
165
|
+
TC string conflicts with the reconciled choice. A conflicting TC string is
|
|
166
|
+
withdrawn, and `__tcfapi` reports no consent until the next save. Vendors never
|
|
167
|
+
receive a stale TC string: c15t withdraws it, or holds it back, before
|
|
168
|
+
`__tcfapi` publishes.
|
|
169
|
+
|
|
170
|
+
1. The `@c15t/iab` module loads the TC string the other tab stored, with its
|
|
171
|
+
purpose, vendor and special-feature selections, so `__tcfapi` and the
|
|
172
|
+
preference controls show the choice now in force. Selections the visitor
|
|
173
|
+
changed in this tab without saving are kept.
|
|
174
|
+
2. A missing or older stored TC string leaves the current one in place, unless
|
|
175
|
+
the current one conflicts with the reconciled choice. Then it is withdrawn.
|
|
176
|
+
3. A category denied after the TC string was saved conflicts if the TC string
|
|
177
|
+
grants any of its purposes. A partial selection saved through IAB, which
|
|
178
|
+
records its category as denied because not every purpose is granted, keeps
|
|
179
|
+
its TC string.
|
|
180
|
+
4. A TC string confirmed before the choice's newest decision no longer
|
|
181
|
+
describes it and is withdrawn, unless a newer receipt replaces it. That
|
|
182
|
+
receipt is adopted even when its TC string is identical, because TC strings
|
|
183
|
+
round their time to the day and custom-vendor selections live only in the
|
|
184
|
+
receipt. The receipt's expiry then applies.
|
|
185
|
+
5. The TC string and its receipt (`euconsent-v2`, `c15t-iab-authority-v1`)
|
|
186
|
+
belong to one origin. After a save on a sibling subdomain that shares the
|
|
187
|
+
consent cookie, this subdomain reports no consent through `__tcfapi` until
|
|
188
|
+
it saves again.
|
|
189
|
+
6. A tab reloads the TC string when another tab on the same origin stores a
|
|
190
|
+
new one. This covers a save in the same millisecond, or one that changed
|
|
191
|
+
only vendors.
|
|
192
|
+
7. When two tabs save in the same millisecond with different selections, the
|
|
193
|
+
more restrictive TC string wins in both, so a revoked vendor is never
|
|
194
|
+
advertised again. If each grants something the other denies, neither is
|
|
195
|
+
published until the next save, and the stored receipt is removed so a page
|
|
196
|
+
opened later does not restore it.
|
|
197
|
+
8. When another tab removes the receipt or clears localStorage, this tab
|
|
198
|
+
withdraws its TC string too, because a page opened now would find none. A
|
|
199
|
+
receipt this tab was still decoding is not installed.
|
|
200
|
+
9. The removal after a tie checks that the stored receipt is the one it read.
|
|
201
|
+
localStorage has no conditional removal, so a receipt another tab stored a
|
|
202
|
+
moment earlier can still be removed. Every tab then withdraws its TC string
|
|
203
|
+
until the next save, so the race only ever withholds consent.
|
|
204
|
+
|
|
222
205
|
### Clearing records across tabs
|
|
223
206
|
|
|
207
|
+
`clearRecords()` also drops every save queued for replay, with or without
|
|
208
|
+
browser persistence, so nothing the visitor decided before the clear reaches
|
|
209
|
+
the backend afterwards.
|
|
210
|
+
|
|
224
211
|
`clearRecords()` removes every record and then stores the time of the clear,
|
|
225
212
|
the clear epoch, under its own key: `c15t-epoch` in localStorage and a cookie
|
|
226
213
|
of the same name (`<storageKey>-epoch` with a custom `storageKey`). Clearing
|
|
@@ -295,15 +282,14 @@ cookie takes, for example because storage is full, the older localStorage copy
|
|
|
295
282
|
is removed.
|
|
296
283
|
|
|
297
284
|
When a server render seeded the page from the consent cookie
|
|
298
|
-
(`skipHydration`), that seed stays authoritative. A denial
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
The notice dismissal
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
one the cookie holds; a copy confirmed before the last clear is ignored, so its
|
|
285
|
+
(`skipHydration`), that seed stays authoritative. A denial that reached only
|
|
286
|
+
localStorage is still applied on top of it when the page mounts, since it can
|
|
287
|
+
only restrict, if it is newer than the seeded decision or from the same
|
|
288
|
+
millisecond as a seeded grant. A stored grant is not.
|
|
289
|
+
|
|
290
|
+
The notice dismissal and vendor denials are stored twice as well. A vendor
|
|
291
|
+
list in localStorage at least as new as the cookie's adds its denials but never
|
|
292
|
+
lifts one the cookie holds; a copy confirmed before the last clear is ignored, so its
|
|
307
293
|
denials never come back. The newer notice dismissal applies; it still only hides a
|
|
308
294
|
notice with the fingerprint it names. The consent record follows the rules
|
|
309
295
|
below.
|
|
@@ -343,20 +329,18 @@ A tab writes its own choices in a later task, not during the click. Before it
|
|
|
343
329
|
reads storage, it lands its own queued writes, so a reconciliation never undoes
|
|
344
330
|
the visitor's latest action in that tab. A queued write follows the same rules
|
|
345
331
|
as a read: it stores the per-category merge of its choice and the stored one,
|
|
346
|
-
|
|
347
|
-
|
|
332
|
+
and never replaces a newer notice or vendor record. It keeps the stored subject
|
|
333
|
+
unless this tab identified a different user.
|
|
348
334
|
When a slow save response returns a server subject id, the tab adds it only to
|
|
349
335
|
the record it wrote; it does not recreate records another tab cleared or
|
|
350
336
|
overwrite another tab's newer choice.
|
|
351
337
|
|
|
352
338
|
Two tabs that write at the same moment can both read storage before either
|
|
353
|
-
writes, and the later write can then drop the other tab's category
|
|
354
|
-
|
|
339
|
+
writes, and the later write can then drop the other tab's category decision.
|
|
340
|
+
The tab whose decision was dropped still holds it, and on its next
|
|
355
341
|
reconciliation it writes it back, merged with what storage holds. It does so
|
|
356
342
|
only for decisions it actually stored itself, never for one a clear voided or
|
|
357
|
-
another tab replaced with a newer one.
|
|
358
|
-
it kept from storage too, so if another write drops one of those, this tab
|
|
359
|
-
restores it and applies it as well.
|
|
343
|
+
another tab replaced with a newer one.
|
|
360
344
|
|
|
361
345
|
If another tab's change reaches this tab while one of its saves is pending, the
|
|
362
346
|
save depends on how far it got. A save not yet sent is dropped. A request
|
|
@@ -1,23 +1,17 @@
|
|
|
1
1
|
---
|
|
2
|
-
title: Data fetching
|
|
3
|
-
description:
|
|
4
|
-
and
|
|
5
|
-
group:
|
|
2
|
+
title: Data fetching
|
|
3
|
+
description: How c15t gets policy data through a cached manifest, backend /init,
|
|
4
|
+
the browser or offline rules, and where consent choices are saved.
|
|
5
|
+
group: concepts
|
|
6
6
|
---
|
|
7
7
|
|
|
8
|
-
##
|
|
8
|
+
## Compare the fetching paths
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
[
|
|
15
|
-
|
|
16
|
-
Backend ownership and data fetching are separate decisions. Inth manages the
|
|
17
|
-
backend for you. A [self-hosted backend](https://c15t.com/docs/self-host/quickstart) uses the same
|
|
18
|
-
protocol while you operate its database, policies and availability. A static
|
|
19
|
-
site can still call Inth. Only `offline()` deliberately removes consent backend
|
|
20
|
-
requests and stores choices locally.
|
|
10
|
+
Where policy resolves and where choices are saved are separate from who runs
|
|
11
|
+
the backend. Inth and a [self-hosted backend](https://c15t.com/docs/self-host/quickstart) speak
|
|
12
|
+
the same protocol, so every path below works with either. Only `offline()`
|
|
13
|
+
removes backend requests. For a recommendation by framework, start with
|
|
14
|
+
[choose your setup](./choose-your-setup.md).
|
|
21
15
|
|
|
22
16
|
| Fetching path | Where policy resolves | Where choices are submitted | Choose it when |
|
|
23
17
|
| ------------------------------ | ------------------------------------------------------------------------------------- | --------------------------- | ------------------------------------------------------------------------------------------------------ |
|
|
@@ -44,6 +38,16 @@ Do not put secrets, visitor identifiers or consent records into a manifest.
|
|
|
44
38
|
Keep personalized init responses out of shared caches. Changing a policy also
|
|
45
39
|
requires a refresh strategy for cached or build-time manifests.
|
|
46
40
|
|
|
41
|
+
## How manifest mode counts visitors
|
|
42
|
+
|
|
43
|
+
The backend counts visitors from `/init` requests, and manifest resolution
|
|
44
|
+
skips them. To keep the count, the server adapters send a session report after
|
|
45
|
+
each resolution: the host posts to the backend's `POST /sessions` from the
|
|
46
|
+
server, after the response. The browser sends nothing, and the report stores
|
|
47
|
+
no identity; the visitor's IP address and user agent are forwarded under the
|
|
48
|
+
backend's usual IP handling. Static output resolves in the browser and sends
|
|
49
|
+
no report. Set `reportSessions: false` on an adapter to turn it off.
|
|
50
|
+
|
|
47
51
|
## What does regular `/init` do?
|
|
48
52
|
|
|
49
53
|
`hosted({ url })` uses `${url}/init` for initialization and `${url}/subjects` for
|
|
@@ -54,7 +58,7 @@ can belong to Inth or your own c15t backend.
|
|
|
54
58
|
import { hosted } from 'c15t';
|
|
55
59
|
|
|
56
60
|
export function createConsentMode(backendURL: string) {
|
|
57
|
-
|
|
61
|
+
return hosted({ url: backendURL });
|
|
58
62
|
}
|
|
59
63
|
```
|
|
60
64
|
|
|
@@ -81,9 +85,9 @@ separate policy reads and record writes. With a backend rewrite mounted at
|
|
|
81
85
|
import { hosted } from 'c15t';
|
|
82
86
|
|
|
83
87
|
export const mode = hosted({
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
88
|
+
url: '/api/c15t',
|
|
89
|
+
initURL: '/api/c15t/init',
|
|
90
|
+
assertDecisionInputs: true,
|
|
87
91
|
});
|
|
88
92
|
```
|
|
89
93
|
|
|
@@ -105,11 +109,11 @@ and TLS connection to the consent backend. The app server still connects to
|
|
|
105
109
|
the upstream backend for manifest refreshes and consent writes. Vendor scripts
|
|
106
110
|
and vendor requests keep their own origins.
|
|
107
111
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
112
|
+
The rewrite destination and the init route use the absolute upstream endpoint,
|
|
113
|
+
such as `https://your-project.inth.app`. The browser uses `/api/c15t` without
|
|
114
|
+
needing the upstream URL. A static export cannot serve a Next.js route or
|
|
115
|
+
rewrite at runtime; use the absolute Inth URL or a proxy provided by the static
|
|
116
|
+
host instead.
|
|
113
117
|
|
|
114
118
|
## When should I use offline mode?
|
|
115
119
|
|
|
@@ -134,7 +138,7 @@ export const mode = offline();
|
|
|
134
138
|
With no `policyRules`, the current offline transport uses the recommended rule
|
|
135
139
|
pack. Supplying `policyRules` replaces that pack. Unknown country and region are
|
|
136
140
|
real resolution inputs; offline mode does not discover a visitor's location.
|
|
137
|
-
Use [policy rules](
|
|
141
|
+
Use [policy rules](./policies.md) to understand
|
|
138
142
|
matching and defaults, and test the missing-location case.
|
|
139
143
|
|
|
140
144
|
Offline mode is an explicit architecture choice, not an automatic fallback for
|
|
@@ -160,4 +164,4 @@ refresh. In the recommended Next.js setup, the browser should call only
|
|
|
160
164
|
upstream destinations.
|
|
161
165
|
|
|
162
166
|
Test different locations, missing location headers, GPC, returning choices and
|
|
163
|
-
backend failure. See [verification](
|
|
167
|
+
backend failure. See [verification](../guides/verify-consent.md).
|