cloudflare-next-intl 0.9.45 → 0.9.47

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
@@ -266,6 +266,7 @@ export default defineConfig({
266
266
  4. **User-Agent Stub (`userAgentStub`)**: Prevents Next.js `user-agent` from importing `node:fs` during workerd runtime execution (which otherwise causes runtime 404 / 500 crashes in Workers proxy/middleware).
267
267
  5. **Cloudflare Workers Client Stub (`cfWorkersClientStub`)**: Stubs `cloudflare:workers` in client builds so shared modules can be referenced without client bundling errors.
268
268
  6. **Build ID Asset Emission (`buildIdAsset`)**: Emits `BUILD_ID` static asset in the client build directory from `process.env.__VINEXT_SHARED_BUILD_ID` or `process.env.__VINEXT_BUILD_ID`.
269
+ 7. **Vinext Route Wiring & Optimistic Prefetch Fix (`vinextRouteWiringFix`)**: Patches Vinext runtime route wiring, route matching, optimistic route template resolution, and prefetch learning so pending prefetches with already cached templates don't block navigation, and leading `:locale` segments route correctly.
269
270
 
270
271
  ##### Plugin Options
271
272
  All features are enabled by default, and can be individually configured or toggled off:
@@ -297,13 +298,14 @@ export default defineConfig({
297
298
  localeFiles: true, // Enable @locale-file & glob bundling (default: true)
298
299
  userAgentStub: true, // Enable regex-based user-agent stub (default: true)
299
300
  cfWorkersClientStub: true, // Enable client cloudflare:workers stub (default: true)
301
+ vinextRouteWiringFix: true, // Enable vinext route wiring, matching, and prefetch fixes (default: true, or options object)
300
302
  }),
301
303
  ],
302
304
  });
