cloudflare-next-intl 0.5.4 → 0.5.6
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 +1 -1
- package/dist/src/client/components/client_provider.d.ts +4 -4
- package/dist/src/client/components/client_provider.js +2 -2
- package/dist/src/client/hooks/client_hooks.bench.d.ts +1 -0
- package/dist/src/client/hooks/client_hooks.bench.js +19 -0
- package/dist/src/client/hooks/use_path_name.bench.d.ts +1 -0
- package/dist/src/client/hooks/use_path_name.bench.js +16 -0
- package/dist/src/cookie_consent/client/components/clarity_script.d.ts +1 -1
- package/dist/src/cookie_consent/client/components/clarity_script.js +1 -1
- package/dist/src/cookie_consent/client/components/cookie_consent_analytics.d.ts +7 -7
- package/dist/src/cookie_consent/client/components/cookie_consent_analytics.js +12 -12
- package/dist/src/cookie_consent/client/cookie_consent_provider.bench.d.ts +1 -0
- package/dist/src/cookie_consent/client/cookie_consent_provider.bench.js +31 -0
- package/dist/src/cookie_consent/client/cookie_consent_provider.d.ts +4 -1
- package/dist/src/cookie_consent/client/cookie_consent_provider.js +15 -1
- package/dist/src/cookie_consent/index.d.ts +1 -1
- package/dist/src/firebase_auth/client/use_auth_user.bench.d.ts +1 -0
- package/dist/src/firebase_auth/client/use_auth_user.bench.js +18 -0
- package/dist/src/server/components/server_provider.js +5 -5
- package/dist/src/types/index.d.ts +1 -1
- package/dist/src/types/types.d.ts +10 -10
- package/package.json +3 -7
package/README.md
CHANGED
|
@@ -189,7 +189,7 @@ import ThemeSwitcher from "cloudflare-next-intl/ThemeSwitcher";
|
|
|
189
189
|
|
|
190
190
|
Set `cookieConsent` on your `RoutingConfig` to enable — `IntlProvider` then
|
|
191
191
|
auto-wires `CookieConsentProvider`, `CookieConsentAnalytics` (if
|
|
192
|
-
`cookieConsent.
|
|
192
|
+
`cookieConsent.analytics`/`getAnalytics` is set), and both `CookieConsentDialog`/
|
|
193
193
|
`PrivacyPolicyUpdateDialog` (with their built-in default styling and
|
|
194
194
|
English/Ukrainian copy) with no manual nesting needed.
|
|
195
195
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { TranslationObject } from "../../types/types";
|
|
2
2
|
import type { SerializedAuthUser } from "../../firebase_auth/types";
|
|
3
|
-
import type {
|
|
3
|
+
import type { CookieConsentAnalyticsConfig } from "../../types/types";
|
|
4
4
|
import type { CookieConsentDialogProps } from "../../cookie_consent/client/components/cookie_consent_dialog";
|
|
5
5
|
import type { PrivacyPolicyUpdateDialogProps } from "../../cookie_consent/client/components/privacy_policy_update_dialog";
|
|
6
6
|
interface LocaleContextType {
|
|
@@ -8,14 +8,14 @@ interface LocaleContextType {
|
|
|
8
8
|
messages: TranslationObject;
|
|
9
9
|
}
|
|
10
10
|
export declare const LocaleContext: import("react").Context<LocaleContextType | undefined>;
|
|
11
|
-
export default function LocationzationClientProvider({ language, messages, initialAuthUser, skipAuthProvider,
|
|
11
|
+
export default function LocationzationClientProvider({ language, messages, initialAuthUser, skipAuthProvider, analyticsConfig, requiresConsent, autoWireDialogs, dialogProps, updateDialogProps, children }: {
|
|
12
12
|
language: string;
|
|
13
13
|
messages: TranslationObject;
|
|
14
14
|
initialAuthUser?: SerializedAuthUser | null;
|
|
15
15
|
/** Set when `firebaseAuth.autoWireClientProvider` is `false` — skips wrapping `children` in the client `AuthUserProvider` entirely. */
|
|
16
16
|
skipAuthProvider?: boolean;
|
|
17
|
-
/** Resolved server-side from `cookieConsent.
|
|
18
|
-
|
|
17
|
+
/** Resolved server-side from `cookieConsent.analytics`/`getAnalytics` when `autoWireAnalytics` isn't `false`. */
|
|
18
|
+
analyticsConfig?: CookieConsentAnalyticsConfig;
|
|
19
19
|
/**
|
|
20
20
|
* Resolved server-side from `cookieConsent.getCountryCode`/`gdprCountries`.
|
|
21
21
|
* `false` means the visitor's country doesn't require the consent
|
|
@@ -17,7 +17,7 @@ const CookieConsentProvider = dynamic(() => import("../../cookie_consent/client/
|
|
|
17
17
|
const CookieConsentAnalytics = dynamic(() => import("../../cookie_consent/client/components/cookie_consent_analytics"));
|
|
18
18
|
const CookieConsentDialog = dynamic(() => import("../../cookie_consent/client/components/cookie_consent_dialog"));
|
|
19
19
|
const PrivacyPolicyUpdateDialog = dynamic(() => import("../../cookie_consent/client/components/privacy_policy_update_dialog"));
|
|
20
|
-
export default function LocationzationClientProvider({ language, messages, initialAuthUser = null, skipAuthProvider = false,
|
|
20
|
+
export default function LocationzationClientProvider({ language, messages, initialAuthUser = null, skipAuthProvider = false, analyticsConfig, requiresConsent = true, autoWireDialogs = true, dialogProps, updateDialogProps, children }) {
|
|
21
21
|
setLocaleCache(language);
|
|
22
22
|
setMessageForLocaleCache(language, messages);
|
|
23
23
|
// `LocaleContext.Provider` stays the outermost element here — the
|
|
@@ -30,7 +30,7 @@ export default function LocationzationClientProvider({ language, messages, initi
|
|
|
30
30
|
providedChildren = _jsx(AuthUserProvider, { initialUser: initialAuthUser, children: children });
|
|
31
31
|
}
|
|
32
32
|
if (config.cookieConsent) {
|
|
33
|
-
providedChildren = _jsxs(CookieConsentProvider, { requiresConsent: requiresConsent, children: [providedChildren,
|
|
33
|
+
providedChildren = _jsxs(CookieConsentProvider, { requiresConsent: requiresConsent, children: [providedChildren, analyticsConfig && _jsx(CookieConsentAnalytics, { config: analyticsConfig }), autoWireDialogs && _jsx(CookieConsentDialog, { ...dialogProps }), autoWireDialogs && _jsx(PrivacyPolicyUpdateDialog, { ...updateDialogProps })] });
|
|
34
34
|
}
|
|
35
35
|
const contextValue = useMemo(() => ({ language, messages }), [language, messages]);
|
|
36
36
|
return _jsx(LocaleContext.Provider, { value: contextValue, children: providedChildren });
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { bench, describe } from 'vitest';
|
|
3
|
+
import { renderHook } from '@testing-library/react';
|
|
4
|
+
import { useLocale, useTranslations } from './client_hooks';
|
|
5
|
+
import { LocaleContext } from '../components/client_provider';
|
|
6
|
+
const messages = { Common: { title: 'Hello' } };
|
|
7
|
+
function wrapper({ children }) {
|
|
8
|
+
return (_jsx(LocaleContext.Provider, { value: { language: 'en', messages }, children: children }));
|
|
9
|
+
}
|
|
10
|
+
describe('useLocale (client)', () => {
|
|
11
|
+
bench('reads the language from context', () => {
|
|
12
|
+
renderHook(() => useLocale(), { wrapper });
|
|
13
|
+
});
|
|
14
|
+
});
|
|
15
|
+
describe('useTranslations (client)', () => {
|
|
16
|
+
bench('builds a namespace-scoped translation function', () => {
|
|
17
|
+
renderHook(() => useTranslations('Common'), { wrapper });
|
|
18
|
+
});
|
|
19
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { bench, describe, vi } from 'vitest';
|
|
3
|
+
import { renderHook } from '@testing-library/react';
|
|
4
|
+
import usePathname from './use_path_name';
|
|
5
|
+
import { LocaleContext } from '../components/client_provider';
|
|
6
|
+
vi.mock('next/navigation', () => ({
|
|
7
|
+
usePathname: vi.fn(() => '/en/about'),
|
|
8
|
+
}));
|
|
9
|
+
function wrapper({ children }) {
|
|
10
|
+
return (_jsx(LocaleContext.Provider, { value: { language: 'en', messages: {} }, children: children }));
|
|
11
|
+
}
|
|
12
|
+
describe('usePathname (locale-stripped)', () => {
|
|
13
|
+
bench('strips the locale prefix from the current pathname', () => {
|
|
14
|
+
renderHook(() => usePathname(), { wrapper });
|
|
15
|
+
});
|
|
16
|
+
});
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Loads and initializes Microsoft Clarity. Split into its own module and
|
|
3
3
|
* loaded via `next/dynamic` from `cookie_consent_analytics.tsx` so it's
|
|
4
4
|
* only fetched as a separate chunk once actually rendered (consent granted
|
|
5
|
-
* and `
|
|
5
|
+
* and `analytics.clarityProjectId` set) — `@microsoft/clarity` is a real
|
|
6
6
|
* dependency of this package, so it's always installed regardless.
|
|
7
7
|
*/
|
|
8
8
|
export default function ClarityScript({ projectId }: {
|
|
@@ -11,7 +11,7 @@ function getClarityModule() {
|
|
|
11
11
|
* Loads and initializes Microsoft Clarity. Split into its own module and
|
|
12
12
|
* loaded via `next/dynamic` from `cookie_consent_analytics.tsx` so it's
|
|
13
13
|
* only fetched as a separate chunk once actually rendered (consent granted
|
|
14
|
-
* and `
|
|
14
|
+
* and `analytics.clarityProjectId` set) — `@microsoft/clarity` is a real
|
|
15
15
|
* dependency of this package, so it's always installed regardless.
|
|
16
16
|
*/
|
|
17
17
|
export default function ClarityScript({ projectId }) {
|
|
@@ -1,19 +1,19 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { CookieConsentAnalyticsConfig } from '../../../types/types';
|
|
2
2
|
/**
|
|
3
3
|
* Renders whichever analytics/ads scripts have a resolved secret, gated on
|
|
4
4
|
* consent: Google Consent Mode bootstrap always loads (defaults to
|
|
5
5
|
* `denied`, only sends `update` once `consent` is decided); Cloudflare Web
|
|
6
6
|
* Analytics beacon and Microsoft Clarity only load once `consent === true`.
|
|
7
|
-
* Rendered automatically by `IntlProvider` when `cookieConsent.
|
|
8
|
-
* `
|
|
7
|
+
* Rendered automatically by `IntlProvider` when `cookieConsent.analytics`/
|
|
8
|
+
* `getAnalytics` resolves at least one field and `autoWireAnalytics` isn't
|
|
9
9
|
* `false` — render manually instead if you set `autoWireAnalytics: false`.
|
|
10
10
|
*/
|
|
11
|
-
export default function CookieConsentAnalytics({
|
|
12
|
-
|
|
11
|
+
export default function CookieConsentAnalytics({ config }: {
|
|
12
|
+
config: CookieConsentAnalyticsConfig;
|
|
13
13
|
}): React.ReactElement | null;
|
|
14
14
|
/**
|
|
15
15
|
* Denies storage by default and loads the configured Google tags; the
|
|
16
16
|
* effect above sends the `update` once consent is known. Only IDs present
|
|
17
|
-
* in `
|
|
17
|
+
* in `config` are included.
|
|
18
18
|
*/
|
|
19
|
-
export declare function googleConsentModeBootstrapScript(
|
|
19
|
+
export declare function googleConsentModeBootstrapScript(config: CookieConsentAnalyticsConfig): string;
|
|
@@ -13,11 +13,11 @@ const ClarityScript = dynamic(() => import('./clarity_script'));
|
|
|
13
13
|
* consent: Google Consent Mode bootstrap always loads (defaults to
|
|
14
14
|
* `denied`, only sends `update` once `consent` is decided); Cloudflare Web
|
|
15
15
|
* Analytics beacon and Microsoft Clarity only load once `consent === true`.
|
|
16
|
-
* Rendered automatically by `IntlProvider` when `cookieConsent.
|
|
17
|
-
* `
|
|
16
|
+
* Rendered automatically by `IntlProvider` when `cookieConsent.analytics`/
|
|
17
|
+
* `getAnalytics` resolves at least one field and `autoWireAnalytics` isn't
|
|
18
18
|
* `false` — render manually instead if you set `autoWireAnalytics: false`.
|
|
19
19
|
*/
|
|
20
|
-
export default function CookieConsentAnalytics({
|
|
20
|
+
export default function CookieConsentAnalytics({ config }) {
|
|
21
21
|
const { consent } = useCookieConsent();
|
|
22
22
|
useEffect(() => {
|
|
23
23
|
if (consent === null)
|
|
@@ -33,33 +33,33 @@ export default function CookieConsentAnalytics({ secrets }) {
|
|
|
33
33
|
analytics_storage: state,
|
|
34
34
|
});
|
|
35
35
|
}, [consent]);
|
|
36
|
-
const hasGoogle = Boolean(
|
|
37
|
-
return (_jsxs(_Fragment, { children: [hasGoogle && (_jsx("script", { id: "cookie-consent-google-consent-mode", dangerouslySetInnerHTML: { __html: googleConsentModeBootstrapScript(
|
|
36
|
+
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) } })), consent === true && config.cloudflareBeaconToken && (_jsx("script", { defer: true, src: "https://static.cloudflareinsights.com/beacon.min.js", "data-cf-beacon": config.cloudflareBeaconToken })), consent === true && config.clarityProjectId && _jsx(ClarityScript, { projectId: config.clarityProjectId })] }));
|
|
38
38
|
}
|
|
39
39
|
/**
|
|
40
40
|
* Denies storage by default and loads the configured Google tags; the
|
|
41
41
|
* effect above sends the `update` once consent is known. Only IDs present
|
|
42
|
-
* in `
|
|
42
|
+
* in `config` are included.
|
|
43
43
|
*/
|
|
44
|
-
export function googleConsentModeBootstrapScript(
|
|
45
|
-
const configCalls = [
|
|
44
|
+
export function googleConsentModeBootstrapScript(config) {
|
|
45
|
+
const configCalls = [config.googleAnalyticsId, config.googleAdsId]
|
|
46
46
|
.filter(Boolean)
|
|
47
47
|
.map((id) => `gtag('config', '${id}');`)
|
|
48
48
|
.join('\n');
|
|
49
|
-
const gtagLoader =
|
|
49
|
+
const gtagLoader = config.googleAnalyticsId || config.googleAdsId
|
|
50
50
|
? `(function(){
|
|
51
51
|
var s = document.createElement('script');
|
|
52
52
|
s.async = true;
|
|
53
|
-
s.src = 'https://www.googletagmanager.com/gtag/js?id=${
|
|
53
|
+
s.src = 'https://www.googletagmanager.com/gtag/js?id=${config.googleAnalyticsId ?? config.googleAdsId}';
|
|
54
54
|
document.head.appendChild(s);
|
|
55
55
|
})();`
|
|
56
56
|
: '';
|
|
57
|
-
const adSenseLoader =
|
|
57
|
+
const adSenseLoader = config.googleAdSenseId
|
|
58
58
|
? `(function(){
|
|
59
59
|
var a = document.createElement('script');
|
|
60
60
|
a.async = true;
|
|
61
61
|
a.crossOrigin = 'anonymous';
|
|
62
|
-
a.src = 'https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=${
|
|
62
|
+
a.src = 'https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=${config.googleAdSenseId}';
|
|
63
63
|
document.head.appendChild(a);
|
|
64
64
|
})();`
|
|
65
65
|
: '';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { bench, describe, vi } from 'vitest';
|
|
3
|
+
import { render, act } from '@testing-library/react';
|
|
4
|
+
vi.mock('../../config/intl_config', () => ({
|
|
5
|
+
default: { cookieConsent: { privacyPolicyDate: '2026-07-20' } },
|
|
6
|
+
}));
|
|
7
|
+
const cookies = new Map();
|
|
8
|
+
vi.mock('../../client/functions/get_cookie', () => ({
|
|
9
|
+
default: (name) => cookies.get(name) ?? null,
|
|
10
|
+
}));
|
|
11
|
+
vi.mock('../../client/functions/set_cookie', () => ({
|
|
12
|
+
default: ({ name, value }) => {
|
|
13
|
+
cookies.set(name, String(value));
|
|
14
|
+
},
|
|
15
|
+
}));
|
|
16
|
+
const { default: CookieConsentProvider } = await import('./cookie_consent_provider');
|
|
17
|
+
describe('CookieConsentProvider mount cost', () => {
|
|
18
|
+
bench('cold mount: no stored consent', async () => {
|
|
19
|
+
cookies.clear();
|
|
20
|
+
await act(async () => {
|
|
21
|
+
render(_jsx(CookieConsentProvider, { children: null }));
|
|
22
|
+
});
|
|
23
|
+
});
|
|
24
|
+
bench('warm mount: consent already granted and privacy-policy date current', async () => {
|
|
25
|
+
cookies.set('__cookie_consent_key__', 'true');
|
|
26
|
+
cookies.set('__privacy_policy_date_key__', new Date('2027-01-01').toISOString());
|
|
27
|
+
await act(async () => {
|
|
28
|
+
render(_jsx(CookieConsentProvider, { children: null }));
|
|
29
|
+
});
|
|
30
|
+
});
|
|
31
|
+
});
|
|
@@ -11,7 +11,10 @@ export declare const CookieConsentContext: import("react").Context<CookieConsent
|
|
|
11
11
|
* `cookieConsent.privacyPolicyDate` is configured: once a visitor has
|
|
12
12
|
* consented, if their stored consent date predates `privacyPolicyDate`,
|
|
13
13
|
* `privacyPolicyUpdated` becomes `true` until they call
|
|
14
|
-
* `acknowledgePrivacyPolicyUpdate()
|
|
14
|
+
* `acknowledgePrivacyPolicyUpdate()` — or until they navigate to
|
|
15
|
+
* `cookieConsent.privacyPolicyPath`, which auto-acknowledges it (visiting
|
|
16
|
+
* the page counts as having seen the update; skipped entirely when
|
|
17
|
+
* `privacyPolicyPath` is `false`).
|
|
15
18
|
*
|
|
16
19
|
* @param requiresConsent Resolved server-side from
|
|
17
20
|
* `cookieConsent.getCountryCode`/`gdprCountries` — `false` means the
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
3
|
import { createContext, useCallback, useEffect, useMemo, useState } from 'react';
|
|
4
|
+
import { usePathname } from 'next/navigation';
|
|
4
5
|
import config from '../../config/intl_config';
|
|
5
6
|
import requireCookieConsentConfig from '../require_config';
|
|
6
7
|
import getCookie from '../../client/functions/get_cookie';
|
|
@@ -25,7 +26,10 @@ function parseConsent(raw) {
|
|
|
25
26
|
* `cookieConsent.privacyPolicyDate` is configured: once a visitor has
|
|
26
27
|
* consented, if their stored consent date predates `privacyPolicyDate`,
|
|
27
28
|
* `privacyPolicyUpdated` becomes `true` until they call
|
|
28
|
-
* `acknowledgePrivacyPolicyUpdate()
|
|
29
|
+
* `acknowledgePrivacyPolicyUpdate()` — or until they navigate to
|
|
30
|
+
* `cookieConsent.privacyPolicyPath`, which auto-acknowledges it (visiting
|
|
31
|
+
* the page counts as having seen the update; skipped entirely when
|
|
32
|
+
* `privacyPolicyPath` is `false`).
|
|
29
33
|
*
|
|
30
34
|
* @param requiresConsent Resolved server-side from
|
|
31
35
|
* `cookieConsent.getCountryCode`/`gdprCountries` — `false` means the
|
|
@@ -59,6 +63,7 @@ export default function CookieConsentProvider({ requiresConsent = true, children
|
|
|
59
63
|
const [consent, setConsentState] = useState(null);
|
|
60
64
|
const [privacyPolicyUpdated, setPrivacyPolicyUpdated] = useState(false);
|
|
61
65
|
const [isMounted, setIsMounted] = useState(false);
|
|
66
|
+
const pathname = usePathname();
|
|
62
67
|
useEffect(() => {
|
|
63
68
|
const storedConsent = parseConsent(getCookie(consentCookieName));
|
|
64
69
|
const autoAccepted = storedConsent === null && !requiresConsent;
|
|
@@ -90,6 +95,15 @@ export default function CookieConsentProvider({ requiresConsent = true, children
|
|
|
90
95
|
setPrivacyPolicyUpdated(false);
|
|
91
96
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
92
97
|
}, []);
|
|
98
|
+
// Visiting the privacy-policy page itself counts as having seen the
|
|
99
|
+
// update — auto-acknowledge instead of also showing the banner there.
|
|
100
|
+
// `pathname` still carries the locale prefix (e.g. `/de/privacy-policy`),
|
|
101
|
+
// so match on a trailing segment rather than strict equality.
|
|
102
|
+
useEffect(() => {
|
|
103
|
+
if (privacyPolicyUpdated && privacyPolicyPath !== false && pathname.endsWith(privacyPolicyPath)) {
|
|
104
|
+
acknowledgePrivacyPolicyUpdate();
|
|
105
|
+
}
|
|
106
|
+
}, [pathname, privacyPolicyUpdated, privacyPolicyPath, acknowledgePrivacyPolicyUpdate]);
|
|
93
107
|
const contextValue = useMemo(() => ({ consent, privacyPolicyUpdated, isMounted, setConsent, acknowledgePrivacyPolicyUpdate, privacyPolicyPath }), [consent, privacyPolicyUpdated, isMounted, setConsent, acknowledgePrivacyPolicyUpdate, privacyPolicyPath]);
|
|
94
108
|
return (_jsx(CookieConsentContext.Provider, { value: contextValue, children: children }));
|
|
95
109
|
}
|
|
@@ -7,4 +7,4 @@ export { defaultGdprCountries } from './gdpr_countries';
|
|
|
7
7
|
export type { CookieConsentContextType, ConsentValue, CookieDialogClassNames, CookieDialogStyles } from './types';
|
|
8
8
|
export type { CookieConsentDialogProps } from './client/components/cookie_consent_dialog';
|
|
9
9
|
export type { PrivacyPolicyUpdateDialogProps } from './client/components/privacy_policy_update_dialog';
|
|
10
|
-
export type { CookieConsentRoutingConfig,
|
|
10
|
+
export type { CookieConsentRoutingConfig, CookieConsentAnalyticsConfig, CookieConsentCloudflareContext, CookieConsentGetCloudflareContext, } from '../types/types';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { bench, describe } from 'vitest';
|
|
3
|
+
import { renderHook } from '@testing-library/react';
|
|
4
|
+
import useAuthUser from './use_auth_user';
|
|
5
|
+
import { AuthUserContext } from './auth_user_provider';
|
|
6
|
+
describe('useAuthUser (client)', () => {
|
|
7
|
+
bench('reads a provided context value', () => {
|
|
8
|
+
renderHook(() => useAuthUser(), {
|
|
9
|
+
wrapper: ({ children }) => (_jsx(AuthUserContext.Provider, { value: {
|
|
10
|
+
user: { uid: 'bench-user' },
|
|
11
|
+
loading: false,
|
|
12
|
+
reloadUser: async () => { },
|
|
13
|
+
sendVerificationEmail: async () => { },
|
|
14
|
+
logout: async () => { },
|
|
15
|
+
}, children: children })),
|
|
16
|
+
});
|
|
17
|
+
});
|
|
18
|
+
});
|
|
@@ -56,7 +56,7 @@ export default async function LocationzationProvider({ language, messages, child
|
|
|
56
56
|
}
|
|
57
57
|
initialAuthUser = await authUserServerProviderModule.resolveAuthUserAndRedirect();
|
|
58
58
|
}
|
|
59
|
-
let
|
|
59
|
+
let analyticsConfig;
|
|
60
60
|
let requiresConsent = true;
|
|
61
61
|
if (config.cookieConsent) {
|
|
62
62
|
const isDevEnvironment = process.env.NODE_ENV === 'development';
|
|
@@ -72,10 +72,10 @@ export default async function LocationzationProvider({ language, messages, child
|
|
|
72
72
|
: false;
|
|
73
73
|
const analyticsAllowedInEnv = config.cookieConsent.enableAnalyticsInDevMode === true || !isDevEnvironment;
|
|
74
74
|
if (config.cookieConsent.autoWireAnalytics !== false && analyticsAllowedInEnv) {
|
|
75
|
-
|
|
76
|
-
? await config.cookieConsent.
|
|
77
|
-
: config.cookieConsent.
|
|
75
|
+
analyticsConfig = config.cookieConsent.getAnalytics
|
|
76
|
+
? await config.cookieConsent.getAnalytics()
|
|
77
|
+
: config.cookieConsent.analytics;
|
|
78
78
|
}
|
|
79
79
|
}
|
|
80
|
-
return _jsx(LocationzationClientProvider, { language: language, messages: messagesValue, initialAuthUser: initialAuthUser, skipAuthProvider: !autoWireClientProvider,
|
|
80
|
+
return _jsx(LocationzationClientProvider, { language: language, messages: messagesValue, initialAuthUser: initialAuthUser, skipAuthProvider: !autoWireClientProvider, analyticsConfig: analyticsConfig, requiresConsent: requiresConsent, autoWireDialogs: config.cookieConsent?.autoWireDialogs !== false, dialogProps: config.cookieConsent?.dialogProps, updateDialogProps: config.cookieConsent?.updateDialogProps, children: children });
|
|
81
81
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export type { CookieAttributes, LocalePrefixMode, Locales, ReturnType, RoutingConfig, TranslationEntry, TranslationObject, TranslatorReturnType, Alternates, changeFrequency, IntlSitemap, CookieConsentRoutingConfig,
|
|
1
|
+
export type { CookieAttributes, LocalePrefixMode, Locales, ReturnType, RoutingConfig, TranslationEntry, TranslationObject, TranslatorReturnType, Alternates, changeFrequency, IntlSitemap, CookieConsentRoutingConfig, CookieConsentAnalyticsConfig, CookieConsentCloudflareContext, CookieConsentGetCloudflareContext, } from './types';
|
|
@@ -117,27 +117,27 @@ export interface CookieConsentRoutingConfig {
|
|
|
117
117
|
/**
|
|
118
118
|
* Whether `IntlProvider` should automatically render the analytics/ads
|
|
119
119
|
* scripts (Cloudflare Web Analytics beacon, Google Consent Mode + gtag,
|
|
120
|
-
* Microsoft Clarity — whichever
|
|
120
|
+
* Microsoft Clarity — whichever config resolves below) once consent is
|
|
121
121
|
* granted, and gate them behind the cookie-consent banner otherwise.
|
|
122
|
-
* Defaults to `true` when `
|
|
123
|
-
* keep `cookieConsent` configured for the dialogs/hook only and wire
|
|
122
|
+
* Defaults to `true` when `analytics`/`getAnalytics` is set; set `false`
|
|
123
|
+
* to keep `cookieConsent` configured for the dialogs/hook only and wire
|
|
124
124
|
* analytics yourself.
|
|
125
125
|
*/
|
|
126
126
|
autoWireAnalytics?: boolean;
|
|
127
127
|
/**
|
|
128
|
-
* Static
|
|
129
|
-
* `
|
|
130
|
-
* set (e.g.
|
|
128
|
+
* Static IDs/tokens for the analytics providers below. Use this OR
|
|
129
|
+
* `getAnalytics`, not both — `getAnalytics` takes precedence when both are
|
|
130
|
+
* set (e.g. values only available at request time from a Cloudflare
|
|
131
131
|
* `env` binding).
|
|
132
132
|
*/
|
|
133
|
-
|
|
133
|
+
analytics?: CookieConsentAnalyticsConfig;
|
|
134
134
|
/**
|
|
135
|
-
* Resolves the same
|
|
135
|
+
* Resolves the same config at request time — e.g. from Cloudflare's
|
|
136
136
|
* `getCloudflareContext().env` (via `@opennextjs/cloudflare`, not a
|
|
137
137
|
* dependency of this package — pass your own getter). Any field left
|
|
138
138
|
* `undefined` in the returned object disables that provider's script.
|
|
139
139
|
*/
|
|
140
|
-
|
|
140
|
+
getAnalytics?: () => CookieConsentAnalyticsConfig | Promise<CookieConsentAnalyticsConfig>;
|
|
141
141
|
/**
|
|
142
142
|
* Resolves the visitor's country code directly (ISO 3166-1 alpha-2,
|
|
143
143
|
* e.g. `"DE"`) — the simplest option when you already have it from
|
|
@@ -231,7 +231,7 @@ export interface CookieConsentGetCloudflareContext {
|
|
|
231
231
|
async: false;
|
|
232
232
|
}): CookieConsentCloudflareContext | null;
|
|
233
233
|
}
|
|
234
|
-
export interface
|
|
234
|
+
export interface CookieConsentAnalyticsConfig {
|
|
235
235
|
/** Cloudflare Web Analytics beacon token, e.g. `'{"token": "..."}'` (the raw `data-cf-beacon` attribute value). */
|
|
236
236
|
cloudflareBeaconToken?: string;
|
|
237
237
|
/** Google Analytics measurement ID, e.g. `"G-XXXXXXX"`. */
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cloudflare-next-intl",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.6",
|
|
4
4
|
"description": "Optimized Next Intl Package Special for App Router and Cloudflare",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -182,10 +182,10 @@
|
|
|
182
182
|
},
|
|
183
183
|
"homepage": "https://github.com/demian-ilnytskyi/cloudflare-next-intl#readme",
|
|
184
184
|
"dependencies": {
|
|
185
|
-
"@microsoft/clarity": "^1.0.2"
|
|
185
|
+
"@microsoft/clarity": "^1.0.2",
|
|
186
|
+
"firebase": "^12.17.0"
|
|
186
187
|
},
|
|
187
188
|
"peerDependencies": {
|
|
188
|
-
"firebase": ">=10.0.0",
|
|
189
189
|
"next": ">=12.0.0",
|
|
190
190
|
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || >=19.0.0-rc <19.0.0 || ^19.0.0",
|
|
191
191
|
"typescript": ">=5.0.0"
|
|
@@ -193,9 +193,6 @@
|
|
|
193
193
|
"peerDependenciesMeta": {
|
|
194
194
|
"typescript": {
|
|
195
195
|
"optional": true
|
|
196
|
-
},
|
|
197
|
-
"firebase": {
|
|
198
|
-
"optional": true
|
|
199
196
|
}
|
|
200
197
|
},
|
|
201
198
|
"devDependencies": {
|
|
@@ -211,7 +208,6 @@
|
|
|
211
208
|
"eslint": "^9.28.0",
|
|
212
209
|
"eslint-config-next": "15.3.3",
|
|
213
210
|
"eslint-config-prettier": "^10.1.2",
|
|
214
|
-
"firebase": "^12.17.0",
|
|
215
211
|
"jsdom": "^29.1.1",
|
|
216
212
|
"next": "^15.3.0",
|
|
217
213
|
"react": "^19.0.0",
|