@c15t/nextjs 2.2.0-canary-20260727202135 → 2.2.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.
- package/dist/version.cjs +1 -1
- package/dist/version.js +1 -1
- package/dist-types/version.d.ts +1 -1
- package/docs/frameworks/next/components/consent-manager-provider.md +99 -0
- package/docs/frameworks/next/script-loader.md +3 -1
- package/docs/integrations/posthog.md +2 -2
- package/docs/shared/react/components/consent-manager-provider.md +42 -0
- package/docs/shared/react/guides/script-loader.md +2 -0
- package/package.json +4 -4
package/dist/version.cjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";const __rslib_import_meta_url__="u"<typeof document?new(require("url".replace("",""))).URL("file:"+__filename).href:document.currentScript&&document.currentScript.src||new URL("main.js",document.baseURI).href;var __webpack_require__={};__webpack_require__.d=(e,_,r)=>{var o=(_,r)=>{for(var o in _)__webpack_require__.o(_,o)&&!__webpack_require__.o(e,o)&&Object.defineProperty(e,o,{enumerable:!0,[r]:_[o]})};o(_,"get"),o(r,"value")},__webpack_require__.o=(e,_)=>Object.prototype.hasOwnProperty.call(e,_),__webpack_require__.r=e=>{"u">typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})};var __webpack_exports__={};__webpack_require__.r(__webpack_exports__);const version="2.2.0
|
|
1
|
+
"use strict";const __rslib_import_meta_url__="u"<typeof document?new(require("url".replace("",""))).URL("file:"+__filename).href:document.currentScript&&document.currentScript.src||new URL("main.js",document.baseURI).href;var __webpack_require__={};__webpack_require__.d=(e,_,r)=>{var o=(_,r)=>{for(var o in _)__webpack_require__.o(_,o)&&!__webpack_require__.o(e,o)&&Object.defineProperty(e,o,{enumerable:!0,[r]:_[o]})};o(_,"get"),o(r,"value")},__webpack_require__.o=(e,_)=>Object.prototype.hasOwnProperty.call(e,_),__webpack_require__.r=e=>{"u">typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})};var __webpack_exports__={};__webpack_require__.r(__webpack_exports__);const version="2.2.0";for(var __rspack_i in __webpack_require__.d(__webpack_exports__,{},{version:"2.2.0"}),exports.version=__webpack_exports__.version,__webpack_exports__)-1===["version"].indexOf(__rspack_i)&&(exports[__rspack_i]=__webpack_exports__[__rspack_i]);Object.defineProperty(exports,"__esModule",{value:!0});
|
package/dist/version.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
let e="2.2.0
|
|
1
|
+
let e="2.2.0";export{e as version};
|
package/dist-types/version.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const version = "2.2.0
|
|
1
|
+
export declare const version = "2.2.0";
|
|
@@ -75,6 +75,105 @@ export default function ConsentManager({ children }: { children: ReactNode }) {
|
|
|
75
75
|
|
|
76
76
|
See [Client Modes](/docs/frameworks/next/concepts/client-modes) for a detailed comparison.
|
|
77
77
|
|
|
78
|
+
## Content Security Policy
|
|
79
|
+
|
|
80
|
+
c15t injects a `<style id="c15t-theme">` element for your theme tokens, and the script loader injects a `<script>` element per consented vendor. Under a nonce-based Content Security Policy, both are blocked unless they carry your nonce.
|
|
81
|
+
|
|
82
|
+
Pass it once through the `nonce` option and c15t applies it to everything it injects:
|
|
83
|
+
|
|
84
|
+
```tsx
|
|
85
|
+
<ConsentManagerProvider
|
|
86
|
+
options={{
|
|
87
|
+
mode: 'offline',
|
|
88
|
+
nonce: yourRequestNonce,
|
|
89
|
+
}}
|
|
90
|
+
>
|
|
91
|
+
{children}
|
|
92
|
+
</ConsentManagerProvider>
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
A `nonce` set on an individual script definition still wins, so you can override a single vendor without changing the provider.
|
|
96
|
+
|
|
97
|
+
> 📝 **Note:**
|
|
98
|
+
> Browsers hide the nonce content attribute once a policy is active. Inspecting the element shows no nonce="", but element.nonce still returns the value — this is expected and not a sign that c15t dropped it.
|
|
99
|
+
|
|
100
|
+
### Inline style attributes
|
|
101
|
+
|
|
102
|
+
The `nonce` option covers the elements c15t injects. It cannot cover inline `style="..."` attributes, which several components rely on — a nonce never authorizes a style attribute, because nonces apply to elements only.
|
|
103
|
+
|
|
104
|
+
Style attributes are governed by `style-src-attr`, and when that directive is absent CSP falls back to `style-src`. A nonce-based style policy therefore blocks them:
|
|
105
|
+
|
|
106
|
+
```http
|
|
107
|
+
style-src 'self' 'nonce-abc123';
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
To keep the nonce requirement for stylesheets while still allowing style attributes, set `style-src-attr` explicitly:
|
|
111
|
+
|
|
112
|
+
```http
|
|
113
|
+
style-src 'self' 'nonce-abc123';
|
|
114
|
+
style-src-attr 'unsafe-inline';
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
> ⚠️ **Warning:**
|
|
118
|
+
> 'unsafe-inline' on style-src-attr permits any inline style attribute on the page. That is weaker than a nonce, but far narrower than replacing your nonce-based style-src with 'unsafe-inline', which would additionally authorize arbitrary injected \<style> elements. Note that adding 'unsafe-inline' alongside a nonce achieves nothing — a directive that specifies a nonce ignores it. If your threat model does not allow this, expect components that use inline styles to render unstyled.
|
|
119
|
+
|
|
120
|
+
### Reading the nonce in the App Router
|
|
121
|
+
|
|
122
|
+
Next.js does not expose the request nonce to client components, so read it in a server component and pass it down. Generate the nonce in middleware, forward it on a request header, then hand it to the provider from your layout:
|
|
123
|
+
|
|
124
|
+
```ts title="middleware.ts"
|
|
125
|
+
import { NextResponse, type NextRequest } from 'next/server';
|
|
126
|
+
|
|
127
|
+
export function middleware(request: NextRequest) {
|
|
128
|
+
const nonce = crypto.randomUUID();
|
|
129
|
+
const isDev = process.env.NODE_ENV === 'development';
|
|
130
|
+
|
|
131
|
+
const csp = [
|
|
132
|
+
`default-src 'self'`,
|
|
133
|
+
// Next.js dev tooling (React Refresh and HMR) requires 'unsafe-eval'.
|
|
134
|
+
`script-src 'self' 'nonce-${nonce}'${isDev ? " 'unsafe-eval'" : ''}`,
|
|
135
|
+
`style-src 'self' 'nonce-${nonce}'`,
|
|
136
|
+
// Nonces cannot authorize inline style attributes — see above.
|
|
137
|
+
`style-src-attr 'unsafe-inline'`,
|
|
138
|
+
].join('; ');
|
|
139
|
+
|
|
140
|
+
const requestHeaders = new Headers(request.headers);
|
|
141
|
+
requestHeaders.set('x-nonce', nonce);
|
|
142
|
+
requestHeaders.set('Content-Security-Policy', csp);
|
|
143
|
+
|
|
144
|
+
const response = NextResponse.next({ request: { headers: requestHeaders } });
|
|
145
|
+
response.headers.set('Content-Security-Policy', csp);
|
|
146
|
+
|
|
147
|
+
return response;
|
|
148
|
+
}
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
> ⚠️ **Warning:**
|
|
152
|
+
> Set the same policy on both the forwarded request headers and the response. Next.js reads the nonce for its own runtime and hydration scripts from the Content-Security-Policy request header during rendering. Setting it only on the response leaves those scripts without a nonce, and the enforced policy then blocks them, so the page never hydrates.
|
|
153
|
+
|
|
154
|
+
```tsx title="app/layout.tsx"
|
|
155
|
+
import { headers } from 'next/headers';
|
|
156
|
+
import { ConsentManagerProvider } from '@c15t/nextjs';
|
|
157
|
+
|
|
158
|
+
export default async function RootLayout({
|
|
159
|
+
children,
|
|
160
|
+
}: {
|
|
161
|
+
children: React.ReactNode;
|
|
162
|
+
}) {
|
|
163
|
+
const nonce = (await headers()).get('x-nonce') ?? undefined;
|
|
164
|
+
|
|
165
|
+
return (
|
|
166
|
+
<html lang="en">
|
|
167
|
+
<body>
|
|
168
|
+
<ConsentManagerProvider options={{ mode: 'offline', nonce }}>
|
|
169
|
+
{children}
|
|
170
|
+
</ConsentManagerProvider>
|
|
171
|
+
</body>
|
|
172
|
+
</html>
|
|
173
|
+
);
|
|
174
|
+
}
|
|
175
|
+
```
|
|
176
|
+
|
|
78
177
|
## Legal Links
|
|
79
178
|
|
|
80
179
|
`legalLinks` defines the URLs shown in consent UI text (banner, dialog, and widget where applicable).
|
|
@@ -298,6 +298,8 @@ Control where the script is injected and whether the element id is anonymized:
|
|
|
298
298
|
|
|
299
299
|
Set `anonymizeId: false` only when another script or test needs a stable DOM id. Pass `nonce` when your CSP requires it; c15t applies it directly to the generated `<script>` element.
|
|
300
300
|
|
|
301
|
+
You usually do not need a per-script `nonce`. Setting `nonce` once on the provider covers every injected script (and the theme stylesheet); a per-script value overrides it for that script alone.
|
|
302
|
+
|
|
301
303
|
## Dynamic Management
|
|
302
304
|
|
|
303
305
|
Framework packages expose script-manager methods so integrations can be added, removed, or inspected at runtime. Use this for tenant-specific tools, feature-flagged scripts, or vendors that are configured after sign-in:
|
|
@@ -487,7 +489,7 @@ For iframe-only embeds, use the [iframe blocking](/docs/frameworks/next/iframe-b
|
|
|
487
489
|
* The provider and any component that calls `useConsentManager()` must be client components.
|
|
488
490
|
* Keep vendor ids out of static examples when they differ per environment — read them from `process.env.NEXT_PUBLIC_*` or runtime config.
|
|
489
491
|
* If a script must be available before a page becomes interactive, prefer a built-in helper that models denied-consent defaults rather than adding a separate `next/script` tag.
|
|
490
|
-
* If you use CSP nonces,
|
|
492
|
+
* If you use CSP nonces, set `nonce` on the provider options so c15t applies it to every injected script element and the theme stylesheet. See [Content Security Policy](/docs/frameworks/next/components/consent-manager-provider#content-security-policy) for reading the nonce in the App Router.
|
|
491
493
|
|
|
492
494
|
## API Reference
|
|
493
495
|
|
|
@@ -83,7 +83,7 @@ This is the recommended approach if you're using the PostHog JS SDK; it's common
|
|
|
83
83
|
|
|
84
84
|
If you want to load PostHog via a script tag, it's recommended to use this approach.
|
|
85
85
|
|
|
86
|
-
1. **Choose a region and loading mode** The c15t helper
|
|
86
|
+
1. **Choose a region and loading mode** The c15t helper seeds PostHog's initialization queue, loads the bootstrap script, and synchronizes consent through `posthog.opt_in_capturing()` / `posthog.opt_out_capturing()`. You do not need to call `posthog.init()` separately.
|
|
87
87
|
|
|
88
88
|
Use region to keep PostHog's API, UI, and bootstrap script hosts aligned. c15t defaults to region: 'eu'; set region: 'us' for PostHog Cloud US. You can still pass apiHost, uiHost, or scriptUrl for self-hosted or proxied setups.
|
|
89
89
|
|
|
@@ -221,7 +221,7 @@ posthog({
|
|
|
221
221
|
The behavior depends on which pattern you chose:
|
|
222
222
|
|
|
223
223
|
* **SDK Implementation** — your app loaded `posthog-js` itself, so `posthog.capture(...)` is available once your SDK setup has run. c15t calls `opt_in_capturing()` / `opt_out_capturing()` for you. Pending events before c15t syncs consent may be dropped; after denial, PostHog captures cookieless events.
|
|
224
|
-
* **Script Implementation with `loadMode: 'always'`** — `window.posthog` is defined early.
|
|
224
|
+
* **Script Implementation with `loadMode: 'always'`** — `window.posthog` is defined early as a queue. Calls to `posthog.capture(...)` before the bootstrap finishes are replayed after the SDK installs instead of being dropped. c15t queues the current consent decision ahead of those calls, then keeps calling `opt_in_capturing()` / `opt_out_capturing()` as consent changes. After denial, PostHog captures cookieless events when your PostHog project supports cookieless mode.
|
|
225
225
|
* **Script Implementation with `loadMode: 'after-consent'`** — PostHog is unavailable until measurement consent is granted. Guard `posthog.capture(...)` calls or call them only after consent.
|
|
226
226
|
|
|
227
227
|
> ⚠️ **Warning:**
|
|
@@ -30,6 +30,48 @@ Every other c15t component and hook must be rendered inside this provider.
|
|
|
30
30
|
|
|
31
31
|
\*ExtractedTypeTable: Could not extract "UIOptions" from "./packages/ui/src/theme/types.ts" using base path "/home/runner/work/c15t/c15t". Verify the path/name and that the file is included by your tsconfig.\*
|
|
32
32
|
|
|
33
|
+
## Content Security Policy
|
|
34
|
+
|
|
35
|
+
c15t injects a `<style id="c15t-theme">` element for your theme tokens, and the script loader injects a `<script>` element per consented vendor. Under a nonce-based Content Security Policy, both are blocked unless they carry your nonce.
|
|
36
|
+
|
|
37
|
+
Pass it once through the `nonce` option and c15t applies it to everything it injects:
|
|
38
|
+
|
|
39
|
+
```tsx
|
|
40
|
+
<ConsentManagerProvider
|
|
41
|
+
options={{
|
|
42
|
+
mode: 'offline',
|
|
43
|
+
nonce: yourRequestNonce,
|
|
44
|
+
}}
|
|
45
|
+
>
|
|
46
|
+
{children}
|
|
47
|
+
</ConsentManagerProvider>
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
A `nonce` set on an individual script definition still wins, so you can override a single vendor without changing the provider.
|
|
51
|
+
|
|
52
|
+
> 📝 **Note:**
|
|
53
|
+
> Browsers hide the nonce content attribute once a policy is active. Inspecting the element shows no nonce="", but element.nonce still returns the value — this is expected and not a sign that c15t dropped it.
|
|
54
|
+
|
|
55
|
+
### Inline style attributes
|
|
56
|
+
|
|
57
|
+
The `nonce` option covers the elements c15t injects. It cannot cover inline `style="..."` attributes, which several components rely on — a nonce never authorizes a style attribute, because nonces apply to elements only.
|
|
58
|
+
|
|
59
|
+
Style attributes are governed by `style-src-attr`, and when that directive is absent CSP falls back to `style-src`. A nonce-based style policy therefore blocks them:
|
|
60
|
+
|
|
61
|
+
```http
|
|
62
|
+
style-src 'self' 'nonce-abc123';
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
To keep the nonce requirement for stylesheets while still allowing style attributes, set `style-src-attr` explicitly:
|
|
66
|
+
|
|
67
|
+
```http
|
|
68
|
+
style-src 'self' 'nonce-abc123';
|
|
69
|
+
style-src-attr 'unsafe-inline';
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
> ⚠️ **Warning:**
|
|
73
|
+
> 'unsafe-inline' on style-src-attr permits any inline style attribute on the page. That is weaker than a nonce, but far narrower than replacing your nonce-based style-src with 'unsafe-inline', which would additionally authorize arbitrary injected \<style> elements. Note that adding 'unsafe-inline' alongside a nonce achieves nothing — a directive that specifies a nonce ignores it. If your threat model does not allow this, expect components that use inline styles to render unstyled.
|
|
74
|
+
|
|
33
75
|
## Legal Links
|
|
34
76
|
|
|
35
77
|
`legalLinks` defines the URLs shown in consent UI text (banner, dialog, and widget where applicable).
|
|
@@ -229,6 +229,8 @@ Control where the script is injected and whether the element id is anonymized:
|
|
|
229
229
|
|
|
230
230
|
Set `anonymizeId: false` only when another script or test needs a stable DOM id. Pass `nonce` when your CSP requires it; c15t applies it directly to the generated `<script>` element.
|
|
231
231
|
|
|
232
|
+
You usually do not need a per-script `nonce`. Setting `nonce` once on the provider covers every injected script (and the theme stylesheet); a per-script value overrides it for that script alone.
|
|
233
|
+
|
|
232
234
|
## Dynamic Management
|
|
233
235
|
|
|
234
236
|
Framework packages expose script-manager methods so integrations can be added, removed, or inspected at runtime. Use this for tenant-specific tools, feature-flagged scripts, or vendors that are configured after sign-in:
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@c15t/nextjs",
|
|
3
|
-
"version": "2.2.0
|
|
3
|
+
"version": "2.2.0",
|
|
4
4
|
"description": "Next.js cookie banner and consent management platform for App Router, Pages Router, SSR, IAB TCF, and Consent Mode.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"nextjs",
|
|
@@ -106,9 +106,9 @@
|
|
|
106
106
|
"test:watch": "bun prebuild && vitest --passWithNoTests"
|
|
107
107
|
},
|
|
108
108
|
"dependencies": {
|
|
109
|
-
"@c15t/react": "2.2.0
|
|
110
|
-
"@c15t/translations": "2.2.0
|
|
111
|
-
"c15t": "2.2.0
|
|
109
|
+
"@c15t/react": "2.2.0",
|
|
110
|
+
"@c15t/translations": "2.2.0",
|
|
111
|
+
"c15t": "2.2.0"
|
|
112
112
|
},
|
|
113
113
|
"devDependencies": {
|
|
114
114
|
"@c15t/typescript-config": "0.0.1",
|