@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
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Keeps a banner tap made before hydration.
3
+ *
4
+ * A server-rendered banner shows long before React attaches its handlers.
5
+ * The inline script records an accept, reject, dismiss or customize tap in
6
+ * that gap and hides the banner. Once this component mounts, the banner's
7
+ * own handlers work: it stops the script and records the tap when the
8
+ * runtime has started. See `@c15t/core/surface-actions`.
9
+ *
10
+ * The script is in the server HTML. A banner that hydrates keeps it, inert
11
+ * once claimed, so hydration changes nothing in the markup. A banner React
12
+ * renders in the browser has its handlers at once, and a script it inserts
13
+ * would never run, so there it renders nothing.
14
+ *
15
+ * @internal
16
+ */
17
+ export declare const EarlyTapScript: () => import("react").JSX.Element | null;
@@ -62,7 +62,7 @@ export interface ConsentBannerCompoundComponent extends FC<ConsentBannerProps> {
62
62
  * ```tsx
63
63
  * <ConsentProvider
64
64
  * options={{
65
- * mode: hosted({ url: '/api/c15t' }),
65
+ * mode: hosted({ backendURL: '/api/c15t' }),
66
66
  * i18n: {
67
67
  * locale: 'en',
68
68
  * messages: {
@@ -2,9 +2,33 @@
2
2
  export interface SurfaceStyleSheet {
3
3
  /** Dedupes the stylesheet: a page renders each `id` once. */
4
4
  id: string;
5
- /** The stylesheet text. */
5
+ /** The stylesheet text. Empty for a deferred sheet. */
6
6
  css: string;
7
+ /**
8
+ * Loads the text of a deferred sheet. A browser bundle gets one in place
9
+ * of `css` when the framework already put the sheet in the server HTML,
10
+ * as the Next.js adapter does, so the text ships once.
11
+ */
12
+ load?: () => Promise<string>;
7
13
  }
14
+ /**
15
+ * Starts loading the deferred sheets the page does not hold yet. Safe to
16
+ * call often: each sheet loads once, and the next call retries a failed
17
+ * load.
18
+ *
19
+ * @internal
20
+ */
21
+ export declare const preloadSurfaceSheets: (sheets: readonly SurfaceStyleSheet[]) => Promise<void>;
22
+ /**
23
+ * Whether a surface can show: its deferred sheets are in the page or
24
+ * loaded. Starts loading the missing ones after mount, so a surface that
25
+ * renders later, like a banner waiting on the browser's consent fetch,
26
+ * finds them ready. Hydration reads `true`: the server rendered the sheets
27
+ * it rendered the surface with.
28
+ *
29
+ * @internal
30
+ */
31
+ export declare const useSurfaceSheetsReady: (sheets: readonly SurfaceStyleSheet[], noStyle?: boolean) => boolean;
8
32
  /**
9
33
  * Renders the stylesheets a stock surface needs as `<style>` elements.
10
34
  *
@@ -33,4 +57,4 @@ export interface SurfaceStyleSheet {
33
57
  export declare const SurfaceStyles: ({ sheets, noStyle, }: {
34
58
  sheets: readonly SurfaceStyleSheet[];
35
59
  noStyle?: boolean;
36
- }) => import("react").JSX.Element[] | null;
60
+ }) => (import("react").JSX.Element | null)[] | null;
@@ -1 +1 @@
1
- export * from './components/consent-gate/index.js';
1
+ export { ConsentGate, ConsentGateButton, type ConsentGateCompoundComponent, type ConsentGateProps, ConsentGateRoot, ConsentGateTitle, Frame, FrameButton, type FrameCompoundComponent, type FrameProps, FrameRoot, FrameTitle, } from './components/consent-gate/index.js';
@@ -1,3 +1,3 @@
1
- export * from '../context.js';
2
- export * from './consent-tracking-context.js';
3
- export * from './theme-context.js';
1
+ export { HydrationSnapshotContext, KernelContext, type ProviderServices, ProviderServicesContext, StreamedPrefetchContext, } from '../context.js';
2
+ export { ConsentTrackingContext, type ConsentTrackingValue, toSaveUISource, useConsentTracking, } from './consent-tracking-context.js';
3
+ export { GlobalThemeContext, LocalThemeContext, type ThemeContextValue, } from './theme-context.js';
@@ -14,6 +14,8 @@ export interface ProviderServices {
14
14
  clearRecords: () => void;
15
15
  getPresentation: () => ConsentPresentation | undefined;
16
16
  getConsentCategories: () => readonly AllConsentNames[];
17
+ /** Whether the runtime has started: records hydrated, prefetch adopted. */
18
+ isStarted: () => boolean;
17
19
  /**
18
20
  * Store a language override and load its copy: init runs again on the
19
21
  * provider's own kernel while it is enabled, and a borrowed runtime
@@ -18,7 +18,7 @@ export type ConsentGPPProps = RuntimeGPPOptions;
18
18
  * ```tsx
19
19
  * import { ConsentGPP } from '@c15t/react/gpp';
20
20
  *
21
- * <ConsentProvider options={{ mode: hosted({ url }) }}>
21
+ * <ConsentProvider options={{ mode: hosted({ backendURL }) }}>
22
22
  * <ConsentGPP usFallback="none" />
23
23
  * <App />
24
24
  * </ConsentProvider>
@@ -0,0 +1,36 @@
1
+ import type { IABConsentDialogCompoundComponent } from './components/iab-panel/index.js';
2
+ /**
3
+ * The IAB TCF preference centre: purposes, stacks, special features and
4
+ * per-vendor consent and legitimate-interest controls.
5
+ *
6
+ * @remarks
7
+ * The component code loads on demand. It downloads in the browser's first
8
+ * idle period after the page's `load` event while the IAB banner is shown,
9
+ * on hover or focus of the banner's Customize button or partners link, or
10
+ * when the dialog opens, whichever comes first. `preloadDialog: 'intent'` in
11
+ * the provider options skips the idle load. `open={true}` or `showTrigger`
12
+ * loads it on mount under a model `models` lists; under another model,
13
+ * `showTrigger` loads only the trigger.
14
+ *
15
+ * The compound parts (`IABConsentDialog.Root`, `.Card`, `.Header`, `.Tabs`,
16
+ * `.TabButton`, `.Content`, `.Footer`, `.Overlay`, `.FeatureItem`,
17
+ * `.PurposeItem`, `.StackItem`, `.VendorList`) load the same module when
18
+ * they render.
19
+ *
20
+ * @example
21
+ * ```tsx
22
+ * <IABProvider cmpId={28}>
23
+ * <IABConsentBanner />
24
+ * <IABConsentDialog />
25
+ * </IABProvider>
26
+ * ```
27
+ *
28
+ * @public
29
+ */
30
+ export declare const IABConsentDialog: IABConsentDialogCompoundComponent;
31
+ /**
32
+ * Reset the warming state shared by mounted dialogs between tests.
33
+ *
34
+ * @internal
35
+ */
36
+ export declare const resetIABDialogWarmingForTests: () => void;
@@ -12,7 +12,7 @@
12
12
  * import { IABProvider, IABConsentBanner, IABConsentDialog } from '@c15t/react/iab';
13
13
  *
14
14
  * <ConsentProvider
15
- * options={{ mode: hosted({ url: '/api/c15t' }) }}
15
+ * options={{ mode: hosted({ backendURL: '/api/c15t' }) }}
16
16
  * >
17
17
  * <IABProvider cmpId={28}>
18
18
  * <IABConsentBanner />
@@ -24,7 +24,8 @@
24
24
  * @packageDocumentation
25
25
  */
26
26
  export { IABConsentBanner, type IABConsentBannerProps, } from './components/iab-prompt/index.js';
27
- export { IABConsentDialog, type IABConsentDialogProps, } from './components/iab-panel/index.js';
27
+ export type { IABConsentDialogProps } from './components/iab-panel/index.js';
28
+ export { IABConsentDialog } from './iab-dialog-deferred.js';
28
29
  export { type HeadlessIABBannerAction, type HeadlessIABBannerState, type HeadlessIABDialogAction, type HeadlessIABDialogState, type HeadlessIABPreferenceTab, type UseHeadlessIABConsentUIResult, useHeadlessIABConsentUI, } from './hooks/use-headless-iab-consent-ui.js';
29
30
  export { IABProvider } from './iab-context.js';
30
31
  export type { IABProviderProps } from './iab-context.js';
@@ -4,4 +4,4 @@
4
4
  * (esbuild) does not treat every module the index names as part of an app
5
5
  * that imports one hook: only the groups it uses are live.
6
6
  */
7
- export { ConsentGate, Frame } from '../components/consent-gate/index.js';
7
+ export { ConsentGate } from '../components/consent-gate/index.js';
@@ -4,5 +4,4 @@
4
4
  * (esbuild) does not treat every module the index names as part of an app
5
5
  * that imports one hook: only the groups it uses are live.
6
6
  */
7
- export { createConsentKernel, custom, hosted, policyRulePresets, } from '@c15t/core';
8
- export { offline } from '../transports/offline.js';
7
+ export { createConsentKernel, custom } from '@c15t/core';
@@ -0,0 +1,7 @@
1
+ /**
2
+ * One group of `@c15t/react`'s public exports. The package index re-exports
3
+ * each group with `export *`, so a bundler that splits code by reachability
4
+ * (esbuild) does not treat every module the index names as part of an app
5
+ * that imports one hook: only the groups it uses are live.
6
+ */
7
+ export { Frame } from '../components/consent-gate/frame.js';
@@ -0,0 +1,7 @@
1
+ /**
2
+ * One group of `@c15t/react`'s public exports. The package index re-exports
3
+ * each group with `export *`, so a bundler that splits code by reachability
4
+ * (esbuild) does not treat every module the index names as part of an app
5
+ * that imports one hook: only the groups it uses are live.
6
+ */
7
+ export { hosted } from '@c15t/core';
@@ -0,0 +1,7 @@
1
+ /**
2
+ * One group of `@c15t/react`'s public exports. The package index re-exports
3
+ * each group with `export *`, so a bundler that splits code by reachability
4
+ * (esbuild) does not treat every module the index names as part of an app
5
+ * that imports one hook: only the groups it uses are live.
6
+ */
7
+ export { offline } from '../transports/offline.js';
@@ -0,0 +1,7 @@
1
+ /**
2
+ * One group of `@c15t/react`'s public exports. The package index re-exports
3
+ * each group with `export *`, so a bundler that splits code by reachability
4
+ * (esbuild) does not treat every module the index names as part of an app
5
+ * that imports one hook: only the groups it uses are live.
6
+ */
7
+ export { policyRulePresets } from '@c15t/core';
@@ -11,7 +11,7 @@
11
11
  *
12
12
  * function App({ children }) {
13
13
  * return (
14
- * <ConsentProvider options={{ mode: hosted({ url: '/api/c15t' }) }}>
14
+ * <ConsentProvider options={{ mode: hosted({ backendURL: '/api/c15t' }) }}>
15
15
  * {children}
16
16
  * </ConsentProvider>
17
17
  * );
@@ -48,11 +48,15 @@ export type { ConsentBannerCompoundComponent } from './components/prompt/index.j
48
48
  export type { ReactUIOptions } from './types/manager.js';
49
49
  export type { ConsentPresentation, PromptPresentation, PreferencesPresentation, PresentationAction, PromptPosition, PromptVariant, ResolvedConsentPresentation, } from '@c15t/core';
50
50
  export * from './index-parts/core.js';
51
+ export * from './index-parts/hosted.js';
52
+ export * from './index-parts/offline.js';
53
+ export * from './index-parts/presets.js';
51
54
  export * from './index-parts/consent-dialog.js';
52
55
  export * from './index-parts/consent-banner.js';
53
56
  export * from './index-parts/consent-dialog-link.js';
54
57
  export * from './index-parts/consent-dialog-trigger.js';
55
58
  export * from './index-parts/consent-gate.js';
59
+ export * from './index-parts/frame.js';
56
60
  export * from './index-parts/draft.js';
57
61
  export * from './index-parts/translations.js';
58
62
  export * from './index-parts/module-hooks.js';
@@ -0,0 +1,26 @@
1
+ /**
2
+ * `@c15t/react/modes` — the single-page app modes, defaulting to what the
3
+ * build integration downloaded.
4
+ *
5
+ * `manifest()` and `hosted()` here read `@c15t/core/generated`, which
6
+ * `consentManifest()` from `c15t/build` fills with the policy snapshot and
7
+ * the backend URL. `c15t/react` exports these in place of the plain
8
+ * `hosted()` from `@c15t/react`.
9
+ *
10
+ * The package index does not import this module. Server-framework packages
11
+ * re-export the index, and their browser bundles must not import
12
+ * `@c15t/core/generated`: Next.js fails the build when they do.
13
+ *
14
+ * @example
15
+ * ```tsx
16
+ * import { ConsentProvider, manifest } from 'c15t/react';
17
+ *
18
+ * <ConsentProvider options={{ mode: manifest() }}>{children}</ConsentProvider>;
19
+ * ```
20
+ */
21
+ export { hosted } from './transports/hosted.js';
22
+ export type { HostedOptions } from './transports/hosted.js';
23
+ export { manifest } from './transports/manifest.js';
24
+ export type { BrowserManifestModeFactory, ManifestModeOptions, } from './transports/manifest.js';
25
+ export { offline } from './transports/offline.js';
26
+ export type { OfflineModeOptions } from './transports/offline.js';
@@ -1 +1 @@
1
- export * from './components/panel-link/index.js';
1
+ export { ConsentDialogLink, type ConsentDialogLinkProps, } from './components/panel-link/index.js';
@@ -1 +1 @@
1
- export * from './components/panel-trigger/index.js';
1
+ export { ConsentDialogTrigger, type ConsentDialogTriggerCompound, type ConsentDialogTriggerProps, ConsentDialogTriggerToolbar, type ConsentDialogTriggerToolbarAction, type ConsentDialogTriggerToolbarPreferences, type ConsentDialogTriggerToolbarProps, type CornerPosition, TriggerButton, type TriggerButtonProps, TriggerIcon, type TriggerIconProps, type TriggerIconType, type TriggerOrientation, TriggerRoot, type TriggerRootProps, type TriggerSize, TriggerText, type TriggerTextProps, type TriggerVisibility, type UseDraggableOptions, type UseDraggableReturn, useDraggable, useTriggerContext, } from './components/panel-trigger/index.js';
@@ -75,7 +75,7 @@ export interface ConsentProviderOptions extends ConsentControlOptions, Pick<Reac
75
75
  * ```tsx
76
76
  * import { ConsentProvider, hosted, offline } from '@c15t/react';
77
77
  *
78
- * <ConsentProvider options={{ mode: hosted({ url: '/api/c15t' }) }}>
78
+ * <ConsentProvider options={{ mode: hosted({ backendURL: '/api/c15t' }) }}>
79
79
  * {children}
80
80
  * </ConsentProvider>
81
81
  *
@@ -119,7 +119,7 @@ export interface ConsentProviderOptions extends ConsentControlOptions, Pick<Reac
119
119
  * // app/layout.tsx — stays synchronous, so the shell prerenders.
120
120
  * const config = resolveConsent({ backendURL });
121
121
  * return (
122
- * <ConsentProvider options={{ mode: hosted({ url }), prefetch: config }}>
122
+ * <ConsentProvider options={{ mode: hosted({ backendURL }), prefetch: config }}>
123
123
  * {children}
124
124
  * </ConsentProvider>
125
125
  * );
@@ -279,7 +279,7 @@ export type ConsentProviderProps = OwnedRuntimeProviderProps | ExternalRuntimePr
279
279
  * ```tsx
280
280
  * import { createConsentRuntime } from '@c15t/core/runtime';
281
281
  *
282
- * const runtime = createConsentRuntime({ mode: hosted({ url: '/api/c15t' }) });
282
+ * const runtime = createConsentRuntime({ mode: hosted({ backendURL: '/api/c15t' }) });
283
283
  * runtime.start();
284
284
  *
285
285
  * <ConsentProvider runtime={runtime} options={{ theme }}>
@@ -0,0 +1,36 @@
1
+ import type { HostedModeFactory, HostedModeOptions } from '@c15t/core';
2
+ /** Options for {@link hosted}. `backendURL` defaults to the build's. */
3
+ export type HostedOptions = Omit<HostedModeOptions, 'backendURL'> & {
4
+ /**
5
+ * Backend URL, relative (`/api/c15t`) or absolute. Defaults to the URL
6
+ * `consentManifest()` from `c15t/build` read from
7
+ * `VITE_C15T_BACKEND_URL` or `VITE_INTH_PROJECT_URL`.
8
+ */
9
+ backendURL?: string;
10
+ };
11
+ /**
12
+ * Build a hosted factory, taking the backend URL from the build when the
13
+ * options name none.
14
+ *
15
+ * @param options - The page's options.
16
+ * @param buildBackendURL - The backend URL the build read, if any.
17
+ * @returns A hosted transport factory.
18
+ * @throws {Error} When neither names a backend URL.
19
+ * @internal
20
+ */
21
+ export declare const hostedWithBuild: (options: HostedOptions, buildBackendURL: string | undefined) => HostedModeFactory;
22
+ /**
23
+ * Ask the backend's `/init` for every visitor's policy.
24
+ *
25
+ * @param options - Backend URL, init headers and request overrides.
26
+ * @returns A transport factory for the provider's `mode`, carrying its
27
+ * options.
28
+ * @throws {Error} When no backend URL is passed and the build set none.
29
+ * @example
30
+ * ```tsx
31
+ * import { ConsentProvider, hosted } from 'c15t/react';
32
+ *
33
+ * <ConsentProvider options={{ mode: hosted() }}>{children}</ConsentProvider>;
34
+ * ```
35
+ */
36
+ export declare const hosted: (options?: HostedOptions) => HostedModeFactory;
@@ -0,0 +1,48 @@
1
+ import type { BrowserManifestModeFactory, BrowserManifestOptions } from '@c15t/core/transports/manifest-browser';
2
+ /**
3
+ * Manifest mode for the React provider: core's browser resolver, with the
4
+ * policy and backend URL the build integration downloaded as defaults.
5
+ */
6
+ import type { ConsentManifest } from '@c15t/schema/types';
7
+ /** What a build integration downloaded, as `@c15t/core/generated` exports it. */
8
+ export interface BuildOutput {
9
+ backendURL?: string;
10
+ snapshot?: ConsentManifest;
11
+ }
12
+ /**
13
+ * Fill the options the page left out from the build: the backend URL that
14
+ * saves go to, and the snapshot unless the page passed one, a
15
+ * `manifestURL`, or `source: 'runtime'`.
16
+ *
17
+ * @param options - The page's options.
18
+ * @param build - What the build downloaded.
19
+ * @returns The options `manifest()` resolves with.
20
+ * @internal
21
+ */
22
+ export declare const withBuildDefaults: (options: BrowserManifestOptions, build: BuildOutput) => BrowserManifestOptions;
23
+ /**
24
+ * Resolve the policy in the browser from the backend's consent manifest.
25
+ *
26
+ * Without options it uses what `consentManifest()` from `c15t/build`
27
+ * downloaded: the snapshot and the backend URL from
28
+ * `VITE_C15T_BACKEND_URL` or `VITE_INTH_PROJECT_URL`, both served as
29
+ * `c15t/generated`. Without a
30
+ * snapshot, such as in dev after a failed fetch, it fetches
31
+ * `${backendURL}/manifest` at runtime. Saves go to the backend either way.
32
+ * With `manifestURL` or `source: 'runtime'`, also pass `source: 'runtime'`
33
+ * to `consentManifest()`, so the build downloads no snapshot.
34
+ *
35
+ * @param options - Overrides: your own `snapshot`, `source: 'runtime'`, a
36
+ * `manifestURL`, a `backendURL`, or the visitor's location as `inputs`.
37
+ * @returns A transport factory for the provider's `mode`, carrying its
38
+ * options.
39
+ * @throws {Error} When no snapshot, manifest URL or backend URL is known.
40
+ * @example
41
+ * ```tsx
42
+ * import { ConsentProvider, manifest } from 'c15t/react';
43
+ *
44
+ * <ConsentProvider options={{ mode: manifest() }}>{children}</ConsentProvider>;
45
+ * ```
46
+ */
47
+ export declare const manifest: (options?: BrowserManifestOptions) => BrowserManifestModeFactory;
48
+ export type { BrowserManifestModeFactory, BrowserManifestOptions as ManifestModeOptions, } from '@c15t/core/transports/manifest-browser';
@@ -1 +1 @@
1
- export declare const version = "3.0.0-alpha.9";
1
+ export declare const version = "3.0.0";
package/docs/README.md CHANGED
@@ -6,7 +6,7 @@ These docs ship inside the package so coding agents can read them offline. Open
6
6
 
7
7
  ## Using these docs
8
8
 
9
- These docs describe c15t v3. Find your app's row in Choose your setup, then follow that framework guide from start to finish; it names the files to create and the backend URL to use. Install c15t packages with the @alpha dist-tag, because npm latest is still v2. A visible banner does not prove anything: check that vendor requests wait for consent, that rejection survives a reload, and that preferences can be reopened.
9
+ These docs describe c15t v3. Find your app's row in Choose your setup, then follow that framework guide from start to finish; it names the files to create and the backend URL to use. A visible banner does not prove anything: check that vendor requests wait for consent, that rejection survives a reload, and that preferences can be reopened.
10
10
 
11
11
  ## Start here
12
12
 
@@ -15,7 +15,7 @@ These docs describe c15t v3. Find your app's row in Choose your setup, then foll
15
15
  - [How consent works](./concepts/how-consent-works.md): What c15t decides on each page load, the difference between a permission and a recorded choice, and what happens when a visitor saves.
16
16
  - [Customize the interface](./customization/overview.md): Change c15t's consent banner and dialog one step at a time, from a prop to your own markup, and find where each step lives in your framework.
17
17
  - [Verify consent](./guides/verify-consent.md): Check in a production build that vendor requests wait for consent, rejection survives a reload, preferences reopen and privacy signals apply.
18
- - [Migrate to v3](./upgrade-v3.md): Upgrade from c15t v2 to v3. Pick the guide for the v2 package you use (@c15t/nextjs, @c15t/react or the c15t store), rename @c15t/scripts, keep visitors' choices, and upgrade a self-hosted backend and the Node.js SDK.
18
+ - [Upgrade a React app from v2](./frameworks/react/upgrade-v3.md): Upgrade a React app from c15t v2 (@c15t/react) to v3. Covers packages, ConsentProvider and transports, the useConsentManager codemod, callbacks, policies, styles, IAB and stored consent.
19
19
 
20
20
  ## More documentation
21
21
 
@@ -58,12 +58,13 @@ These docs describe c15t v3. Find your app's row in Choose your setup, then foll
58
58
  - [Consent state reference](./concepts/consent-state.md): How c15t saves choices, gates IAB vendors, hydrates server records and keeps browser tabs and storage in step.
59
59
  - [Data fetching](./concepts/data-fetching.md): Choose the recommended build-time policy snapshot or runtime manifest fetching, backend /init, browser resolution or offline rules, and understand where consent choices are saved.
60
60
  - [How consent works](./concepts/how-consent-works.md): What c15t decides on each page load, the difference between a permission and a recorded choice, and what happens when a visitor saves.
61
+ - [Consent modes](./concepts/modes.md): Reference for manifest(), hosted() and offline() in every c15t framework package, where each one resolves the visitor's policy, what it adds to first-load JavaScript, the build-time manifest fetch and its failure policy, and the backend URL variable each framework reads.
61
62
  - [Policies](./concepts/policies.md): How policy models, prompts and scope decide what c15t asks visitors, where to change the rules, and why a banner may not appear.
62
63
 
63
64
  ## Guides
64
65
 
65
66
  - [Banner experiments](./guides/banner-experiments.md): Run A/B tests on consent banner presentation with any feature-flag provider or built-in weighted assignment, and attribute every impression and choice to its arm.
66
- - [Troubleshooting](./guides/troubleshooting.md): Fix a missing banner, analytics that load before consent, choices lost on reload, CORS errors, hydration differences and failed static builds in c15t v3.
67
+ - [Troubleshooting](./guides/troubleshooting.md): Fix a missing banner, analytics that load before consent, choices lost on reload, CORS errors, hydration differences, failed manifest downloads, mode errors and failed static builds in c15t v3.
67
68
  - [Verify consent](./guides/verify-consent.md): Check in a production build that vendor requests wait for consent, rejection survives a reload, preferences reopen and privacy signals apply.
68
69
 
69
70
  ## Customization
@@ -93,8 +94,8 @@ These docs describe c15t v3. Find your app's row in Choose your setup, then foll
93
94
  - [Fathom Analytics](./integrations/fathom-analytics.md): Load Fathom Analytics only after measurement consent with the c15t fathomAnalytics helper, set its SPA mode, and check it in DevTools.
94
95
  - [Front Chat](./integrations/front-chat.md): Load the Front Chat widget only after functionality consent with the c15t frontChat helper, forward CSP nonces, clear the session on revocation and check it in DevTools.
95
96
  - [Google Maps](./integrations/google-maps.md): Gate a Google Maps iframe embed with c15t v3 so the map loads only after the visitor allows its consent category, in Next.js, TanStack Start, React, Nuxt, Vue, Astro, Svelte, SvelteKit, HTML or JavaScript.
96
- - [Google Tag](./integrations/google-tag.md): Load gtag.js for Google Analytics or Google Ads with c15t Consent Mode v2 signals, and verify the consent commands in DevTools.
97
- - [Google Tag Manager](./integrations/google-tag-manager.md): Load a Google Tag Manager container with c15t Consent Mode v2 signals, configure consent checks inside the container, and verify both in DevTools.
97
+ - [Google Tag](./integrations/google-tag.md): Load gtag.js for Google Analytics or Google Ads before or after consent with c15t Consent Mode v2 signals, and verify the consent commands in DevTools.
98
+ - [Google Tag Manager](./integrations/google-tag-manager.md): Load a Google Tag Manager container before or after consent with c15t Consent Mode v2 signals, configure consent checks inside the container, and verify both in DevTools.
98
99
  - [Heap](./integrations/heap.md): Load the Heap config script and heap.js only after measurement consent with the c15t heap helper, and check it in DevTools.
99
100
  - [Hightouch](./integrations/hightouch.md): Load the Hightouch Events browser SDK only after measurement consent with the c15t hightouch helper, and check page events in DevTools.
100
101
  - [Hotjar](./integrations/hotjar.md): Load Hotjar only after measurement consent with the c15t hotjar helper, and check its loader and recordings in DevTools.
@@ -124,9 +125,6 @@ These docs describe c15t v3. Find your app's row in Choose your setup, then foll
124
125
  - [TikTok Pixel](./integrations/tiktok-pixel.md): Load the TikTok Pixel only after marketing consent with the c15t tiktokPixel helper, guard ttq event calls, and check it in DevTools.
125
126
  - [Umami Analytics](./integrations/umami-analytics.md): Load the Umami Analytics tracker only after measurement consent with the c15t umamiAnalytics helper, point it at a self-hosted instance, and check it in DevTools.
126
127
  - [Vercel Analytics](./integrations/vercel-analytics.md): Load the Vercel Web Analytics script only after measurement consent with the c15t vercelAnalytics helper, choose the debug script, and check it in DevTools.
128
+ - [Vercel Speed Insights](./integrations/vercel-speed-insights.md): Load the Vercel Speed Insights collector only after measurement consent with the c15t vercelSpeedInsights helper, drop Web Vitals reports on revocation, and check it in DevTools.
127
129
  - [X Pixel](./integrations/x-pixel.md): Load the X Pixel only after marketing consent with the c15t xPixel helper, guard twq conversion events, and check it in DevTools.
128
130
  - [YouTube](./integrations/youtube.md): Gate YouTube embeds with c15t v3 in Next.js, TanStack Start, React, Nuxt, Vue, Astro, Svelte, SvelteKit or JavaScript.
129
-
130
- ## Reference
131
-
132
- - [Migrate to v3](./upgrade-v3.md): Upgrade from c15t v2 to v3. Pick the guide for the v2 package you use (@c15t/nextjs, @c15t/react or the c15t store), rename @c15t/scripts, keep visitors' choices, and upgrade a self-hosted backend and the Node.js SDK.
@@ -76,10 +76,15 @@ public policy manifest, never a response that contains a visitor's consent.
76
76
  ## How the server gets the policy
77
77
 
78
78
  Use a build-time manifest as the default for supported production builds.
79
- Next.js, TanStack Start, Nuxt server output, Astro server output and SvelteKit
80
- can resolve requests from the deployment's snapshot. Vite apps can bundle it
81
- for browser resolution too; location-based policies may still need a location
82
- request. The framework quickstarts show the build hook and snapshot wiring.
79
+ `manifest()` is the default mode in Next.js, TanStack Start, Nuxt, Astro and
80
+ SvelteKit, so their servers resolve requests from the deployment's snapshot.
81
+ React, Vue and Svelte single-page apps have no server, so their quickstarts
82
+ use `hosted()`: the backend reads each visitor's location from the `/init`
83
+ request. They can bundle the manifest for browser resolution too, but a
84
+ regional policy still needs `/init` on a first visit unless the page knows
85
+ the location. The framework quickstarts show the build
86
+ integration. [Consent modes](./modes.md) lists every mode, its
87
+ options and the backend URL variable each framework reads.
83
88
 
84
89
  The build fetches your public policy once and bundles it, so the server never
85
90
  fetches it at runtime.
@@ -88,19 +93,49 @@ The snapshot is fixed at build time:
88
93
 
89
94
  * Rebuild after changing policies, translations or vendors. If your CI caches
90
95
  build output, force a fresh build.
91
- * The build must reach the backend. A failed fetch stops Next.js, TanStack
92
- Start, SvelteKit and Vite builds. By default, Astro and Nuxt log a warning
93
- instead and fetch the policy at runtime.
94
96
  * Consent choices still go to the backend.
95
97
 
98
+ The build reads the backend URL from your public backend URL variable when
99
+ the config doesn't pass one: `NEXT_PUBLIC_C15T_BACKEND_URL` in Next.js,
100
+ `NUXT_PUBLIC_C15T_BACKEND_URL` in Nuxt, `PUBLIC_C15T_BACKEND_URL` in Astro,
101
+ Svelte and SvelteKit, and `VITE_C15T_BACKEND_URL` in TanStack Start and other
102
+ Vite apps. Each also reads the matching Inth variable, such as
103
+ `NEXT_PUBLIC_INTH_PROJECT_URL`, when the c15t one is unset. See
104
+ [set the backend URL](./modes.md#set-the-backend-url).
105
+
106
+ The fetch waits at most 10 seconds. When it fails, or no backend URL is set,
107
+ every framework does the same thing:
108
+
109
+ | Command | Default when the fetch fails |
110
+ | ------------------------------------------------------------------------- | -------------------------------------------------------- |
111
+ | Production build: `next build`, `vite build`, `nuxt build`, `astro build` | The build stops with an error. |
112
+ | Dev: `next dev`, `vite dev`, `nuxt dev`, `astro dev` | A warning, and the server fetches the policy at runtime. |
113
+
114
+ Set `onBuildError` to use one behaviour for both. `'fail'` stops dev too.
115
+ `'runtime'` lets a production build finish, and the server fetches the policy
116
+ at runtime. The `C15T_ON_BUILD_ERROR` environment variable overrides the
117
+ option, so you can deploy during a backend outage without a code change:
118
+
119
+ ```sh
120
+ C15T_ON_BUILD_ERROR=runtime npm run build
121
+ ```
122
+
123
+ Turborepo's strict environment mode hides undeclared variables from tasks, so
124
+ list `C15T_ON_BUILD_ERROR` in the build task's `passThroughEnv` there.
125
+
126
+ The build skips the fetch, without an error, when it can't use a snapshot,
127
+ for example when the backend URL is relative. With `onBuildError: 'fail'`, a
128
+ relative URL stops the build. [Consent modes](./modes.md#what-happens-when-the-download-fails)
129
+ lists every case.
130
+
96
131
  For deployments that need policy updates without a rebuild, choose a runtime
97
132
  path:
98
133
 
99
- * **Cached manifest**.
134
+ * **Cached manifest**, `manifest({ source: 'runtime' })`.
100
135
  Your server downloads the project's public policy file, caches it, and
101
136
  resolves each visitor locally. Consent saves still go to the backend.
102
- * **`/init`**. Each visit asks the backend to resolve the visitor. Simpler, but
103
- every page load makes a request to the backend.
137
+ * **`/init`**, `hosted()`. Each visit asks the backend to resolve the visitor.
138
+ Simpler, but every page load makes a request to the backend.
104
139
 
105
140
  [Data fetching](./data-fetching.md) compares them in detail,
106
141
  including proxies and custom transports.
@@ -81,4 +81,4 @@ client render. Set
81
81
 
82
82
  Use effective permissions to gate work and explicit choices to inspect what the
83
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.
84
+ distinction and [policies](./policies.md) for v3 policy configuration.
@@ -130,8 +130,11 @@ Forward that configuration intact. Copying an allowed category into a receipt
130
130
  would invent a grant and lose its original confirmation time.
131
131
 
132
132
  Valid v2 records can be read without a startup rewrite. The next explicit action
133
- writes the v3 format. See [migration](../upgrade-v3.md) for expiry, partial saves,
134
- custom transports and backend contract changes.
133
+ writes the v3 format. The upgrade guides for
134
+ [Next.js](https://c15t.com/docs/frameworks/next/upgrade-v3#keep-visitors-existing-choices),
135
+ [React](../frameworks/react/upgrade-v3.md#keep-visitors-existing-choices) and
136
+ [JavaScript](https://c15t.com/docs/frameworks/javascript/upgrade-v3#keep-visitors-existing-choices)
137
+ cover how v3 treats v2 records.
135
138
 
136
139
  ## Keep open tabs in step
137
140