cloudflare-next-intl 0.6.16 → 0.6.18
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/src/cookie_consent/client/components/cookie_consent_analytics.js +6 -5
- package/dist/src/cookie_consent/client/components/cookie_consent_dialog.js +2 -2
- package/dist/src/cookie_consent/client/cookie_consent_provider.js +10 -11
- package/dist/src/cookie_consent/types.d.ts +9 -0
- package/dist/src/server/components/server_provider.d.ts +15 -15
- package/dist/src/server/components/server_provider.js +16 -16
- package/package.json +1 -1
|
@@ -18,23 +18,24 @@ const ClarityScript = dynamic(() => import('./clarity_script'));
|
|
|
18
18
|
* `false` — render manually instead if you set `autoWireAnalytics: false`.
|
|
19
19
|
*/
|
|
20
20
|
export default function CookieConsentAnalytics({ config }) {
|
|
21
|
-
const { consent } = useCookieConsent();
|
|
21
|
+
const { consent, requiresConsent } = useCookieConsent();
|
|
22
|
+
const granted = consent === true || !requiresConsent;
|
|
22
23
|
useEffect(() => {
|
|
23
|
-
if (consent === null)
|
|
24
|
+
if (consent === null && requiresConsent)
|
|
24
25
|
return;
|
|
25
26
|
const w = window;
|
|
26
27
|
if (typeof w.gtag !== 'function')
|
|
27
28
|
return;
|
|
28
|
-
const state =
|
|
29
|
+
const state = granted ? 'granted' : 'denied';
|
|
29
30
|
w.gtag('consent', 'update', {
|
|
30
31
|
ad_storage: state,
|
|
31
32
|
ad_user_data: state,
|
|
32
33
|
ad_personalization: state,
|
|
33
34
|
analytics_storage: state,
|
|
34
35
|
});
|
|
35
|
-
}, [consent]);
|
|
36
|
+
}, [consent, requiresConsent, granted]);
|
|
36
37
|
const hasGoogle = Boolean(config.googleAnalyticsId || config.googleAdsId || config.googleAdSenseId);
|
|
37
|
-
return (_jsxs(_Fragment, { children: [hasGoogle && (_jsx("script", { id: "cookie-consent-google-consent-mode", dangerouslySetInnerHTML: { __html: googleConsentModeBootstrapScript(config) } })),
|
|
38
|
+
return (_jsxs(_Fragment, { children: [hasGoogle && (_jsx("script", { id: "cookie-consent-google-consent-mode", dangerouslySetInnerHTML: { __html: googleConsentModeBootstrapScript(config) } })), granted && config.cloudflareBeaconToken && (_jsx("script", { defer: true, src: "https://static.cloudflareinsights.com/beacon.min.js", "data-cf-beacon": config.cloudflareBeaconToken })), granted && config.clarityProjectId && _jsx(ClarityScript, { projectId: config.clarityProjectId })] }));
|
|
38
39
|
}
|
|
39
40
|
/**
|
|
40
41
|
* Denies storage by default and loads the configured Google tags; the
|
|
@@ -13,8 +13,8 @@ import { defaultCookieDialogClassNames } from './default_dialog_styles';
|
|
|
13
13
|
* particular design system.
|
|
14
14
|
*/
|
|
15
15
|
export default function CookieConsentDialog({ message, link, privacyPolicyLinkText, acceptText, declineText, hideDecline = false, id = 'cookie-consent-dialog', classNames, styles, render, }) {
|
|
16
|
-
const { consent, isMounted, setConsent, privacyPolicyPath } = useCookieConsent();
|
|
17
|
-
if (!isMounted || consent !== null)
|
|
16
|
+
const { consent, requiresConsent, isMounted, setConsent, privacyPolicyPath } = useCookieConsent();
|
|
17
|
+
if (!isMounted || !requiresConsent || consent !== null)
|
|
18
18
|
return null;
|
|
19
19
|
if (render)
|
|
20
20
|
return _jsx(_Fragment, { children: render({ setConsent }) });
|
|
@@ -65,19 +65,18 @@ export default function CookieConsentProvider({ requiresConsent = true, children
|
|
|
65
65
|
const [isMounted, setIsMounted] = useState(false);
|
|
66
66
|
const pathname = usePathname();
|
|
67
67
|
useEffect(() => {
|
|
68
|
-
// `
|
|
69
|
-
//
|
|
70
|
-
//
|
|
71
|
-
//
|
|
72
|
-
//
|
|
73
|
-
//
|
|
74
|
-
//
|
|
75
|
-
//
|
|
68
|
+
// `consent` reflects only what the visitor actually decided — never
|
|
69
|
+
// auto-set to `true` just because `requiresConsent` is `false`.
|
|
70
|
+
// Whether the banner shows / analytics unlock for a not-required
|
|
71
|
+
// visitor is `requiresConsent`'s job (see `CookieConsentDialog`/
|
|
72
|
+
// `CookieConsentAnalytics`), not something baked into `consent`
|
|
73
|
+
// itself; otherwise there's no way to tell "not required" apart
|
|
74
|
+
// from "explicitly accepted" (e.g. the settings button in the nav
|
|
75
|
+
// bar, which only renders once consent has been decided).
|
|
76
76
|
const rawConsent = getCookie(consentCookieName);
|
|
77
77
|
const storedConsent = parseConsent(rawConsent);
|
|
78
78
|
const isFirstVisit = rawConsent === null;
|
|
79
|
-
|
|
80
|
-
setConsentState(autoAccepted ? true : storedConsent);
|
|
79
|
+
setConsentState(storedConsent);
|
|
81
80
|
setIsMounted(true);
|
|
82
81
|
if (isFirstVisit || !policyDate)
|
|
83
82
|
return;
|
|
@@ -121,6 +120,6 @@ export default function CookieConsentProvider({ requiresConsent = true, children
|
|
|
121
120
|
acknowledgePrivacyPolicyUpdate();
|
|
122
121
|
}
|
|
123
122
|
}, [pathname, privacyPolicyUpdated, privacyPolicyPath, acknowledgePrivacyPolicyUpdate]);
|
|
124
|
-
const contextValue = useMemo(() => ({ consent, privacyPolicyUpdated, isMounted, setConsent, acknowledgePrivacyPolicyUpdate, privacyPolicyPath }), [consent, privacyPolicyUpdated, isMounted, setConsent, acknowledgePrivacyPolicyUpdate, privacyPolicyPath]);
|
|
123
|
+
const contextValue = useMemo(() => ({ consent, requiresConsent, privacyPolicyUpdated, isMounted, setConsent, acknowledgePrivacyPolicyUpdate, privacyPolicyPath }), [consent, requiresConsent, privacyPolicyUpdated, isMounted, setConsent, acknowledgePrivacyPolicyUpdate, privacyPolicyPath]);
|
|
125
124
|
return (_jsx(CookieConsentContext.Provider, { value: contextValue, children: children }));
|
|
126
125
|
}
|
|
@@ -4,6 +4,15 @@ export type ConsentValue = boolean | null;
|
|
|
4
4
|
export interface CookieConsentContextType {
|
|
5
5
|
/** Current consent value; `null` until the visitor decides. */
|
|
6
6
|
consent: ConsentValue;
|
|
7
|
+
/**
|
|
8
|
+
* Resolved server-side from `cookieConsent.getCountryCode`/
|
|
9
|
+
* `gdprCountries` (see `resolveRequiresConsent`). `false` means the
|
|
10
|
+
* visitor's country doesn't require consent at all — the default
|
|
11
|
+
* `CookieConsentDialog` stays hidden and `CookieConsentAnalytics`
|
|
12
|
+
* unlocks immediately, even while `consent` itself is still `null`
|
|
13
|
+
* (never decided, since there was nothing to decide).
|
|
14
|
+
*/
|
|
15
|
+
requiresConsent: boolean;
|
|
7
16
|
/**
|
|
8
17
|
* `false` until the client has read the stored consent/date cookies once
|
|
9
18
|
* (always `false` during SSR and the first client render, to avoid a
|
|
@@ -17,11 +17,12 @@ import type { TranslationObject } from "../../types/types";
|
|
|
17
17
|
* to serve from static rendering / ISR — i.e. the caller already knows
|
|
18
18
|
* the current route never needs a server-resolved auth user (a public
|
|
19
19
|
* page: marketing, privacy policy, docs, etc). Concretely, setting this
|
|
20
|
-
* to `true` skips the internal `resolveAuthUserAndRedirect()`
|
|
20
|
+
* to `true` (the default) skips the internal `resolveAuthUserAndRedirect()`
|
|
21
|
+
* call. Pass `staticSafe: false` to opt back into resolving it.
|
|
21
22
|
*
|
|
22
|
-
* ── Why this call
|
|
23
|
-
* When `firebaseAuth` is configured
|
|
24
|
-
* `resolveAuthUserAndRedirect()`, which:
|
|
23
|
+
* ── Why this call exists, and why skipping it by default is safe ──
|
|
24
|
+
* When `firebaseAuth` is configured and `staticSafe: false` is passed,
|
|
25
|
+
* `IntlProvider` calls `resolveAuthUserAndRedirect()`, which:
|
|
25
26
|
* 1. Reads the session cookie via `cookies()` and verifies it against
|
|
26
27
|
* Firebase (server-side, authoritative check for "who is this?").
|
|
27
28
|
* 2. Reads the current pathname via `headers()` (`x-pathname`, set by
|
|
@@ -55,22 +56,21 @@ import type { TranslationObject } from "../../types/types";
|
|
|
55
56
|
* render before the real user data appears — never wrong/protected
|
|
56
57
|
* content, since middleware already gated that; just a delayed value.
|
|
57
58
|
*
|
|
58
|
-
* ── When to
|
|
59
|
-
*
|
|
60
|
-
*
|
|
61
|
-
*
|
|
62
|
-
* default (`
|
|
63
|
-
*
|
|
64
|
-
*
|
|
65
|
-
* `initialAuthUser`-derived content immediately). If you need
|
|
59
|
+
* ── When to opt out (staticSafe: false) ──
|
|
60
|
+
* Pass `staticSafe: false` on `IntlProvider` calls that wrap protected
|
|
61
|
+
* routes or routes where the auth-state flash would be visually jarring
|
|
62
|
+
* (dashboards, account pages, anything showing `initialAuthUser`-derived
|
|
63
|
+
* content immediately). Leave the default (`true`) for routes you know
|
|
64
|
+
* are always public and don't render auth-dependent UI above the fold
|
|
65
|
+
* (or can tolerate that UI appearing a moment late). If you need
|
|
66
66
|
* different behavior for public vs protected routes within the SAME
|
|
67
67
|
* app, render `IntlProvider` twice — once per layout/route-group, each
|
|
68
68
|
* with its own `staticSafe` value — rather than picking one value for
|
|
69
69
|
* the whole app. If `firebaseAuth.middlewareEnabled` is explicitly
|
|
70
|
-
* `false` (middleware auth disabled),
|
|
70
|
+
* `false` (middleware auth disabled), always pass `staticSafe: false` —
|
|
71
71
|
* this component becomes the ONLY place performing the auth redirect,
|
|
72
|
-
* so
|
|
73
|
-
* the flash.
|
|
72
|
+
* so leaving the `true` default there really does remove the security
|
|
73
|
+
* check, not just the flash.
|
|
74
74
|
*
|
|
75
75
|
* @example
|
|
76
76
|
* ```tsx
|
|
@@ -27,11 +27,12 @@ let authUserServerProviderModule;
|
|
|
27
27
|
* to serve from static rendering / ISR — i.e. the caller already knows
|
|
28
28
|
* the current route never needs a server-resolved auth user (a public
|
|
29
29
|
* page: marketing, privacy policy, docs, etc). Concretely, setting this
|
|
30
|
-
* to `true` skips the internal `resolveAuthUserAndRedirect()`
|
|
30
|
+
* to `true` (the default) skips the internal `resolveAuthUserAndRedirect()`
|
|
31
|
+
* call. Pass `staticSafe: false` to opt back into resolving it.
|
|
31
32
|
*
|
|
32
|
-
* ── Why this call
|
|
33
|
-
* When `firebaseAuth` is configured
|
|
34
|
-
* `resolveAuthUserAndRedirect()`, which:
|
|
33
|
+
* ── Why this call exists, and why skipping it by default is safe ──
|
|
34
|
+
* When `firebaseAuth` is configured and `staticSafe: false` is passed,
|
|
35
|
+
* `IntlProvider` calls `resolveAuthUserAndRedirect()`, which:
|
|
35
36
|
* 1. Reads the session cookie via `cookies()` and verifies it against
|
|
36
37
|
* Firebase (server-side, authoritative check for "who is this?").
|
|
37
38
|
* 2. Reads the current pathname via `headers()` (`x-pathname`, set by
|
|
@@ -65,22 +66,21 @@ let authUserServerProviderModule;
|
|
|
65
66
|
* render before the real user data appears — never wrong/protected
|
|
66
67
|
* content, since middleware already gated that; just a delayed value.
|
|
67
68
|
*
|
|
68
|
-
* ── When to
|
|
69
|
-
*
|
|
70
|
-
*
|
|
71
|
-
*
|
|
72
|
-
* default (`
|
|
73
|
-
*
|
|
74
|
-
*
|
|
75
|
-
* `initialAuthUser`-derived content immediately). If you need
|
|
69
|
+
* ── When to opt out (staticSafe: false) ──
|
|
70
|
+
* Pass `staticSafe: false` on `IntlProvider` calls that wrap protected
|
|
71
|
+
* routes or routes where the auth-state flash would be visually jarring
|
|
72
|
+
* (dashboards, account pages, anything showing `initialAuthUser`-derived
|
|
73
|
+
* content immediately). Leave the default (`true`) for routes you know
|
|
74
|
+
* are always public and don't render auth-dependent UI above the fold
|
|
75
|
+
* (or can tolerate that UI appearing a moment late). If you need
|
|
76
76
|
* different behavior for public vs protected routes within the SAME
|
|
77
77
|
* app, render `IntlProvider` twice — once per layout/route-group, each
|
|
78
78
|
* with its own `staticSafe` value — rather than picking one value for
|
|
79
79
|
* the whole app. If `firebaseAuth.middlewareEnabled` is explicitly
|
|
80
|
-
* `false` (middleware auth disabled),
|
|
80
|
+
* `false` (middleware auth disabled), always pass `staticSafe: false` —
|
|
81
81
|
* this component becomes the ONLY place performing the auth redirect,
|
|
82
|
-
* so
|
|
83
|
-
* the flash.
|
|
82
|
+
* so leaving the `true` default there really does remove the security
|
|
83
|
+
* check, not just the flash.
|
|
84
84
|
*
|
|
85
85
|
* @example
|
|
86
86
|
* ```tsx
|
|
@@ -96,7 +96,7 @@ let authUserServerProviderModule;
|
|
|
96
96
|
* }
|
|
97
97
|
* ```
|
|
98
98
|
*/
|
|
99
|
-
export default async function LocationzationProvider({ language, messages, staticSafe =
|
|
99
|
+
export default async function LocationzationProvider({ language, messages, staticSafe = true, children }) {
|
|
100
100
|
if (!localesSet.has(language)) {
|
|
101
101
|
const { notFound } = await import("next/navigation");
|
|
102
102
|
notFound();
|