cloudflare-next-intl 0.9.51 → 0.9.52

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
@@ -690,7 +690,7 @@ through `@intl-config`.
690
690
 
691
691
  When a new version of your application is deployed to Cloudflare Workers, users on older client sessions may encounter `ChunkLoadError` or failed dynamic imports when requesting outdated chunks.
692
692
 
693
- `IntlHelperScript` renders an early-catch `<script>` (production only, id `stale-deploy-early-catch`) that runs before hydration and listens for `window.error`/`unhandledrejection` events matching the same patterns as `isStaleDeployError` (inlined as JSON, so it stays in sync with `staleDeployPatterns` config), then force-reloads once per build id. This covers the case a React-level recovery (`useStaleDeployRecovery` below) cannot: when the chunk that failed to load is part of your own error boundary/global-error bundle, React never gets a chance to render the recovery UI. Both layers share the same `sessionStorage['stale-deploy-recovery-reloaded']` marker keyed by build id, so they can't double-reload each other. No setup beyond rendering `<IntlHelperScript />` is required.
693
+ `IntlHelperScript` renders an early-catch `<script>` (production only, id `stale-deploy-early-catch`) that runs before hydration and listens for `window.error`/`unhandledrejection` events matching the same patterns as `isStaleDeployError` (inlined as JSON, so it stays in sync with `staleDeployPatterns` config), then force-reloads once per build id (throttled to once per 15s so a later build-id marker can re-arm recovery instead of being blocked indefinitely). It also listens for `error` events during the capture phase to catch resource-load failures (a chunk `<script>`/`<link>` 404ing or served with a disallowed MIME type), which fire a non-bubbling, message-less `error` event on the element itself rather than surfacing as a catchable message. This covers the case a React-level recovery (`useStaleDeployRecovery` below) cannot: when the chunk that failed to load is part of your own error boundary/global-error bundle, React never gets a chance to render the recovery UI. Both layers share the same `sessionStorage['stale-deploy-recovery-reloaded']` marker keyed by build id, so they can't double-reload each other. No setup beyond rendering `<IntlHelperScript />` is required.
694
694
 
695
695
  For errors that don't crash the module graph itself (a normal thrown error reaching an error boundary), use `isStaleDeployError` and `clearClientCache` in error boundaries or global error handlers to automatically recover:
696
696
 
@@ -17,6 +17,8 @@ export default function HelperScript() {
17
17
  try {
18
18
  var patterns = ${JSON.stringify(defaultStaleDeployPatterns)};
19
19
  var key = 'stale-deploy-recovery-reloaded';
20
+ var timeKey = 'stale-deploy-recovery-time';
21
+ var throttleMs = 15000;
20
22
  var attemptedThisLoad = false;
21
23
  function isStale(msg) {
22
24
  if (msg === undefined || msg === null) return true;
@@ -34,12 +36,16 @@ export default function HelperScript() {
34
36
  if (!stale) return;
35
37
  var buildId = localStorage.getItem('buildId') || 'unknown';
36
38
  var marker = sessionStorage.getItem(key);
37
- if (marker === buildId) {
39
+ var lastRaw = sessionStorage.getItem(timeKey);
40
+ var last = lastRaw ? Number(lastRaw) : null;
41
+ var throttled = last !== null && (Date.now() - last) < throttleMs;
42
+ if (marker === buildId && throttled) {
38
43
  console.warn('[StaleDeploy early-catch] Skipping reload, already attempted for buildId:', buildId);
39
44
  return;
40
45
  }
41
46
  attemptedThisLoad = true;
42
47
  sessionStorage.setItem(key, buildId);
48
+ sessionStorage.setItem(timeKey, String(Date.now()));
43
49
  try {
44
50
  if (document.documentElement) {
45
51
  document.documentElement.style.backgroundColor = '#ffffff';
@@ -61,6 +67,21 @@ export default function HelperScript() {
61
67
  }
62
68
  }
63
69
  window.addEventListener('error', function(e) { recover(e.message, 'error-event'); });
70
+ // Resource-load failures (a chunk 404ing or served with a
71
+ // disallowed MIME type) fire a non-bubbling 'error' event on the
72
+ // element itself, so they only reach window during capture, and
73
+ // they carry no message. Treat a failed script/link as stale.
74
+ window.addEventListener('error', function(e) {
75
+ try {
76
+ var el = e.target;
77
+ if (!el || el === window) return;
78
+ var tag = (el.tagName || '').toLowerCase();
79
+ if (tag !== 'script' && tag !== 'link') return;
80
+ var src = el.src || el.href || '';
81
+ if (!src) return;
82
+ recover('chunk resource failed to load: ' + src, 'resource-error');
83
+ } catch (err) {}
84
+ }, true);
64
85
  window.addEventListener('unhandledrejection', function(e) {
65
86
  recover(e.reason && (e.reason.message || e.reason), 'unhandledrejection');
66
87
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cloudflare-next-intl",
3
- "version": "0.9.51",
3
+ "version": "0.9.52",
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",