cloudflare-next-intl 0.9.21 → 0.9.22

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.
@@ -44,9 +44,24 @@ function canRecover(error) {
44
44
  try {
45
45
  const reloadTimeRaw = sessionStorage.getItem(RECOVERY_TIME_KEY);
46
46
  const reloadTime = reloadTimeRaw ? Number(reloadTimeRaw) : null;
47
- return shouldRecoverFromStaleDeploy(error, currentBuildId(), sessionStorage.getItem(RECOVERY_RELOAD_KEY), isRecentBuild(buildIdSetAt(), Date.now()), reloadTime, Date.now());
47
+ const bId = currentBuildId();
48
+ const marker = sessionStorage.getItem(RECOVERY_RELOAD_KEY);
49
+ const isRecent = isRecentBuild(buildIdSetAt(), Date.now());
50
+ const isStale = isStaleDeployError(error);
51
+ const result = shouldRecoverFromStaleDeploy(error, bId, marker, isRecent, reloadTime, Date.now());
52
+ console.warn('[useStaleDeployRecovery]', {
53
+ error,
54
+ isStale,
55
+ bId,
56
+ marker,
57
+ isRecent,
58
+ reloadTime,
59
+ result,
60
+ });
61
+ return result;
48
62
  }
49
- catch {
63
+ catch (e) {
64
+ console.error('[useStaleDeployRecovery] Error in canRecover:', e);
50
65
  return false;
51
66
  }
52
67
  }
@@ -23,23 +23,21 @@ export default function HelperScript() {
23
23
  }
24
24
  return false;
25
25
  }
