cloudflare-next-intl 0.9.28 → 0.9.32

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.
@@ -7,4 +7,6 @@ export type ResolvedDbMode = {
7
7
  mode: 'supabase';
8
8
  supabase: SupabaseDbConfig;
9
9
  };
10
- export default function resolveDbMode(db: DbRoutingConfig, generate?: GenerateRoutingConfig): Promise<ResolvedDbMode>;
10
+ declare function resolveDbModeUncached(db: DbRoutingConfig, generate?: GenerateRoutingConfig): Promise<ResolvedDbMode>;
11
+ declare const resolveDbMode: typeof resolveDbModeUncached;
12
+ export default resolveDbMode;
@@ -1,6 +1,7 @@
1
+ import { cache } from 'react';
1
2
  import resolveConfigValue from './resolve_config_value.js';
2
3
  import { resolveHyperdriveConnectionString } from './resolve_hyperdrive_connection_string.js';
3
- export default async function resolveDbMode(db, generate) {
4
+ async function resolveDbModeUncached(db, generate) {
4
5
  const connectionString = await resolveConfigValue(db.connectionString);
5
6
  if (connectionString)
6
7
  return { mode: 'postgres', connectionString };
@@ -13,3 +14,5 @@ export default async function resolveDbMode(db, generate) {
13
14
  return { mode: 'supabase', supabase: db.supabase };
14
15
  return { mode: 'postgres', connectionString: undefined };
15
16
  }
17
+ const resolveDbMode = cache(resolveDbModeUncached);
18
+ export default resolveDbMode;
@@ -1,12 +1,11 @@
1
+ import { cache } from 'react';
1
2
  import resolveSupabaseEndpoint from './supabase_config.js';
3
+ const getOrCreateClient = cache(async (supabase, bearerToken) => {
4
+ const { url, anonKey } = await resolveSupabaseEndpoint(supabase);
5
+ const { createClient } = await import('@supabase/supabase-js');
6
+ return createClient(url, anonKey, { accessToken: async () => bearerToken });
7
+ });
2
8
  export default function createRestClient(supabase, bearerToken) {
3
9
  let clientPromise = null;
4
- return () => {
5
- clientPromise ?? (clientPromise = (async () => {
6
- const { url, anonKey } = await resolveSupabaseEndpoint(supabase);
7
- const { createClient } = await import('@supabase/supabase-js');
8
- return createClient(url, anonKey, { accessToken: async () => bearerToken });
9
- })());
10
- return clientPromise;
11
- };
10
+ return () => (clientPromise ?? (clientPromise = getOrCreateClient(supabase, bearerToken)));
12
11
  }
@@ -1,8 +1,9 @@
1
1
  import { type LinkProps } from 'next/link.js';
2
2
  import { type ComponentProps } from 'react';
3
- export type PrefetchType = 'custom' | 'default';
3
+ export type PrefetchType = 'custom' | 'eager' | 'default';
4
4
  type NextLinkProps = Omit<ComponentProps<'a'>, keyof LinkProps> & Omit<LinkProps, 'locale'> & {
5
5
  prefetchType?: PrefetchType;
6
6
  };
7
+ export declare const PENDING_NAVIGATION_EVENT = "cloudflare-next-intl:pending-navigation";
7
8
  declare const Link: import("react").ForwardRefExoticComponent<Omit<NextLinkProps, "ref"> & import("react").RefAttributes<HTMLAnchorElement>>;
8
9
  export default Link;
@@ -1,14 +1,16 @@
1
1
  "use client";
2
2
  import { jsx as _jsx } from "react/jsx-runtime";
3
3
  import LinkComponent from 'next/link.js';
4
- import { forwardRef, useCallback, useEffect, useState, useTransition, } from 'react';
4
+ import { forwardRef, useCallback, useEffect, useRef, useState, useTransition, } from 'react';
5
5
  import config from '../../config/intl_config.js';
6
6
  import { getLocaleCache } from '../../general/cache_variables.js';
7
7
  import { usePathname, useRouter } from 'next/navigation.js';
8
8
  const prefetchedRoutes = new Set();
9
- function CustomLinkFunction({ href, prefetch, prefetchType = 'custom', onClick, onMouseEnter, onPointerDown, ...rest }, ref) {
9
+ export const PENDING_NAVIGATION_EVENT = 'cloudflare-next-intl:pending-navigation';
10
+ function CustomLinkFunction({ href, prefetch, prefetchType = 'custom', onClick, onMouseEnter, onMouseLeave, onPointerDown, ...rest }, ref) {
10
11
  const localeValue = getLocaleCache();
11
12
  const router = useRouter();
13
+ prefetch ?? (prefetch = config.link?.defaultPrefetch ?? false);
12
14
  const needsLangPath = localeValue !== config.defaultLocale || !localeValue;
13
15
  let pathnames;
14
16
  let urlString;
@@ -26,10 +28,19 @@ function CustomLinkFunction({ href, prefetch, prefetchType = 'custom', onClick,
26
28
  const [isNavigating, setIsNavigating] = useState(false);
27
29
  useEffect(() => {
28
30
  setIsNavigating(false);
31
+ window.dispatchEvent(new CustomEvent(PENDING_NAVIGATION_EVENT, { detail: null }));
29
32
  }, [pathname]);
30
- const isCustom = prefetchType === 'custom' && prefetch !== false;
31
- const triggerPrefetch = useCallback(() => {
32
- if (!isCustom || !urlString || urlString.startsWith('#') || prefetchedRoutes.has(urlString)) {
33
+ useEffect(() => {
34
+ if (!isNavigating)
35
+ return;
36
+ const timer = setTimeout(() => setIsNavigating(false), 10000);
37
+ return () => clearTimeout(timer);
38
+ }, [isNavigating]);
39
+ const isCustom = prefetchType === 'custom' || prefetchType === 'eager';
40
+ const prefetchEnabled = isCustom && prefetch !== false;
41
+ const isEager = prefetchType === 'eager' && prefetchEnabled;
42
+ const doPrefetch = useCallback(() => {
43
+ if (!urlString || urlString.startsWith('#') || prefetchedRoutes.has(urlString)) {
33
44
  return;
34
45
  }
35
46
  prefetchedRoutes.add(urlString);
@@ -38,18 +49,35 @@ function CustomLinkFunction({ href, prefetch, prefetchType = 'custom', onClick,
38
49
  }
39
50
  catch {
40
51
  }
41
- }, [isCustom, urlString, pathnames, router]);
42
- useEffect(() => {
52
+ }, [urlString, pathnames, router]);
53
+ const triggerPointerDownPrefetch = useCallback(() => {
43
54
  if (!isCustom)
44
55
  return;
45
- const timer = setTimeout(triggerPrefetch, 100);
56
+ doPrefetch();
57
+ }, [isCustom, doPrefetch]);
58
+ useEffect(() => {
59
+ if (!isEager)
60
+ return;
61
+ const timer = setTimeout(doPrefetch, 100);
46
62
  return () => clearTimeout(timer);
47
- }, [urlString, isCustom, triggerPrefetch]);
48
- const handleHoverPrefetch = () => {
49
- if (isCustom) {
50
- triggerPrefetch();
63
+ }, [urlString, isEager, doPrefetch]);
64
+ const hoverTimerRef = useRef(null);
65
+ const clearHoverTimer = () => {
66
+ if (hoverTimerRef.current) {
67
+ clearTimeout(hoverTimerRef.current);
68
+ hoverTimerRef.current = null;
51
69
  }
52
70
  };
71
+ const handleHoverStart = () => {
72
+ if (!prefetchEnabled)
73
+ return;
74
+ clearHoverTimer();
75
+ hoverTimerRef.current = setTimeout(doPrefetch, 100);
76
+ };
77
+ const handleHoverEnd = () => {
78
+ clearHoverTimer();
79
+ };
80
+ useEffect(() => clearHoverTimer, []);
53
81
  const handleClick = (e) => {
54
82
  onClick?.(e);
55
83
  if (e.defaultPrevented)
@@ -62,6 +90,12 @@ function CustomLinkFunction({ href, prefetch, prefetchType = 'custom', onClick,
62
90
  const targetPath = typeof pathnames === 'string' ? pathnames : urlString;
63
91
  if (pathname !== targetPath) {
64
92
  setIsNavigating(true);
93
+ try {
94
+ window.history.replaceState(window.history.state, '', targetPath);
95
+ window.dispatchEvent(new CustomEvent(PENDING_NAVIGATION_EVENT, { detail: targetPath }));
96
+ }
97
+ catch {
98
+ }
65
99
  }
66
100
  startTransition(() => {
67
101
  router.push(targetPath);
@@ -71,10 +105,13 @@ function CustomLinkFunction({ href, prefetch, prefetchType = 'custom', onClick,
71
105
  };
72
106
  const effectivePrefetch = isCustom ? false : prefetch;
73
107
  return _jsx(LinkComponent, { ref: ref, href: pathnames, prefetch: effectivePrefetch, onClick: handleClick, onMouseEnter: (e) => {
74
- handleHoverPrefetch();
108
+ handleHoverStart();
75
109
  onMouseEnter?.(e);
110
+ }, onMouseLeave: (e) => {
111
+ handleHoverEnd();
112
+ onMouseLeave?.(e);
76
113
  }, onPointerDown: (e) => {
77
- handleHoverPrefetch();
114
+ triggerPointerDownPrefetch();
78
115
  onPointerDown?.(e);
79
116
  }, ...rest });
80
117
  }
@@ -3,4 +3,6 @@ export declare const defaultCountryHeaderNames: readonly string[];
3
3
  export declare const defaultTimezoneHeaderNames: readonly string[];
4
4
  export declare function getCountry(input?: RequestOrHeaders, generate?: GenerateRoutingConfig, headerNames?: readonly string[]): Promise<string | undefined>;
5
5
  export declare function getTimezone(input?: RequestOrHeaders, fallback?: string, generate?: GenerateRoutingConfig, headerNames?: readonly string[]): Promise<string | undefined>;
6
- export declare function resolveEnv(generate?: GenerateRoutingConfig): Promise<Record<string, unknown> | undefined>;
6
+ declare function resolveEnvUncached(generate?: GenerateRoutingConfig): Promise<Record<string, unknown> | undefined>;
7
+ export declare const resolveEnv: typeof resolveEnvUncached;
8
+ export {};
@@ -1,3 +1,4 @@
1
+ import { cache } from 'react';
1
2
  import { countryCookieKey, timezoneCookieKey } from '../../config/cookie_key.js';
2
3
  export const defaultCountryHeaderNames = ['x-cf-country', 'cf-ipcountry'];
3
4
  export const defaultTimezoneHeaderNames = ['x-cf-timezone', 'cf-timezone'];
@@ -201,7 +202,7 @@ export async function getTimezone(input, fallback, generate, headerNames) {
201
202
  }
202
203
  return fallback;
203
204
  }
204
- export async function resolveEnv(generate) {
205
+ async function resolveEnvUncached(generate) {
205
206
  if (!generate)
206
207
  return undefined;
207
208
  if (generate.env) {
@@ -219,3 +220,4 @@ export async function resolveEnv(generate) {
219
220
  }
220
221
  return undefined;
221
222
  }
223
+ export const resolveEnv = cache(resolveEnvUncached);
@@ -19,6 +19,10 @@ export interface RoutingConfig<AppLocales extends Locales, AppLocalePrefixMode e
19
19
  cookieConsent?: CookieConsentRoutingConfig;
20
20
  generate?: GenerateRoutingConfig;
21
21
  errorHandling?: ErrorHandlingRoutingConfig;
22
+ link?: LinkRoutingConfig;
23
+ }
24
+ export interface LinkRoutingConfig {
25
+ defaultPrefetch?: boolean;
22
26
  }
23
27
  export interface GenerateRoutingConfig {
24
28
  env?: object | Record<string, unknown> | (() => object | Record<string, unknown> | Promise<object | Record<string, unknown>>);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cloudflare-next-intl",
3
- "version": "0.9.28",
3
+ "version": "0.9.32",
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",