@cookieyes/nextjs 0.4.0 → 0.5.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/README.md CHANGED
@@ -160,6 +160,142 @@ import { CookieBanner, CookiePreferences, CookieOptOut, RecallButton } from "@co
160
160
  </>
161
161
  ```
162
162
 
163
+ ## Region-based regulation (server-detected)
164
+
165
+ Pick the banner's regulation from the visitor's region. On the server you read the location
166
+ header your host adds (Cloudflare/Vercel) with `regionFromHeaders`, pass it to your client
167
+ component, and wrap the banner in `<CookieYesProvider>` — so the **correct banner is
168
+ server-rendered for each visitor**, on the first paint, with no post-hydration flicker.
169
+
170
+ ```tsx
171
+ // app/layout.tsx — a Server Component
172
+ import { headers } from "next/headers";
173
+ import { regionFromHeaders } from "@cookieyes/nextjs";
174
+ import { CookieYesRoot } from "./cookieyes-root"; // your "use client" module
175
+
176
+ export default async function RootLayout({ children }) {
177
+ const region = regionFromHeaders(await headers()); // "US-CA" | "DE" | undefined
178
+ return (
179
+ <html>
180
+ <body>
181
+ <CookieYesRoot region={region} />
182
+ {children}
183
+ </body>
184
+ </html>
185
+ );
186
+ }
187
+ ```
188
+
189
+ ```tsx
190
+ // cookieyes-root.tsx — "use client"
191
+ "use client";
192
+ import { initCookieYes, CookieYesProvider, CookieBanner, CookieOptOut } from "@cookieyes/nextjs";
193
+
194
+ const map = { "US-CA": "CCPA", DE: "GDPR" } as const;
195
+
196
+ export function CookieYesRoot({ region }: { region?: string }) {
197
+ const regionConfig = { detect: () => region, map };
198
+ initCookieYes({ mode: "cookie-only", region: regionConfig });
199
+ return (
200
+ <CookieYesProvider region={regionConfig}>
201
+ <CookieBanner />
202
+ <CookieOptOut /> {/* render this too if any region maps to CCPA */}
203
+ </CookieYesProvider>
204
+ );
205
+ }
206
+ ```
207
+
208
+ - **What it reads:** by default the well-known Vercel (`x-vercel-ip-country` + `-region`) and
209
+ Cloudflare (`cf-ipcountry`) headers. Pass `regionFromHeaders(h, { header: "x-your-header" })`
210
+ to read a custom one.
211
+ - `headers()` is `await`ed on Next.js 15+ and synchronous on 14 — use whichever your version needs.
212
+ - **First paint:** with the provider, the server resolves the region per request and renders the
213
+ right banner directly into the HTML — a US visitor gets CCPA, an EU visitor gets GDPR, on the
214
+ first byte. The provider resolves the same value on the client, so there's no hydration mismatch.
215
+ (Without the provider, the banner still works but is corrected after hydration rather than
216
+ server-rendered per request.)
217
+ - **GPC:** on a CCPA banner, the browser's "do not sell" signal (`navigator.globalPrivacyControl`)
218
+ starts the visitor **opted out** — non-required categories denied, so gated scripts/iframes never
219
+ load — until they choose otherwise. It's read in the browser (the server can't see it), so it
220
+ applies right after hydration; it never changes *which* banner shows. Set `region.honorGpc: false`
221
+ to ignore it.
222
+
223
+ ## Returning visitors — no banner flash
224
+
225
+ By default the server doesn't know whether a visitor has already chosen, so it renders the
226
+ banner for everyone and the client removes it after hydration. A returning visitor **sees the
227
+ banner appear and then vanish**, which reads as a bug rather than as a remembered choice.
228
+
229
+ Read their decision from the request and pass it to the provider, and the banner is never in
230
+ their HTML at all — nothing to hide, so nothing flashes:
231
+
232
+ ```tsx
233
+ // app/layout.tsx — a Server Component
234
+ import { CookieYesProvider } from "@cookieyes/nextjs";
235
+ import { getServerConsent } from "@cookieyes/nextjs/server";
236
+ import { CookieYesRoot } from "./cookieyes-root";
237
+
238
+ export default async function RootLayout({ children }) {
239
+ const initialConsent = await getServerConsent({ regulation: "GDPR" });
240
+ return (
241
+ <html lang="en">
242
+ <body>
243
+ <CookieYesProvider regulation="GDPR" initialConsent={initialConsent}>
244
+ <CookieYesRoot />
245
+ </CookieYesProvider>
246
+ {children}
247
+ </body>
248
+ </html>
249
+ );
250
+ }
251
+ ```
252
+
253
+ - **Import from `@cookieyes/nextjs/server`,** not the main entry. It reads `next/headers` and is
254
+ server-only; the main entry is `"use client"`.
255
+ - **Returns `null` when the banner should show** — a first-time visitor, a cookie recording no
256
+ choice yet, a corrupt cookie, or one written against a different category taxonomy (which the
257
+ client re-requests too). Passing `null` renders exactly as before, so this is safe to add
258
+ everywhere.
259
+ - **`initialConsent` is a provider prop, never an `initCookieYes` option.** The consent runtime is
260
+ a module-level singleton shared across concurrent requests, so per-visitor state there would leak
261
+ between visitors — the same reason `region`/`regulation` go through the provider.
262
+ - Combine it with `region` from the section above; both are per-request and both belong on the
263
+ provider.
264
+ - `getServerConsent()` calls `cookies()`, which opts the route into **dynamic rendering**, as any
265
+ `cookies()` call does. On a statically rendered route there's no request to read, so the banner
266
+ is server-rendered for everyone and hidden on the client as before.
267
+ - Framework-agnostic alternative: `readServerConsent(cookieHeader, options)` from
268
+ `@cookieyes/core` takes the raw `Cookie` header, for Pages Router `getServerSideProps`,
269
+ middleware, or any other SSR setup.
270
+
271
+ ## Google Consent Mode (GA4, Ads, GTM)
272
+
273
+ Google tags need a Consent Mode **deny-by-default** set before any tag runs and
274
+ before the SDK boots — so a returning visitor's saved choice applies from first
275
+ paint. Render `<GoogleConsentMode />` high in your root layout:
276
+
277
+ ```tsx
278
+ // app/layout.tsx
279
+ import { CookieYesProvider } from "@cookieyes/nextjs";
280
+ import { GoogleConsentMode } from "@cookieyes/nextjs/server";
281
+
282
+ export default function RootLayout({ children }) {
283
+ return (
284
+ <html lang="en">
285
+ <body>
286
+ <GoogleConsentMode />
287
+ <CookieYesProvider regulation="GDPR">{children}</CookieYesProvider>
288
+ </body>
289
+ </html>
290
+ );
291
+ }
292
+ ```
293
+
294
+ Then load the tags on the client with a preset from
295
+ [`@cookieyes/scripts`](https://github.com/cookieyes/cookieyes/tree/main/sdk/scripts)
296
+ — `ga4()`, `googleAds()`, or `googleTagManager()`. The SDK broadcasts each
297
+ consent change to Google as a Consent Mode `update`; you don't wire that up.
298
+
163
299
  ## API
164
300
 
165
301
  This package re-exports the entire `@cookieyes/react` surface — the setup function
@@ -167,6 +303,13 @@ This package re-exports the entire `@cookieyes/react` surface — the setup func
167
303
  `RecallButton`, `GatedScript`, `GatedFrame`), headless primitives (`Banner`, `Preferences`,
168
304
  `OptOut`), and all hooks (`useConsent`, `useConsentActions`, …).
169
305
 
306
+ It also adds **server-only** exports on their own subpath, kept out of the `"use client"` barrel:
307
+
308
+ | Import | Export | Purpose |
309
+ |---|---|---|
310
+ | `@cookieyes/nextjs/server` | `getServerConsent(options?)` | Reads the request's cookies and returns a returning visitor's stored decision (or `null`), for `<CookieYesProvider initialConsent>` |
311
+ | `@cookieyes/nextjs/server` | `<GoogleConsentMode />` | Renders the Google Consent Mode deny-by-default into the page `<head>` (see below) |
312
+
170
313
  - Full option reference: **[Configuration](https://github.com/cookieyes/cookieyes/blob/main/docs/configuration.md)**.
171
314
  - Component/hook reference: the **[`@cookieyes/react` README](https://github.com/cookieyes/cookieyes/tree/main/sdk/react#readme)**.
172
315
 
package/dist/index.cjs CHANGED
@@ -1,3 +1,3 @@
1
1
  "use client";
2
- "use strict";var e=require("@cookieyes/react");Object.defineProperty(exports,"Banner",{enumerable:!0,get:function(){return e.Banner}}),Object.defineProperty(exports,"CY_PART",{enumerable:!0,get:function(){return e.CY_PART}}),Object.defineProperty(exports,"CY_STATE",{enumerable:!0,get:function(){return e.CY_STATE}}),Object.defineProperty(exports,"CookieBanner",{enumerable:!0,get:function(){return e.CookieBanner}}),Object.defineProperty(exports,"CookieOptOut",{enumerable:!0,get:function(){return e.CookieOptOut}}),Object.defineProperty(exports,"CookiePreferences",{enumerable:!0,get:function(){return e.CookiePreferences}}),Object.defineProperty(exports,"DEFAULT_CATEGORIES",{enumerable:!0,get:function(){return e.DEFAULT_CATEGORIES}}),Object.defineProperty(exports,"GatedFrame",{enumerable:!0,get:function(){return e.GatedFrame}}),Object.defineProperty(exports,"GatedScript",{enumerable:!0,get:function(){return e.GatedScript}}),Object.defineProperty(exports,"OptOut",{enumerable:!0,get:function(){return e.OptOut}}),Object.defineProperty(exports,"Preferences",{enumerable:!0,get:function(){return e.Preferences}}),Object.defineProperty(exports,"RecallButton",{enumerable:!0,get:function(){return e.RecallButton}}),Object.defineProperty(exports,"ReloadNotice",{enumerable:!0,get:function(){return e.ReloadNotice}}),Object.defineProperty(exports,"createCookieYes",{enumerable:!0,get:function(){return e.createCookieYes}}),Object.defineProperty(exports,"defaultTranslations",{enumerable:!0,get:function(){return e.defaultTranslations}}),Object.defineProperty(exports,"getCookieYes",{enumerable:!0,get:function(){return e.getCookieYes}}),Object.defineProperty(exports,"getOrCreateConsentRuntime",{enumerable:!0,get:function(){return e.getOrCreateConsentRuntime}}),Object.defineProperty(exports,"getTextDirection",{enumerable:!0,get:function(){return e.getTextDirection}}),Object.defineProperty(exports,"initCookieYes",{enumerable:!0,get:function(){return e.initCookieYes}}),Object.defineProperty(exports,"mergeTranslations",{enumerable:!0,get:function(){return e.mergeTranslations}}),Object.defineProperty(exports,"registerStopHandler",{enumerable:!0,get:function(){return e.registerStopHandler}}),Object.defineProperty(exports,"resetConsentRuntime",{enumerable:!0,get:function(){return e.resetConsentRuntime}}),Object.defineProperty(exports,"resetCookieYes",{enumerable:!0,get:function(){return e.resetCookieYes}}),Object.defineProperty(exports,"resolveBuiltInIntegration",{enumerable:!0,get:function(){return e.resolveBuiltInIntegration}}),Object.defineProperty(exports,"resolveCategories",{enumerable:!0,get:function(){return e.resolveCategories}}),Object.defineProperty(exports,"resolveTranslations",{enumerable:!0,get:function(){return e.resolveTranslations}}),Object.defineProperty(exports,"useBannerVisibility",{enumerable:!0,get:function(){return e.useBannerVisibility}}),Object.defineProperty(exports,"useCategories",{enumerable:!0,get:function(){return e.useCategories}}),Object.defineProperty(exports,"useConsent",{enumerable:!0,get:function(){return e.useConsent}}),Object.defineProperty(exports,"useConsentActions",{enumerable:!0,get:function(){return e.useConsentActions}}),Object.defineProperty(exports,"useConsentCategory",{enumerable:!0,get:function(){return e.useConsentCategory}}),Object.defineProperty(exports,"useConsentRuntime",{enumerable:!0,get:function(){return e.useConsentRuntime}}),Object.defineProperty(exports,"useLanguage",{enumerable:!0,get:function(){return e.useLanguage}}),Object.defineProperty(exports,"useOnConsentChange",{enumerable:!0,get:function(){return e.useOnConsentChange}}),Object.defineProperty(exports,"useOptOutOpen",{enumerable:!0,get:function(){return e.useOptOutOpen}}),Object.defineProperty(exports,"usePreferencesOpen",{enumerable:!0,get:function(){return e.usePreferencesOpen}}),Object.defineProperty(exports,"useRegulation",{enumerable:!0,get:function(){return e.useRegulation}}),Object.defineProperty(exports,"useReloadNotice",{enumerable:!0,get:function(){return e.useReloadNotice}}),Object.defineProperty(exports,"useTranslations",{enumerable:!0,get:function(){return e.useTranslations}});
2
+ "use strict";var e=require("@cookieyes/react");Object.defineProperty(exports,"Banner",{enumerable:!0,get:function(){return e.Banner}}),Object.defineProperty(exports,"CY_PART",{enumerable:!0,get:function(){return e.CY_PART}}),Object.defineProperty(exports,"CY_STATE",{enumerable:!0,get:function(){return e.CY_STATE}}),Object.defineProperty(exports,"CookieBanner",{enumerable:!0,get:function(){return e.CookieBanner}}),Object.defineProperty(exports,"CookieOptOut",{enumerable:!0,get:function(){return e.CookieOptOut}}),Object.defineProperty(exports,"CookiePreferences",{enumerable:!0,get:function(){return e.CookiePreferences}}),Object.defineProperty(exports,"CookieYesProvider",{enumerable:!0,get:function(){return e.CookieYesProvider}}),Object.defineProperty(exports,"DEFAULT_CATEGORIES",{enumerable:!0,get:function(){return e.DEFAULT_CATEGORIES}}),Object.defineProperty(exports,"GatedFrame",{enumerable:!0,get:function(){return e.GatedFrame}}),Object.defineProperty(exports,"GatedScript",{enumerable:!0,get:function(){return e.GatedScript}}),Object.defineProperty(exports,"OptOut",{enumerable:!0,get:function(){return e.OptOut}}),Object.defineProperty(exports,"Preferences",{enumerable:!0,get:function(){return e.Preferences}}),Object.defineProperty(exports,"RecallButton",{enumerable:!0,get:function(){return e.RecallButton}}),Object.defineProperty(exports,"ReloadNotice",{enumerable:!0,get:function(){return e.ReloadNotice}}),Object.defineProperty(exports,"createCookieYes",{enumerable:!0,get:function(){return e.createCookieYes}}),Object.defineProperty(exports,"defaultTranslations",{enumerable:!0,get:function(){return e.defaultTranslations}}),Object.defineProperty(exports,"getCookieYes",{enumerable:!0,get:function(){return e.getCookieYes}}),Object.defineProperty(exports,"getOrCreateConsentRuntime",{enumerable:!0,get:function(){return e.getOrCreateConsentRuntime}}),Object.defineProperty(exports,"getTextDirection",{enumerable:!0,get:function(){return e.getTextDirection}}),Object.defineProperty(exports,"initCookieYes",{enumerable:!0,get:function(){return e.initCookieYes}}),Object.defineProperty(exports,"mergeTranslations",{enumerable:!0,get:function(){return e.mergeTranslations}}),Object.defineProperty(exports,"readGpc",{enumerable:!0,get:function(){return e.readGpc}}),Object.defineProperty(exports,"readServerConsent",{enumerable:!0,get:function(){return e.readServerConsent}}),Object.defineProperty(exports,"regionFromHeaders",{enumerable:!0,get:function(){return e.regionFromHeaders}}),Object.defineProperty(exports,"registerStopHandler",{enumerable:!0,get:function(){return e.registerStopHandler}}),Object.defineProperty(exports,"resetConsentRuntime",{enumerable:!0,get:function(){return e.resetConsentRuntime}}),Object.defineProperty(exports,"resetCookieYes",{enumerable:!0,get:function(){return e.resetCookieYes}}),Object.defineProperty(exports,"resolveBuiltInIntegration",{enumerable:!0,get:function(){return e.resolveBuiltInIntegration}}),Object.defineProperty(exports,"resolveCategories",{enumerable:!0,get:function(){return e.resolveCategories}}),Object.defineProperty(exports,"resolveRegion",{enumerable:!0,get:function(){return e.resolveRegion}}),Object.defineProperty(exports,"resolveTranslations",{enumerable:!0,get:function(){return e.resolveTranslations}}),Object.defineProperty(exports,"useBannerVisibility",{enumerable:!0,get:function(){return e.useBannerVisibility}}),Object.defineProperty(exports,"useCategories",{enumerable:!0,get:function(){return e.useCategories}}),Object.defineProperty(exports,"useConsent",{enumerable:!0,get:function(){return e.useConsent}}),Object.defineProperty(exports,"useConsentActions",{enumerable:!0,get:function(){return e.useConsentActions}}),Object.defineProperty(exports,"useConsentCategory",{enumerable:!0,get:function(){return e.useConsentCategory}}),Object.defineProperty(exports,"useConsentRuntime",{enumerable:!0,get:function(){return e.useConsentRuntime}}),Object.defineProperty(exports,"useLanguage",{enumerable:!0,get:function(){return e.useLanguage}}),Object.defineProperty(exports,"useOnConsentChange",{enumerable:!0,get:function(){return e.useOnConsentChange}}),Object.defineProperty(exports,"useOptOutOpen",{enumerable:!0,get:function(){return e.useOptOutOpen}}),Object.defineProperty(exports,"usePreferencesOpen",{enumerable:!0,get:function(){return e.usePreferencesOpen}}),Object.defineProperty(exports,"useRegion",{enumerable:!0,get:function(){return e.useRegion}}),Object.defineProperty(exports,"useRegulation",{enumerable:!0,get:function(){return e.useRegulation}}),Object.defineProperty(exports,"useReloadNotice",{enumerable:!0,get:function(){return e.useReloadNotice}}),Object.defineProperty(exports,"useTranslations",{enumerable:!0,get:function(){return e.useTranslations}});
3
3
  //# sourceMappingURL=index.cjs.map
package/dist/index.d.ts CHANGED
@@ -1 +1 @@
1
- export { ActiveUI, AnyStopHandler, Banner, Builder, BuiltInIntegration, CY_PART, CY_STATE, CategoryDef, CategoryText, ColorScheme, ColorSchemePref, ConsentActions, ConsentBackend, ConsentCategory, ConsentChangePayload, ConsentConfig, ConsentEventListener, ConsentEventOptions, ConsentEventPayload, ConsentEventType, ConsentManager, ConsentPayload, ConsentRuntime, ConsentRuntimeMode, ConsentRuntimeOptions, ConsentSnapshot, ConsentStore, ConsentStoreState, CookieBanner, CookieBannerProps, CookieOptOut, CookieOptOutProps, CookiePreferences, CookiePreferencesProps, CookieYesConfig, CookieYesOfflineConfig, CookieYesRuntime, CookieYesSelfHostedConfig, CookieYesSnapshot, CyPart, CyState, DEFAULT_CATEGORIES, GatedFrame, GatedScript, GoogleConsentSignal, I18nConfig, LanguageInfo, OptOut, PartialTranslations, Preferences, RecallButton, Regulation, ReloadNotice, ReloadNoticeState, ReloadOnlyHandler, ResolvedCategories, RuntimeMode, ScriptEntry, StopHandler, TextDirection, ThemeConfig, TranslationMap, UseLanguageResult, UseReloadNoticeResult, createCookieYes, defaultTranslations, getCookieYes, getOrCreateConsentRuntime, getTextDirection, initCookieYes, mergeTranslations, registerStopHandler, resetConsentRuntime, resetCookieYes, resolveBuiltInIntegration, resolveCategories, resolveTranslations, useBannerVisibility, useCategories, useConsent, useConsentActions, useConsentCategory, useConsentRuntime, useLanguage, useOnConsentChange, useOptOutOpen, usePreferencesOpen, useRegulation, useReloadNotice, useTranslations } from '@cookieyes/react';
1
+ export { ActiveUI, AnyStopHandler, Banner, Builder, BuiltInIntegration, CY_PART, CY_STATE, CategoryDef, CategoryText, ColorScheme, ColorSchemePref, ConsentActions, ConsentBackend, ConsentCategory, ConsentChangePayload, ConsentConfig, ConsentEventListener, ConsentEventOptions, ConsentEventPayload, ConsentEventType, ConsentManager, ConsentPayload, ConsentRuntime, ConsentRuntimeMode, ConsentRuntimeOptions, ConsentSnapshot, ConsentStore, ConsentStoreState, CookieBanner, CookieBannerProps, CookieOptOut, CookieOptOutProps, CookiePreferences, CookiePreferencesProps, CookieYesConfig, CookieYesOfflineConfig, CookieYesProvider, CookieYesProviderProps, CookieYesRuntime, CookieYesSelfHostedConfig, CookieYesSnapshot, CyPart, CyState, DEFAULT_CATEGORIES, GatedFrame, GatedScript, GoogleConsentSignal, HeaderSource, I18nConfig, LanguageInfo, OptOut, PartialTranslations, Preferences, RecallButton, RegionConfig, RegionDecision, RegionDetector, RegionSource, Regulation, ReloadNotice, ReloadNoticeState, ReloadOnlyHandler, ResolvedCategories, RuntimeMode, ScriptEntry, ServerConsentOptions, StopHandler, TextDirection, ThemeConfig, TranslationMap, UseLanguageResult, UseReloadNoticeResult, createCookieYes, defaultTranslations, getCookieYes, getOrCreateConsentRuntime, getTextDirection, initCookieYes, mergeTranslations, readGpc, readServerConsent, regionFromHeaders, registerStopHandler, resetConsentRuntime, resetCookieYes, resolveBuiltInIntegration, resolveCategories, resolveRegion, resolveTranslations, useBannerVisibility, useCategories, useConsent, useConsentActions, useConsentCategory, useConsentRuntime, useLanguage, useOnConsentChange, useOptOutOpen, usePreferencesOpen, useRegion, useRegulation, useReloadNotice, useTranslations } from '@cookieyes/react';
package/dist/index.js CHANGED
@@ -1,3 +1,3 @@
1
1
  "use client";
2
- export{Banner,CY_PART,CY_STATE,CookieBanner,CookieOptOut,CookiePreferences,DEFAULT_CATEGORIES,GatedFrame,GatedScript,OptOut,Preferences,RecallButton,ReloadNotice,createCookieYes,defaultTranslations,getCookieYes,getOrCreateConsentRuntime,getTextDirection,initCookieYes,mergeTranslations,registerStopHandler,resetConsentRuntime,resetCookieYes,resolveBuiltInIntegration,resolveCategories,resolveTranslations,useBannerVisibility,useCategories,useConsent,useConsentActions,useConsentCategory,useConsentRuntime,useLanguage,useOnConsentChange,useOptOutOpen,usePreferencesOpen,useRegulation,useReloadNotice,useTranslations}from"@cookieyes/react";
2
+ export{Banner,CY_PART,CY_STATE,CookieBanner,CookieOptOut,CookiePreferences,CookieYesProvider,DEFAULT_CATEGORIES,GatedFrame,GatedScript,OptOut,Preferences,RecallButton,ReloadNotice,createCookieYes,defaultTranslations,getCookieYes,getOrCreateConsentRuntime,getTextDirection,initCookieYes,mergeTranslations,readGpc,readServerConsent,regionFromHeaders,registerStopHandler,resetConsentRuntime,resetCookieYes,resolveBuiltInIntegration,resolveCategories,resolveRegion,resolveTranslations,useBannerVisibility,useCategories,useConsent,useConsentActions,useConsentCategory,useConsentRuntime,useLanguage,useOnConsentChange,useOptOutOpen,usePreferencesOpen,useRegion,useRegulation,useReloadNotice,useTranslations}from"@cookieyes/react";
3
3
  //# sourceMappingURL=index.js.map
@@ -0,0 +1,2 @@
1
+ "use strict";var e=require("@cookieyes/core"),r=require("react/jsx-runtime"),t=require("@cookieyes/scripts");exports.GoogleConsentMode=function(e={}){return r.jsx("script",{dangerouslySetInnerHTML:{__html:t.googleConsentModeSnippet(e)}})},exports.getServerConsent=async function(r={}){const{cookies:t}=await import("next/headers"),o=(await t()).getAll().map(e=>`${e.name}=${e.value}`).join("; ");return e.readServerConsent(o,r)};
2
+ //# sourceMappingURL=server.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.cjs","sources":["../src/google-consent-mode.tsx","../src/server.ts"],"sourcesContent":["import { type ConsentModeOptions, googleConsentModeSnippet } from \"@cookieyes/scripts\";\n\n/**\n * Renders the Google Consent Mode **deny-by-default** as an inline `<script>`,\n * to be placed high in your root layout — before any Google tag and before the\n * SDK boots. This is what lets a returning visitor's saved choice (which the SDK\n * broadcasts as a Consent Mode `update`) land on top of a clean default, instead\n * of the visitor being stuck denied until they act again.\n *\n * The `<script>` is inline and synchronous, so it runs in document order before\n * the client bundle (and before `gtag.js`, which the `ga4()`/`googleAds()`\n * loaders inject). Pass it the same {@link ConsentModeOptions} as the loaders if\n * you need to change the defaults.\n *\n * ```tsx\n * // app/layout.tsx\n * import { GoogleConsentMode } from \"@cookieyes/nextjs/server\";\n *\n * export default function RootLayout({ children }) {\n * return (\n * <html lang=\"en\">\n * <body>\n * <GoogleConsentMode />\n * {children}\n * </body>\n * </html>\n * );\n * }\n * ```\n *\n * Then load the tags on the client with a preset from `@cookieyes/scripts`:\n * `initCookieYes({ integrations: [ga4({ measurementId: \"G-XXXX\" })] })`.\n */\nexport function GoogleConsentMode(props: ConsentModeOptions = {}) {\n return (\n <script\n // biome-ignore lint/security/noDangerouslySetInnerHtml: an inline Consent Mode default is the standard, documented Google pattern.\n dangerouslySetInnerHTML={{ __html: googleConsentModeSnippet(props) }}\n />\n );\n}\n","import {\n type ConsentSnapshot,\n readServerConsent,\n type ServerConsentOptions,\n} from \"@cookieyes/core\";\n\nexport { GoogleConsentMode } from \"./google-consent-mode.js\";\n\n/**\n * Read a returning visitor's stored consent from the incoming request, in a\n * Server Component, Route Handler or middleware.\n *\n * Pass the result to `<CookieYesProvider initialConsent={…}>` and the banner is\n * never sent to a visitor who has already chosen — instead of being sent to\n * everyone and removed on the client, which the visitor sees as the banner\n * appearing and then vanishing.\n *\n * ```tsx\n * // app/layout.tsx\n * import { CookieYesProvider } from \"@cookieyes/nextjs\";\n * import { getServerConsent } from \"@cookieyes/nextjs/server\";\n *\n * export default async function RootLayout({ children }) {\n * const initialConsent = await getServerConsent({ regulation: \"GDPR\" });\n * return (\n * <html lang=\"en\">\n * <body>\n * <CookieYesProvider regulation=\"GDPR\" initialConsent={initialConsent}>\n * {children}\n * </CookieYesProvider>\n * </body>\n * </html>\n * );\n * }\n * ```\n *\n * Returns `null` when there is no decision on record — a first-time visitor, a\n * cookie recording no choice yet, or one written against a different category\n * taxonomy — in which case the banner renders as usual.\n *\n * This module is server-only: it imports `next/headers`, so keep it out of client\n * components. It lives in `@cookieyes/nextjs/server` rather than the main entry\n * for exactly that reason — the main entry is `\"use client\"`.\n *\n * Reading cookies opts the route into dynamic rendering, as any `cookies()` call\n * does. A statically rendered route has no request to read, so there the banner\n * is server-rendered for everyone and hidden on the client as before.\n */\nexport async function getServerConsent(\n options: ServerConsentOptions = {},\n): Promise<ConsentSnapshot | null> {\n // Imported lazily so merely importing this module doesn't pull `next/headers`\n // into a build that never calls it.\n const { cookies } = await import(\"next/headers\");\n const store = await cookies();\n const header = store\n .getAll()\n .map((c) => `${c.name}=${c.value}`)\n .join(\"; \");\n return readServerConsent(header, options);\n}\n"],"names":["props","jsx","dangerouslySetInnerHTML","__html","googleConsentModeSnippet","async","options","cookies","import","header","getAll","map","c","name","value","join","readServerConsent"],"mappings":"uIAiCO,SAA2BA,EAA4B,IAC5D,OACEC,EAAAA,IAAC,SAAA,CAECC,wBAAyB,CAAEC,OAAQC,EAAAA,yBAAyBJ,KAGlE,2BCQAK,eACEC,EAAgC,IAIhC,MAAMC,QAAEA,SAAkBC,OAAO,gBAE3BC,SADcF,KAEjBG,SACAC,IAAKC,GAAM,GAAGA,EAAEC,QAAQD,EAAEE,SAC1BC,KAAK,MACR,OAAOC,EAAAA,kBAAkBP,EAAQH,EACnC"}
@@ -0,0 +1,80 @@
1
+ import { ServerConsentOptions, ConsentSnapshot } from '@cookieyes/core';
2
+ import * as react from 'react';
3
+ import { ConsentModeOptions } from '@cookieyes/scripts';
4
+
5
+ /**
6
+ * Renders the Google Consent Mode **deny-by-default** as an inline `<script>`,
7
+ * to be placed high in your root layout — before any Google tag and before the
8
+ * SDK boots. This is what lets a returning visitor's saved choice (which the SDK
9
+ * broadcasts as a Consent Mode `update`) land on top of a clean default, instead
10
+ * of the visitor being stuck denied until they act again.
11
+ *
12
+ * The `<script>` is inline and synchronous, so it runs in document order before
13
+ * the client bundle (and before `gtag.js`, which the `ga4()`/`googleAds()`
14
+ * loaders inject). Pass it the same {@link ConsentModeOptions} as the loaders if
15
+ * you need to change the defaults.
16
+ *
17
+ * ```tsx
18
+ * // app/layout.tsx
19
+ * import { GoogleConsentMode } from "@cookieyes/nextjs/server";
20
+ *
21
+ * export default function RootLayout({ children }) {
22
+ * return (
23
+ * <html lang="en">
24
+ * <body>
25
+ * <GoogleConsentMode />
26
+ * {children}
27
+ * </body>
28
+ * </html>
29
+ * );
30
+ * }
31
+ * ```
32
+ *
33
+ * Then load the tags on the client with a preset from `@cookieyes/scripts`:
34
+ * `initCookieYes({ integrations: [ga4({ measurementId: "G-XXXX" })] })`.
35
+ */
36
+ declare function GoogleConsentMode(props?: ConsentModeOptions): react.JSX.Element;
37
+
38
+ /**
39
+ * Read a returning visitor's stored consent from the incoming request, in a
40
+ * Server Component, Route Handler or middleware.
41
+ *
42
+ * Pass the result to `<CookieYesProvider initialConsent={…}>` and the banner is
43
+ * never sent to a visitor who has already chosen — instead of being sent to
44
+ * everyone and removed on the client, which the visitor sees as the banner
45
+ * appearing and then vanishing.
46
+ *
47
+ * ```tsx
48
+ * // app/layout.tsx
49
+ * import { CookieYesProvider } from "@cookieyes/nextjs";
50
+ * import { getServerConsent } from "@cookieyes/nextjs/server";
51
+ *
52
+ * export default async function RootLayout({ children }) {
53
+ * const initialConsent = await getServerConsent({ regulation: "GDPR" });
54
+ * return (
55
+ * <html lang="en">
56
+ * <body>
57
+ * <CookieYesProvider regulation="GDPR" initialConsent={initialConsent}>
58
+ * {children}
59
+ * </CookieYesProvider>
60
+ * </body>
61
+ * </html>
62
+ * );
63
+ * }
64
+ * ```
65
+ *
66
+ * Returns `null` when there is no decision on record — a first-time visitor, a
67
+ * cookie recording no choice yet, or one written against a different category
68
+ * taxonomy — in which case the banner renders as usual.
69
+ *
70
+ * This module is server-only: it imports `next/headers`, so keep it out of client
71
+ * components. It lives in `@cookieyes/nextjs/server` rather than the main entry
72
+ * for exactly that reason — the main entry is `"use client"`.
73
+ *
74
+ * Reading cookies opts the route into dynamic rendering, as any `cookies()` call
75
+ * does. A statically rendered route has no request to read, so there the banner
76
+ * is server-rendered for everyone and hidden on the client as before.
77
+ */
78
+ declare function getServerConsent(options?: ServerConsentOptions): Promise<ConsentSnapshot | null>;
79
+
80
+ export { GoogleConsentMode, getServerConsent };
package/dist/server.js ADDED
@@ -0,0 +1,2 @@
1
+ import{readServerConsent as e}from"@cookieyes/core";import{jsx as o}from"react/jsx-runtime";import{googleConsentModeSnippet as r}from"@cookieyes/scripts";function t(e={}){return o("script",{dangerouslySetInnerHTML:{__html:r(e)}})}async function i(o={}){const{cookies:r}=await import("next/headers"),t=(await r()).getAll().map(e=>`${e.name}=${e.value}`).join("; ");return e(t,o)}export{t as GoogleConsentMode,i as getServerConsent};
2
+ //# sourceMappingURL=server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.js","sources":["../src/google-consent-mode.tsx","../src/server.ts"],"sourcesContent":["import { type ConsentModeOptions, googleConsentModeSnippet } from \"@cookieyes/scripts\";\n\n/**\n * Renders the Google Consent Mode **deny-by-default** as an inline `<script>`,\n * to be placed high in your root layout — before any Google tag and before the\n * SDK boots. This is what lets a returning visitor's saved choice (which the SDK\n * broadcasts as a Consent Mode `update`) land on top of a clean default, instead\n * of the visitor being stuck denied until they act again.\n *\n * The `<script>` is inline and synchronous, so it runs in document order before\n * the client bundle (and before `gtag.js`, which the `ga4()`/`googleAds()`\n * loaders inject). Pass it the same {@link ConsentModeOptions} as the loaders if\n * you need to change the defaults.\n *\n * ```tsx\n * // app/layout.tsx\n * import { GoogleConsentMode } from \"@cookieyes/nextjs/server\";\n *\n * export default function RootLayout({ children }) {\n * return (\n * <html lang=\"en\">\n * <body>\n * <GoogleConsentMode />\n * {children}\n * </body>\n * </html>\n * );\n * }\n * ```\n *\n * Then load the tags on the client with a preset from `@cookieyes/scripts`:\n * `initCookieYes({ integrations: [ga4({ measurementId: \"G-XXXX\" })] })`.\n */\nexport function GoogleConsentMode(props: ConsentModeOptions = {}) {\n return (\n <script\n // biome-ignore lint/security/noDangerouslySetInnerHtml: an inline Consent Mode default is the standard, documented Google pattern.\n dangerouslySetInnerHTML={{ __html: googleConsentModeSnippet(props) }}\n />\n );\n}\n","import {\n type ConsentSnapshot,\n readServerConsent,\n type ServerConsentOptions,\n} from \"@cookieyes/core\";\n\nexport { GoogleConsentMode } from \"./google-consent-mode.js\";\n\n/**\n * Read a returning visitor's stored consent from the incoming request, in a\n * Server Component, Route Handler or middleware.\n *\n * Pass the result to `<CookieYesProvider initialConsent={…}>` and the banner is\n * never sent to a visitor who has already chosen — instead of being sent to\n * everyone and removed on the client, which the visitor sees as the banner\n * appearing and then vanishing.\n *\n * ```tsx\n * // app/layout.tsx\n * import { CookieYesProvider } from \"@cookieyes/nextjs\";\n * import { getServerConsent } from \"@cookieyes/nextjs/server\";\n *\n * export default async function RootLayout({ children }) {\n * const initialConsent = await getServerConsent({ regulation: \"GDPR\" });\n * return (\n * <html lang=\"en\">\n * <body>\n * <CookieYesProvider regulation=\"GDPR\" initialConsent={initialConsent}>\n * {children}\n * </CookieYesProvider>\n * </body>\n * </html>\n * );\n * }\n * ```\n *\n * Returns `null` when there is no decision on record — a first-time visitor, a\n * cookie recording no choice yet, or one written against a different category\n * taxonomy — in which case the banner renders as usual.\n *\n * This module is server-only: it imports `next/headers`, so keep it out of client\n * components. It lives in `@cookieyes/nextjs/server` rather than the main entry\n * for exactly that reason — the main entry is `\"use client\"`.\n *\n * Reading cookies opts the route into dynamic rendering, as any `cookies()` call\n * does. A statically rendered route has no request to read, so there the banner\n * is server-rendered for everyone and hidden on the client as before.\n */\nexport async function getServerConsent(\n options: ServerConsentOptions = {},\n): Promise<ConsentSnapshot | null> {\n // Imported lazily so merely importing this module doesn't pull `next/headers`\n // into a build that never calls it.\n const { cookies } = await import(\"next/headers\");\n const store = await cookies();\n const header = store\n .getAll()\n .map((c) => `${c.name}=${c.value}`)\n .join(\"; \");\n return readServerConsent(header, options);\n}\n"],"names":["GoogleConsentMode","props","jsx","dangerouslySetInnerHTML","__html","googleConsentModeSnippet","async","getServerConsent","options","cookies","import","header","getAll","map","c","name","value","join","readServerConsent"],"mappings":"0JAiCO,SAASA,EAAkBC,EAA4B,IAC5D,OACEC,EAAC,SAAA,CAECC,wBAAyB,CAAEC,OAAQC,EAAyBJ,KAGlE,CCQAK,eAAsBC,EACpBC,EAAgC,IAIhC,MAAMC,QAAEA,SAAkBC,OAAO,gBAE3BC,SADcF,KAEjBG,SACAC,IAAKC,GAAM,GAAGA,EAAEC,QAAQD,EAAEE,SAC1BC,KAAK,MACR,OAAOC,EAAkBP,EAAQH,EACnC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cookieyes/nextjs",
3
- "version": "0.4.0",
3
+ "version": "0.5.0",
4
4
  "description": "Next.js App Router adapter for the CookieYes consent SDK",
5
5
  "keywords": [
6
6
  "cookie consent",
@@ -33,6 +33,11 @@
33
33
  "types": "./dist/index.d.ts",
34
34
  "import": "./dist/index.js",
35
35
  "require": "./dist/index.cjs"
36
+ },
37
+ "./server": {
38
+ "types": "./dist/server.d.ts",
39
+ "import": "./dist/server.js",
40
+ "require": "./dist/server.cjs"
36
41
  }
37
42
  },
38
43
  "main": "./dist/index.cjs",
@@ -56,8 +61,9 @@
56
61
  "react-dom": ">=18.0.0"
57
62
  },
58
63
  "dependencies": {
59
- "@cookieyes/core": "0.3.0",
60
- "@cookieyes/react": "0.4.0"
64
+ "@cookieyes/core": "0.4.0",
65
+ "@cookieyes/react": "0.5.0",
66
+ "@cookieyes/scripts": "0.1.0"
61
67
  },
62
68
  "devDependencies": {
63
69
  "@types/node": "^26.1.1",