26
- function recover(msg) {
27
- // One reload attempt per page load, no matter how many matching
28
- // errors fire in a row (a single stale deploy commonly throws
29
- // several near-simultaneous chunk failures) — without this guard
30
- // each one would race to read-then-write the same sessionStorage
31
- // key and could re-trigger reload() multiple times before the
32
- // first navigation lands.
26
+ function recover(msg, source) {
33
27
  if (attemptedThisLoad) return;
34
28
  try {
35
- if (!isStale(msg)) return;
29
+ var stale = isStale(msg);
30
+ console.warn('[StaleDeploy early-catch] Intercepted:', { source: source, msg: msg, isStale: stale });
31
+ if (!stale) return;
36
32
  var buildId = localStorage.getItem('buildId') || 'unknown';
37
- // Same marker/key as useStaleDeployRecovery: one reload per
38
- // build id, so a repeat failure on a build that already spent
39
- // its reload falls through instead of reloading forever.
40
- if (sessionStorage.getItem(key) === buildId) return;
33
+ var marker = sessionStorage.getItem(key);
34
+ if (marker === buildId) {
35
+ console.warn('[StaleDeploy early-catch] Skipping reload, already attempted for buildId:', buildId);
36
+ return;
37
+ }
41
38
  attemptedThisLoad = true;
42
39
  sessionStorage.setItem(key, buildId);
40
+ console.warn('[StaleDeploy early-catch] Reloading for buildId:', buildId);
43
41
  try {
44
42
  var u = new URL(window.location.href);
45
43
  u.searchParams.set('_stale_reload', String(Date.now()));
@@ -51,9 +49,9 @@ export default function HelperScript() {
51
49
  console.error('Stale Deploy Early Catch Script Error:', e);
52
50
  }
53
51
  }
54
- window.addEventListener('error', function(e) { recover(e.message); });
52
+ window.addEventListener('error', function(e) { recover(e.message, 'error-event'); });
55
53
  window.addEventListener('unhandledrejection', function(e) {
56
- recover(e.reason && e.reason.message);
54
+ recover(e.reason && (e.reason.message || e.reason), 'unhandledrejection');
57
55
  });
58
56
  })();`
59
57
  } }), !isDev &&
@@ -1,5 +1,8 @@
1
1
  import { type LinkProps } from 'next/link.js';
2
2
  import { type ComponentProps } from 'react';
3
- type NextLinkProps = Omit<ComponentProps<'a'>, keyof LinkProps> & Omit<LinkProps, 'locale'>;
3
+ export type PrefetchType = 'custom' | 'default';
4
+ type NextLinkProps = Omit<ComponentProps<'a'>, keyof LinkProps> & Omit<LinkProps, 'locale'> & {
5
+ prefetchType?: PrefetchType;
6
+ };
4
7
  declare const Link: import("react").ForwardRefExoticComponent<Omit<NextLinkProps, "ref"> & import("react").RefAttributes<HTMLAnchorElement>>;
5
8
  export default Link;
@@ -1,26 +1,79 @@
1
+ "use client";
1
2
  import { jsx as _jsx } from "react/jsx-runtime";
2
3
  import LinkComponent from 'next/link.js';
3
- import { forwardRef, } from 'react';
4
+ import { forwardRef, useEffect, useState, useTransition, } from 'react';
4
5
  import config from '../../config/intl_config.js';
5
6
  import { getLocaleCache } from '../../general/cache_variables.js';
6
- function CustomLinkFunction({ href, prefetch, ...rest }, ref) {
7
+ import { usePathname, useRouter } from 'next/navigation.js';
8
+ const prefetchedRoutes = new Set();
9
+ function CustomLinkFunction({ href, prefetch, prefetchType = 'custom', onClick, onMouseEnter, onPointerDown, ...rest }, ref) {
7
10
  const localeValue = getLocaleCache();
11
+ const router = useRouter();
12
+ const pathname = usePathname();
13
+ const [isPending, startTransition] = useTransition();
14
+ const [isNavigating, setIsNavigating] = useState(false);
8
15
  const needsLangPath = localeValue !== config.defaultLocale || !localeValue;
9
16
  let pathnames;
17
+ let urlString;
10
18
  if (needsLangPath) {
11
- let pathname;
12
- if (typeof href === 'object') {
13
- pathname = href.pathname || '';
14
- }
15
- else {
16
- pathname = href;
17
- }
18
- pathnames = `/${localeValue}${pathname}`;
19
+ const pathPart = typeof href === 'object' ? (href.pathname || '') : (href || '');
20
+ pathnames = `/${localeValue}${pathPart}`;
21
+ urlString = pathnames;
19
22
  }
20
23
  else {
21
24
  pathnames = href;
25
+ urlString = typeof href === 'object' ? (href.pathname || '') : (href || '');
22
26
  }
23
- return _jsx(LinkComponent, { ref: ref, href: pathnames, prefetch: prefetch, ...rest });
27
+ const isCustom = prefetchType === 'custom';
28
+ useEffect(() => {
29
+ setIsNavigating(false);
30
+ }, [pathname]);
31
+ const triggerPrefetch = () => {
32
+ if (!isCustom || !urlString || urlString.startsWith('#') || prefetchedRoutes.has(urlString)) {
33
+ return;
34
+ }
35
+ prefetchedRoutes.add(urlString);
36
+ try {
37
+ router.prefetch(typeof pathnames === 'string' ? pathnames : urlString);
38
+ }
39
+ catch {
40
+ }
41
+ };
42
+ useEffect(() => {
43
+ if (!isCustom)
44
+ return;
45
+ const timer = setTimeout(triggerPrefetch, 600);
46
+ return () => clearTimeout(timer);
47
+ }, [urlString, isCustom]);
48
+ const handleHoverPrefetch = () => {
49
+ if (isCustom) {
50
+ triggerPrefetch();
51
+ }
52
+ };
53
+ const handleClick = (e) => {
54
+ onClick?.(e);
55
+ if (e.defaultPrevented)
56
+ return;
57
+ if (isCustom && (isNavigating || isPending)) {
58
+ e.preventDefault();
59
+ return;
60
+ }
61
+ if (isCustom) {
62
+ setIsNavigating(true);
63
+ startTransition(() => {
64
+ router.push(typeof pathnames === 'string' ? pathnames : urlString);
65
+ });
66
+ e.preventDefault();
67
+ }
68
+ };
69
+ const effectivePrefetch = isCustom ? (prefetch ?? false) : prefetch;
70
+ return _jsx(LinkComponent, { ref: ref, href: pathnames, prefetch: effectivePrefetch, onClick: handleClick, onMouseEnter: (e) => {
71
+ handleHoverPrefetch();
72
+ onMouseEnter?.(e);
73
+ }, onPointerDown: (e) => {
74
+ handleHoverPrefetch();
75
+ onPointerDown?.(e);
76
+ }, ...rest });
24
77
  }
25
78
  const Link = forwardRef(CustomLinkFunction);
26
79
  export default Link;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cloudflare-next-intl",
3
- "version": "0.9.21",
3
+ "version": "0.9.22",
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",