cloudflare-next-intl 0.4.0 → 0.4.2
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 +10 -0
- package/dist/src/client/components/client_provider.d.ts +8 -1
- package/dist/src/client/components/client_provider.js +2 -2
- package/dist/src/cookie_consent/client/cookie_consent_provider.d.ts +10 -1
- package/dist/src/cookie_consent/client/cookie_consent_provider.js +13 -1
- package/dist/src/cookie_consent/gdpr_countries.bench.d.ts +1 -0
- package/dist/src/cookie_consent/gdpr_countries.bench.js +37 -0
- package/dist/src/cookie_consent/gdpr_countries.d.ts +18 -0
- package/dist/src/cookie_consent/gdpr_countries.js +52 -0
- package/dist/src/cookie_consent/index.d.ts +2 -1
- package/dist/src/cookie_consent/index.js +1 -0
- package/dist/src/server/components/server_provider.js +12 -5
- package/dist/src/types/index.d.ts +1 -1
- package/dist/src/types/types.d.ts +72 -0
- package/llms.txt +2 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -193,11 +193,21 @@ auto-wires `CookieConsentProvider` (and `CookieConsentAnalytics`, if
|
|
|
193
193
|
|
|
194
194
|
```typescript
|
|
195
195
|
// intl-config.ts
|
|
196
|
+
import { getCloudflareContext } from "@opennextjs/cloudflare";
|
|
197
|
+
|
|
196
198
|
export default setIntlConfig({
|
|
197
199
|
locales: ["en", "de"],
|
|
198
200
|
defaultLocale: "en",
|
|
199
201
|
cookieConsent: {
|
|
200
202
|
privacyPolicyDate: "2026-01-01",
|
|
203
|
+
// Optional: gate the banner to GDPR-region visitors only. Omit both
|
|
204
|
+
// getters to disable country-based gating (consent always implicit).
|
|
205
|
+
// Pass @opennextjs/cloudflare's getCloudflareContext directly — its
|
|
206
|
+
// exact overloaded signature is accepted as-is, called internally
|
|
207
|
+
// with { async: true }.
|
|
208
|
+
getCloudflareContext,
|
|
209
|
+
// gdprCountries: [...], // defaults to EU/EEA + UK + Switzerland
|
|
210
|
+
// enableAnalyticsInDevMode: true, // analytics stay off in dev otherwise
|
|
201
211
|
},
|
|
202
212
|
});
|
|
203
213
|
```
|
|
@@ -6,7 +6,7 @@ interface LocaleContextType {
|
|
|
6
6
|
messages: TranslationObject;
|
|
7
7
|
}
|
|
8
8
|
export declare const LocaleContext: import("react").Context<LocaleContextType | undefined>;
|
|
9
|
-
export default function LocationzationClientProvider({ language, messages, initialAuthUser, skipAuthProvider, analyticsSecrets, children }: {
|
|
9
|
+
export default function LocationzationClientProvider({ language, messages, initialAuthUser, skipAuthProvider, analyticsSecrets, requiresConsent, children }: {
|
|
10
10
|
language: string;
|
|
11
11
|
messages: TranslationObject;
|
|
12
12
|
initialAuthUser?: SerializedAuthUser | null;
|
|
@@ -14,6 +14,13 @@ export default function LocationzationClientProvider({ language, messages, initi
|
|
|
14
14
|
skipAuthProvider?: boolean;
|
|
15
15
|
/** Resolved server-side from `cookieConsent.secrets`/`getSecrets` when `autoWireAnalytics` isn't `false`. */
|
|
16
16
|
analyticsSecrets?: CookieConsentAnalyticsSecrets;
|
|
17
|
+
/**
|
|
18
|
+
* Resolved server-side from `cookieConsent.getCountryCode`/`gdprCountries`.
|
|
19
|
+
* `false` means the visitor's country doesn't require the consent
|
|
20
|
+
* banner — `CookieConsentProvider` seeds consent as implicitly granted
|
|
21
|
+
* for a first-time visitor instead of `null`.
|
|
22
|
+
*/
|
|
23
|
+
requiresConsent?: boolean;
|
|
17
24
|
children: React.ReactNode;
|
|
18
25
|
}): Component;
|
|
19
26
|
export {};
|
|
@@ -15,7 +15,7 @@ export const LocaleContext = createContext(undefined);
|
|
|
15
15
|
const AuthUserProvider = dynamic(() => import("../../firebase_auth/client/auth_user_provider"));
|
|
16
16
|
const CookieConsentProvider = dynamic(() => import("../../cookie_consent/client/cookie_consent_provider"));
|
|
17
17
|
const CookieConsentAnalytics = dynamic(() => import("../../cookie_consent/client/components/cookie_consent_analytics"));
|
|
18
|
-
export default function LocationzationClientProvider({ language, messages, initialAuthUser = null, skipAuthProvider = false, analyticsSecrets, children }) {
|
|
18
|
+
export default function LocationzationClientProvider({ language, messages, initialAuthUser = null, skipAuthProvider = false, analyticsSecrets, requiresConsent = true, children }) {
|
|
19
19
|
setLocaleCache(language);
|
|
20
20
|
setMessageForLocaleCache(language, messages);
|
|
21
21
|
// `LocaleContext.Provider` stays the outermost element here — the
|
|
@@ -28,7 +28,7 @@ export default function LocationzationClientProvider({ language, messages, initi
|
|
|
28
28
|
providedChildren = _jsx(AuthUserProvider, { initialUser: initialAuthUser, children: children });
|
|
29
29
|
}
|
|
30
30
|
if (config.cookieConsent) {
|
|
31
|
-
providedChildren = _jsxs(CookieConsentProvider, { children: [providedChildren, analyticsSecrets && _jsx(CookieConsentAnalytics, { secrets: analyticsSecrets })] });
|
|
31
|
+
providedChildren = _jsxs(CookieConsentProvider, { requiresConsent: requiresConsent, children: [providedChildren, analyticsSecrets && _jsx(CookieConsentAnalytics, { secrets: analyticsSecrets })] });
|
|
32
32
|
}
|
|
33
33
|
const contextValue = useMemo(() => ({ language, messages }), [language, messages]);
|
|
34
34
|
return _jsx(LocaleContext.Provider, { value: contextValue, children: providedChildren });
|
|
@@ -13,6 +13,14 @@ export declare const CookieConsentContext: import("react").Context<CookieConsent
|
|
|
13
13
|
* `privacyPolicyUpdated` becomes `true` until they call
|
|
14
14
|
* `acknowledgePrivacyPolicyUpdate()`.
|
|
15
15
|
*
|
|
16
|
+
* @param requiresConsent Resolved server-side from
|
|
17
|
+
* `cookieConsent.getCountryCode`/`gdprCountries` — `false` means the
|
|
18
|
+
* visitor's country doesn't require the banner at all, so a first-time
|
|
19
|
+
* visitor (no stored cookie) gets `consent` seeded to `true` instead of
|
|
20
|
+
* `null`, skipping the dialog and unlocking analytics immediately.
|
|
21
|
+
* Defaults to `true` (always show the banner) when omitted, e.g. when
|
|
22
|
+
* `cookieConsent.getCountryCode` isn't configured.
|
|
23
|
+
*
|
|
16
24
|
* @example
|
|
17
25
|
* ```tsx
|
|
18
26
|
* <CookieConsentProvider>
|
|
@@ -22,6 +30,7 @@ export declare const CookieConsentContext: import("react").Context<CookieConsent
|
|
|
22
30
|
* </CookieConsentProvider>
|
|
23
31
|
* ```
|
|
24
32
|
*/
|
|
25
|
-
export default function CookieConsentProvider({ children }: {
|
|
33
|
+
export default function CookieConsentProvider({ requiresConsent, children }: {
|
|
34
|
+
requiresConsent?: boolean;
|
|
26
35
|
children: React.ReactNode;
|
|
27
36
|
}): React.ReactElement;
|
|
@@ -27,6 +27,14 @@ function parseConsent(raw) {
|
|
|
27
27
|
* `privacyPolicyUpdated` becomes `true` until they call
|
|
28
28
|
* `acknowledgePrivacyPolicyUpdate()`.
|
|
29
29
|
*
|
|
30
|
+
* @param requiresConsent Resolved server-side from
|
|
31
|
+
* `cookieConsent.getCountryCode`/`gdprCountries` — `false` means the
|
|
32
|
+
* visitor's country doesn't require the banner at all, so a first-time
|
|
33
|
+
* visitor (no stored cookie) gets `consent` seeded to `true` instead of
|
|
34
|
+
* `null`, skipping the dialog and unlocking analytics immediately.
|
|
35
|
+
* Defaults to `true` (always show the banner) when omitted, e.g. when
|
|
36
|
+
* `cookieConsent.getCountryCode` isn't configured.
|
|
37
|
+
*
|
|
30
38
|
* @example
|
|
31
39
|
* ```tsx
|
|
32
40
|
* <CookieConsentProvider>
|
|
@@ -36,7 +44,7 @@ function parseConsent(raw) {
|
|
|
36
44
|
* </CookieConsentProvider>
|
|
37
45
|
* ```
|
|
38
46
|
*/
|
|
39
|
-
export default function CookieConsentProvider({ children }) {
|
|
47
|
+
export default function CookieConsentProvider({ requiresConsent = true, children }) {
|
|
40
48
|
const { consentCookieName, dateCookieName, maxAge, policyDate } = useMemo(() => {
|
|
41
49
|
const cc = requireCookieConsentConfig(config.cookieConsent);
|
|
42
50
|
return {
|
|
@@ -51,6 +59,10 @@ export default function CookieConsentProvider({ children }) {
|
|
|
51
59
|
const [privacyPolicyUpdated, setPrivacyPolicyUpdated] = useState(false);
|
|
52
60
|
useEffect(() => {
|
|
53
61
|
const storedConsent = parseConsent(getCookie(consentCookieName));
|
|
62
|
+
if (storedConsent === null && !requiresConsent) {
|
|
63
|
+
setConsentState(true);
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
54
66
|
setConsentState(storedConsent);
|
|
55
67
|
if (storedConsent === null || !policyDate)
|
|
56
68
|
return;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { bench, describe } from 'vitest';
|
|
2
|
+
import resolveRequiresConsent, { defaultGdprCountries } from './gdpr_countries';
|
|
3
|
+
const fakeGetCloudflareContext = ((options) => {
|
|
4
|
+
const context = { cf: { country: 'DE' } };
|
|
5
|
+
return options?.async === false ? context : Promise.resolve(context);
|
|
6
|
+
});
|
|
7
|
+
const customList = ['US', 'CA', 'MX'];
|
|
8
|
+
describe('resolveRequiresConsent: default GDPR list lookup', () => {
|
|
9
|
+
bench('country inside the list (worst case for Array.includes: near the end)', async () => {
|
|
10
|
+
await resolveRequiresConsent(() => 'CH', undefined, undefined);
|
|
11
|
+
});
|
|
12
|
+
bench('country outside the list', async () => {
|
|
13
|
+
await resolveRequiresConsent(() => 'US', undefined, undefined);
|
|
14
|
+
});
|
|
15
|
+
});
|
|
16
|
+
describe('resolveRequiresConsent: custom GDPR list lookup (cached Set)', () => {
|
|
17
|
+
bench('repeated calls with the same list reference', async () => {
|
|
18
|
+
await resolveRequiresConsent(() => 'US', undefined, customList);
|
|
19
|
+
});
|
|
20
|
+
});
|
|
21
|
+
describe('resolveRequiresConsent: gating disabled', () => {
|
|
22
|
+
bench('neither getter set (fast path, no lookup at all)', async () => {
|
|
23
|
+
await resolveRequiresConsent(undefined, undefined, undefined);
|
|
24
|
+
});
|
|
25
|
+
});
|
|
26
|
+
describe('resolveRequiresConsent: getCloudflareContext path', () => {
|
|
27
|
+
bench('resolves cf.country from an async context getter', async () => {
|
|
28
|
+
await resolveRequiresConsent(undefined, fakeGetCloudflareContext, undefined);
|
|
29
|
+
});
|
|
30
|
+
});
|
|
31
|
+
// Sanity check the list itself isn't accidentally growing unbounded — a
|
|
32
|
+
// regression here would also regress the Set-build cost on first use.
|
|
33
|
+
describe('defaultGdprCountries', () => {
|
|
34
|
+
bench('Set construction cost (paid once per process, not per request)', () => {
|
|
35
|
+
new Set(defaultGdprCountries);
|
|
36
|
+
});
|
|
37
|
+
});
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { CookieConsentGetCloudflareContext } from '../types/types';
|
|
2
|
+
/**
|
|
3
|
+
* Default `cookieConsent.gdprCountries` — EU/EEA member states (GDPR),
|
|
4
|
+
* Iceland/Liechtenstein/Norway (EEA), the UK (UK-GDPR), and Switzerland
|
|
5
|
+
* (nFADP). ISO 3166-1 alpha-2.
|
|
6
|
+
*/
|
|
7
|
+
export declare const defaultGdprCountries: readonly string[];
|
|
8
|
+
/**
|
|
9
|
+
* Resolves whether the cookie-consent banner is required for a visitor.
|
|
10
|
+
*
|
|
11
|
+
* - Neither getter set: country-based gating is off entirely — consent is
|
|
12
|
+
* never required (the simplest opt-in-by-default setup).
|
|
13
|
+
* - Either getter set: fail-safe — a country that couldn't be resolved
|
|
14
|
+
* still requires consent; only a resolved country OUTSIDE
|
|
15
|
+
* `gdprCountries` skips the banner. `getCountryCode` takes precedence
|
|
16
|
+
* over `getCloudflareContext` when both are set.
|
|
17
|
+
*/
|
|
18
|
+
export default function resolveRequiresConsent(getCountryCode: (() => string | undefined | Promise<string | undefined>) | undefined, getCloudflareContext: CookieConsentGetCloudflareContext | undefined, gdprCountries: readonly string[] | undefined): Promise<boolean>;
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Default `cookieConsent.gdprCountries` — EU/EEA member states (GDPR),
|
|
3
|
+
* Iceland/Liechtenstein/Norway (EEA), the UK (UK-GDPR), and Switzerland
|
|
4
|
+
* (nFADP). ISO 3166-1 alpha-2.
|
|
5
|
+
*/
|
|
6
|
+
export const defaultGdprCountries = [
|
|
7
|
+
'AT', 'BE', 'BG', 'CY', 'CZ', 'DE', 'DK', 'EE', 'ES', 'FI', 'FR',
|
|
8
|
+
'GR', 'HR', 'HU', 'IE', 'IT', 'LT', 'LU', 'LV', 'MT', 'NL', 'PL',
|
|
9
|
+
'PT', 'RO', 'SE', 'SI', 'SK',
|
|
10
|
+
'IS', 'LI', 'NO',
|
|
11
|
+
'GB',
|
|
12
|
+
'CH',
|
|
13
|
+
];
|
|
14
|
+
// `Set.has()` is O(1) vs `Array.includes()`'s O(n) — this runs on every
|
|
15
|
+
// request that has country-based gating enabled, so the lookup cost matters.
|
|
16
|
+
// Mirrors this package's `localesSet` convention in `config/middleware.ts`.
|
|
17
|
+
const defaultGdprCountriesSet = new Set(defaultGdprCountries);
|
|
18
|
+
// Custom `gdprCountries` lists are typically static config passed at
|
|
19
|
+
// `setIntlConfig` call time (module-scope, stable reference) — caching one
|
|
20
|
+
// Set per distinct array reference avoids rebuilding it on every request
|
|
21
|
+
// while still supporting a caller that legitimately swaps the array.
|
|
22
|
+
const customGdprCountriesSetCache = new WeakMap();
|
|
23
|
+
function getGdprCountriesSet(gdprCountries) {
|
|
24
|
+
if (!gdprCountries)
|
|
25
|
+
return defaultGdprCountriesSet;
|
|
26
|
+
let set = customGdprCountriesSetCache.get(gdprCountries);
|
|
27
|
+
if (!set) {
|
|
28
|
+
set = new Set(gdprCountries);
|
|
29
|
+
customGdprCountriesSetCache.set(gdprCountries, set);
|
|
30
|
+
}
|
|
31
|
+
return set;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Resolves whether the cookie-consent banner is required for a visitor.
|
|
35
|
+
*
|
|
36
|
+
* - Neither getter set: country-based gating is off entirely — consent is
|
|
37
|
+
* never required (the simplest opt-in-by-default setup).
|
|
38
|
+
* - Either getter set: fail-safe — a country that couldn't be resolved
|
|
39
|
+
* still requires consent; only a resolved country OUTSIDE
|
|
40
|
+
* `gdprCountries` skips the banner. `getCountryCode` takes precedence
|
|
41
|
+
* over `getCloudflareContext` when both are set.
|
|
42
|
+
*/
|
|
43
|
+
export default async function resolveRequiresConsent(getCountryCode, getCloudflareContext, gdprCountries) {
|
|
44
|
+
if (!getCountryCode && !getCloudflareContext)
|
|
45
|
+
return false;
|
|
46
|
+
const countryCode = getCountryCode
|
|
47
|
+
? await getCountryCode()
|
|
48
|
+
: (await getCloudflareContext({ async: true }))?.cf?.country;
|
|
49
|
+
if (!countryCode)
|
|
50
|
+
return true;
|
|
51
|
+
return getGdprCountriesSet(gdprCountries).has(countryCode);
|
|
52
|
+
}
|
|
@@ -3,5 +3,6 @@ export { default as useCookieConsent } from './client/use_cookie_consent';
|
|
|
3
3
|
export { default as CookieConsentDialog } from './client/components/cookie_consent_dialog';
|
|
4
4
|
export { default as PrivacyPolicyUpdateDialog } from './client/components/privacy_policy_update_dialog';
|
|
5
5
|
export { default as CookieConsentAnalytics } from './client/components/cookie_consent_analytics';
|
|
6
|
+
export { defaultGdprCountries } from './gdpr_countries';
|
|
6
7
|
export type { CookieConsentContextType, ConsentValue, CookieDialogClassNames, CookieDialogStyles } from './types';
|
|
7
|
-
export type { CookieConsentRoutingConfig, CookieConsentAnalyticsSecrets } from '../types/types';
|
|
8
|
+
export type { CookieConsentRoutingConfig, CookieConsentAnalyticsSecrets, CookieConsentCloudflareContext, CookieConsentGetCloudflareContext, } from '../types/types';
|
|
@@ -3,3 +3,4 @@ export { default as useCookieConsent } from './client/use_cookie_consent';
|
|
|
3
3
|
export { default as CookieConsentDialog } from './client/components/cookie_consent_dialog';
|
|
4
4
|
export { default as PrivacyPolicyUpdateDialog } from './client/components/privacy_policy_update_dialog';
|
|
5
5
|
export { default as CookieConsentAnalytics } from './client/components/cookie_consent_analytics';
|
|
6
|
+
export { defaultGdprCountries } from './gdpr_countries';
|
|
@@ -4,6 +4,7 @@ import { getMessage } from "../functions/server";
|
|
|
4
4
|
import dynamic from "next/dynamic";
|
|
5
5
|
import { localesSet } from "../../config/middleware";
|
|
6
6
|
import config from "../../config/intl_config";
|
|
7
|
+
import resolveRequiresConsent from "../../cookie_consent/gdpr_countries";
|
|
7
8
|
const LocationzationClientProvider = dynamic(() => import("../../client/components/client_provider"));
|
|
8
9
|
let authUserServerProviderModule;
|
|
9
10
|
/**
|
|
@@ -56,10 +57,16 @@ export default async function LocationzationProvider({ language, messages, child
|
|
|
56
57
|
initialAuthUser = await authUserServerProviderModule.resolveAuthUserAndRedirect();
|
|
57
58
|
}
|
|
58
59
|
let analyticsSecrets;
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
60
|
+
let requiresConsent = true;
|
|
61
|
+
if (config.cookieConsent) {
|
|
62
|
+
requiresConsent = await resolveRequiresConsent(config.cookieConsent.getCountryCode, config.cookieConsent.getCloudflareContext, config.cookieConsent.gdprCountries);
|
|
63
|
+
const isDevEnvironment = process.env.NODE_ENV === 'development';
|
|
64
|
+
const analyticsAllowedInEnv = config.cookieConsent.enableAnalyticsInDevMode === true || !isDevEnvironment;
|
|
65
|
+
if (config.cookieConsent.autoWireAnalytics !== false && analyticsAllowedInEnv) {
|
|
66
|
+
analyticsSecrets = config.cookieConsent.getSecrets
|
|
67
|
+
? await config.cookieConsent.getSecrets()
|
|
68
|
+
: config.cookieConsent.secrets;
|
|
69
|
+
}
|
|
63
70
|
}
|
|
64
|
-
return _jsx(LocationzationClientProvider, { language: language, messages: messagesValue, initialAuthUser: initialAuthUser, skipAuthProvider: !autoWireClientProvider, analyticsSecrets: analyticsSecrets, children: children });
|
|
71
|
+
return _jsx(LocationzationClientProvider, { language: language, messages: messagesValue, initialAuthUser: initialAuthUser, skipAuthProvider: !autoWireClientProvider, analyticsSecrets: analyticsSecrets, requiresConsent: requiresConsent, children: children });
|
|
65
72
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export type { CookieAttributes, LocalePrefixMode, Locales, ReturnType, RoutingConfig, TranslationEntry, TranslationObject, TranslatorReturnType, Alternates, changeFrequency, IntlSitemap, CookieConsentRoutingConfig, CookieConsentAnalyticsSecrets, } from './types';
|
|
1
|
+
export type { CookieAttributes, LocalePrefixMode, Locales, ReturnType, RoutingConfig, TranslationEntry, TranslationObject, TranslatorReturnType, Alternates, changeFrequency, IntlSitemap, CookieConsentRoutingConfig, CookieConsentAnalyticsSecrets, CookieConsentCloudflareContext, CookieConsentGetCloudflareContext, } from './types';
|
|
@@ -128,6 +128,78 @@ export interface CookieConsentRoutingConfig {
|
|
|
128
128
|
* `undefined` in the returned object disables that provider's script.
|
|
129
129
|
*/
|
|
130
130
|
getSecrets?: () => CookieConsentAnalyticsSecrets | Promise<CookieConsentAnalyticsSecrets>;
|
|
131
|
+
/**
|
|
132
|
+
* Resolves the visitor's country code directly (ISO 3166-1 alpha-2,
|
|
133
|
+
* e.g. `"DE"`) — the simplest option when you already have it from
|
|
134
|
+
* somewhere (a header, a KV lookup, your own logic). Takes precedence
|
|
135
|
+
* over `getCloudflareContext` when both are set.
|
|
136
|
+
*/
|
|
137
|
+
getCountryCode?: () => string | undefined | Promise<string | undefined>;
|
|
138
|
+
/**
|
|
139
|
+
* Pass `getCloudflareContext` from `@opennextjs/cloudflare` directly
|
|
140
|
+
* (not a dependency of this package, so bring your own import) — its
|
|
141
|
+
* exact overloaded signature is accepted as-is; called internally with
|
|
142
|
+
* `{ async: true }`, so you never need to wrap it yourself. Only
|
|
143
|
+
* `cf.country` is read from the resolved context. Ignored when
|
|
144
|
+
* `getCountryCode` is also set.
|
|
145
|
+
*
|
|
146
|
+
* Country-based gating (via either `getCountryCode` or
|
|
147
|
+
* `getCloudflareContext`) decides whether the cookie-consent banner is
|
|
148
|
+
* required at all: visitors outside `gdprCountries` skip the banner and
|
|
149
|
+
* get analytics immediately (still gated by `enableAnalyticsInDevMode`).
|
|
150
|
+
* Omit BOTH to skip country-based gating entirely — the banner is never
|
|
151
|
+
* shown and consent is treated as implicitly granted for everyone. This
|
|
152
|
+
* is the simplest opt-in-by-default setup; set one of the two getters
|
|
153
|
+
* once you need real GDPR-region gating.
|
|
154
|
+
*/
|
|
155
|
+
getCloudflareContext?: CookieConsentGetCloudflareContext;
|
|
156
|
+
/**
|
|
157
|
+
* Country codes (ISO 3166-1 alpha-2) for which the cookie-consent banner
|
|
158
|
+
* is required. Only consulted when `getCountryCode` or
|
|
159
|
+
* `getCloudflareContext` is set. Defaults to the EU/EEA + UK +
|
|
160
|
+
* Switzerland (GDPR/UK-GDPR/nFADP scope). A visitor whose resolved
|
|
161
|
+
* country isn't in this set is treated as NOT requiring consent; a
|
|
162
|
+
* country that couldn't be resolved still requires it (fail-safe:
|
|
163
|
+
* unknown defaults to "ask").
|
|
164
|
+
*/
|
|
165
|
+
gdprCountries?: readonly string[];
|
|
166
|
+
/**
|
|
167
|
+
* Whether the auto-wired analytics scripts (see `autoWireAnalytics`)
|
|
168
|
+
* are allowed to load in your local/dev environment. Defaults to
|
|
169
|
+
* `false` — analytics stay off during local development regardless of
|
|
170
|
+
* consent, matching most analytics providers' own recommendation not to
|
|
171
|
+
* pollute production data with dev traffic. Set `true` to test the
|
|
172
|
+
* scripts locally.
|
|
173
|
+
*/
|
|
174
|
+
enableAnalyticsInDevMode?: boolean;
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* Minimal shape read from your `getCloudflareContext()` return value — only
|
|
178
|
+
* `cf.country` is consulted, so any superset (the real `CloudflareContext`
|
|
179
|
+
* from `@opennextjs/cloudflare`) is accepted as-is without a hard
|
|
180
|
+
* dependency on that package.
|
|
181
|
+
*/
|
|
182
|
+
export interface CookieConsentCloudflareContext {
|
|
183
|
+
cf?: {
|
|
184
|
+
country?: string;
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
* Matches `@opennextjs/cloudflare`'s `getCloudflareContext` overloaded
|
|
189
|
+
* signature exactly, so that function can be passed as
|
|
190
|
+
* `cookieConsent.getCloudflareContext` directly — this package always
|
|
191
|
+
* calls it with `{ async: true }` internally (the first overload), which is
|
|
192
|
+
* why that overload's return type drives `resolveRequiresConsent`'s
|
|
193
|
+
* awaited result; the sync overload is accepted structurally only so the
|
|
194
|
+
* real function's type (which has both) is assignable as-is.
|
|
195
|
+
*/
|
|
196
|
+
export interface CookieConsentGetCloudflareContext {
|
|
197
|
+
(options: {
|
|
198
|
+
async: true;
|
|
199
|
+
}): Promise<CookieConsentCloudflareContext | null>;
|
|
200
|
+
(options?: {
|
|
201
|
+
async: false;
|
|
202
|
+
}): CookieConsentCloudflareContext | null;
|
|
131
203
|
}
|
|
132
204
|
export interface CookieConsentAnalyticsSecrets {
|
|
133
205
|
/** Cloudflare Web Analytics beacon token, e.g. `'{"token": "..."}'` (the raw `data-cf-beacon` attribute value). */
|
package/llms.txt
CHANGED
|
@@ -41,7 +41,8 @@ other subpath can be used.
|
|
|
41
41
|
- `./useCookieConsent` — context hook; throws `"useCookieConsent must be used within a CookieConsentProvider"` if called outside one.
|
|
42
42
|
- `./CookieConsentDialog` — default cookie-consent banner; accepts per-slot `classNames`/`styles` or a `render` prop for fully custom markup.
|
|
43
43
|
- `./PrivacyPolicyUpdateDialog` — "privacy policy updated" banner; auto-enabled only when `cookieConsent.privacyPolicyDate` is set.
|
|
44
|
-
- `./cookieConsentAnalytics` — `CookieConsentAnalytics`: gates Cloudflare Web Analytics / Google Ads / Google Analytics / AdSense / Microsoft Clarity behind consent; rendered automatically by `IntlProvider` when `cookieConsent.secrets` or `getSecrets` is set (and `autoWireAnalytics !== false`).
|
|
44
|
+
- `./cookieConsentAnalytics` — `CookieConsentAnalytics`: gates Cloudflare Web Analytics / Google Ads / Google Analytics / AdSense / Microsoft Clarity behind consent; rendered automatically by `IntlProvider` when `cookieConsent.secrets` or `getSecrets` is set (and `autoWireAnalytics !== false`). Never renders in local dev (`NODE_ENV === 'development'`) unless `cookieConsent.enableAnalyticsInDevMode` is `true`.
|
|
45
|
+
- Country-based gating (`cookieConsent.getCountryCode` / `getCloudflareContext` + `gdprCountries`): resolved server-side by `IntlProvider` into a `requiresConsent` boolean passed to `CookieConsentProvider`. Neither getter set → gating off, consent always implicitly granted. `getCountryCode` (direct country resolver) takes precedence over `getCloudflareContext` (reads `cf.country`) when both are set. `getCloudflareContext` accepts `@opennextjs/cloudflare`'s `getCloudflareContext` function directly (its exact overloaded signature — `CookieConsentGetCloudflareContext`), called internally with `{ async: true }`. Unresolved country (or a `null` context) always requires consent (fail-safe). `./cookieConsent` also exports `defaultGdprCountries` (EU/EEA + UK + Switzerland).
|
|
45
46
|
|
|
46
47
|
## Conventions
|
|
47
48
|
|