cloudflare-next-intl 0.8.46 → 0.8.48

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
@@ -557,8 +557,9 @@ export default function GlobalError({
557
557
  }
558
558
  ```
559
559
 
560
- - `useStaleDeployRecovery(error: Error, onRecover?: () => Promise<unknown>, delayMs = 5000): boolean`: Detects a stale-deploy error via `isStaleDeployError` and, **once per build id**, waits `delayMs`, runs `onRecover` (if provided) and `clearClientCache` in parallel, then reloads the page. The build id is read from `localStorage['buildId']` (set by `IntlHelperScript`'s `BUILD_ID` check) and a `sessionStorage` marker records which build id already spent its reload, so a repeat failure on the same build falls through to `false` (render your normal error UI) instead of reloading forever. A redeploy changes the build id and re-arms exactly one more attempt.
561
- - `shouldRecoverFromStaleDeploy(error: Error, buildId: string, marker: string | null): boolean`: The pure predicate behind the hook, exported for testing.
560
+ - `useStaleDeployRecovery(error: Error, onRecover?: () => Promise<unknown>, delayMs = 5000): boolean`: Detects a stale-deploy error via `isStaleDeployError` and, **once per build id**, waits `delayMs`, runs `onRecover` (if provided) and `clearClientCache` in parallel, then reloads the page. The build id is read from `localStorage['buildId']` (set by `IntlHelperScript`'s `BUILD_ID` check) and a `sessionStorage` marker records which build id already spent its reload, so a repeat failure on the same build falls through to `false` (render your normal error UI) instead of reloading forever. A redeploy changes the build id and re-arms exactly one more attempt. **Exception:** if the build id was written within the last 60 seconds (`localStorage['buildIdSetAt']`, also set by `IntlHelperScript`), the hook recovers anyway — a stale-deploy error moments after adopting a new build is the deploy still settling, not a real failure, so the one-reload cap is bypassed for that window.
561
+ - `shouldRecoverFromStaleDeploy(error: Error, buildId: string, marker: string | null, recentBuild = false): boolean`: The pure predicate behind the hook, exported for testing.
562
+ - `isRecentBuild(setAt: number | null, now: number, windowMs = 60000): boolean`: Whether `setAt` (typically `buildIdSetAt`) falls within `windowMs` of `now`.
562
563
 
563
564
  ### Database (`db`)
564
565
 
@@ -32,6 +32,7 @@ export const defaultIgnoredConsoleErrors = [
32
32
  'The `punycode` module is deprecated. Please use a userland alternative instead.',
33
33
  'failed to pipe response',
34
34
  "FirebaseServerApp authIdToken is invalid: the token has expired.",
35
+ "FirebaseServerApp appCheckToken is invalid: the token has expired.",
35
36
  "failed Error: Database is closing/hidden",
36
37
  'Failed to fetch RSC payload',
37
38
  'The above error occurred in a React component',
@@ -1,4 +1,13 @@
1
- export declare function shouldRecoverFromStaleDeploy(error: Error, buildId: string, marker: string | null): boolean;
1
+ /**
2
+ * True when this build id was written within the last `windowMs` — i.e. the
3
+ * client just picked up a new deploy (via `IntlHelperScript`'s BUILD_ID
4
+ * check). A stale-deploy error in that window is the deploy itself still
5
+ * settling (new chunks, in-flight RSC requests against the old build), not a
6
+ * failure a reload can't fix — so it recovers even on a build id the reload
7
+ * marker already covers.
8
+ */
9
+ export declare function isRecentBuild(setAt: number | null, now: number, windowMs?: number): boolean;
10
+ export declare function shouldRecoverFromStaleDeploy(error: Error, buildId: string, marker: string | null, recentBuild?: boolean): boolean;
2
11
  /**
3
12
  * Detects a stale-deploy error and, once per build id, silently clears client
4
13
  * caches and reloads after `delayMs`. Returns whether a reload is pending so
@@ -4,6 +4,8 @@ import isStaleDeployError from './is_stale_deploy_error';
4
4
  import clearClientCache from './clear_client_cache';
5
5
  const RECOVERY_RELOAD_KEY = 'stale-deploy-recovery-reloaded';
6
6
  const BUILD_ID_KEY = 'buildId';
7
+ const BUILD_ID_SET_AT_KEY = 'buildIdSetAt';
8
+ const RECENT_BUILD_WINDOW_MS = 60000;
7
9
  function currentBuildId() {
8
10
  try {
9
11
  return localStorage.getItem(BUILD_ID_KEY) ?? 'unknown';
@@ -12,18 +14,39 @@ function currentBuildId() {
12
14
  return 'unknown';
13
15
  }
14
16
  }
15
- // One silent reload per deployment. The marker carries the build id the reload
16
- // was spent on, so a redeploy re-arms exactly one more attempt while a repeat
17
- // failure on the same build falls through to the caller's error UI instead of
18
- // spinning forever.
19
- export function shouldRecoverFromStaleDeploy(error, buildId, marker) {
20
- return isStaleDeployError(error) && marker !== buildId;
17
+ function buildIdSetAt() {
18
+ try {
19
+ const raw = localStorage.getItem(BUILD_ID_SET_AT_KEY);
20
+ return raw ? Number(raw) : null;
21
+ }
22
+ catch {
23
+ return null;
24
+ }
25
+ }
26
+ /**
27
+ * True when this build id was written within the last `windowMs` — i.e. the
28
+ * client just picked up a new deploy (via `IntlHelperScript`'s BUILD_ID
29
+ * check). A stale-deploy error in that window is the deploy itself still
30
+ * settling (new chunks, in-flight RSC requests against the old build), not a
31
+ * failure a reload can't fix — so it recovers even on a build id the reload
32
+ * marker already covers.
33
+ */
34
+ export function isRecentBuild(setAt, now, windowMs = RECENT_BUILD_WINDOW_MS) {
35
+ return setAt !== null && now - setAt < windowMs;
36
+ }
37
+ // One silent reload per deployment, UNLESS the build id was written moments
38
+ // ago — see `isRecentBuild`. The marker carries the build id the reload was
39
+ // spent on, so a redeploy re-arms exactly one more attempt while a repeat
40
+ // failure well after the deploy settled falls through to the caller's error
41
+ // UI instead of spinning forever.
42
+ export function shouldRecoverFromStaleDeploy(error, buildId, marker, recentBuild = false) {
43
+ return isStaleDeployError(error) && (marker !== buildId || recentBuild);
21
44
  }
22
45
  function canRecover(error) {
23
46
  if (typeof window === 'undefined')
24
47
  return false;
25
48
  try {
26
- return shouldRecoverFromStaleDeploy(error, currentBuildId(), sessionStorage.getItem(RECOVERY_RELOAD_KEY));
49
+ return shouldRecoverFromStaleDeploy(error, currentBuildId(), sessionStorage.getItem(RECOVERY_RELOAD_KEY), isRecentBuild(buildIdSetAt(), Date.now()));
27
50
  }
28
51
  catch {
29
52
  return false;
@@ -44,6 +44,7 @@ export default function HelperScript() {
44
44
 
45
45
  if (prevBuild !== BUILD_ID) {
46
46
  localStorage.setItem('buildId', BUILD_ID);
47
+ localStorage.setItem('buildIdSetAt', String(Date.now()));
47
48
  if(prevBuild){
48
49
  window.location.reload(true);
49
50
  }
package/llms.txt CHANGED
@@ -28,10 +28,10 @@ other subpath can be used.
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
30
  - `./vite` — `buildIdAsset(fileName?)`: Vite plugin to emit the client `BUILD_ID` asset (from `__VINEXT_SHARED_BUILD_ID` or `__VINEXT_BUILD_ID`) during build in Vinext/Vite environments.
31
- - `./errorHandling` — error reporting & stale deploy recovery barrel: `reportError`, `withErrorHandling`, `installConsoleErrorOverride`, `installGlobalErrorOverride`, `stringifyUnknown`, `formatErrorMessage`, `defaultIgnoredConsoleErrors`, `createServerErrorAction`, `isStaleDeployError`, `defaultStaleDeployPatterns`, `setStaleDeployPatterns`, `getStaleDeployPatterns`, `clearClientCache`, `useStaleDeployRecovery`, `shouldRecoverFromStaleDeploy`.
31
+ - `./errorHandling` — error reporting & stale deploy recovery barrel: `reportError`, `withErrorHandling`, `installConsoleErrorOverride`, `installGlobalErrorOverride`, `stringifyUnknown`, `formatErrorMessage`, `defaultIgnoredConsoleErrors`, `createServerErrorAction`, `isStaleDeployError`, `defaultStaleDeployPatterns`, `setStaleDeployPatterns`, `getStaleDeployPatterns`, `clearClientCache`, `useStaleDeployRecovery`, `shouldRecoverFromStaleDeploy`, `isRecentBuild`.
32
32
  - `./isStaleDeployError` — `isStaleDeployError(error, patterns?)`, `setStaleDeployPatterns(patterns)`, `getStaleDeployPatterns()`: detector returning `true` for version skew / chunk load / hydration errors (ChunkLoadError, failed to fetch, loading CSS chunk, connection closed, RSC payload failure, minified error #412) with fast pre-lowercased pattern cache and intl-config integration (`errorHandling.staleDeployPatterns`).
33
33
  - `./clearClientCache` — `clearClientCache()`: async helper wiping `window.caches`, unregistering service workers, and clearing `sessionStorage` for recovering from stale deployments.
34
- - `useStaleDeployRecovery(error, onRecover?, delayMs?)` (client hook, in `./errorHandling`) — once per build id (`localStorage['buildId']`, `sessionStorage` marker), waits `delayMs` (default 5000ms), runs optional `onRecover()` + `clearClientCache()` in parallel, then `window.location.reload()`. Returns whether a reload is pending, so caller renders a loading state instead of error UI. `shouldRecoverFromStaleDeploy(error, buildId, marker)` is the pure predicate.
34
+ - `useStaleDeployRecovery(error, onRecover?, delayMs?)` (client hook, in `./errorHandling`) — once per build id (`localStorage['buildId']`, `sessionStorage` marker), waits `delayMs` (default 5000ms), runs optional `onRecover()` + `clearClientCache()` in parallel, then `window.location.reload()`. Returns whether a reload is pending, so caller renders a loading state instead of error UI. Recovers even past the one-reload cap when `localStorage['buildIdSetAt']` is <60s old (new deploy still settling). `shouldRecoverFromStaleDeploy(error, buildId, marker, recentBuild?)` and `isRecentBuild(setAt, now, windowMs?)` are the pure predicates.
35
35
  - `./createServerErrorAction` — `createServerErrorAction(action, config)`: wrapper for server actions with standardized error reporting.
36
36
 
37
37
  ## `firebaseAuth*` subpaths (require `firebaseAuth` set on your `RoutingConfig`)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cloudflare-next-intl",
3
- "version": "0.8.46",
3
+ "version": "0.8.48",
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",