cloudflare-next-intl 0.4.5 → 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.
@@ -1,13 +1,9 @@
1
1
  /**
2
2
  * Loads and initializes Microsoft Clarity. Split into its own module and
3
- * loaded via `next/dynamic` from `cookie_consent_analytics.tsx` bundlers
4
- * (webpack/Turbopack) resolve every literal `import()` specifier they can
5
- * reach at build time, so keeping this file's `import('@microsoft/clarity')`
6
- * out of the main analytics module means consumers who never set
7
- * `secrets.clarityProjectId` (and never render this component) don't need
8
- * `@microsoft/clarity` (an optional peer dependency) installed at all — the
9
- * dynamic-imported chunk containing this file is only built/requested the
10
- * first time it's actually rendered.
3
+ * loaded via `next/dynamic` from `cookie_consent_analytics.tsx` so it's
4
+ * only fetched as a separate chunk once actually rendered (consent granted
5
+ * and `secrets.clarityProjectId` set) `@microsoft/clarity` is a real
6
+ * dependency of this package, so it's always installed regardless.
11
7
  */
12
8
  export default function ClarityScript({ projectId }: {
13
9
  projectId: string;
@@ -9,14 +9,10 @@ function getClarityModule() {
9
9
  }
10
10
  /**
11
11
  * Loads and initializes Microsoft Clarity. Split into its own module and
12
- * loaded via `next/dynamic` from `cookie_consent_analytics.tsx` bundlers
13
- * (webpack/Turbopack) resolve every literal `import()` specifier they can
14
- * reach at build time, so keeping this file's `import('@microsoft/clarity')`
15
- * out of the main analytics module means consumers who never set
16
- * `secrets.clarityProjectId` (and never render this component) don't need
17
- * `@microsoft/clarity` (an optional peer dependency) installed at all — the
18
- * dynamic-imported chunk containing this file is only built/requested the
19
- * first time it's actually rendered.
12
+ * loaded via `next/dynamic` from `cookie_consent_analytics.tsx` so it's
13
+ * only fetched as a separate chunk once actually rendered (consent granted
14
+ * and `secrets.clarityProjectId` set) `@microsoft/clarity` is a real
15
+ * dependency of this package, so it's always installed regardless.
20
16
  */
21
17
  export default function ClarityScript({ projectId }) {
22
18
  useEffect(() => {
@@ -3,12 +3,10 @@ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-run
3
3
  import { useEffect } from 'react';
4
4
  import dynamic from 'next/dynamic';
5
5
  import useCookieConsent from '../use_cookie_consent';
6
- // Hoisted to module scope, like this package's other `dynamic()` calls
7
- // calling it inside the component body would create a new component
8
- // identity every render. Deferring to a separate chunk keeps
9
- // `@microsoft/clarity`'s `import()` (in `clarity_script.tsx`) out of this
10
- // module, so bundlers only resolve it when `ClarityScript` is actually
11
- // rendered — see that file's doc comment for why this matters.
6
+ // Hoisted to module scope calling `dynamic()` inside the component body
7
+ // creates a brand-new component identity every render, forcing a
8
+ // remount. Splitting into a separate chunk keeps `@microsoft/clarity`'s
9
+ // import out of this module until `ClarityScript` is actually rendered.
12
10
  const ClarityScript = dynamic(() => import('./clarity_script'));
13
11
  /**
14
12
  * Renders whichever analytics/ads scripts have a resolved secret, gated on
@@ -8,8 +8,8 @@ export declare const defaultGdprCountries: readonly string[];
8
8
  /**
9
9
  * Resolves whether the cookie-consent banner is required for a visitor.
10
10
  *
11
- * - Neither getter set: country-based gating is off entirely consent is
12
- * never required (the simplest opt-in-by-default setup).
11
+ * - Neither getter set: fail-safe consent is required by default since
12
+ * the visitor's country can't be determined at all.
13
13
  * - Either getter set: fail-safe — a country that couldn't be resolved
14
14
  * still requires consent; only a resolved country OUTSIDE
15
15
  * `gdprCountries` skips the banner. `getCountryCode` takes precedence
@@ -33,8 +33,8 @@ function getGdprCountriesSet(gdprCountries) {
33
33
  /**
34
34
  * Resolves whether the cookie-consent banner is required for a visitor.
35
35
  *
36
- * - Neither getter set: country-based gating is off entirely consent is
37
- * never required (the simplest opt-in-by-default setup).
36
+ * - Neither getter set: fail-safe consent is required by default since
37
+ * the visitor's country can't be determined at all.
38
38
  * - Either getter set: fail-safe — a country that couldn't be resolved
39
39
  * still requires consent; only a resolved country OUTSIDE
40
40
  * `gdprCountries` skips the banner. `getCountryCode` takes precedence
@@ -42,7 +42,7 @@ function getGdprCountriesSet(gdprCountries) {
42
42
  */
43
43
  export default async function resolveRequiresConsent(getCountryCode, getCloudflareContext, gdprCountries) {
44
44
  if (!getCountryCode && !getCloudflareContext)
45
- return false;
45
+ return true;
46
46
  const countryCode = getCountryCode
47
47
  ? await getCountryCode()
48
48
  : (await getCloudflareContext({ async: true }))?.cf?.country;
@@ -59,8 +59,17 @@ export default async function LocationzationProvider({ language, messages, child
59
59
  let analyticsSecrets;
60
60
  let requiresConsent = true;
61
61
  if (config.cookieConsent) {
62
- requiresConsent = await resolveRequiresConsent(config.cookieConsent.getCountryCode, config.cookieConsent.getCloudflareContext, config.cookieConsent.gdprCountries);
63
62
  const isDevEnvironment = process.env.NODE_ENV === 'development';
63
+ // `getCloudflareContext` under `next dev`'s Cloudflare dev shim can
64
+ // crash the local workerd process (a native RPC panic, not a
65
+ // catchable JS error — see cloudflare/workers-sdk#8687) merely by
66
+ // being called, regardless of what it resolves to. `getCountryCode`
67
+ // is caller-supplied and may be dev-safe, so only skip the
68
+ // `getCloudflareContext` path in dev; fail-safe to `true`
69
+ // (banner shown) same as an unresolved country would.
70
+ requiresConsent = !isDevEnvironment
71
+ ? await resolveRequiresConsent(config.cookieConsent.getCountryCode, config.cookieConsent.getCloudflareContext, config.cookieConsent.gdprCountries)
72
+ : true;
64
73
  const analyticsAllowedInEnv = config.cookieConsent.enableAnalyticsInDevMode === true || !isDevEnvironment;
65
74
  if (config.cookieConsent.autoWireAnalytics !== false && analyticsAllowedInEnv) {
66
75
  analyticsSecrets = config.cookieConsent.getSecrets
@@ -155,10 +155,9 @@ export interface CookieConsentRoutingConfig {
155
155
  * `getCloudflareContext`) decides whether the cookie-consent banner is
156
156
  * required at all: visitors outside `gdprCountries` skip the banner and
157
157
  * get analytics immediately (still gated by `enableAnalyticsInDevMode`).
158
- * Omit BOTH to skip country-based gating entirely — the banner is never
159
- * shown and consent is treated as implicitly granted for everyone. This
160
- * is the simplest opt-in-by-default setup; set one of the two getters
161
- * once you need real GDPR-region gating.
158
+ * Omit BOTH to require consent for everyone (fail-safe default — the
159
+ * visitor's country can't be determined at all without either getter).
160
+ * Set one of the two getters to scope the banner to GDPR regions only.
162
161
  */
163
162
  getCloudflareContext?: CookieConsentGetCloudflareContext;
164
163
  /**
@@ -219,7 +218,11 @@ export interface CookieConsentAnalyticsSecrets {
219
218
  googleAdsId?: string;
220
219
  /** Google AdSense publisher ID, e.g. `"ca-pub-XXXXXXXXXXXXXXXX"`. */
221
220
  googleAdSenseId?: string;
222
- /** Microsoft Clarity project ID. */
221
+ /**
222
+ * Microsoft Clarity project ID. `@microsoft/clarity` is a real
223
+ * dependency of this package (small, so always installed) — loaded and
224
+ * initialized automatically once consent is granted.
225
+ */
223
226
  clarityProjectId?: string;
224
227
  }
225
228
  export interface FirebaseAuthRoutingConfig {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cloudflare-next-intl",
3
- "version": "0.4.5",
3
+ "version": "0.5.0",
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",
@@ -181,8 +181,10 @@
181
181
  "url": "https://github.com/demian-ilnytskyi/cloudflare-next-intl/issues"
182
182
  },
183
183
  "homepage": "https://github.com/demian-ilnytskyi/cloudflare-next-intl#readme",
184
+ "dependencies": {
185
+ "@microsoft/clarity": "^1.0.2"
186
+ },
184
187
  "peerDependencies": {
185
- "@microsoft/clarity": ">=1.0.0",
186
188
  "firebase": ">=10.0.0",
187
189
  "next": ">=12.0.0",
188
190
  "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || >=19.0.0-rc <19.0.0 || ^19.0.0",
@@ -194,15 +196,11 @@
194
196
  },
195
197
  "firebase": {
196
198
  "optional": true
197
- },
198
- "@microsoft/clarity": {
199
- "optional": true
200
199
  }
201
200
  },
202
201
  "devDependencies": {
203
202
  "@eslint/eslintrc": "^3",
204
203
  "@eslint/js": "^9.27.0",
205
- "@microsoft/clarity": "^1.0.2",
206
204
  "@testing-library/dom": "^10.4.1",
207
205
  "@testing-library/jest-dom": "^7.0.0",
208
206
  "@testing-library/react": "^16.3.2",