303
305
  ```
304
306
 
305
307
  Individual standalone plugins are also exported if you only need a specific feature:
306
- `imageOptimizerPlugin` (or `imageOptimizer`), `buildIdAsset`, `localeFilePlugin`, `userAgentStubPlugin`, `cfWorkersClientStubPlugin`.
308
+ `imageOptimizerPlugin` (or `imageOptimizer`), `buildIdAsset`, `localeFilePlugin`, `userAgentStubPlugin`, `cfWorkersClientStubPlugin`, `vinextRouteWiringFixPlugin`.
307
309
 
308
310
  ##### Per-Image Optimizer Settings
309
311
 
@@ -22,8 +22,6 @@ function bindingsFromClause(clause) {
22
22
  const namespaceMatch = /^\*\s*as\s+(\w+)$/.exec(clause.trim());
23
23
  if (namespaceMatch)
24
24
  return [namespaceMatch[1]];
25
- if (clause.trim() === '*')
26
- return [];
27
25
  const braceMatch = /\{([^}]*)\}/.exec(clause);
28
26
  if (braceMatch) {
29
27
  for (const rawItem of braceMatch[1].split(',')) {
@@ -7,6 +7,10 @@ export declare function patchRouteMatching(code: string): string;
7
7
  export declare function isOptimisticRoutingFile(id: string): boolean;
8
8
  export declare function isOptimisticRoutingAlreadyFixed(code: string): boolean;
9
9
  export declare function patchOptimisticRouting(code: string): string;
10
+ export declare function isPrefetchLearningFile(id: string): boolean;
11
+ export declare function isPrefetchLearningAlreadyFixed(code: string): boolean;
12
+ export declare function patchPrefetchLearning(code: string): string;
13
+ export declare function resolveVinextBrowserEntryPath(root?: string): string | null;
10
14
  export declare function isAppPageRouteWiringFile(id: string): boolean;
11
15
  export declare function resolveVinextAppPageRouteWiringPath(root?: string): string | null;
12
16
  export declare function resolveVinextRouteMatchingPath(root?: string): string | null;
@@ -15,11 +19,14 @@ export interface SyncPatchVinextOnDiskOptions {
15
19
  routeWiring?: boolean;
16
20
  routeMatching?: boolean;
17
21
  optimisticRouting?: boolean;
22
+ prefetchLearning?: boolean;
18
23
  }
19
24
  export declare function syncPatchVinextOnDisk(root?: string, options?: SyncPatchVinextOnDiskOptions): boolean;
25
+ export declare function bustVinextOptimizeDepsCache(cacheDir: string): boolean;
20
26
  export interface VinextRouteWiringFixPluginOptions {
21
27
  routeWiring?: boolean;
22
28
  routeMatching?: boolean;
23
29
  optimisticRouting?: boolean;
30
+ prefetchLearning?: boolean;
24
31
  }
25
32
  export declare function vinextRouteWiringFixPlugin(options?: VinextRouteWiringFixPluginOptions): Plugin;
@@ -1,4 +1,4 @@
1
- import { existsSync, readFileSync, writeFileSync } from "node:fs";
1
+ import { existsSync, readFileSync, writeFileSync, rmSync } from "node:fs";
2
2
  import { resolve } from "node:path";
3
3
  const PREFETCH_LOADING_FN_RE = /function\s+getPrefetchLoadingEntry\s*\(\s*route\s*\)\s*\{[\s\S]*?return\s+getDefaultExport\s*\(\s*route\.loading\s*\)\s*\?[\s\S]*?:\s*null\s*;\s*\}/;
4
4
  const FIXED_PREFETCH_LOADING_FN = `function getPrefetchLoadingEntry(route) {
@@ -113,14 +113,17 @@ export function isOptimisticRoutingAlreadyFixed(code) {
113
113
  const hasRawPartsFix = code.includes("options.rawUrlParts[0] !== options.match.params.locale");
114
114
  return hasLocalePrefixFirst && hasRawPartsFix;
115
115
  }
116
- const MATCH_OPTIMISTIC_ROUTE_RE = /function\s+matchOptimisticRouteManifestRoute\s*\(\s*options\s*\)\s*\{[\s\S]*?const\s+trie\s*=\s*getRouteTrie\([\s\S]*?\);[\s\S]*?const\s+match\s*=\s*matchNode\([\s\S]*?\);[\s\S]*?return\s+null;\s*\}/;
117
- const FIXED_MATCH_OPTIMISTIC_ROUTE = `function matchOptimisticRouteManifestRoute(options) {
116
+ const MATCH_OPTIMISTIC_ROUTE_RE = /function\s+matchOptimisticRouteManifestRoute\s*\(\s*options\s*\)\s*\{[\s\S]*?getRouteTrie\([\s\S]*?\)[\s\S]*?\n\}/;
117
+ const FIXED_MATCH_OPTIMISTIC_ROUTE = `function getActiveRouteLocale() {
118
+ return (typeof document !== "undefined" && (document.documentElement?.lang || document.cookie.match(/__user_locale_key__=([^;]+)/)?.[1])) || (typeof window !== "undefined" && window.__VINEXT_LOCALE__) || "en";
119
+ }
120
+ function matchOptimisticRouteManifestRoute(options) {
118
121
  const urlParts = hrefToRouteParts(options.href, options.basePath);
119
122
  if (urlParts === null) return null;
120
123
  const trie = getRouteTrie(options.routeManifest);
121
124
  const hasLeadingLocaleParam = Array.from(options.routeManifest?.segmentGraph?.routes?.values() ?? []).some((r) => r.patternParts?.[0] === ":locale");
122
125
  if (hasLeadingLocaleParam) {
123
- const activeLocale = (typeof document !== "undefined" && (document.documentElement?.lang || document.cookie.match(/__user_locale_key__=([^;]+)/)?.[1])) || (typeof window !== "undefined" && window.__VINEXT_LOCALE__) || "en";
126
+ const activeLocale = getActiveRouteLocale();
124
127
  if (urlParts.normalized[0] !== activeLocale) {
125
128
  const localeMatch = matchNode(trie, [activeLocale, ...urlParts.normalized], 0, []);
126
129
  if (localeMatch !== null) {
@@ -147,14 +150,109 @@ export function patchOptimisticRouting(code) {
147
150
  return code;
148
151
  }
149
152
  let result = code;
150
- if (MATCH_OPTIMISTIC_ROUTE_RE.test(result)) {
153
+ if (!result.includes("hasLeadingLocaleParam") && MATCH_OPTIMISTIC_ROUTE_RE.test(result)) {
151
154
  result = result.replace(MATCH_OPTIMISTIC_ROUTE_RE, FIXED_MATCH_OPTIMISTIC_ROUTE);
152
155
  }
153
- if (RESOLVE_OPTIMISTIC_NAV_PARAMS_RE.test(result)) {
156
+ if (!result.includes("options.rawUrlParts[0] !== options.match.params.locale") && RESOLVE_OPTIMISTIC_NAV_PARAMS_RE.test(result)) {
154
157
  result = result.replace(RESOLVE_OPTIMISTIC_NAV_PARAMS_RE, FIXED_RESOLVE_OPTIMISTIC_NAV_PARAMS);
155
158
  }
156
159
  return result;
157
160
  }
161
+ export function isPrefetchLearningFile(id) {
162
+ const cleanId = id.split("?")[0].replace(/\\/g, "/");
163
+ return cleanId.endsWith("/app-browser-entry.js") || cleanId.endsWith("/app-browser-entry.ts");
164
+ }
165
+ export function isPrefetchLearningAlreadyFixed(code) {
166
+ const hasFixedFunction = code.includes("stripRsc(parsePrefetchCacheKey(cacheKey).rscUrl)") && code.includes("hasOptimisticTemplate");
167
+ const hasFixedCallSite = code.includes("targetHref: currentHref,") && code.includes("targetRscUrl: rscUrl,") && !LEARN_TEMPLATES_CALL_RE.test(code);
168
+ return hasFixedFunction && hasFixedCallSite;
169
+ }
170
+ const LEARN_TEMPLATES_FN_RE = /async\s+function\s+learnOptimisticRouteTemplatesFromPrefetchCache\s*\(\s*options\s*\)\s*\{[\s\S]*?await\s+Promise\.allSettled\(\s*learning\s*\);\s*\}/;
171
+ const FIXED_LEARN_TEMPLATES_FN = `async function learnOptimisticRouteTemplatesFromPrefetchCache(options) {
172
+ if (options.routeManifest === null) return;
173
+ const hasOptimisticTemplate = options.targetHref !== void 0 && resolveOptimisticNavigationPayload({
174
+ basePath: __basePath,
175
+ href: options.targetHref,
176
+ interceptionContext: options.interceptionContext,
177
+ mountedSlotsHeader: options.mountedSlotsHeader,
178
+ routeManifest: options.routeManifest,
179
+ templates: optimisticRouteTemplates
180
+ }) !== null;
181
+ const stripRsc = (u) => {
182
+ if (!u) return "";
183
+ const q = u.indexOf("?");
184
+ if (q === -1) return u;
185
+ const sp = new URLSearchParams(u.slice(q + 1));
186
+ sp.delete("_rsc");
187
+ sp.delete("%5Frsc");
188
+ const s = sp.toString();
189
+ return s ? \`\${u.slice(0, q)}?\${s}\` : u.slice(0, q);
190
+ };
191
+ const learning = [...optimisticRouteTemplateLearning.values()];
192
+ for (const [cacheKey, entry] of getPrefetchCache()) {
193
+ const sourceKey = getOptimisticPrefetchSourceKey({
194
+ cacheKey,
195
+ interceptionContext: options.interceptionContext,
196
+ mountedSlotsHeader: options.mountedSlotsHeader
197
+ });
198
+ if (optimisticRouteTemplateSources.has(sourceKey)) continue;
199
+ if (optimisticRouteTemplateLearning.has(sourceKey)) continue;
200
+ if (entry.prefetchKind === "route-tree") continue;
201
+ const isPendingNavigationTarget = !hasOptimisticTemplate && !isSettledPrefetchCacheEntry(entry) && entry.pending !== void 0 && options.targetRscUrl !== void 0 && stripRsc(parsePrefetchCacheKey(cacheKey).rscUrl) === stripRsc(options.targetRscUrl);
202
+ if (!isSettledPrefetchCacheEntry(entry) && !isPendingNavigationTarget) continue;
203
+ const promise = (async () => {
204
+ let settledEntry = entry;
205
+ if (!isSettledPrefetchCacheEntry(settledEntry)) {
206
+ await Promise.race([
207
+ settledEntry.pending?.catch(() => {}),
208
+ new Promise((resolve) => setTimeout(resolve, 3000))
209
+ ]);
210
+ settledEntry = getPrefetchCache().get(cacheKey) ?? settledEntry;
211
+ if (!isSettledPrefetchCacheEntry(settledEntry)) return;
212
+ }
213
+ return learnOptimisticRouteTemplateFromPrefetch({
214
+ cacheKey,
215
+ entry: settledEntry,
216
+ interceptionContext: options.interceptionContext,
217
+ mountedSlotsHeader: options.mountedSlotsHeader,
218
+ routeManifest: options.routeManifest
219
+ });
220
+ })().then((learned) => {
221
+ if (learned) optimisticRouteTemplateSources.add(sourceKey);
222
+ }).finally(() => {
223
+ optimisticRouteTemplateLearning.delete(sourceKey);
224
+ });
225
+ optimisticRouteTemplateLearning.set(sourceKey, promise);
226
+ learning.push(promise);
227
+ }
228
+ if (learning.length === 0) return;
229
+ await Promise.allSettled(learning);
230
+ }`;
231
+ const LEARN_TEMPLATES_CALL_RE = /await\s+learnOptimisticRouteTemplatesFromPrefetchCache\(\s*\{\s*interceptionContext:\s*requestInterceptionContext,\s*(targetRscUrl:\s*rscUrl,\s*)?mountedSlotsHeader,[\s\S]*?routeManifest\s*\n\s*\}\);/;
232
+ const FIXED_LEARN_TEMPLATES_CALL = `await learnOptimisticRouteTemplatesFromPrefetchCache({
233
+ interceptionContext: requestInterceptionContext,
234
+ targetHref: currentHref,
235
+ targetRscUrl: rscUrl,
236
+ mountedSlotsHeader,
237
+ routeManifest
238
+ });`;
239
+ export function patchPrefetchLearning(code) {
240
+ if (isPrefetchLearningAlreadyFixed(code)) {
241
+ return code;
242
+ }
243
+ let result = code;
244
+ if (!result.includes("hasOptimisticTemplate") && LEARN_TEMPLATES_FN_RE.test(result)) {
245
+ result = result.replace(LEARN_TEMPLATES_FN_RE, FIXED_LEARN_TEMPLATES_FN);
246
+ }
247
+ if (LEARN_TEMPLATES_CALL_RE.test(result)) {
248
+ result = result.replace(LEARN_TEMPLATES_CALL_RE, FIXED_LEARN_TEMPLATES_CALL);
249
+ }
250
+ return result;
251
+ }
252
+ export function resolveVinextBrowserEntryPath(root = process.cwd()) {
253
+ const directPath = resolve(root, "node_modules/vinext/dist/server/app-browser-entry.js");
254
+ return existsSync(directPath) ? directPath : null;
255
+ }
158
256
  export function isAppPageRouteWiringFile(id) {
159
257
  const cleanId = id.split("?")[0].replace(/\\/g, "/");
160
258
  return cleanId.endsWith("/app-page-route-wiring.js") || cleanId.endsWith("/app-page-route-wiring.tsx") || cleanId.endsWith("/app-page-route-wiring.ts");
@@ -172,7 +270,7 @@ export function resolveVinextOptimisticRoutingPath(root = process.cwd()) {
172
270
  return existsSync(directPath) ? directPath : null;
173
271
  }
174
272
  export function syncPatchVinextOnDisk(root = process.cwd(), options = {}) {
175
- const { routeWiring = true, routeMatching = true, optimisticRouting = true } = options;
273
+ const { routeWiring = true, routeMatching = true, optimisticRouting = true, prefetchLearning = true } = options;
176
274
  let changed = false;
177
275
  const wiringPath = routeWiring ? resolveVinextAppPageRouteWiringPath(root) : null;
178
276
  if (wiringPath) {
@@ -184,6 +282,9 @@ export function syncPatchVinextOnDisk(root = process.cwd(), options = {}) {
184
282
  writeFileSync(wiringPath, patched, "utf8");
185
283
  changed = true;
186
284
  }
285
+ else {
286
+ console.warn(`[cfni:vinext-route-wiring-fix] ${wiringPath} does not match the expected shape for patchAppPageRouteWiring — this vinext version may have changed; the route-wiring fix was NOT applied.`);
287
+ }
187
288
  }
188
289
  }
189
290
  catch {
@@ -199,6 +300,9 @@ export function syncPatchVinextOnDisk(root = process.cwd(), options = {}) {
199
300
  writeFileSync(matchingPath, patched, "utf8");
200
301
  changed = true;
201
302
  }
303
+ else {
304
+ console.warn(`[cfni:vinext-route-wiring-fix] ${matchingPath} does not match the expected shape for patchRouteMatching — this vinext version may have changed; the route-matching fix was NOT applied.`);
305
+ }
202
306
  }
203
307
  }
204
308
  catch {
@@ -214,6 +318,27 @@ export function syncPatchVinextOnDisk(root = process.cwd(), options = {}) {
214
318
  writeFileSync(optimisticPath, patched, "utf8");
215
319
  changed = true;
216
320
  }
321
+ else {
322
+ console.warn(`[cfni:vinext-route-wiring-fix] ${optimisticPath} does not match the expected shape for patchOptimisticRouting — this vinext version may have changed; the optimistic-routing fix was NOT applied.`);
323
+ }
324
+ }
325
+ }
326
+ catch {
327
+ }
328
+ }
329
+ const browserEntryPath = prefetchLearning ? resolveVinextBrowserEntryPath(root) : null;
330
+ if (browserEntryPath) {
331
+ try {
332
+ const content = readFileSync(browserEntryPath, "utf8");
333
+ if (!isPrefetchLearningAlreadyFixed(content)) {
334
+ const patched = patchPrefetchLearning(content);
335
+ if (patched !== content) {
336
+ writeFileSync(browserEntryPath, patched, "utf8");
337
+ changed = true;
338
+ }
339
+ else {
340
+ console.warn(`[cfni:vinext-route-wiring-fix] ${browserEntryPath} does not match the expected shape for patchPrefetchLearning — this vinext version may have changed; the prefetch-learning fix was NOT applied.`);
341
+ }
217
342
  }
218
343
  }
219
344
  catch {
@@ -221,16 +346,39 @@ export function syncPatchVinextOnDisk(root = process.cwd(), options = {}) {
221
346
  }
222
347
  return changed;
223
348
  }
349
+ export function bustVinextOptimizeDepsCache(cacheDir) {
350
+ let removed = false;
351
+ for (const sub of ["deps", "deps_ssr", "deps_rsc"]) {
352
+ const dir = resolve(cacheDir, sub);
353
+ if (!existsSync(dir))
354
+ continue;
355
+ try {
356
+ rmSync(dir, { recursive: true, force: true });
357
+ removed = true;
358
+ }
359
+ catch {
360
+ }
361
+ }
362
+ return removed;
363
+ }
224
364
  export function vinextRouteWiringFixPlugin(options = {}) {
225
365
  const routeWiring = options.routeWiring !== false;
226
366
  const routeMatching = options.routeMatching !== false;
227
367
  const optimisticRouting = options.optimisticRouting !== false;
368
+ const prefetchLearning = options.prefetchLearning !== false;
228
369
  return {
229
370
  name: "cfni:vinext-route-wiring-fix",
230
371
  enforce: "pre",
231
372
  configResolved(config) {
232
373
  const root = config.root || process.cwd();
233
- syncPatchVinextOnDisk(root, { routeWiring, routeMatching, optimisticRouting });
374
+ const changed = syncPatchVinextOnDisk(root, { routeWiring, routeMatching, optimisticRouting, prefetchLearning });
375
+ if (changed) {
376
+ const cacheDir = config.cacheDir || resolve(root, "node_modules/.vite");
377
+ const busted = bustVinextOptimizeDepsCache(cacheDir);
378
+ if (busted) {
379
+ console.log("[cfni:vinext-route-wiring-fix] patched vinext on disk and cleared its stale Vite optimizeDeps cache — dependencies will re-bundle on next request.");
380
+ }
381
+ }
234
382
  },
235
383
  transform(code, id) {
236
384
  if (routeWiring && isAppPageRouteWiringFile(id)) {
@@ -253,6 +401,16 @@ export function vinextRouteWiringFixPlugin(options = {}) {
253
401
  map: null,
254
402
  };
255
403
  }
404
+ if (prefetchLearning && isPrefetchLearningFile(id)) {
405
+ if (isPrefetchLearningAlreadyFixed(code)) {
406
+ return;
407
+ }
408
+ const patched = patchPrefetchLearning(code);
409
+ return {
410
+ code: patched,
411
+ map: null,
412
+ };
413
+ }
256
414
  if (optimisticRouting && isOptimisticRoutingFile(id)) {
257
415
  if (isOptimisticRoutingAlreadyFixed(code)) {
258
416
  return;
package/llms.txt CHANGED
@@ -27,7 +27,7 @@ other subpath can be used.
27
27
  - `./db` — `withPublicDb(fn)` / `withUserDb(fn, uid?)` server-side Postgres/Drizzle context helpers (require `db` set on your `RoutingConfig`; direct Postgres or Supabase Data API with automatic PostgREST REST translation and `cfni_exec` fallback, see below).
28
28
  - `./dbEslint` — flat-config ESLint fragment banning direct `@supabase/supabase-js`, `pg`, `postgres`, and deep `dist/` imports in application code.
29
29
  - `./dbHelpers` — generic Drizzle SQL helper functions (`excluded`, `onConflictSet`, `ago`, `currentDate`, `windowCount`, `unnestLateral`, `ascNullsLast`, `alwaysTrue`, `lateral`, `aliasColumn`, `minOf`, `maxOf`, `roundReal`, `multiply`, `scalarFromCte`) for use with `./db`.
30
- - `./vite` — `cloudflareNextIntl(options?)` / `cloudflareNextIntlPlugin`, `imageOptimizerPlugin(options?)` / `imageOptimizer`, `buildIdAsset(fileName?)`, `localeFilePlugin(options?)`, `userAgentStubPlugin()`, `cfWorkersClientStubPlugin()`: All-in-one Vite plugin required for Vinext/Cloudflare Workers environments (bundles `@locale-file/*` via eager glob, resolves `@intl-config`, stubs Node.js `user-agent` to prevent runtime `node:fs` errors, stubs `cloudflare:workers` for client builds, emits client `BUILD_ID`, and runs build-time/dev Image Optimizer with Next.js blur placeholder shimming).
30
+ - `./vite` — `cloudflareNextIntl(options?)` / `cloudflareNextIntlPlugin`, `imageOptimizerPlugin(options?)` / `imageOptimizer`, `buildIdAsset(fileName?)`, `localeFilePlugin(options?)`, `userAgentStubPlugin()`, `cfWorkersClientStubPlugin()`, `vinextRouteWiringFixPlugin(options?)`: All-in-one Vite plugin required for Vinext/Cloudflare Workers environments (bundles `@locale-file/*` via eager glob, resolves `@intl-config`, stubs Node.js `user-agent` to prevent runtime `node:fs` errors, stubs `cloudflare:workers` for client builds, emits client `BUILD_ID`, runs build-time/dev Image Optimizer with Next.js blur placeholder shimming, and fixes vinext route wiring, matching, and non-blocking optimistic prefetch learning).
31
31
  - `./image-optimizer` / `./imageOptimizer` — image optimization suite: `imageOptimizerPlugin`, `imageOptimizer`, `resolveOptions`, `resolveImageConfig`, `resolveBlurOptions`, `processImage`, `makeBlurDataURL`, `getImageBlurSvg`, `renderManifest`, `writeManifest`, `isFresh`, `loadCache`, `saveCache`, `collectImages`, `run`.
32
32
  - `./errorHandling` — error reporting & stale deploy recovery barrel: `reportError`, `withErrorHandling`, `installConsoleErrorOverride`, `installGlobalErrorOverride`, `stringifyUnknown`, `formatErrorMessage`, `defaultIgnoredConsoleErrors`, `createServerErrorAction`, `isStaleDeployError`, `defaultStaleDeployPatterns`, `setStaleDeployPatterns`, `getStaleDeployPatterns`, `clearClientCache`, `useStaleDeployRecovery`, `shouldRecoverFromStaleDeploy`, `isRecentBuild`.
33
33
  - `./isStaleDeployError` — `isStaleDeployError(error, patterns?)`, `setStaleDeployPatterns(patterns)`, `getStaleDeployPatterns()`: detector returning `true` for version skew / chunk load / dynamic import / server action 404 / hydration errors (ChunkLoadError, UnrecognizedActionError, server action not found, failed to fetch, dynamically imported module failure, loading CSS chunk, connection closed, RSC payload failure, minified error #412, or missing stream error `undefined`) with fast pre-lowercased pattern cache and intl-config integration (`errorHandling.staleDeployPatterns`).
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cloudflare-next-intl",
3
- "version": "0.9.45",
3
+ "version": "0.9.47",
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",