@lolyjs/core 0.1.0-alpha.2 → 0.1.0-alpha.4
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/dist/cli.cjs.map +1 -1
- package/dist/cli.js +1 -1
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +25 -9
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +26 -10
- package/dist/index.js.map +1 -1
- package/dist/runtime.cjs +25 -9
- package/dist/runtime.cjs.map +1 -1
- package/dist/runtime.js +26 -10
- package/dist/runtime.js.map +1 -1
- package/package.json +1 -1
package/dist/runtime.cjs
CHANGED
|
@@ -472,11 +472,19 @@ async function navigate(nextUrl, handlers, options) {
|
|
|
472
472
|
}
|
|
473
473
|
function createClickHandler(navigate2) {
|
|
474
474
|
return function handleClick(ev) {
|
|
475
|
+
const path = ev.composedPath();
|
|
476
|
+
for (const element of path) {
|
|
477
|
+
if (!(element instanceof HTMLElement)) continue;
|
|
478
|
+
const tagName = element.tagName.toLowerCase();
|
|
479
|
+
if (tagName === "input" || tagName === "textarea" || tagName === "button" || tagName === "select" || element.isContentEditable || tagName === "label" && element.control) {
|
|
480
|
+
return;
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
const target = ev.target;
|
|
484
|
+
if (!target) return;
|
|
475
485
|
if (ev.defaultPrevented) return;
|
|
476
486
|
if (ev.button !== 0) return;
|
|
477
487
|
if (ev.metaKey || ev.ctrlKey || ev.shiftKey || ev.altKey) return;
|
|
478
|
-
const target = ev.target;
|
|
479
|
-
if (!target) return;
|
|
480
488
|
const anchor = target.closest("a[href]");
|
|
481
489
|
if (!anchor) return;
|
|
482
490
|
const href = anchor.getAttribute("href");
|
|
@@ -510,25 +518,33 @@ function AppShell({
|
|
|
510
518
|
errorRoute
|
|
511
519
|
}) {
|
|
512
520
|
const [state, setState] = (0, import_react.useState)(initialState);
|
|
521
|
+
const handlersRef = (0, import_react.useRef)({
|
|
522
|
+
setState,
|
|
523
|
+
routes,
|
|
524
|
+
notFoundRoute,
|
|
525
|
+
errorRoute
|
|
526
|
+
});
|
|
513
527
|
(0, import_react.useEffect)(() => {
|
|
514
|
-
|
|
528
|
+
handlersRef.current = {
|
|
515
529
|
setState,
|
|
516
530
|
routes,
|
|
517
531
|
notFoundRoute,
|
|
518
532
|
errorRoute
|
|
519
533
|
};
|
|
534
|
+
}, [routes, notFoundRoute, errorRoute]);
|
|
535
|
+
(0, import_react.useEffect)(() => {
|
|
520
536
|
async function handleNavigate(nextUrl, options) {
|
|
521
|
-
await navigate(nextUrl,
|
|
537
|
+
await navigate(nextUrl, handlersRef.current, options);
|
|
522
538
|
}
|
|
523
539
|
const handleClick = createClickHandler(handleNavigate);
|
|
524
540
|
const handlePopState = createPopStateHandler(handleNavigate);
|
|
525
|
-
window.addEventListener("click", handleClick);
|
|
526
|
-
window.addEventListener("popstate", handlePopState);
|
|
541
|
+
window.addEventListener("click", handleClick, { capture: false });
|
|
542
|
+
window.addEventListener("popstate", handlePopState, { capture: false });
|
|
527
543
|
return () => {
|
|
528
|
-
window.removeEventListener("click", handleClick);
|
|
529
|
-
window.removeEventListener("popstate", handlePopState);
|
|
544
|
+
window.removeEventListener("click", handleClick, { capture: false });
|
|
545
|
+
window.removeEventListener("popstate", handlePopState, { capture: false });
|
|
530
546
|
};
|
|
531
|
-
}, [
|
|
547
|
+
}, []);
|
|
532
548
|
const isError = state.route === errorRoute;
|
|
533
549
|
const isNotFound = state.route === notFoundRoute;
|
|
534
550
|
const routeType = isError ? "error" : isNotFound ? "notfound" : "normal";
|
package/dist/runtime.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../modules/runtime/client/index.tsx","../modules/runtime/client/bootstrap.tsx","../modules/runtime/client/constants.ts","../modules/runtime/client/window-data.ts","../modules/runtime/client/route-matcher.ts","../modules/runtime/client/metadata.ts","../modules/runtime/client/AppShell.tsx","../modules/runtime/client/RouterView.tsx","../modules/react/cache/client-data-cache/index.ts","../modules/runtime/client/navigation.ts"],"sourcesContent":["// Re-export all public types and functions\r\nexport type {\r\n ClientLoadedComponents,\r\n ClientRouteLoaded,\r\n ClientRouteMatch,\r\n RouteViewState,\r\n InitialData,\r\n} from \"./types\";\r\n\r\nexport { bootstrapClient } from \"./bootstrap\";\r\nexport type { AppShellProps } from \"./AppShell\";\r\n","import { hydrateRoot } from \"react-dom/client\";\r\nimport { APP_CONTAINER_ID } from \"./constants\";\r\nimport { getWindowData } from \"./window-data\";\r\nimport { matchRouteClient } from \"./route-matcher\";\r\nimport { applyMetadata } from \"./metadata\";\r\nimport { AppShell } from \"./AppShell\";\r\nimport type {\r\n InitialData,\r\n ClientRouteLoaded,\r\n RouteViewState,\r\n} from \"./types\";\r\n\r\nexport async function loadInitialRoute(\r\n initialUrl: string,\r\n initialData: InitialData | null,\r\n routes: ClientRouteLoaded[],\r\n notFoundRoute: ClientRouteLoaded | null,\r\n errorRoute: ClientRouteLoaded | null\r\n): Promise<RouteViewState> {\r\n const isInitialNotFound = initialData?.notFound === true;\r\n const isInitialError = initialData?.error === true;\r\n\r\n let initialRoute: ClientRouteLoaded | null = null;\r\n let initialParams: Record<string, string> = {};\r\n let initialComponents = null;\r\n\r\n if (isInitialError && errorRoute) {\r\n initialRoute = errorRoute;\r\n initialParams = initialData?.params ?? {};\r\n initialComponents = await errorRoute.load();\r\n } else if (isInitialNotFound && notFoundRoute) {\r\n initialRoute = notFoundRoute;\r\n initialParams = {};\r\n initialComponents = await notFoundRoute.load();\r\n } else {\r\n const match = matchRouteClient(initialUrl, routes);\r\n if (match) {\r\n initialRoute = match.route;\r\n initialParams = match.params;\r\n initialComponents = await match.route.load();\r\n } else if (notFoundRoute) {\r\n initialRoute = notFoundRoute;\r\n initialParams = {};\r\n initialComponents = await notFoundRoute.load();\r\n } else {\r\n console.warn(\r\n `[client] No route match found for ${initialUrl}. Available routes:`,\r\n routes.map((r) => r.pattern)\r\n );\r\n }\r\n }\r\n\r\n return {\r\n url: initialUrl,\r\n route: initialRoute,\r\n params: initialParams,\r\n components: initialComponents,\r\n props: initialData?.props ?? {},\r\n };\r\n}\r\n\r\n/**\r\n * Bootstraps the client-side application.\r\n *\r\n * @param routes - Array of client routes\r\n * @param notFoundRoute - Not-found route definition\r\n * @param errorRoute - Error route definition\r\n */\r\nexport function bootstrapClient(\r\n routes: ClientRouteLoaded[],\r\n notFoundRoute: ClientRouteLoaded | null,\r\n errorRoute: ClientRouteLoaded | null = null\r\n): void {\r\n (async function bootstrap() {\r\n const container = document.getElementById(APP_CONTAINER_ID);\r\n const initialData = getWindowData();\r\n\r\n if (!container) {\r\n console.error(`Container #${APP_CONTAINER_ID} not found for hydration`);\r\n return;\r\n }\r\n\r\n const initialUrl = window.location.pathname + window.location.search;\r\n\r\n try {\r\n const initialState = await loadInitialRoute(\r\n initialUrl,\r\n initialData,\r\n routes,\r\n notFoundRoute,\r\n errorRoute\r\n );\r\n\r\n if (initialData?.metadata) {\r\n applyMetadata(initialData.metadata);\r\n }\r\n\r\n hydrateRoot(\r\n container,\r\n <AppShell\r\n initialState={initialState}\r\n routes={routes}\r\n notFoundRoute={notFoundRoute}\r\n errorRoute={errorRoute}\r\n />\r\n );\r\n } catch (error) {\r\n console.error(\r\n \"[client] Error loading initial route components for\",\r\n initialUrl,\r\n error\r\n );\r\n\r\n window.location.reload();\r\n }\r\n })();\r\n}\r\n\r\n","// Client-side constants (hardcoded to avoid alias resolution issues in Rspack)\r\nexport const WINDOW_DATA_KEY = \"__FW_DATA__\";\r\nexport const APP_CONTAINER_ID = \"__app\";\r\n\r\n","import { WINDOW_DATA_KEY } from \"./constants\";\r\nimport type { InitialData } from \"./types\";\r\n\r\nexport function getWindowData(): InitialData | null {\r\n return ((window as any)[WINDOW_DATA_KEY] as InitialData | undefined) ?? null;\r\n}\r\n\r\nexport function setWindowData(data: InitialData): void {\r\n (window as any)[WINDOW_DATA_KEY] = data;\r\n \r\n // Dispatch event for components to listen to (e.g., usePageProps, ThemeProvider)\r\n // This ensures components update when navigating in SPA mode\r\n if (typeof window !== \"undefined\") {\r\n window.dispatchEvent(\r\n new CustomEvent(\"fw-data-refresh\", {\r\n detail: { data },\r\n })\r\n );\r\n }\r\n}\r\n\r\nexport function getCurrentTheme(): string | null {\r\n return getWindowData()?.theme ?? null;\r\n}\r\n\r\n","import type { ClientRouteLoaded, ClientRouteMatch } from \"./types\";\r\n\r\nexport function buildClientRegexFromPattern(pattern: string): RegExp {\r\n const segments = pattern.split(\"/\").filter(Boolean);\r\n const regexParts: string[] = [];\r\n\r\n for (let i = 0; i < segments.length; i++) {\r\n const seg = segments[i];\r\n\r\n // catch-all [...slug]\r\n if (seg.startsWith(\"[...\") && seg.endsWith(\"]\")) {\r\n if (i !== segments.length - 1) {\r\n throw new Error(\r\n `Catch-all segment \"${seg}\" in \"${pattern}\" must be the last segment.`\r\n );\r\n }\r\n regexParts.push(\"(.+)\");\r\n continue;\r\n }\r\n\r\n // dynamic [id]\r\n if (seg.startsWith(\"[\") && seg.endsWith(\"]\")) {\r\n regexParts.push(\"([^/]+)\");\r\n continue;\r\n }\r\n\r\n // static segment\r\n const escaped = seg.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\r\n regexParts.push(escaped);\r\n }\r\n\r\n const regexSource = \"^/\" + regexParts.join(\"/\") + \"/?$\";\r\n return new RegExp(regexSource);\r\n}\r\n\r\nexport function matchRouteClient(\r\n pathWithSearch: string,\r\n routes: ClientRouteLoaded[]\r\n): ClientRouteMatch | null {\r\n const [pathname] = pathWithSearch.split(\"?\");\r\n for (const r of routes) {\r\n const regex = buildClientRegexFromPattern(r.pattern);\r\n const match = regex.exec(pathname);\r\n if (!match) continue;\r\n\r\n const params: Record<string, string> = {};\r\n r.paramNames.forEach((name, idx) => {\r\n params[name] = decodeURIComponent(match[idx + 1] || \"\");\r\n });\r\n\r\n return { route: r, params };\r\n }\r\n return null;\r\n}\r\n\r\n","export function applyMetadata(\r\n md?: { title?: string; description?: string } | null\r\n) {\r\n if (!md) return;\r\n\r\n if (md.title) {\r\n document.title = md.title;\r\n }\r\n\r\n if (md.description) {\r\n let meta = document.querySelector(\r\n 'meta[name=\"description\"]'\r\n ) as HTMLMetaElement | null;\r\n\r\n if (!meta) {\r\n meta = document.createElement(\"meta\");\r\n meta.name = \"description\";\r\n document.head.appendChild(meta);\r\n }\r\n\r\n meta.content = md.description;\r\n }\r\n}\r\n\r\n","import { useEffect, useState } from \"react\";\r\nimport { RouterView } from \"./RouterView\";\r\nimport {\r\n navigate,\r\n createClickHandler,\r\n createPopStateHandler,\r\n type NavigationHandlers,\r\n} from \"./navigation\";\r\nimport type {\r\n RouteViewState,\r\n ClientRouteLoaded,\r\n} from \"./types\";\r\n\r\nexport interface AppShellProps {\r\n initialState: RouteViewState;\r\n routes: ClientRouteLoaded[];\r\n notFoundRoute: ClientRouteLoaded | null;\r\n errorRoute: ClientRouteLoaded | null;\r\n}\r\n\r\nexport function AppShell({\r\n initialState,\r\n routes,\r\n notFoundRoute,\r\n errorRoute,\r\n}: AppShellProps) {\r\n const [state, setState] = useState<RouteViewState>(initialState);\r\n\r\n useEffect(() => {\r\n const handlers: NavigationHandlers = {\r\n setState,\r\n routes,\r\n notFoundRoute,\r\n errorRoute,\r\n };\r\n\r\n async function handleNavigate(\r\n nextUrl: string,\r\n options?: { revalidate?: boolean }\r\n ) {\r\n await navigate(nextUrl, handlers, options);\r\n }\r\n\r\n const handleClick = createClickHandler(handleNavigate);\r\n const handlePopState = createPopStateHandler(handleNavigate);\r\n\r\n window.addEventListener(\"click\", handleClick);\r\n window.addEventListener(\"popstate\", handlePopState);\r\n\r\n return () => {\r\n window.removeEventListener(\"click\", handleClick);\r\n window.removeEventListener(\"popstate\", handlePopState);\r\n };\r\n }, [routes, notFoundRoute, errorRoute]);\r\n\r\n const isError = state.route === errorRoute;\r\n const isNotFound = state.route === notFoundRoute;\r\n const routeType = isError ? \"error\" : isNotFound ? \"notfound\" : \"normal\";\r\n const routeKey = `${state.url}:${routeType}`;\r\n\r\n return <RouterView key={routeKey} state={state} />;\r\n}\r\n\r\n","import type { RouteViewState } from \"./types\";\r\n\r\nexport function RouterView({ state }: { state: RouteViewState }) {\r\n if (!state.route) {\r\n // Don't show 404 if we're waiting for components to load\r\n if (state.components === null) {\r\n return null;\r\n }\r\n return <h1>404 - Route not found</h1>;\r\n }\r\n\r\n if (!state.components) {\r\n return null;\r\n }\r\n\r\n const { Page, layouts } = state.components;\r\n const { params, props } = state;\r\n\r\n let element = <Page params={params} {...props} />;\r\n\r\n const layoutChain = layouts.slice().reverse();\r\n for (const Layout of layoutChain) {\r\n element = (\r\n <Layout params={params} {...props}>\r\n {element}\r\n </Layout>\r\n );\r\n }\r\n\r\n return element;\r\n}\r\n\r\n","type RouteData = {\n ok: boolean;\n status: number;\n json: any;\n};\n\ntype CacheEntry =\n | { status: \"pending\"; promise: Promise<RouteData> }\n | { status: \"fulfilled\"; value: RouteData }\n | { status: \"rejected\"; error: any };\n\n// Use window to guarantee a single shared cache instance\n// across all bundles/modules\nconst CACHE_KEY = \"__FW_DATA_CACHE__\";\n\n// Maximum number of entries in the cache (LRU)\nconst MAX_CACHE_SIZE = 100;\n\ntype CacheStore = {\n data: Map<string, CacheEntry>;\n index: Map<string, Set<string>>; // pathBase -> Set of keys\n lru: string[]; // Ordered list: most recent at end, oldest at start\n};\n\nfunction getCacheStore(): CacheStore {\n if (typeof window !== \"undefined\") {\n if (!(window as any)[CACHE_KEY]) {\n (window as any)[CACHE_KEY] = {\n data: new Map<string, CacheEntry>(),\n index: new Map<string, Set<string>>(),\n lru: [],\n };\n }\n return (window as any)[CACHE_KEY];\n }\n // Fallback for SSR (though this shouldn't be used on the client)\n return {\n data: new Map<string, CacheEntry>(),\n index: new Map<string, Set<string>>(),\n lru: [],\n };\n}\n\nconst cacheStore = getCacheStore();\nconst dataCache = cacheStore.data;\nconst pathIndex = cacheStore.index;\nconst lru = cacheStore.lru;\n\n// Helper functions for cache management\n\n/**\n * Extract base path from a cache key (removes query params)\n */\nfunction extractPathBase(key: string): string {\n return key.split(\"?\")[0];\n}\n\n/**\n * Add key to path index\n */\nfunction addToIndex(key: string): void {\n const pathBase = extractPathBase(key);\n if (!pathIndex.has(pathBase)) {\n pathIndex.set(pathBase, new Set());\n }\n pathIndex.get(pathBase)!.add(key);\n}\n\n/**\n * Remove key from path index\n */\nfunction removeFromIndex(key: string): void {\n const pathBase = extractPathBase(key);\n const keys = pathIndex.get(pathBase);\n if (keys) {\n keys.delete(key);\n if (keys.size === 0) {\n pathIndex.delete(pathBase);\n }\n }\n}\n\n/**\n * Update LRU order - move key to end (most recent)\n */\nfunction updateLRU(key: string): void {\n const index = lru.indexOf(key);\n if (index !== -1) {\n lru.splice(index, 1);\n }\n lru.push(key);\n}\n\n/**\n * Remove oldest entries if cache exceeds MAX_CACHE_SIZE\n */\nfunction evictOldest(): void {\n while (lru.length >= MAX_CACHE_SIZE && lru.length > 0) {\n const oldestKey = lru.shift()!;\n dataCache.delete(oldestKey);\n removeFromIndex(oldestKey);\n }\n}\n\n/**\n * Set cache entry and maintain indexes\n */\nfunction setCacheEntry(key: string, entry: CacheEntry): void {\n const existingEntry = dataCache.get(key);\n const wasFulfilled = existingEntry?.status === \"fulfilled\";\n \n dataCache.set(key, entry);\n \n // Only track fulfilled entries in LRU and index (not pending/rejected)\n if (entry.status === \"fulfilled\") {\n // Add to index if it wasn't already fulfilled (new entry or transition from pending/rejected)\n if (!wasFulfilled) {\n addToIndex(key);\n }\n updateLRU(key);\n evictOldest();\n } else if (wasFulfilled) {\n // If entry was fulfilled and now isn't (transitioning to pending/rejected), remove from index\n removeFromIndex(key);\n }\n}\n\n/**\n * Delete cache entry and clean up indexes\n */\nfunction deleteCacheEntry(key: string): void {\n if (dataCache.has(key)) {\n dataCache.delete(key);\n removeFromIndex(key);\n const lruIndex = lru.indexOf(key);\n if (lruIndex !== -1) {\n lru.splice(lruIndex, 1);\n }\n }\n}\n\nfunction buildDataUrl(url: string): string {\n return url + (url.includes(\"?\") ? \"&\" : \"?\") + \"__fw_data=1\";\n}\n\nasync function fetchRouteDataOnce(url: string): Promise<RouteData> {\n const dataUrl = buildDataUrl(url);\n\n const res = await fetch(dataUrl, {\n headers: {\n \"x-fw-data\": \"1\",\n Accept: \"application/json\",\n },\n });\n\n let json: any = {};\n\n try {\n const text = await res.text();\n if (text) {\n json = JSON.parse(text);\n }\n } catch (parseError) {\n console.error(\n \"[client][cache] Failed to parse response as JSON:\",\n parseError\n );\n }\n\n const result: RouteData = {\n ok: res.ok,\n status: res.status,\n json,\n };\n\n return result;\n}\n\n/**\n * Revalidates route data by removing it from the cache.\n * The next time you navigate to this route, fresh data will be fetched from the server.\n * This is a client-side function and does not require a server-side revalidation.\n *\n * @param path - The route path to revalidate (e.g., '/posts/1' or '/posts/1?page=2')\n * If query params are not included, revalidates all variants of that route.\n *\n * @example\n * ```ts\n * // After saving something to the DB, revalidate the route\n * await saveToDatabase(data);\n * revalidatePath('/posts');\n * \n * // Revalidate a specific route with query params\n * revalidatePath('/posts?page=2');\n * ```\n */\nexport function revalidatePath(path: string): void {\n // Normalize the base path (without query params)\n const normalizedPath = path.split(\"?\")[0];\n const hasQueryParams = path.includes(\"?\");\n \n // Get all keys for this path base from index (O(1) lookup)\n const keysForPath = pathIndex.get(normalizedPath);\n \n if (!keysForPath || keysForPath.size === 0) {\n return; // No entries to revalidate\n }\n \n // If the path includes specific query params, extract them\n let specificQueryParams: string | undefined;\n if (hasQueryParams) {\n const queryPart = path.split(\"?\")[1];\n // Sort query params for consistent comparison\n specificQueryParams = queryPart\n .split(\"&\")\n .filter((p) => !p.startsWith(\"__fw_data=\"))\n .sort()\n .join(\"&\");\n }\n \n // Iterate only over keys for this path (much smaller set)\n const keysToDelete: string[] = [];\n for (const key of keysForPath) {\n // If specific query params were specified, check if they match\n if (hasQueryParams && specificQueryParams) {\n const [, keyQuery = \"\"] = key.split(\"?\");\n const keyQueryParams = keyQuery\n .split(\"&\")\n .filter((p) => !p.startsWith(\"__fw_data=\"))\n .sort()\n .join(\"&\");\n \n if (keyQueryParams === specificQueryParams) {\n keysToDelete.push(key);\n }\n } else {\n // If no specific query params, revalidate all variants\n keysToDelete.push(key);\n }\n }\n \n // Delete matching entries\n keysToDelete.forEach((key) => {\n deleteCacheEntry(key);\n });\n}\n\n/**\n * Revalidates and refreshes the current page data.\n * Similar to Next.js's `router.refresh()`.\n * \n * This function:\n * 1. Removes the current route from cache\n * 2. Fetches fresh data from the server\n * 3. Updates window.__FW_DATA__ with the new data\n * 4. Dispatches a 'fw-data-refresh' event for components to listen to\n * \n * @returns Promise that resolves with the fresh route data\n * \n * @example\n * ```ts\n * // Refresh current page data after a mutation\n * await revalidate();\n * ```\n */\nexport async function revalidate(): Promise<RouteData> {\n if (typeof window === \"undefined\") {\n throw new Error(\"revalidate() can only be called on the client\");\n }\n\n const pathname = window.location.pathname + window.location.search;\n \n // Revalidate the path (remove from cache)\n revalidatePath(pathname);\n \n // Fetch fresh data\n const freshData = await getRouteData(pathname, { revalidate: true });\n \n // Update window.__FW_DATA__ if it exists\n if ((window as any).__FW_DATA__ && freshData.ok && freshData.json) {\n const currentData = (window as any).__FW_DATA__;\n (window as any).__FW_DATA__ = {\n ...currentData,\n pathname: pathname.split(\"?\")[0],\n params: freshData.json.params || currentData.params || {},\n props: freshData.json.props || currentData.props || {},\n metadata: freshData.json.metadata ?? currentData.metadata ?? null,\n notFound: freshData.json.notFound ?? false,\n error: freshData.json.error ?? false,\n };\n \n // Dispatch event for components to listen to\n window.dispatchEvent(new CustomEvent(\"fw-data-refresh\", {\n detail: { data: freshData },\n }));\n }\n \n return freshData;\n}\n\n/**\n * @deprecated Use `revalidatePath()` instead. This function is kept for backwards compatibility.\n */\nexport function revalidateRouteData(url: string): void {\n revalidatePath(url);\n}\n\nexport function prefetchRouteData(url: string): void {\n const key = buildDataUrl(url);\n\n const cached = dataCache.get(key);\n\n if (cached && cached.status !== \"rejected\") {\n // Update LRU if it exists and is fulfilled\n if (cached.status === \"fulfilled\") {\n updateLRU(key);\n }\n return;\n }\n\n const promise = fetchRouteDataOnce(url)\n .then((value) => {\n setCacheEntry(key, { status: \"fulfilled\", value });\n return value;\n })\n .catch((error) => {\n console.error(\"[client][cache] Error prefetching route data:\", error);\n dataCache.set(key, { status: \"rejected\", error });\n throw error;\n });\n\n dataCache.set(key, { status: \"pending\", promise });\n}\n\nexport type GetRouteDataOptions = {\n /**\n * If true, forces revalidation of route data,\n * ignoring the cache and fetching fresh data from the server.\n * Similar to Next.js's `router.refresh()` behavior.\n */\n revalidate?: boolean;\n};\n\nexport async function getRouteData(\n url: string,\n options?: GetRouteDataOptions\n): Promise<RouteData> {\n const key = buildDataUrl(url);\n\n // If revalidation is requested, remove the entry from cache\n if (options?.revalidate) {\n deleteCacheEntry(key);\n }\n\n const entry = dataCache.get(key);\n\n if (entry) {\n if (entry.status === \"fulfilled\") {\n // Update LRU: mark as recently used\n updateLRU(key);\n return entry.value;\n }\n if (entry.status === \"pending\") {\n return entry.promise;\n }\n }\n\n // No entry in cache, fetch it\n const promise = fetchRouteDataOnce(url)\n .then((value) => {\n setCacheEntry(key, { status: \"fulfilled\", value });\n return value;\n })\n .catch((error) => {\n console.error(\"[client][cache] Error fetching route data:\", error);\n dataCache.set(key, { status: \"rejected\", error });\n throw error;\n });\n\n dataCache.set(key, { status: \"pending\", promise });\n return promise;\n}\n","import { getRouteData } from \"../../react/cache/index\";\r\nimport { matchRouteClient } from \"./route-matcher\";\r\nimport { applyMetadata } from \"./metadata\";\r\nimport { setWindowData, getCurrentTheme } from \"./window-data\";\r\nimport type {\r\n ClientRouteLoaded,\r\n RouteViewState,\r\n InitialData,\r\n} from \"./types\";\r\n\r\nexport type NavigationHandlers = {\r\n setState: (state: RouteViewState) => void;\r\n routes: ClientRouteLoaded[];\r\n notFoundRoute: ClientRouteLoaded | null;\r\n errorRoute: ClientRouteLoaded | null;\r\n};\r\n\r\nasync function handleErrorRoute(\r\n nextUrl: string,\r\n json: any,\r\n errorRoute: ClientRouteLoaded,\r\n setState: (state: RouteViewState) => void\r\n): Promise<boolean> {\r\n try {\r\n const components = await errorRoute.load();\r\n \r\n // Get theme: prioritize cookie, then server, then window data, then default\r\n let theme: string = \"light\";\r\n if (typeof document !== \"undefined\") {\r\n const cookieMatch = document.cookie.match(/theme=([^;]+)/);\r\n if (cookieMatch) {\r\n theme = cookieMatch[1];\r\n } else if (json.theme) {\r\n theme = json.theme;\r\n } else {\r\n const currentTheme = getCurrentTheme();\r\n if (currentTheme) theme = currentTheme;\r\n }\r\n } else if (json.theme) {\r\n theme = json.theme;\r\n }\r\n \r\n const errorProps = {\r\n ...(json.props || {\r\n error: json.message || \"An error occurred\",\r\n }),\r\n theme,\r\n };\r\n\r\n const windowData: InitialData = {\r\n pathname: nextUrl,\r\n params: json.params || {},\r\n props: errorProps,\r\n metadata: json.metadata ?? null,\r\n theme,\r\n notFound: false,\r\n error: true,\r\n };\r\n\r\n setWindowData(windowData);\r\n\r\n setState({\r\n url: nextUrl,\r\n route: errorRoute,\r\n params: json.params || {},\r\n components,\r\n props: errorProps,\r\n });\r\n return true;\r\n } catch (loadError) {\r\n console.error(\r\n \"[client] Error loading error route components:\",\r\n loadError\r\n );\r\n window.location.href = nextUrl;\r\n return false;\r\n }\r\n}\r\n\r\nasync function handleNotFoundRoute(\r\n nextUrl: string,\r\n json: any,\r\n notFoundRoute: ClientRouteLoaded | null,\r\n setState: (state: RouteViewState) => void\r\n): Promise<void> {\r\n // Get theme: prioritize cookie, then server, then window data, then default\r\n let theme: string = \"light\";\r\n if (typeof document !== \"undefined\") {\r\n const cookieMatch = document.cookie.match(/theme=([^;]+)/);\r\n if (cookieMatch) {\r\n theme = cookieMatch[1];\r\n } else if (json.theme) {\r\n theme = json.theme;\r\n } else {\r\n const currentTheme = getCurrentTheme();\r\n if (currentTheme) theme = currentTheme;\r\n }\r\n } else if (json.theme) {\r\n theme = json.theme;\r\n }\r\n \r\n const notFoundProps = {\r\n ...(json.props ?? {}),\r\n theme,\r\n };\r\n\r\n const windowData: InitialData = {\r\n pathname: nextUrl,\r\n params: {},\r\n props: notFoundProps,\r\n metadata: json.metadata ?? null,\r\n theme,\r\n notFound: true,\r\n error: false,\r\n };\r\n\r\n setWindowData(windowData);\r\n\r\n if (notFoundRoute) {\r\n const components = await notFoundRoute.load();\r\n setState({\r\n url: nextUrl,\r\n route: notFoundRoute,\r\n params: {},\r\n components,\r\n props: notFoundProps,\r\n });\r\n } else {\r\n setState({\r\n url: nextUrl,\r\n route: null,\r\n params: {},\r\n components: null,\r\n props: {},\r\n });\r\n }\r\n}\r\n\r\nasync function handleNormalRoute(\r\n nextUrl: string,\r\n json: any,\r\n routes: ClientRouteLoaded[],\r\n setState: (state: RouteViewState) => void\r\n): Promise<boolean> {\r\n applyMetadata(json.metadata ?? null);\r\n \r\n // Get theme: prioritize cookie (source of truth), then server response, then window data, then default\r\n // Cookie is the source of truth because it persists across navigation\r\n let theme: string = \"light\"; // Default\r\n if (typeof document !== \"undefined\") {\r\n const cookieMatch = document.cookie.match(/theme=([^;]+)/);\r\n if (cookieMatch) {\r\n theme = cookieMatch[1];\r\n } else if (json.theme) {\r\n theme = json.theme;\r\n } else {\r\n const currentTheme = getCurrentTheme();\r\n if (currentTheme) {\r\n theme = currentTheme;\r\n }\r\n }\r\n } else if (json.theme) {\r\n theme = json.theme;\r\n }\r\n \r\n // Include theme in props so layouts receive it during SPA navigation\r\n const newProps = {\r\n ...(json.props ?? {}),\r\n theme, // Always include theme\r\n };\r\n\r\n const matched = matchRouteClient(nextUrl, routes);\r\n\r\n if (!matched) {\r\n window.location.href = nextUrl;\r\n return false;\r\n }\r\n\r\n const windowData: InitialData = {\r\n pathname: nextUrl,\r\n params: matched.params,\r\n props: newProps,\r\n metadata: json.metadata ?? null,\r\n theme,\r\n notFound: false,\r\n error: false,\r\n };\r\n\r\n setWindowData(windowData);\r\n\r\n const components = await matched.route.load();\r\n\r\n window.scrollTo({\r\n top: 0,\r\n behavior: \"smooth\",\r\n });\r\n\r\n setState({\r\n url: nextUrl,\r\n route: matched.route,\r\n params: matched.params,\r\n components,\r\n props: newProps,\r\n });\r\n\r\n return true;\r\n}\r\n\r\nexport type NavigateOptions = {\r\n /**\r\n * If true, forces revalidation of route data,\r\n * ignoring the cache and fetching fresh data from the server.\r\n * Similar to Next.js's `router.refresh()` behavior.\r\n */\r\n revalidate?: boolean;\r\n};\r\n\r\nexport async function navigate(\r\n nextUrl: string,\r\n handlers: NavigationHandlers,\r\n options?: NavigateOptions\r\n): Promise<void> {\r\n const { setState, routes, notFoundRoute, errorRoute } = handlers;\r\n\r\n try {\r\n const { ok, json } = await getRouteData(nextUrl, {\r\n revalidate: options?.revalidate,\r\n });\r\n\r\n if (json && json.error) {\r\n console.log(\"[client] Error detected in response:\", json);\r\n\r\n if (errorRoute) {\r\n const handled = await handleErrorRoute(\r\n nextUrl,\r\n json,\r\n errorRoute,\r\n setState\r\n );\r\n if (handled) return;\r\n } else {\r\n console.warn(\r\n \"[client] Error route not available, reloading page.\",\r\n errorRoute\r\n );\r\n window.location.href = nextUrl;\r\n return;\r\n }\r\n }\r\n\r\n // 🔴 HTTP error (404/500/etc)\r\n if (!ok) {\r\n if (json && (json as any).redirect) {\r\n window.location.href = (json as any).redirect.destination;\r\n return;\r\n }\r\n window.location.href = nextUrl;\r\n return;\r\n }\r\n\r\n // Redirección vía JSON\r\n if (json.redirect) {\r\n window.location.href = json.redirect.destination;\r\n return;\r\n }\r\n\r\n // Manejo de notFound\r\n if (json.notFound) {\r\n await handleNotFoundRoute(nextUrl, json, notFoundRoute, setState);\r\n return;\r\n }\r\n\r\n // Ruta normal\r\n await handleNormalRoute(nextUrl, json, routes, setState);\r\n } catch (err) {\r\n console.error(\"[client] Error fetching FW data:\", err);\r\n window.location.href = nextUrl;\r\n }\r\n}\r\n\r\nexport function createClickHandler(\r\n navigate: (url: string, options?: NavigateOptions) => void\r\n): (ev: MouseEvent) => void {\r\n return function handleClick(ev: MouseEvent) {\r\n if (ev.defaultPrevented) return;\r\n if (ev.button !== 0) return;\r\n if (ev.metaKey || ev.ctrlKey || ev.shiftKey || ev.altKey) return;\r\n\r\n const target = ev.target as HTMLElement | null;\r\n if (!target) return;\r\n\r\n const anchor = target.closest(\"a[href]\") as HTMLAnchorElement | null;\r\n if (!anchor) return;\r\n\r\n const href = anchor.getAttribute(\"href\");\r\n if (!href) return;\r\n if (href.startsWith(\"#\")) return;\r\n\r\n const url = new URL(href, window.location.href);\r\n if (url.origin !== window.location.origin) return;\r\n if (anchor.target && anchor.target !== \"_self\") return;\r\n\r\n ev.preventDefault();\r\n\r\n const nextUrl = url.pathname + url.search;\r\n const currentUrl = window.location.pathname + window.location.search;\r\n if (nextUrl === currentUrl) return;\r\n\r\n // Detectar si el link tiene data-revalidate para forzar revalidación\r\n const shouldRevalidate =\r\n anchor.hasAttribute(\"data-revalidate\") &&\r\n anchor.getAttribute(\"data-revalidate\") !== \"false\";\r\n\r\n window.history.pushState({}, \"\", nextUrl);\r\n navigate(nextUrl, shouldRevalidate ? { revalidate: true } : undefined);\r\n };\r\n}\r\n\r\nexport function createPopStateHandler(\r\n navigate: (url: string, options?: NavigateOptions) => void\r\n): () => void {\r\n return function handlePopState() {\r\n const nextUrl = window.location.pathname + window.location.search;\r\n navigate(nextUrl);\r\n };\r\n}\r\n\r\n"],"mappings":"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","names":["navigate","import_jsx_runtime","import_jsx_runtime"]}
|
|
1
|
+
{"version":3,"sources":["../modules/runtime/client/index.tsx","../modules/runtime/client/bootstrap.tsx","../modules/runtime/client/constants.ts","../modules/runtime/client/window-data.ts","../modules/runtime/client/route-matcher.ts","../modules/runtime/client/metadata.ts","../modules/runtime/client/AppShell.tsx","../modules/runtime/client/RouterView.tsx","../modules/react/cache/client-data-cache/index.ts","../modules/runtime/client/navigation.ts"],"sourcesContent":["// Re-export all public types and functions\r\nexport type {\r\n ClientLoadedComponents,\r\n ClientRouteLoaded,\r\n ClientRouteMatch,\r\n RouteViewState,\r\n InitialData,\r\n} from \"./types\";\r\n\r\nexport { bootstrapClient } from \"./bootstrap\";\r\nexport type { AppShellProps } from \"./AppShell\";\r\n","import { hydrateRoot } from \"react-dom/client\";\r\nimport { APP_CONTAINER_ID } from \"./constants\";\r\nimport { getWindowData } from \"./window-data\";\r\nimport { matchRouteClient } from \"./route-matcher\";\r\nimport { applyMetadata } from \"./metadata\";\r\nimport { AppShell } from \"./AppShell\";\r\nimport type {\r\n InitialData,\r\n ClientRouteLoaded,\r\n RouteViewState,\r\n} from \"./types\";\r\n\r\nexport async function loadInitialRoute(\r\n initialUrl: string,\r\n initialData: InitialData | null,\r\n routes: ClientRouteLoaded[],\r\n notFoundRoute: ClientRouteLoaded | null,\r\n errorRoute: ClientRouteLoaded | null\r\n): Promise<RouteViewState> {\r\n const isInitialNotFound = initialData?.notFound === true;\r\n const isInitialError = initialData?.error === true;\r\n\r\n let initialRoute: ClientRouteLoaded | null = null;\r\n let initialParams: Record<string, string> = {};\r\n let initialComponents = null;\r\n\r\n if (isInitialError && errorRoute) {\r\n initialRoute = errorRoute;\r\n initialParams = initialData?.params ?? {};\r\n initialComponents = await errorRoute.load();\r\n } else if (isInitialNotFound && notFoundRoute) {\r\n initialRoute = notFoundRoute;\r\n initialParams = {};\r\n initialComponents = await notFoundRoute.load();\r\n } else {\r\n const match = matchRouteClient(initialUrl, routes);\r\n if (match) {\r\n initialRoute = match.route;\r\n initialParams = match.params;\r\n initialComponents = await match.route.load();\r\n } else if (notFoundRoute) {\r\n initialRoute = notFoundRoute;\r\n initialParams = {};\r\n initialComponents = await notFoundRoute.load();\r\n } else {\r\n console.warn(\r\n `[client] No route match found for ${initialUrl}. Available routes:`,\r\n routes.map((r) => r.pattern)\r\n );\r\n }\r\n }\r\n\r\n return {\r\n url: initialUrl,\r\n route: initialRoute,\r\n params: initialParams,\r\n components: initialComponents,\r\n props: initialData?.props ?? {},\r\n };\r\n}\r\n\r\n/**\r\n * Bootstraps the client-side application.\r\n *\r\n * @param routes - Array of client routes\r\n * @param notFoundRoute - Not-found route definition\r\n * @param errorRoute - Error route definition\r\n */\r\nexport function bootstrapClient(\r\n routes: ClientRouteLoaded[],\r\n notFoundRoute: ClientRouteLoaded | null,\r\n errorRoute: ClientRouteLoaded | null = null\r\n): void {\r\n (async function bootstrap() {\r\n const container = document.getElementById(APP_CONTAINER_ID);\r\n const initialData = getWindowData();\r\n\r\n if (!container) {\r\n console.error(`Container #${APP_CONTAINER_ID} not found for hydration`);\r\n return;\r\n }\r\n\r\n const initialUrl = window.location.pathname + window.location.search;\r\n\r\n try {\r\n const initialState = await loadInitialRoute(\r\n initialUrl,\r\n initialData,\r\n routes,\r\n notFoundRoute,\r\n errorRoute\r\n );\r\n\r\n if (initialData?.metadata) {\r\n applyMetadata(initialData.metadata);\r\n }\r\n\r\n hydrateRoot(\r\n container,\r\n <AppShell\r\n initialState={initialState}\r\n routes={routes}\r\n notFoundRoute={notFoundRoute}\r\n errorRoute={errorRoute}\r\n />\r\n );\r\n } catch (error) {\r\n console.error(\r\n \"[client] Error loading initial route components for\",\r\n initialUrl,\r\n error\r\n );\r\n\r\n window.location.reload();\r\n }\r\n })();\r\n}\r\n\r\n","// Client-side constants (hardcoded to avoid alias resolution issues in Rspack)\r\nexport const WINDOW_DATA_KEY = \"__FW_DATA__\";\r\nexport const APP_CONTAINER_ID = \"__app\";\r\n\r\n","import { WINDOW_DATA_KEY } from \"./constants\";\r\nimport type { InitialData } from \"./types\";\r\n\r\nexport function getWindowData(): InitialData | null {\r\n return ((window as any)[WINDOW_DATA_KEY] as InitialData | undefined) ?? null;\r\n}\r\n\r\nexport function setWindowData(data: InitialData): void {\r\n (window as any)[WINDOW_DATA_KEY] = data;\r\n \r\n // Dispatch event for components to listen to (e.g., usePageProps, ThemeProvider)\r\n // This ensures components update when navigating in SPA mode\r\n if (typeof window !== \"undefined\") {\r\n window.dispatchEvent(\r\n new CustomEvent(\"fw-data-refresh\", {\r\n detail: { data },\r\n })\r\n );\r\n }\r\n}\r\n\r\nexport function getCurrentTheme(): string | null {\r\n return getWindowData()?.theme ?? null;\r\n}\r\n\r\n","import type { ClientRouteLoaded, ClientRouteMatch } from \"./types\";\r\n\r\nexport function buildClientRegexFromPattern(pattern: string): RegExp {\r\n const segments = pattern.split(\"/\").filter(Boolean);\r\n const regexParts: string[] = [];\r\n\r\n for (let i = 0; i < segments.length; i++) {\r\n const seg = segments[i];\r\n\r\n // catch-all [...slug]\r\n if (seg.startsWith(\"[...\") && seg.endsWith(\"]\")) {\r\n if (i !== segments.length - 1) {\r\n throw new Error(\r\n `Catch-all segment \"${seg}\" in \"${pattern}\" must be the last segment.`\r\n );\r\n }\r\n regexParts.push(\"(.+)\");\r\n continue;\r\n }\r\n\r\n // dynamic [id]\r\n if (seg.startsWith(\"[\") && seg.endsWith(\"]\")) {\r\n regexParts.push(\"([^/]+)\");\r\n continue;\r\n }\r\n\r\n // static segment\r\n const escaped = seg.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\r\n regexParts.push(escaped);\r\n }\r\n\r\n const regexSource = \"^/\" + regexParts.join(\"/\") + \"/?$\";\r\n return new RegExp(regexSource);\r\n}\r\n\r\nexport function matchRouteClient(\r\n pathWithSearch: string,\r\n routes: ClientRouteLoaded[]\r\n): ClientRouteMatch | null {\r\n const [pathname] = pathWithSearch.split(\"?\");\r\n for (const r of routes) {\r\n const regex = buildClientRegexFromPattern(r.pattern);\r\n const match = regex.exec(pathname);\r\n if (!match) continue;\r\n\r\n const params: Record<string, string> = {};\r\n r.paramNames.forEach((name, idx) => {\r\n params[name] = decodeURIComponent(match[idx + 1] || \"\");\r\n });\r\n\r\n return { route: r, params };\r\n }\r\n return null;\r\n}\r\n\r\n","export function applyMetadata(\r\n md?: { title?: string; description?: string } | null\r\n) {\r\n if (!md) return;\r\n\r\n if (md.title) {\r\n document.title = md.title;\r\n }\r\n\r\n if (md.description) {\r\n let meta = document.querySelector(\r\n 'meta[name=\"description\"]'\r\n ) as HTMLMetaElement | null;\r\n\r\n if (!meta) {\r\n meta = document.createElement(\"meta\");\r\n meta.name = \"description\";\r\n document.head.appendChild(meta);\r\n }\r\n\r\n meta.content = md.description;\r\n }\r\n}\r\n\r\n","import { useEffect, useState, useRef } from \"react\";\r\nimport { RouterView } from \"./RouterView\";\r\nimport {\r\n navigate,\r\n createClickHandler,\r\n createPopStateHandler,\r\n type NavigationHandlers,\r\n} from \"./navigation\";\r\nimport type {\r\n RouteViewState,\r\n ClientRouteLoaded,\r\n} from \"./types\";\r\n\r\nexport interface AppShellProps {\r\n initialState: RouteViewState;\r\n routes: ClientRouteLoaded[];\r\n notFoundRoute: ClientRouteLoaded | null;\r\n errorRoute: ClientRouteLoaded | null;\r\n}\r\n\r\nexport function AppShell({\r\n initialState,\r\n routes,\r\n notFoundRoute,\r\n errorRoute,\r\n}: AppShellProps) {\r\n const [state, setState] = useState<RouteViewState>(initialState);\r\n const handlersRef = useRef<NavigationHandlers>({\r\n setState,\r\n routes,\r\n notFoundRoute,\r\n errorRoute,\r\n });\r\n\r\n // Mantener handlersRef actualizado\r\n useEffect(() => {\r\n handlersRef.current = {\r\n setState,\r\n routes,\r\n notFoundRoute,\r\n errorRoute,\r\n };\r\n }, [routes, notFoundRoute, errorRoute]);\r\n\r\n useEffect(() => {\r\n async function handleNavigate(\r\n nextUrl: string,\r\n options?: { revalidate?: boolean }\r\n ) {\r\n await navigate(nextUrl, handlersRef.current, options);\r\n }\r\n\r\n const handleClick = createClickHandler(handleNavigate);\r\n const handlePopState = createPopStateHandler(handleNavigate);\r\n\r\n // Usar capture: false (burbujeo) para que los eventos del input se manejen primero\r\n window.addEventListener(\"click\", handleClick, { capture: false });\r\n window.addEventListener(\"popstate\", handlePopState, { capture: false });\r\n\r\n return () => {\r\n window.removeEventListener(\"click\", handleClick, { capture: false } as EventListenerOptions);\r\n window.removeEventListener(\"popstate\", handlePopState, { capture: false } as EventListenerOptions);\r\n };\r\n }, []); // Solo ejecutar una vez al montar\r\n\r\n const isError = state.route === errorRoute;\r\n const isNotFound = state.route === notFoundRoute;\r\n const routeType = isError ? \"error\" : isNotFound ? \"notfound\" : \"normal\";\r\n const routeKey = `${state.url}:${routeType}`;\r\n\r\n return <RouterView key={routeKey} state={state} />;\r\n}\r\n\r\n","import type { RouteViewState } from \"./types\";\r\n\r\nexport function RouterView({ state }: { state: RouteViewState }) {\r\n if (!state.route) {\r\n // Don't show 404 if we're waiting for components to load\r\n if (state.components === null) {\r\n return null;\r\n }\r\n return <h1>404 - Route not found</h1>;\r\n }\r\n\r\n if (!state.components) {\r\n return null;\r\n }\r\n\r\n const { Page, layouts } = state.components;\r\n const { params, props } = state;\r\n\r\n let element = <Page params={params} {...props} />;\r\n\r\n const layoutChain = layouts.slice().reverse();\r\n for (const Layout of layoutChain) {\r\n element = (\r\n <Layout params={params} {...props}>\r\n {element}\r\n </Layout>\r\n );\r\n }\r\n\r\n return element;\r\n}\r\n\r\n","type RouteData = {\n ok: boolean;\n status: number;\n json: any;\n};\n\ntype CacheEntry =\n | { status: \"pending\"; promise: Promise<RouteData> }\n | { status: \"fulfilled\"; value: RouteData }\n | { status: \"rejected\"; error: any };\n\n// Use window to guarantee a single shared cache instance\n// across all bundles/modules\nconst CACHE_KEY = \"__FW_DATA_CACHE__\";\n\n// Maximum number of entries in the cache (LRU)\nconst MAX_CACHE_SIZE = 100;\n\ntype CacheStore = {\n data: Map<string, CacheEntry>;\n index: Map<string, Set<string>>; // pathBase -> Set of keys\n lru: string[]; // Ordered list: most recent at end, oldest at start\n};\n\nfunction getCacheStore(): CacheStore {\n if (typeof window !== \"undefined\") {\n if (!(window as any)[CACHE_KEY]) {\n (window as any)[CACHE_KEY] = {\n data: new Map<string, CacheEntry>(),\n index: new Map<string, Set<string>>(),\n lru: [],\n };\n }\n return (window as any)[CACHE_KEY];\n }\n // Fallback for SSR (though this shouldn't be used on the client)\n return {\n data: new Map<string, CacheEntry>(),\n index: new Map<string, Set<string>>(),\n lru: [],\n };\n}\n\nconst cacheStore = getCacheStore();\nconst dataCache = cacheStore.data;\nconst pathIndex = cacheStore.index;\nconst lru = cacheStore.lru;\n\n// Helper functions for cache management\n\n/**\n * Extract base path from a cache key (removes query params)\n */\nfunction extractPathBase(key: string): string {\n return key.split(\"?\")[0];\n}\n\n/**\n * Add key to path index\n */\nfunction addToIndex(key: string): void {\n const pathBase = extractPathBase(key);\n if (!pathIndex.has(pathBase)) {\n pathIndex.set(pathBase, new Set());\n }\n pathIndex.get(pathBase)!.add(key);\n}\n\n/**\n * Remove key from path index\n */\nfunction removeFromIndex(key: string): void {\n const pathBase = extractPathBase(key);\n const keys = pathIndex.get(pathBase);\n if (keys) {\n keys.delete(key);\n if (keys.size === 0) {\n pathIndex.delete(pathBase);\n }\n }\n}\n\n/**\n * Update LRU order - move key to end (most recent)\n */\nfunction updateLRU(key: string): void {\n const index = lru.indexOf(key);\n if (index !== -1) {\n lru.splice(index, 1);\n }\n lru.push(key);\n}\n\n/**\n * Remove oldest entries if cache exceeds MAX_CACHE_SIZE\n */\nfunction evictOldest(): void {\n while (lru.length >= MAX_CACHE_SIZE && lru.length > 0) {\n const oldestKey = lru.shift()!;\n dataCache.delete(oldestKey);\n removeFromIndex(oldestKey);\n }\n}\n\n/**\n * Set cache entry and maintain indexes\n */\nfunction setCacheEntry(key: string, entry: CacheEntry): void {\n const existingEntry = dataCache.get(key);\n const wasFulfilled = existingEntry?.status === \"fulfilled\";\n \n dataCache.set(key, entry);\n \n // Only track fulfilled entries in LRU and index (not pending/rejected)\n if (entry.status === \"fulfilled\") {\n // Add to index if it wasn't already fulfilled (new entry or transition from pending/rejected)\n if (!wasFulfilled) {\n addToIndex(key);\n }\n updateLRU(key);\n evictOldest();\n } else if (wasFulfilled) {\n // If entry was fulfilled and now isn't (transitioning to pending/rejected), remove from index\n removeFromIndex(key);\n }\n}\n\n/**\n * Delete cache entry and clean up indexes\n */\nfunction deleteCacheEntry(key: string): void {\n if (dataCache.has(key)) {\n dataCache.delete(key);\n removeFromIndex(key);\n const lruIndex = lru.indexOf(key);\n if (lruIndex !== -1) {\n lru.splice(lruIndex, 1);\n }\n }\n}\n\nfunction buildDataUrl(url: string): string {\n return url + (url.includes(\"?\") ? \"&\" : \"?\") + \"__fw_data=1\";\n}\n\nasync function fetchRouteDataOnce(url: string): Promise<RouteData> {\n const dataUrl = buildDataUrl(url);\n\n const res = await fetch(dataUrl, {\n headers: {\n \"x-fw-data\": \"1\",\n Accept: \"application/json\",\n },\n });\n\n let json: any = {};\n\n try {\n const text = await res.text();\n if (text) {\n json = JSON.parse(text);\n }\n } catch (parseError) {\n console.error(\n \"[client][cache] Failed to parse response as JSON:\",\n parseError\n );\n }\n\n const result: RouteData = {\n ok: res.ok,\n status: res.status,\n json,\n };\n\n return result;\n}\n\n/**\n * Revalidates route data by removing it from the cache.\n * The next time you navigate to this route, fresh data will be fetched from the server.\n * This is a client-side function and does not require a server-side revalidation.\n *\n * @param path - The route path to revalidate (e.g., '/posts/1' or '/posts/1?page=2')\n * If query params are not included, revalidates all variants of that route.\n *\n * @example\n * ```ts\n * // After saving something to the DB, revalidate the route\n * await saveToDatabase(data);\n * revalidatePath('/posts');\n * \n * // Revalidate a specific route with query params\n * revalidatePath('/posts?page=2');\n * ```\n */\nexport function revalidatePath(path: string): void {\n // Normalize the base path (without query params)\n const normalizedPath = path.split(\"?\")[0];\n const hasQueryParams = path.includes(\"?\");\n \n // Get all keys for this path base from index (O(1) lookup)\n const keysForPath = pathIndex.get(normalizedPath);\n \n if (!keysForPath || keysForPath.size === 0) {\n return; // No entries to revalidate\n }\n \n // If the path includes specific query params, extract them\n let specificQueryParams: string | undefined;\n if (hasQueryParams) {\n const queryPart = path.split(\"?\")[1];\n // Sort query params for consistent comparison\n specificQueryParams = queryPart\n .split(\"&\")\n .filter((p) => !p.startsWith(\"__fw_data=\"))\n .sort()\n .join(\"&\");\n }\n \n // Iterate only over keys for this path (much smaller set)\n const keysToDelete: string[] = [];\n for (const key of keysForPath) {\n // If specific query params were specified, check if they match\n if (hasQueryParams && specificQueryParams) {\n const [, keyQuery = \"\"] = key.split(\"?\");\n const keyQueryParams = keyQuery\n .split(\"&\")\n .filter((p) => !p.startsWith(\"__fw_data=\"))\n .sort()\n .join(\"&\");\n \n if (keyQueryParams === specificQueryParams) {\n keysToDelete.push(key);\n }\n } else {\n // If no specific query params, revalidate all variants\n keysToDelete.push(key);\n }\n }\n \n // Delete matching entries\n keysToDelete.forEach((key) => {\n deleteCacheEntry(key);\n });\n}\n\n/**\n * Revalidates and refreshes the current page data.\n * Similar to Next.js's `router.refresh()`.\n * \n * This function:\n * 1. Removes the current route from cache\n * 2. Fetches fresh data from the server\n * 3. Updates window.__FW_DATA__ with the new data\n * 4. Dispatches a 'fw-data-refresh' event for components to listen to\n * \n * @returns Promise that resolves with the fresh route data\n * \n * @example\n * ```ts\n * // Refresh current page data after a mutation\n * await revalidate();\n * ```\n */\nexport async function revalidate(): Promise<RouteData> {\n if (typeof window === \"undefined\") {\n throw new Error(\"revalidate() can only be called on the client\");\n }\n\n const pathname = window.location.pathname + window.location.search;\n \n // Revalidate the path (remove from cache)\n revalidatePath(pathname);\n \n // Fetch fresh data\n const freshData = await getRouteData(pathname, { revalidate: true });\n \n // Update window.__FW_DATA__ if it exists\n if ((window as any).__FW_DATA__ && freshData.ok && freshData.json) {\n const currentData = (window as any).__FW_DATA__;\n (window as any).__FW_DATA__ = {\n ...currentData,\n pathname: pathname.split(\"?\")[0],\n params: freshData.json.params || currentData.params || {},\n props: freshData.json.props || currentData.props || {},\n metadata: freshData.json.metadata ?? currentData.metadata ?? null,\n notFound: freshData.json.notFound ?? false,\n error: freshData.json.error ?? false,\n };\n \n // Dispatch event for components to listen to\n window.dispatchEvent(new CustomEvent(\"fw-data-refresh\", {\n detail: { data: freshData },\n }));\n }\n \n return freshData;\n}\n\n/**\n * @deprecated Use `revalidatePath()` instead. This function is kept for backwards compatibility.\n */\nexport function revalidateRouteData(url: string): void {\n revalidatePath(url);\n}\n\nexport function prefetchRouteData(url: string): void {\n const key = buildDataUrl(url);\n\n const cached = dataCache.get(key);\n\n if (cached && cached.status !== \"rejected\") {\n // Update LRU if it exists and is fulfilled\n if (cached.status === \"fulfilled\") {\n updateLRU(key);\n }\n return;\n }\n\n const promise = fetchRouteDataOnce(url)\n .then((value) => {\n setCacheEntry(key, { status: \"fulfilled\", value });\n return value;\n })\n .catch((error) => {\n console.error(\"[client][cache] Error prefetching route data:\", error);\n dataCache.set(key, { status: \"rejected\", error });\n throw error;\n });\n\n dataCache.set(key, { status: \"pending\", promise });\n}\n\nexport type GetRouteDataOptions = {\n /**\n * If true, forces revalidation of route data,\n * ignoring the cache and fetching fresh data from the server.\n * Similar to Next.js's `router.refresh()` behavior.\n */\n revalidate?: boolean;\n};\n\nexport async function getRouteData(\n url: string,\n options?: GetRouteDataOptions\n): Promise<RouteData> {\n const key = buildDataUrl(url);\n\n // If revalidation is requested, remove the entry from cache\n if (options?.revalidate) {\n deleteCacheEntry(key);\n }\n\n const entry = dataCache.get(key);\n\n if (entry) {\n if (entry.status === \"fulfilled\") {\n // Update LRU: mark as recently used\n updateLRU(key);\n return entry.value;\n }\n if (entry.status === \"pending\") {\n return entry.promise;\n }\n }\n\n // No entry in cache, fetch it\n const promise = fetchRouteDataOnce(url)\n .then((value) => {\n setCacheEntry(key, { status: \"fulfilled\", value });\n return value;\n })\n .catch((error) => {\n console.error(\"[client][cache] Error fetching route data:\", error);\n dataCache.set(key, { status: \"rejected\", error });\n throw error;\n });\n\n dataCache.set(key, { status: \"pending\", promise });\n return promise;\n}\n","import { getRouteData } from \"../../react/cache/index\";\r\nimport { matchRouteClient } from \"./route-matcher\";\r\nimport { applyMetadata } from \"./metadata\";\r\nimport { setWindowData, getCurrentTheme } from \"./window-data\";\r\nimport type {\r\n ClientRouteLoaded,\r\n RouteViewState,\r\n InitialData,\r\n} from \"./types\";\r\n\r\nexport type NavigationHandlers = {\r\n setState: (state: RouteViewState) => void;\r\n routes: ClientRouteLoaded[];\r\n notFoundRoute: ClientRouteLoaded | null;\r\n errorRoute: ClientRouteLoaded | null;\r\n};\r\n\r\nasync function handleErrorRoute(\r\n nextUrl: string,\r\n json: any,\r\n errorRoute: ClientRouteLoaded,\r\n setState: (state: RouteViewState) => void\r\n): Promise<boolean> {\r\n try {\r\n const components = await errorRoute.load();\r\n \r\n // Get theme: prioritize cookie, then server, then window data, then default\r\n let theme: string = \"light\";\r\n if (typeof document !== \"undefined\") {\r\n const cookieMatch = document.cookie.match(/theme=([^;]+)/);\r\n if (cookieMatch) {\r\n theme = cookieMatch[1];\r\n } else if (json.theme) {\r\n theme = json.theme;\r\n } else {\r\n const currentTheme = getCurrentTheme();\r\n if (currentTheme) theme = currentTheme;\r\n }\r\n } else if (json.theme) {\r\n theme = json.theme;\r\n }\r\n \r\n const errorProps = {\r\n ...(json.props || {\r\n error: json.message || \"An error occurred\",\r\n }),\r\n theme,\r\n };\r\n\r\n const windowData: InitialData = {\r\n pathname: nextUrl,\r\n params: json.params || {},\r\n props: errorProps,\r\n metadata: json.metadata ?? null,\r\n theme,\r\n notFound: false,\r\n error: true,\r\n };\r\n\r\n setWindowData(windowData);\r\n\r\n setState({\r\n url: nextUrl,\r\n route: errorRoute,\r\n params: json.params || {},\r\n components,\r\n props: errorProps,\r\n });\r\n return true;\r\n } catch (loadError) {\r\n console.error(\r\n \"[client] Error loading error route components:\",\r\n loadError\r\n );\r\n window.location.href = nextUrl;\r\n return false;\r\n }\r\n}\r\n\r\nasync function handleNotFoundRoute(\r\n nextUrl: string,\r\n json: any,\r\n notFoundRoute: ClientRouteLoaded | null,\r\n setState: (state: RouteViewState) => void\r\n): Promise<void> {\r\n // Get theme: prioritize cookie, then server, then window data, then default\r\n let theme: string = \"light\";\r\n if (typeof document !== \"undefined\") {\r\n const cookieMatch = document.cookie.match(/theme=([^;]+)/);\r\n if (cookieMatch) {\r\n theme = cookieMatch[1];\r\n } else if (json.theme) {\r\n theme = json.theme;\r\n } else {\r\n const currentTheme = getCurrentTheme();\r\n if (currentTheme) theme = currentTheme;\r\n }\r\n } else if (json.theme) {\r\n theme = json.theme;\r\n }\r\n \r\n const notFoundProps = {\r\n ...(json.props ?? {}),\r\n theme,\r\n };\r\n\r\n const windowData: InitialData = {\r\n pathname: nextUrl,\r\n params: {},\r\n props: notFoundProps,\r\n metadata: json.metadata ?? null,\r\n theme,\r\n notFound: true,\r\n error: false,\r\n };\r\n\r\n setWindowData(windowData);\r\n\r\n if (notFoundRoute) {\r\n const components = await notFoundRoute.load();\r\n setState({\r\n url: nextUrl,\r\n route: notFoundRoute,\r\n params: {},\r\n components,\r\n props: notFoundProps,\r\n });\r\n } else {\r\n setState({\r\n url: nextUrl,\r\n route: null,\r\n params: {},\r\n components: null,\r\n props: {},\r\n });\r\n }\r\n}\r\n\r\nasync function handleNormalRoute(\r\n nextUrl: string,\r\n json: any,\r\n routes: ClientRouteLoaded[],\r\n setState: (state: RouteViewState) => void\r\n): Promise<boolean> {\r\n applyMetadata(json.metadata ?? null);\r\n \r\n // Get theme: prioritize cookie (source of truth), then server response, then window data, then default\r\n // Cookie is the source of truth because it persists across navigation\r\n let theme: string = \"light\"; // Default\r\n if (typeof document !== \"undefined\") {\r\n const cookieMatch = document.cookie.match(/theme=([^;]+)/);\r\n if (cookieMatch) {\r\n theme = cookieMatch[1];\r\n } else if (json.theme) {\r\n theme = json.theme;\r\n } else {\r\n const currentTheme = getCurrentTheme();\r\n if (currentTheme) {\r\n theme = currentTheme;\r\n }\r\n }\r\n } else if (json.theme) {\r\n theme = json.theme;\r\n }\r\n \r\n // Include theme in props so layouts receive it during SPA navigation\r\n const newProps = {\r\n ...(json.props ?? {}),\r\n theme, // Always include theme\r\n };\r\n\r\n const matched = matchRouteClient(nextUrl, routes);\r\n\r\n if (!matched) {\r\n window.location.href = nextUrl;\r\n return false;\r\n }\r\n\r\n const windowData: InitialData = {\r\n pathname: nextUrl,\r\n params: matched.params,\r\n props: newProps,\r\n metadata: json.metadata ?? null,\r\n theme,\r\n notFound: false,\r\n error: false,\r\n };\r\n\r\n setWindowData(windowData);\r\n\r\n const components = await matched.route.load();\r\n\r\n window.scrollTo({\r\n top: 0,\r\n behavior: \"smooth\",\r\n });\r\n\r\n setState({\r\n url: nextUrl,\r\n route: matched.route,\r\n params: matched.params,\r\n components,\r\n props: newProps,\r\n });\r\n\r\n return true;\r\n}\r\n\r\nexport type NavigateOptions = {\r\n /**\r\n * If true, forces revalidation of route data,\r\n * ignoring the cache and fetching fresh data from the server.\r\n * Similar to Next.js's `router.refresh()` behavior.\r\n */\r\n revalidate?: boolean;\r\n};\r\n\r\nexport async function navigate(\r\n nextUrl: string,\r\n handlers: NavigationHandlers,\r\n options?: NavigateOptions\r\n): Promise<void> {\r\n const { setState, routes, notFoundRoute, errorRoute } = handlers;\r\n\r\n try {\r\n const { ok, json } = await getRouteData(nextUrl, {\r\n revalidate: options?.revalidate,\r\n });\r\n\r\n if (json && json.error) {\r\n console.log(\"[client] Error detected in response:\", json);\r\n\r\n if (errorRoute) {\r\n const handled = await handleErrorRoute(\r\n nextUrl,\r\n json,\r\n errorRoute,\r\n setState\r\n );\r\n if (handled) return;\r\n } else {\r\n console.warn(\r\n \"[client] Error route not available, reloading page.\",\r\n errorRoute\r\n );\r\n window.location.href = nextUrl;\r\n return;\r\n }\r\n }\r\n\r\n // 🔴 HTTP error (404/500/etc)\r\n if (!ok) {\r\n if (json && (json as any).redirect) {\r\n window.location.href = (json as any).redirect.destination;\r\n return;\r\n }\r\n window.location.href = nextUrl;\r\n return;\r\n }\r\n\r\n // Redirección vía JSON\r\n if (json.redirect) {\r\n window.location.href = json.redirect.destination;\r\n return;\r\n }\r\n\r\n // Manejo de notFound\r\n if (json.notFound) {\r\n await handleNotFoundRoute(nextUrl, json, notFoundRoute, setState);\r\n return;\r\n }\r\n\r\n // Ruta normal\r\n await handleNormalRoute(nextUrl, json, routes, setState);\r\n } catch (err) {\r\n console.error(\"[client] Error fetching FW data:\", err);\r\n window.location.href = nextUrl;\r\n }\r\n}\r\n\r\nexport function createClickHandler(\r\n navigate: (url: string, options?: NavigateOptions) => void\r\n): (ev: MouseEvent) => void {\r\n return function handleClick(ev: MouseEvent) {\r\n // Verificar PRIMERO si es un elemento interactivo antes de hacer cualquier otra cosa\r\n // Usar composedPath para verificar todos los elementos en la cadena del evento\r\n const path = ev.composedPath();\r\n \r\n // Verificar si algún elemento en la cadena es interactivo\r\n for (const element of path) {\r\n if (!(element instanceof HTMLElement)) continue;\r\n \r\n const tagName = element.tagName.toLowerCase();\r\n if (\r\n tagName === \"input\" ||\r\n tagName === \"textarea\" ||\r\n tagName === \"button\" ||\r\n tagName === \"select\" ||\r\n element.isContentEditable ||\r\n (tagName === \"label\" && (element as HTMLLabelElement).control)\r\n ) {\r\n return; // Es un elemento interactivo, no procesar\r\n }\r\n }\r\n\r\n const target = ev.target as HTMLElement | null;\r\n if (!target) return;\r\n\r\n // Ahora verificar las otras condiciones\r\n if (ev.defaultPrevented) return;\r\n if (ev.button !== 0) return;\r\n if (ev.metaKey || ev.ctrlKey || ev.shiftKey || ev.altKey) return;\r\n\r\n const anchor = target.closest(\"a[href]\") as HTMLAnchorElement | null;\r\n if (!anchor) return;\r\n\r\n const href = anchor.getAttribute(\"href\");\r\n if (!href) return;\r\n if (href.startsWith(\"#\")) return;\r\n\r\n const url = new URL(href, window.location.href);\r\n if (url.origin !== window.location.origin) return;\r\n if (anchor.target && anchor.target !== \"_self\") return;\r\n\r\n ev.preventDefault();\r\n\r\n const nextUrl = url.pathname + url.search;\r\n const currentUrl = window.location.pathname + window.location.search;\r\n if (nextUrl === currentUrl) return;\r\n\r\n // Detectar si el link tiene data-revalidate para forzar revalidación\r\n const shouldRevalidate =\r\n anchor.hasAttribute(\"data-revalidate\") &&\r\n anchor.getAttribute(\"data-revalidate\") !== \"false\";\r\n\r\n window.history.pushState({}, \"\", nextUrl);\r\n navigate(nextUrl, shouldRevalidate ? { revalidate: true } : undefined);\r\n };\r\n}\r\n\r\nexport function createPopStateHandler(\r\n navigate: (url: string, options?: NavigateOptions) => void\r\n): () => void {\r\n return function handlePopState() {\r\n const nextUrl = window.location.pathname + window.location.search;\r\n navigate(nextUrl);\r\n };\r\n}\r\n\r\n"],"mappings":"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","names":["navigate","import_jsx_runtime","import_jsx_runtime"]}
|
package/dist/runtime.js
CHANGED
|
@@ -83,7 +83,7 @@ function applyMetadata(md) {
|
|
|
83
83
|
}
|
|
84
84
|
|
|
85
85
|
// modules/runtime/client/AppShell.tsx
|
|
86
|
-
import { useEffect, useState } from "react";
|
|
86
|
+
import { useEffect, useState, useRef } from "react";
|
|
87
87
|
|
|
88
88
|
// modules/runtime/client/RouterView.tsx
|
|
89
89
|
import { jsx } from "react/jsx-runtime";
|
|
@@ -446,11 +446,19 @@ async function navigate(nextUrl, handlers, options) {
|
|
|
446
446
|
}
|
|
447
447
|
function createClickHandler(navigate2) {
|
|
448
448
|
return function handleClick(ev) {
|
|
449
|
+
const path = ev.composedPath();
|
|
450
|
+
for (const element of path) {
|
|
451
|
+
if (!(element instanceof HTMLElement)) continue;
|
|
452
|
+
const tagName = element.tagName.toLowerCase();
|
|
453
|
+
if (tagName === "input" || tagName === "textarea" || tagName === "button" || tagName === "select" || element.isContentEditable || tagName === "label" && element.control) {
|
|
454
|
+
return;
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
const target = ev.target;
|
|
458
|
+
if (!target) return;
|
|
449
459
|
if (ev.defaultPrevented) return;
|
|
450
460
|
if (ev.button !== 0) return;
|
|
451
461
|
if (ev.metaKey || ev.ctrlKey || ev.shiftKey || ev.altKey) return;
|
|
452
|
-
const target = ev.target;
|
|
453
|
-
if (!target) return;
|
|
454
462
|
const anchor = target.closest("a[href]");
|
|
455
463
|
if (!anchor) return;
|
|
456
464
|
const href = anchor.getAttribute("href");
|
|
@@ -484,25 +492,33 @@ function AppShell({
|
|
|
484
492
|
errorRoute
|
|
485
493
|
}) {
|
|
486
494
|
const [state, setState] = useState(initialState);
|
|
495
|
+
const handlersRef = useRef({
|
|
496
|
+
setState,
|
|
497
|
+
routes,
|
|
498
|
+
notFoundRoute,
|
|
499
|
+
errorRoute
|
|
500
|
+
});
|
|
487
501
|
useEffect(() => {
|
|
488
|
-
|
|
502
|
+
handlersRef.current = {
|
|
489
503
|
setState,
|
|
490
504
|
routes,
|
|
491
505
|
notFoundRoute,
|
|
492
506
|
errorRoute
|
|
493
507
|
};
|
|
508
|
+
}, [routes, notFoundRoute, errorRoute]);
|
|
509
|
+
useEffect(() => {
|
|
494
510
|
async function handleNavigate(nextUrl, options) {
|
|
495
|
-
await navigate(nextUrl,
|
|
511
|
+
await navigate(nextUrl, handlersRef.current, options);
|
|
496
512
|
}
|
|
497
513
|
const handleClick = createClickHandler(handleNavigate);
|
|
498
514
|
const handlePopState = createPopStateHandler(handleNavigate);
|
|
499
|
-
window.addEventListener("click", handleClick);
|
|
500
|
-
window.addEventListener("popstate", handlePopState);
|
|
515
|
+
window.addEventListener("click", handleClick, { capture: false });
|
|
516
|
+
window.addEventListener("popstate", handlePopState, { capture: false });
|
|
501
517
|
return () => {
|
|
502
|
-
window.removeEventListener("click", handleClick);
|
|
503
|
-
window.removeEventListener("popstate", handlePopState);
|
|
518
|
+
window.removeEventListener("click", handleClick, { capture: false });
|
|
519
|
+
window.removeEventListener("popstate", handlePopState, { capture: false });
|
|
504
520
|
};
|
|
505
|
-
}, [
|
|
521
|
+
}, []);
|
|
506
522
|
const isError = state.route === errorRoute;
|
|
507
523
|
const isNotFound = state.route === notFoundRoute;
|
|
508
524
|
const routeType = isError ? "error" : isNotFound ? "notfound" : "normal";
|
package/dist/runtime.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../modules/runtime/client/bootstrap.tsx","../modules/runtime/client/constants.ts","../modules/runtime/client/window-data.ts","../modules/runtime/client/route-matcher.ts","../modules/runtime/client/metadata.ts","../modules/runtime/client/AppShell.tsx","../modules/runtime/client/RouterView.tsx","../modules/react/cache/client-data-cache/index.ts","../modules/runtime/client/navigation.ts"],"sourcesContent":["import { hydrateRoot } from \"react-dom/client\";\r\nimport { APP_CONTAINER_ID } from \"./constants\";\r\nimport { getWindowData } from \"./window-data\";\r\nimport { matchRouteClient } from \"./route-matcher\";\r\nimport { applyMetadata } from \"./metadata\";\r\nimport { AppShell } from \"./AppShell\";\r\nimport type {\r\n InitialData,\r\n ClientRouteLoaded,\r\n RouteViewState,\r\n} from \"./types\";\r\n\r\nexport async function loadInitialRoute(\r\n initialUrl: string,\r\n initialData: InitialData | null,\r\n routes: ClientRouteLoaded[],\r\n notFoundRoute: ClientRouteLoaded | null,\r\n errorRoute: ClientRouteLoaded | null\r\n): Promise<RouteViewState> {\r\n const isInitialNotFound = initialData?.notFound === true;\r\n const isInitialError = initialData?.error === true;\r\n\r\n let initialRoute: ClientRouteLoaded | null = null;\r\n let initialParams: Record<string, string> = {};\r\n let initialComponents = null;\r\n\r\n if (isInitialError && errorRoute) {\r\n initialRoute = errorRoute;\r\n initialParams = initialData?.params ?? {};\r\n initialComponents = await errorRoute.load();\r\n } else if (isInitialNotFound && notFoundRoute) {\r\n initialRoute = notFoundRoute;\r\n initialParams = {};\r\n initialComponents = await notFoundRoute.load();\r\n } else {\r\n const match = matchRouteClient(initialUrl, routes);\r\n if (match) {\r\n initialRoute = match.route;\r\n initialParams = match.params;\r\n initialComponents = await match.route.load();\r\n } else if (notFoundRoute) {\r\n initialRoute = notFoundRoute;\r\n initialParams = {};\r\n initialComponents = await notFoundRoute.load();\r\n } else {\r\n console.warn(\r\n `[client] No route match found for ${initialUrl}. Available routes:`,\r\n routes.map((r) => r.pattern)\r\n );\r\n }\r\n }\r\n\r\n return {\r\n url: initialUrl,\r\n route: initialRoute,\r\n params: initialParams,\r\n components: initialComponents,\r\n props: initialData?.props ?? {},\r\n };\r\n}\r\n\r\n/**\r\n * Bootstraps the client-side application.\r\n *\r\n * @param routes - Array of client routes\r\n * @param notFoundRoute - Not-found route definition\r\n * @param errorRoute - Error route definition\r\n */\r\nexport function bootstrapClient(\r\n routes: ClientRouteLoaded[],\r\n notFoundRoute: ClientRouteLoaded | null,\r\n errorRoute: ClientRouteLoaded | null = null\r\n): void {\r\n (async function bootstrap() {\r\n const container = document.getElementById(APP_CONTAINER_ID);\r\n const initialData = getWindowData();\r\n\r\n if (!container) {\r\n console.error(`Container #${APP_CONTAINER_ID} not found for hydration`);\r\n return;\r\n }\r\n\r\n const initialUrl = window.location.pathname + window.location.search;\r\n\r\n try {\r\n const initialState = await loadInitialRoute(\r\n initialUrl,\r\n initialData,\r\n routes,\r\n notFoundRoute,\r\n errorRoute\r\n );\r\n\r\n if (initialData?.metadata) {\r\n applyMetadata(initialData.metadata);\r\n }\r\n\r\n hydrateRoot(\r\n container,\r\n <AppShell\r\n initialState={initialState}\r\n routes={routes}\r\n notFoundRoute={notFoundRoute}\r\n errorRoute={errorRoute}\r\n />\r\n );\r\n } catch (error) {\r\n console.error(\r\n \"[client] Error loading initial route components for\",\r\n initialUrl,\r\n error\r\n );\r\n\r\n window.location.reload();\r\n }\r\n })();\r\n}\r\n\r\n","// Client-side constants (hardcoded to avoid alias resolution issues in Rspack)\r\nexport const WINDOW_DATA_KEY = \"__FW_DATA__\";\r\nexport const APP_CONTAINER_ID = \"__app\";\r\n\r\n","import { WINDOW_DATA_KEY } from \"./constants\";\r\nimport type { InitialData } from \"./types\";\r\n\r\nexport function getWindowData(): InitialData | null {\r\n return ((window as any)[WINDOW_DATA_KEY] as InitialData | undefined) ?? null;\r\n}\r\n\r\nexport function setWindowData(data: InitialData): void {\r\n (window as any)[WINDOW_DATA_KEY] = data;\r\n \r\n // Dispatch event for components to listen to (e.g., usePageProps, ThemeProvider)\r\n // This ensures components update when navigating in SPA mode\r\n if (typeof window !== \"undefined\") {\r\n window.dispatchEvent(\r\n new CustomEvent(\"fw-data-refresh\", {\r\n detail: { data },\r\n })\r\n );\r\n }\r\n}\r\n\r\nexport function getCurrentTheme(): string | null {\r\n return getWindowData()?.theme ?? null;\r\n}\r\n\r\n","import type { ClientRouteLoaded, ClientRouteMatch } from \"./types\";\r\n\r\nexport function buildClientRegexFromPattern(pattern: string): RegExp {\r\n const segments = pattern.split(\"/\").filter(Boolean);\r\n const regexParts: string[] = [];\r\n\r\n for (let i = 0; i < segments.length; i++) {\r\n const seg = segments[i];\r\n\r\n // catch-all [...slug]\r\n if (seg.startsWith(\"[...\") && seg.endsWith(\"]\")) {\r\n if (i !== segments.length - 1) {\r\n throw new Error(\r\n `Catch-all segment \"${seg}\" in \"${pattern}\" must be the last segment.`\r\n );\r\n }\r\n regexParts.push(\"(.+)\");\r\n continue;\r\n }\r\n\r\n // dynamic [id]\r\n if (seg.startsWith(\"[\") && seg.endsWith(\"]\")) {\r\n regexParts.push(\"([^/]+)\");\r\n continue;\r\n }\r\n\r\n // static segment\r\n const escaped = seg.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\r\n regexParts.push(escaped);\r\n }\r\n\r\n const regexSource = \"^/\" + regexParts.join(\"/\") + \"/?$\";\r\n return new RegExp(regexSource);\r\n}\r\n\r\nexport function matchRouteClient(\r\n pathWithSearch: string,\r\n routes: ClientRouteLoaded[]\r\n): ClientRouteMatch | null {\r\n const [pathname] = pathWithSearch.split(\"?\");\r\n for (const r of routes) {\r\n const regex = buildClientRegexFromPattern(r.pattern);\r\n const match = regex.exec(pathname);\r\n if (!match) continue;\r\n\r\n const params: Record<string, string> = {};\r\n r.paramNames.forEach((name, idx) => {\r\n params[name] = decodeURIComponent(match[idx + 1] || \"\");\r\n });\r\n\r\n return { route: r, params };\r\n }\r\n return null;\r\n}\r\n\r\n","export function applyMetadata(\r\n md?: { title?: string; description?: string } | null\r\n) {\r\n if (!md) return;\r\n\r\n if (md.title) {\r\n document.title = md.title;\r\n }\r\n\r\n if (md.description) {\r\n let meta = document.querySelector(\r\n 'meta[name=\"description\"]'\r\n ) as HTMLMetaElement | null;\r\n\r\n if (!meta) {\r\n meta = document.createElement(\"meta\");\r\n meta.name = \"description\";\r\n document.head.appendChild(meta);\r\n }\r\n\r\n meta.content = md.description;\r\n }\r\n}\r\n\r\n","import { useEffect, useState } from \"react\";\r\nimport { RouterView } from \"./RouterView\";\r\nimport {\r\n navigate,\r\n createClickHandler,\r\n createPopStateHandler,\r\n type NavigationHandlers,\r\n} from \"./navigation\";\r\nimport type {\r\n RouteViewState,\r\n ClientRouteLoaded,\r\n} from \"./types\";\r\n\r\nexport interface AppShellProps {\r\n initialState: RouteViewState;\r\n routes: ClientRouteLoaded[];\r\n notFoundRoute: ClientRouteLoaded | null;\r\n errorRoute: ClientRouteLoaded | null;\r\n}\r\n\r\nexport function AppShell({\r\n initialState,\r\n routes,\r\n notFoundRoute,\r\n errorRoute,\r\n}: AppShellProps) {\r\n const [state, setState] = useState<RouteViewState>(initialState);\r\n\r\n useEffect(() => {\r\n const handlers: NavigationHandlers = {\r\n setState,\r\n routes,\r\n notFoundRoute,\r\n errorRoute,\r\n };\r\n\r\n async function handleNavigate(\r\n nextUrl: string,\r\n options?: { revalidate?: boolean }\r\n ) {\r\n await navigate(nextUrl, handlers, options);\r\n }\r\n\r\n const handleClick = createClickHandler(handleNavigate);\r\n const handlePopState = createPopStateHandler(handleNavigate);\r\n\r\n window.addEventListener(\"click\", handleClick);\r\n window.addEventListener(\"popstate\", handlePopState);\r\n\r\n return () => {\r\n window.removeEventListener(\"click\", handleClick);\r\n window.removeEventListener(\"popstate\", handlePopState);\r\n };\r\n }, [routes, notFoundRoute, errorRoute]);\r\n\r\n const isError = state.route === errorRoute;\r\n const isNotFound = state.route === notFoundRoute;\r\n const routeType = isError ? \"error\" : isNotFound ? \"notfound\" : \"normal\";\r\n const routeKey = `${state.url}:${routeType}`;\r\n\r\n return <RouterView key={routeKey} state={state} />;\r\n}\r\n\r\n","import type { RouteViewState } from \"./types\";\r\n\r\nexport function RouterView({ state }: { state: RouteViewState }) {\r\n if (!state.route) {\r\n // Don't show 404 if we're waiting for components to load\r\n if (state.components === null) {\r\n return null;\r\n }\r\n return <h1>404 - Route not found</h1>;\r\n }\r\n\r\n if (!state.components) {\r\n return null;\r\n }\r\n\r\n const { Page, layouts } = state.components;\r\n const { params, props } = state;\r\n\r\n let element = <Page params={params} {...props} />;\r\n\r\n const layoutChain = layouts.slice().reverse();\r\n for (const Layout of layoutChain) {\r\n element = (\r\n <Layout params={params} {...props}>\r\n {element}\r\n </Layout>\r\n );\r\n }\r\n\r\n return element;\r\n}\r\n\r\n","type RouteData = {\n ok: boolean;\n status: number;\n json: any;\n};\n\ntype CacheEntry =\n | { status: \"pending\"; promise: Promise<RouteData> }\n | { status: \"fulfilled\"; value: RouteData }\n | { status: \"rejected\"; error: any };\n\n// Use window to guarantee a single shared cache instance\n// across all bundles/modules\nconst CACHE_KEY = \"__FW_DATA_CACHE__\";\n\n// Maximum number of entries in the cache (LRU)\nconst MAX_CACHE_SIZE = 100;\n\ntype CacheStore = {\n data: Map<string, CacheEntry>;\n index: Map<string, Set<string>>; // pathBase -> Set of keys\n lru: string[]; // Ordered list: most recent at end, oldest at start\n};\n\nfunction getCacheStore(): CacheStore {\n if (typeof window !== \"undefined\") {\n if (!(window as any)[CACHE_KEY]) {\n (window as any)[CACHE_KEY] = {\n data: new Map<string, CacheEntry>(),\n index: new Map<string, Set<string>>(),\n lru: [],\n };\n }\n return (window as any)[CACHE_KEY];\n }\n // Fallback for SSR (though this shouldn't be used on the client)\n return {\n data: new Map<string, CacheEntry>(),\n index: new Map<string, Set<string>>(),\n lru: [],\n };\n}\n\nconst cacheStore = getCacheStore();\nconst dataCache = cacheStore.data;\nconst pathIndex = cacheStore.index;\nconst lru = cacheStore.lru;\n\n// Helper functions for cache management\n\n/**\n * Extract base path from a cache key (removes query params)\n */\nfunction extractPathBase(key: string): string {\n return key.split(\"?\")[0];\n}\n\n/**\n * Add key to path index\n */\nfunction addToIndex(key: string): void {\n const pathBase = extractPathBase(key);\n if (!pathIndex.has(pathBase)) {\n pathIndex.set(pathBase, new Set());\n }\n pathIndex.get(pathBase)!.add(key);\n}\n\n/**\n * Remove key from path index\n */\nfunction removeFromIndex(key: string): void {\n const pathBase = extractPathBase(key);\n const keys = pathIndex.get(pathBase);\n if (keys) {\n keys.delete(key);\n if (keys.size === 0) {\n pathIndex.delete(pathBase);\n }\n }\n}\n\n/**\n * Update LRU order - move key to end (most recent)\n */\nfunction updateLRU(key: string): void {\n const index = lru.indexOf(key);\n if (index !== -1) {\n lru.splice(index, 1);\n }\n lru.push(key);\n}\n\n/**\n * Remove oldest entries if cache exceeds MAX_CACHE_SIZE\n */\nfunction evictOldest(): void {\n while (lru.length >= MAX_CACHE_SIZE && lru.length > 0) {\n const oldestKey = lru.shift()!;\n dataCache.delete(oldestKey);\n removeFromIndex(oldestKey);\n }\n}\n\n/**\n * Set cache entry and maintain indexes\n */\nfunction setCacheEntry(key: string, entry: CacheEntry): void {\n const existingEntry = dataCache.get(key);\n const wasFulfilled = existingEntry?.status === \"fulfilled\";\n \n dataCache.set(key, entry);\n \n // Only track fulfilled entries in LRU and index (not pending/rejected)\n if (entry.status === \"fulfilled\") {\n // Add to index if it wasn't already fulfilled (new entry or transition from pending/rejected)\n if (!wasFulfilled) {\n addToIndex(key);\n }\n updateLRU(key);\n evictOldest();\n } else if (wasFulfilled) {\n // If entry was fulfilled and now isn't (transitioning to pending/rejected), remove from index\n removeFromIndex(key);\n }\n}\n\n/**\n * Delete cache entry and clean up indexes\n */\nfunction deleteCacheEntry(key: string): void {\n if (dataCache.has(key)) {\n dataCache.delete(key);\n removeFromIndex(key);\n const lruIndex = lru.indexOf(key);\n if (lruIndex !== -1) {\n lru.splice(lruIndex, 1);\n }\n }\n}\n\nfunction buildDataUrl(url: string): string {\n return url + (url.includes(\"?\") ? \"&\" : \"?\") + \"__fw_data=1\";\n}\n\nasync function fetchRouteDataOnce(url: string): Promise<RouteData> {\n const dataUrl = buildDataUrl(url);\n\n const res = await fetch(dataUrl, {\n headers: {\n \"x-fw-data\": \"1\",\n Accept: \"application/json\",\n },\n });\n\n let json: any = {};\n\n try {\n const text = await res.text();\n if (text) {\n json = JSON.parse(text);\n }\n } catch (parseError) {\n console.error(\n \"[client][cache] Failed to parse response as JSON:\",\n parseError\n );\n }\n\n const result: RouteData = {\n ok: res.ok,\n status: res.status,\n json,\n };\n\n return result;\n}\n\n/**\n * Revalidates route data by removing it from the cache.\n * The next time you navigate to this route, fresh data will be fetched from the server.\n * This is a client-side function and does not require a server-side revalidation.\n *\n * @param path - The route path to revalidate (e.g., '/posts/1' or '/posts/1?page=2')\n * If query params are not included, revalidates all variants of that route.\n *\n * @example\n * ```ts\n * // After saving something to the DB, revalidate the route\n * await saveToDatabase(data);\n * revalidatePath('/posts');\n * \n * // Revalidate a specific route with query params\n * revalidatePath('/posts?page=2');\n * ```\n */\nexport function revalidatePath(path: string): void {\n // Normalize the base path (without query params)\n const normalizedPath = path.split(\"?\")[0];\n const hasQueryParams = path.includes(\"?\");\n \n // Get all keys for this path base from index (O(1) lookup)\n const keysForPath = pathIndex.get(normalizedPath);\n \n if (!keysForPath || keysForPath.size === 0) {\n return; // No entries to revalidate\n }\n \n // If the path includes specific query params, extract them\n let specificQueryParams: string | undefined;\n if (hasQueryParams) {\n const queryPart = path.split(\"?\")[1];\n // Sort query params for consistent comparison\n specificQueryParams = queryPart\n .split(\"&\")\n .filter((p) => !p.startsWith(\"__fw_data=\"))\n .sort()\n .join(\"&\");\n }\n \n // Iterate only over keys for this path (much smaller set)\n const keysToDelete: string[] = [];\n for (const key of keysForPath) {\n // If specific query params were specified, check if they match\n if (hasQueryParams && specificQueryParams) {\n const [, keyQuery = \"\"] = key.split(\"?\");\n const keyQueryParams = keyQuery\n .split(\"&\")\n .filter((p) => !p.startsWith(\"__fw_data=\"))\n .sort()\n .join(\"&\");\n \n if (keyQueryParams === specificQueryParams) {\n keysToDelete.push(key);\n }\n } else {\n // If no specific query params, revalidate all variants\n keysToDelete.push(key);\n }\n }\n \n // Delete matching entries\n keysToDelete.forEach((key) => {\n deleteCacheEntry(key);\n });\n}\n\n/**\n * Revalidates and refreshes the current page data.\n * Similar to Next.js's `router.refresh()`.\n * \n * This function:\n * 1. Removes the current route from cache\n * 2. Fetches fresh data from the server\n * 3. Updates window.__FW_DATA__ with the new data\n * 4. Dispatches a 'fw-data-refresh' event for components to listen to\n * \n * @returns Promise that resolves with the fresh route data\n * \n * @example\n * ```ts\n * // Refresh current page data after a mutation\n * await revalidate();\n * ```\n */\nexport async function revalidate(): Promise<RouteData> {\n if (typeof window === \"undefined\") {\n throw new Error(\"revalidate() can only be called on the client\");\n }\n\n const pathname = window.location.pathname + window.location.search;\n \n // Revalidate the path (remove from cache)\n revalidatePath(pathname);\n \n // Fetch fresh data\n const freshData = await getRouteData(pathname, { revalidate: true });\n \n // Update window.__FW_DATA__ if it exists\n if ((window as any).__FW_DATA__ && freshData.ok && freshData.json) {\n const currentData = (window as any).__FW_DATA__;\n (window as any).__FW_DATA__ = {\n ...currentData,\n pathname: pathname.split(\"?\")[0],\n params: freshData.json.params || currentData.params || {},\n props: freshData.json.props || currentData.props || {},\n metadata: freshData.json.metadata ?? currentData.metadata ?? null,\n notFound: freshData.json.notFound ?? false,\n error: freshData.json.error ?? false,\n };\n \n // Dispatch event for components to listen to\n window.dispatchEvent(new CustomEvent(\"fw-data-refresh\", {\n detail: { data: freshData },\n }));\n }\n \n return freshData;\n}\n\n/**\n * @deprecated Use `revalidatePath()` instead. This function is kept for backwards compatibility.\n */\nexport function revalidateRouteData(url: string): void {\n revalidatePath(url);\n}\n\nexport function prefetchRouteData(url: string): void {\n const key = buildDataUrl(url);\n\n const cached = dataCache.get(key);\n\n if (cached && cached.status !== \"rejected\") {\n // Update LRU if it exists and is fulfilled\n if (cached.status === \"fulfilled\") {\n updateLRU(key);\n }\n return;\n }\n\n const promise = fetchRouteDataOnce(url)\n .then((value) => {\n setCacheEntry(key, { status: \"fulfilled\", value });\n return value;\n })\n .catch((error) => {\n console.error(\"[client][cache] Error prefetching route data:\", error);\n dataCache.set(key, { status: \"rejected\", error });\n throw error;\n });\n\n dataCache.set(key, { status: \"pending\", promise });\n}\n\nexport type GetRouteDataOptions = {\n /**\n * If true, forces revalidation of route data,\n * ignoring the cache and fetching fresh data from the server.\n * Similar to Next.js's `router.refresh()` behavior.\n */\n revalidate?: boolean;\n};\n\nexport async function getRouteData(\n url: string,\n options?: GetRouteDataOptions\n): Promise<RouteData> {\n const key = buildDataUrl(url);\n\n // If revalidation is requested, remove the entry from cache\n if (options?.revalidate) {\n deleteCacheEntry(key);\n }\n\n const entry = dataCache.get(key);\n\n if (entry) {\n if (entry.status === \"fulfilled\") {\n // Update LRU: mark as recently used\n updateLRU(key);\n return entry.value;\n }\n if (entry.status === \"pending\") {\n return entry.promise;\n }\n }\n\n // No entry in cache, fetch it\n const promise = fetchRouteDataOnce(url)\n .then((value) => {\n setCacheEntry(key, { status: \"fulfilled\", value });\n return value;\n })\n .catch((error) => {\n console.error(\"[client][cache] Error fetching route data:\", error);\n dataCache.set(key, { status: \"rejected\", error });\n throw error;\n });\n\n dataCache.set(key, { status: \"pending\", promise });\n return promise;\n}\n","import { getRouteData } from \"../../react/cache/index\";\r\nimport { matchRouteClient } from \"./route-matcher\";\r\nimport { applyMetadata } from \"./metadata\";\r\nimport { setWindowData, getCurrentTheme } from \"./window-data\";\r\nimport type {\r\n ClientRouteLoaded,\r\n RouteViewState,\r\n InitialData,\r\n} from \"./types\";\r\n\r\nexport type NavigationHandlers = {\r\n setState: (state: RouteViewState) => void;\r\n routes: ClientRouteLoaded[];\r\n notFoundRoute: ClientRouteLoaded | null;\r\n errorRoute: ClientRouteLoaded | null;\r\n};\r\n\r\nasync function handleErrorRoute(\r\n nextUrl: string,\r\n json: any,\r\n errorRoute: ClientRouteLoaded,\r\n setState: (state: RouteViewState) => void\r\n): Promise<boolean> {\r\n try {\r\n const components = await errorRoute.load();\r\n \r\n // Get theme: prioritize cookie, then server, then window data, then default\r\n let theme: string = \"light\";\r\n if (typeof document !== \"undefined\") {\r\n const cookieMatch = document.cookie.match(/theme=([^;]+)/);\r\n if (cookieMatch) {\r\n theme = cookieMatch[1];\r\n } else if (json.theme) {\r\n theme = json.theme;\r\n } else {\r\n const currentTheme = getCurrentTheme();\r\n if (currentTheme) theme = currentTheme;\r\n }\r\n } else if (json.theme) {\r\n theme = json.theme;\r\n }\r\n \r\n const errorProps = {\r\n ...(json.props || {\r\n error: json.message || \"An error occurred\",\r\n }),\r\n theme,\r\n };\r\n\r\n const windowData: InitialData = {\r\n pathname: nextUrl,\r\n params: json.params || {},\r\n props: errorProps,\r\n metadata: json.metadata ?? null,\r\n theme,\r\n notFound: false,\r\n error: true,\r\n };\r\n\r\n setWindowData(windowData);\r\n\r\n setState({\r\n url: nextUrl,\r\n route: errorRoute,\r\n params: json.params || {},\r\n components,\r\n props: errorProps,\r\n });\r\n return true;\r\n } catch (loadError) {\r\n console.error(\r\n \"[client] Error loading error route components:\",\r\n loadError\r\n );\r\n window.location.href = nextUrl;\r\n return false;\r\n }\r\n}\r\n\r\nasync function handleNotFoundRoute(\r\n nextUrl: string,\r\n json: any,\r\n notFoundRoute: ClientRouteLoaded | null,\r\n setState: (state: RouteViewState) => void\r\n): Promise<void> {\r\n // Get theme: prioritize cookie, then server, then window data, then default\r\n let theme: string = \"light\";\r\n if (typeof document !== \"undefined\") {\r\n const cookieMatch = document.cookie.match(/theme=([^;]+)/);\r\n if (cookieMatch) {\r\n theme = cookieMatch[1];\r\n } else if (json.theme) {\r\n theme = json.theme;\r\n } else {\r\n const currentTheme = getCurrentTheme();\r\n if (currentTheme) theme = currentTheme;\r\n }\r\n } else if (json.theme) {\r\n theme = json.theme;\r\n }\r\n \r\n const notFoundProps = {\r\n ...(json.props ?? {}),\r\n theme,\r\n };\r\n\r\n const windowData: InitialData = {\r\n pathname: nextUrl,\r\n params: {},\r\n props: notFoundProps,\r\n metadata: json.metadata ?? null,\r\n theme,\r\n notFound: true,\r\n error: false,\r\n };\r\n\r\n setWindowData(windowData);\r\n\r\n if (notFoundRoute) {\r\n const components = await notFoundRoute.load();\r\n setState({\r\n url: nextUrl,\r\n route: notFoundRoute,\r\n params: {},\r\n components,\r\n props: notFoundProps,\r\n });\r\n } else {\r\n setState({\r\n url: nextUrl,\r\n route: null,\r\n params: {},\r\n components: null,\r\n props: {},\r\n });\r\n }\r\n}\r\n\r\nasync function handleNormalRoute(\r\n nextUrl: string,\r\n json: any,\r\n routes: ClientRouteLoaded[],\r\n setState: (state: RouteViewState) => void\r\n): Promise<boolean> {\r\n applyMetadata(json.metadata ?? null);\r\n \r\n // Get theme: prioritize cookie (source of truth), then server response, then window data, then default\r\n // Cookie is the source of truth because it persists across navigation\r\n let theme: string = \"light\"; // Default\r\n if (typeof document !== \"undefined\") {\r\n const cookieMatch = document.cookie.match(/theme=([^;]+)/);\r\n if (cookieMatch) {\r\n theme = cookieMatch[1];\r\n } else if (json.theme) {\r\n theme = json.theme;\r\n } else {\r\n const currentTheme = getCurrentTheme();\r\n if (currentTheme) {\r\n theme = currentTheme;\r\n }\r\n }\r\n } else if (json.theme) {\r\n theme = json.theme;\r\n }\r\n \r\n // Include theme in props so layouts receive it during SPA navigation\r\n const newProps = {\r\n ...(json.props ?? {}),\r\n theme, // Always include theme\r\n };\r\n\r\n const matched = matchRouteClient(nextUrl, routes);\r\n\r\n if (!matched) {\r\n window.location.href = nextUrl;\r\n return false;\r\n }\r\n\r\n const windowData: InitialData = {\r\n pathname: nextUrl,\r\n params: matched.params,\r\n props: newProps,\r\n metadata: json.metadata ?? null,\r\n theme,\r\n notFound: false,\r\n error: false,\r\n };\r\n\r\n setWindowData(windowData);\r\n\r\n const components = await matched.route.load();\r\n\r\n window.scrollTo({\r\n top: 0,\r\n behavior: \"smooth\",\r\n });\r\n\r\n setState({\r\n url: nextUrl,\r\n route: matched.route,\r\n params: matched.params,\r\n components,\r\n props: newProps,\r\n });\r\n\r\n return true;\r\n}\r\n\r\nexport type NavigateOptions = {\r\n /**\r\n * If true, forces revalidation of route data,\r\n * ignoring the cache and fetching fresh data from the server.\r\n * Similar to Next.js's `router.refresh()` behavior.\r\n */\r\n revalidate?: boolean;\r\n};\r\n\r\nexport async function navigate(\r\n nextUrl: string,\r\n handlers: NavigationHandlers,\r\n options?: NavigateOptions\r\n): Promise<void> {\r\n const { setState, routes, notFoundRoute, errorRoute } = handlers;\r\n\r\n try {\r\n const { ok, json } = await getRouteData(nextUrl, {\r\n revalidate: options?.revalidate,\r\n });\r\n\r\n if (json && json.error) {\r\n console.log(\"[client] Error detected in response:\", json);\r\n\r\n if (errorRoute) {\r\n const handled = await handleErrorRoute(\r\n nextUrl,\r\n json,\r\n errorRoute,\r\n setState\r\n );\r\n if (handled) return;\r\n } else {\r\n console.warn(\r\n \"[client] Error route not available, reloading page.\",\r\n errorRoute\r\n );\r\n window.location.href = nextUrl;\r\n return;\r\n }\r\n }\r\n\r\n // 🔴 HTTP error (404/500/etc)\r\n if (!ok) {\r\n if (json && (json as any).redirect) {\r\n window.location.href = (json as any).redirect.destination;\r\n return;\r\n }\r\n window.location.href = nextUrl;\r\n return;\r\n }\r\n\r\n // Redirección vía JSON\r\n if (json.redirect) {\r\n window.location.href = json.redirect.destination;\r\n return;\r\n }\r\n\r\n // Manejo de notFound\r\n if (json.notFound) {\r\n await handleNotFoundRoute(nextUrl, json, notFoundRoute, setState);\r\n return;\r\n }\r\n\r\n // Ruta normal\r\n await handleNormalRoute(nextUrl, json, routes, setState);\r\n } catch (err) {\r\n console.error(\"[client] Error fetching FW data:\", err);\r\n window.location.href = nextUrl;\r\n }\r\n}\r\n\r\nexport function createClickHandler(\r\n navigate: (url: string, options?: NavigateOptions) => void\r\n): (ev: MouseEvent) => void {\r\n return function handleClick(ev: MouseEvent) {\r\n if (ev.defaultPrevented) return;\r\n if (ev.button !== 0) return;\r\n if (ev.metaKey || ev.ctrlKey || ev.shiftKey || ev.altKey) return;\r\n\r\n const target = ev.target as HTMLElement | null;\r\n if (!target) return;\r\n\r\n const anchor = target.closest(\"a[href]\") as HTMLAnchorElement | null;\r\n if (!anchor) return;\r\n\r\n const href = anchor.getAttribute(\"href\");\r\n if (!href) return;\r\n if (href.startsWith(\"#\")) return;\r\n\r\n const url = new URL(href, window.location.href);\r\n if (url.origin !== window.location.origin) return;\r\n if (anchor.target && anchor.target !== \"_self\") return;\r\n\r\n ev.preventDefault();\r\n\r\n const nextUrl = url.pathname + url.search;\r\n const currentUrl = window.location.pathname + window.location.search;\r\n if (nextUrl === currentUrl) return;\r\n\r\n // Detectar si el link tiene data-revalidate para forzar revalidación\r\n const shouldRevalidate =\r\n anchor.hasAttribute(\"data-revalidate\") &&\r\n anchor.getAttribute(\"data-revalidate\") !== \"false\";\r\n\r\n window.history.pushState({}, \"\", nextUrl);\r\n navigate(nextUrl, shouldRevalidate ? { revalidate: true } : undefined);\r\n };\r\n}\r\n\r\nexport function createPopStateHandler(\r\n navigate: (url: string, options?: NavigateOptions) => void\r\n): () => void {\r\n return function handlePopState() {\r\n const nextUrl = window.location.pathname + window.location.search;\r\n navigate(nextUrl);\r\n };\r\n}\r\n\r\n"],"mappings":"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","names":["navigate","jsx","jsx"]}
|
|
1
|
+
{"version":3,"sources":["../modules/runtime/client/bootstrap.tsx","../modules/runtime/client/constants.ts","../modules/runtime/client/window-data.ts","../modules/runtime/client/route-matcher.ts","../modules/runtime/client/metadata.ts","../modules/runtime/client/AppShell.tsx","../modules/runtime/client/RouterView.tsx","../modules/react/cache/client-data-cache/index.ts","../modules/runtime/client/navigation.ts"],"sourcesContent":["import { hydrateRoot } from \"react-dom/client\";\r\nimport { APP_CONTAINER_ID } from \"./constants\";\r\nimport { getWindowData } from \"./window-data\";\r\nimport { matchRouteClient } from \"./route-matcher\";\r\nimport { applyMetadata } from \"./metadata\";\r\nimport { AppShell } from \"./AppShell\";\r\nimport type {\r\n InitialData,\r\n ClientRouteLoaded,\r\n RouteViewState,\r\n} from \"./types\";\r\n\r\nexport async function loadInitialRoute(\r\n initialUrl: string,\r\n initialData: InitialData | null,\r\n routes: ClientRouteLoaded[],\r\n notFoundRoute: ClientRouteLoaded | null,\r\n errorRoute: ClientRouteLoaded | null\r\n): Promise<RouteViewState> {\r\n const isInitialNotFound = initialData?.notFound === true;\r\n const isInitialError = initialData?.error === true;\r\n\r\n let initialRoute: ClientRouteLoaded | null = null;\r\n let initialParams: Record<string, string> = {};\r\n let initialComponents = null;\r\n\r\n if (isInitialError && errorRoute) {\r\n initialRoute = errorRoute;\r\n initialParams = initialData?.params ?? {};\r\n initialComponents = await errorRoute.load();\r\n } else if (isInitialNotFound && notFoundRoute) {\r\n initialRoute = notFoundRoute;\r\n initialParams = {};\r\n initialComponents = await notFoundRoute.load();\r\n } else {\r\n const match = matchRouteClient(initialUrl, routes);\r\n if (match) {\r\n initialRoute = match.route;\r\n initialParams = match.params;\r\n initialComponents = await match.route.load();\r\n } else if (notFoundRoute) {\r\n initialRoute = notFoundRoute;\r\n initialParams = {};\r\n initialComponents = await notFoundRoute.load();\r\n } else {\r\n console.warn(\r\n `[client] No route match found for ${initialUrl}. Available routes:`,\r\n routes.map((r) => r.pattern)\r\n );\r\n }\r\n }\r\n\r\n return {\r\n url: initialUrl,\r\n route: initialRoute,\r\n params: initialParams,\r\n components: initialComponents,\r\n props: initialData?.props ?? {},\r\n };\r\n}\r\n\r\n/**\r\n * Bootstraps the client-side application.\r\n *\r\n * @param routes - Array of client routes\r\n * @param notFoundRoute - Not-found route definition\r\n * @param errorRoute - Error route definition\r\n */\r\nexport function bootstrapClient(\r\n routes: ClientRouteLoaded[],\r\n notFoundRoute: ClientRouteLoaded | null,\r\n errorRoute: ClientRouteLoaded | null = null\r\n): void {\r\n (async function bootstrap() {\r\n const container = document.getElementById(APP_CONTAINER_ID);\r\n const initialData = getWindowData();\r\n\r\n if (!container) {\r\n console.error(`Container #${APP_CONTAINER_ID} not found for hydration`);\r\n return;\r\n }\r\n\r\n const initialUrl = window.location.pathname + window.location.search;\r\n\r\n try {\r\n const initialState = await loadInitialRoute(\r\n initialUrl,\r\n initialData,\r\n routes,\r\n notFoundRoute,\r\n errorRoute\r\n );\r\n\r\n if (initialData?.metadata) {\r\n applyMetadata(initialData.metadata);\r\n }\r\n\r\n hydrateRoot(\r\n container,\r\n <AppShell\r\n initialState={initialState}\r\n routes={routes}\r\n notFoundRoute={notFoundRoute}\r\n errorRoute={errorRoute}\r\n />\r\n );\r\n } catch (error) {\r\n console.error(\r\n \"[client] Error loading initial route components for\",\r\n initialUrl,\r\n error\r\n );\r\n\r\n window.location.reload();\r\n }\r\n })();\r\n}\r\n\r\n","// Client-side constants (hardcoded to avoid alias resolution issues in Rspack)\r\nexport const WINDOW_DATA_KEY = \"__FW_DATA__\";\r\nexport const APP_CONTAINER_ID = \"__app\";\r\n\r\n","import { WINDOW_DATA_KEY } from \"./constants\";\r\nimport type { InitialData } from \"./types\";\r\n\r\nexport function getWindowData(): InitialData | null {\r\n return ((window as any)[WINDOW_DATA_KEY] as InitialData | undefined) ?? null;\r\n}\r\n\r\nexport function setWindowData(data: InitialData): void {\r\n (window as any)[WINDOW_DATA_KEY] = data;\r\n \r\n // Dispatch event for components to listen to (e.g., usePageProps, ThemeProvider)\r\n // This ensures components update when navigating in SPA mode\r\n if (typeof window !== \"undefined\") {\r\n window.dispatchEvent(\r\n new CustomEvent(\"fw-data-refresh\", {\r\n detail: { data },\r\n })\r\n );\r\n }\r\n}\r\n\r\nexport function getCurrentTheme(): string | null {\r\n return getWindowData()?.theme ?? null;\r\n}\r\n\r\n","import type { ClientRouteLoaded, ClientRouteMatch } from \"./types\";\r\n\r\nexport function buildClientRegexFromPattern(pattern: string): RegExp {\r\n const segments = pattern.split(\"/\").filter(Boolean);\r\n const regexParts: string[] = [];\r\n\r\n for (let i = 0; i < segments.length; i++) {\r\n const seg = segments[i];\r\n\r\n // catch-all [...slug]\r\n if (seg.startsWith(\"[...\") && seg.endsWith(\"]\")) {\r\n if (i !== segments.length - 1) {\r\n throw new Error(\r\n `Catch-all segment \"${seg}\" in \"${pattern}\" must be the last segment.`\r\n );\r\n }\r\n regexParts.push(\"(.+)\");\r\n continue;\r\n }\r\n\r\n // dynamic [id]\r\n if (seg.startsWith(\"[\") && seg.endsWith(\"]\")) {\r\n regexParts.push(\"([^/]+)\");\r\n continue;\r\n }\r\n\r\n // static segment\r\n const escaped = seg.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\r\n regexParts.push(escaped);\r\n }\r\n\r\n const regexSource = \"^/\" + regexParts.join(\"/\") + \"/?$\";\r\n return new RegExp(regexSource);\r\n}\r\n\r\nexport function matchRouteClient(\r\n pathWithSearch: string,\r\n routes: ClientRouteLoaded[]\r\n): ClientRouteMatch | null {\r\n const [pathname] = pathWithSearch.split(\"?\");\r\n for (const r of routes) {\r\n const regex = buildClientRegexFromPattern(r.pattern);\r\n const match = regex.exec(pathname);\r\n if (!match) continue;\r\n\r\n const params: Record<string, string> = {};\r\n r.paramNames.forEach((name, idx) => {\r\n params[name] = decodeURIComponent(match[idx + 1] || \"\");\r\n });\r\n\r\n return { route: r, params };\r\n }\r\n return null;\r\n}\r\n\r\n","export function applyMetadata(\r\n md?: { title?: string; description?: string } | null\r\n) {\r\n if (!md) return;\r\n\r\n if (md.title) {\r\n document.title = md.title;\r\n }\r\n\r\n if (md.description) {\r\n let meta = document.querySelector(\r\n 'meta[name=\"description\"]'\r\n ) as HTMLMetaElement | null;\r\n\r\n if (!meta) {\r\n meta = document.createElement(\"meta\");\r\n meta.name = \"description\";\r\n document.head.appendChild(meta);\r\n }\r\n\r\n meta.content = md.description;\r\n }\r\n}\r\n\r\n","import { useEffect, useState, useRef } from \"react\";\r\nimport { RouterView } from \"./RouterView\";\r\nimport {\r\n navigate,\r\n createClickHandler,\r\n createPopStateHandler,\r\n type NavigationHandlers,\r\n} from \"./navigation\";\r\nimport type {\r\n RouteViewState,\r\n ClientRouteLoaded,\r\n} from \"./types\";\r\n\r\nexport interface AppShellProps {\r\n initialState: RouteViewState;\r\n routes: ClientRouteLoaded[];\r\n notFoundRoute: ClientRouteLoaded | null;\r\n errorRoute: ClientRouteLoaded | null;\r\n}\r\n\r\nexport function AppShell({\r\n initialState,\r\n routes,\r\n notFoundRoute,\r\n errorRoute,\r\n}: AppShellProps) {\r\n const [state, setState] = useState<RouteViewState>(initialState);\r\n const handlersRef = useRef<NavigationHandlers>({\r\n setState,\r\n routes,\r\n notFoundRoute,\r\n errorRoute,\r\n });\r\n\r\n // Mantener handlersRef actualizado\r\n useEffect(() => {\r\n handlersRef.current = {\r\n setState,\r\n routes,\r\n notFoundRoute,\r\n errorRoute,\r\n };\r\n }, [routes, notFoundRoute, errorRoute]);\r\n\r\n useEffect(() => {\r\n async function handleNavigate(\r\n nextUrl: string,\r\n options?: { revalidate?: boolean }\r\n ) {\r\n await navigate(nextUrl, handlersRef.current, options);\r\n }\r\n\r\n const handleClick = createClickHandler(handleNavigate);\r\n const handlePopState = createPopStateHandler(handleNavigate);\r\n\r\n // Usar capture: false (burbujeo) para que los eventos del input se manejen primero\r\n window.addEventListener(\"click\", handleClick, { capture: false });\r\n window.addEventListener(\"popstate\", handlePopState, { capture: false });\r\n\r\n return () => {\r\n window.removeEventListener(\"click\", handleClick, { capture: false } as EventListenerOptions);\r\n window.removeEventListener(\"popstate\", handlePopState, { capture: false } as EventListenerOptions);\r\n };\r\n }, []); // Solo ejecutar una vez al montar\r\n\r\n const isError = state.route === errorRoute;\r\n const isNotFound = state.route === notFoundRoute;\r\n const routeType = isError ? \"error\" : isNotFound ? \"notfound\" : \"normal\";\r\n const routeKey = `${state.url}:${routeType}`;\r\n\r\n return <RouterView key={routeKey} state={state} />;\r\n}\r\n\r\n","import type { RouteViewState } from \"./types\";\r\n\r\nexport function RouterView({ state }: { state: RouteViewState }) {\r\n if (!state.route) {\r\n // Don't show 404 if we're waiting for components to load\r\n if (state.components === null) {\r\n return null;\r\n }\r\n return <h1>404 - Route not found</h1>;\r\n }\r\n\r\n if (!state.components) {\r\n return null;\r\n }\r\n\r\n const { Page, layouts } = state.components;\r\n const { params, props } = state;\r\n\r\n let element = <Page params={params} {...props} />;\r\n\r\n const layoutChain = layouts.slice().reverse();\r\n for (const Layout of layoutChain) {\r\n element = (\r\n <Layout params={params} {...props}>\r\n {element}\r\n </Layout>\r\n );\r\n }\r\n\r\n return element;\r\n}\r\n\r\n","type RouteData = {\n ok: boolean;\n status: number;\n json: any;\n};\n\ntype CacheEntry =\n | { status: \"pending\"; promise: Promise<RouteData> }\n | { status: \"fulfilled\"; value: RouteData }\n | { status: \"rejected\"; error: any };\n\n// Use window to guarantee a single shared cache instance\n// across all bundles/modules\nconst CACHE_KEY = \"__FW_DATA_CACHE__\";\n\n// Maximum number of entries in the cache (LRU)\nconst MAX_CACHE_SIZE = 100;\n\ntype CacheStore = {\n data: Map<string, CacheEntry>;\n index: Map<string, Set<string>>; // pathBase -> Set of keys\n lru: string[]; // Ordered list: most recent at end, oldest at start\n};\n\nfunction getCacheStore(): CacheStore {\n if (typeof window !== \"undefined\") {\n if (!(window as any)[CACHE_KEY]) {\n (window as any)[CACHE_KEY] = {\n data: new Map<string, CacheEntry>(),\n index: new Map<string, Set<string>>(),\n lru: [],\n };\n }\n return (window as any)[CACHE_KEY];\n }\n // Fallback for SSR (though this shouldn't be used on the client)\n return {\n data: new Map<string, CacheEntry>(),\n index: new Map<string, Set<string>>(),\n lru: [],\n };\n}\n\nconst cacheStore = getCacheStore();\nconst dataCache = cacheStore.data;\nconst pathIndex = cacheStore.index;\nconst lru = cacheStore.lru;\n\n// Helper functions for cache management\n\n/**\n * Extract base path from a cache key (removes query params)\n */\nfunction extractPathBase(key: string): string {\n return key.split(\"?\")[0];\n}\n\n/**\n * Add key to path index\n */\nfunction addToIndex(key: string): void {\n const pathBase = extractPathBase(key);\n if (!pathIndex.has(pathBase)) {\n pathIndex.set(pathBase, new Set());\n }\n pathIndex.get(pathBase)!.add(key);\n}\n\n/**\n * Remove key from path index\n */\nfunction removeFromIndex(key: string): void {\n const pathBase = extractPathBase(key);\n const keys = pathIndex.get(pathBase);\n if (keys) {\n keys.delete(key);\n if (keys.size === 0) {\n pathIndex.delete(pathBase);\n }\n }\n}\n\n/**\n * Update LRU order - move key to end (most recent)\n */\nfunction updateLRU(key: string): void {\n const index = lru.indexOf(key);\n if (index !== -1) {\n lru.splice(index, 1);\n }\n lru.push(key);\n}\n\n/**\n * Remove oldest entries if cache exceeds MAX_CACHE_SIZE\n */\nfunction evictOldest(): void {\n while (lru.length >= MAX_CACHE_SIZE && lru.length > 0) {\n const oldestKey = lru.shift()!;\n dataCache.delete(oldestKey);\n removeFromIndex(oldestKey);\n }\n}\n\n/**\n * Set cache entry and maintain indexes\n */\nfunction setCacheEntry(key: string, entry: CacheEntry): void {\n const existingEntry = dataCache.get(key);\n const wasFulfilled = existingEntry?.status === \"fulfilled\";\n \n dataCache.set(key, entry);\n \n // Only track fulfilled entries in LRU and index (not pending/rejected)\n if (entry.status === \"fulfilled\") {\n // Add to index if it wasn't already fulfilled (new entry or transition from pending/rejected)\n if (!wasFulfilled) {\n addToIndex(key);\n }\n updateLRU(key);\n evictOldest();\n } else if (wasFulfilled) {\n // If entry was fulfilled and now isn't (transitioning to pending/rejected), remove from index\n removeFromIndex(key);\n }\n}\n\n/**\n * Delete cache entry and clean up indexes\n */\nfunction deleteCacheEntry(key: string): void {\n if (dataCache.has(key)) {\n dataCache.delete(key);\n removeFromIndex(key);\n const lruIndex = lru.indexOf(key);\n if (lruIndex !== -1) {\n lru.splice(lruIndex, 1);\n }\n }\n}\n\nfunction buildDataUrl(url: string): string {\n return url + (url.includes(\"?\") ? \"&\" : \"?\") + \"__fw_data=1\";\n}\n\nasync function fetchRouteDataOnce(url: string): Promise<RouteData> {\n const dataUrl = buildDataUrl(url);\n\n const res = await fetch(dataUrl, {\n headers: {\n \"x-fw-data\": \"1\",\n Accept: \"application/json\",\n },\n });\n\n let json: any = {};\n\n try {\n const text = await res.text();\n if (text) {\n json = JSON.parse(text);\n }\n } catch (parseError) {\n console.error(\n \"[client][cache] Failed to parse response as JSON:\",\n parseError\n );\n }\n\n const result: RouteData = {\n ok: res.ok,\n status: res.status,\n json,\n };\n\n return result;\n}\n\n/**\n * Revalidates route data by removing it from the cache.\n * The next time you navigate to this route, fresh data will be fetched from the server.\n * This is a client-side function and does not require a server-side revalidation.\n *\n * @param path - The route path to revalidate (e.g., '/posts/1' or '/posts/1?page=2')\n * If query params are not included, revalidates all variants of that route.\n *\n * @example\n * ```ts\n * // After saving something to the DB, revalidate the route\n * await saveToDatabase(data);\n * revalidatePath('/posts');\n * \n * // Revalidate a specific route with query params\n * revalidatePath('/posts?page=2');\n * ```\n */\nexport function revalidatePath(path: string): void {\n // Normalize the base path (without query params)\n const normalizedPath = path.split(\"?\")[0];\n const hasQueryParams = path.includes(\"?\");\n \n // Get all keys for this path base from index (O(1) lookup)\n const keysForPath = pathIndex.get(normalizedPath);\n \n if (!keysForPath || keysForPath.size === 0) {\n return; // No entries to revalidate\n }\n \n // If the path includes specific query params, extract them\n let specificQueryParams: string | undefined;\n if (hasQueryParams) {\n const queryPart = path.split(\"?\")[1];\n // Sort query params for consistent comparison\n specificQueryParams = queryPart\n .split(\"&\")\n .filter((p) => !p.startsWith(\"__fw_data=\"))\n .sort()\n .join(\"&\");\n }\n \n // Iterate only over keys for this path (much smaller set)\n const keysToDelete: string[] = [];\n for (const key of keysForPath) {\n // If specific query params were specified, check if they match\n if (hasQueryParams && specificQueryParams) {\n const [, keyQuery = \"\"] = key.split(\"?\");\n const keyQueryParams = keyQuery\n .split(\"&\")\n .filter((p) => !p.startsWith(\"__fw_data=\"))\n .sort()\n .join(\"&\");\n \n if (keyQueryParams === specificQueryParams) {\n keysToDelete.push(key);\n }\n } else {\n // If no specific query params, revalidate all variants\n keysToDelete.push(key);\n }\n }\n \n // Delete matching entries\n keysToDelete.forEach((key) => {\n deleteCacheEntry(key);\n });\n}\n\n/**\n * Revalidates and refreshes the current page data.\n * Similar to Next.js's `router.refresh()`.\n * \n * This function:\n * 1. Removes the current route from cache\n * 2. Fetches fresh data from the server\n * 3. Updates window.__FW_DATA__ with the new data\n * 4. Dispatches a 'fw-data-refresh' event for components to listen to\n * \n * @returns Promise that resolves with the fresh route data\n * \n * @example\n * ```ts\n * // Refresh current page data after a mutation\n * await revalidate();\n * ```\n */\nexport async function revalidate(): Promise<RouteData> {\n if (typeof window === \"undefined\") {\n throw new Error(\"revalidate() can only be called on the client\");\n }\n\n const pathname = window.location.pathname + window.location.search;\n \n // Revalidate the path (remove from cache)\n revalidatePath(pathname);\n \n // Fetch fresh data\n const freshData = await getRouteData(pathname, { revalidate: true });\n \n // Update window.__FW_DATA__ if it exists\n if ((window as any).__FW_DATA__ && freshData.ok && freshData.json) {\n const currentData = (window as any).__FW_DATA__;\n (window as any).__FW_DATA__ = {\n ...currentData,\n pathname: pathname.split(\"?\")[0],\n params: freshData.json.params || currentData.params || {},\n props: freshData.json.props || currentData.props || {},\n metadata: freshData.json.metadata ?? currentData.metadata ?? null,\n notFound: freshData.json.notFound ?? false,\n error: freshData.json.error ?? false,\n };\n \n // Dispatch event for components to listen to\n window.dispatchEvent(new CustomEvent(\"fw-data-refresh\", {\n detail: { data: freshData },\n }));\n }\n \n return freshData;\n}\n\n/**\n * @deprecated Use `revalidatePath()` instead. This function is kept for backwards compatibility.\n */\nexport function revalidateRouteData(url: string): void {\n revalidatePath(url);\n}\n\nexport function prefetchRouteData(url: string): void {\n const key = buildDataUrl(url);\n\n const cached = dataCache.get(key);\n\n if (cached && cached.status !== \"rejected\") {\n // Update LRU if it exists and is fulfilled\n if (cached.status === \"fulfilled\") {\n updateLRU(key);\n }\n return;\n }\n\n const promise = fetchRouteDataOnce(url)\n .then((value) => {\n setCacheEntry(key, { status: \"fulfilled\", value });\n return value;\n })\n .catch((error) => {\n console.error(\"[client][cache] Error prefetching route data:\", error);\n dataCache.set(key, { status: \"rejected\", error });\n throw error;\n });\n\n dataCache.set(key, { status: \"pending\", promise });\n}\n\nexport type GetRouteDataOptions = {\n /**\n * If true, forces revalidation of route data,\n * ignoring the cache and fetching fresh data from the server.\n * Similar to Next.js's `router.refresh()` behavior.\n */\n revalidate?: boolean;\n};\n\nexport async function getRouteData(\n url: string,\n options?: GetRouteDataOptions\n): Promise<RouteData> {\n const key = buildDataUrl(url);\n\n // If revalidation is requested, remove the entry from cache\n if (options?.revalidate) {\n deleteCacheEntry(key);\n }\n\n const entry = dataCache.get(key);\n\n if (entry) {\n if (entry.status === \"fulfilled\") {\n // Update LRU: mark as recently used\n updateLRU(key);\n return entry.value;\n }\n if (entry.status === \"pending\") {\n return entry.promise;\n }\n }\n\n // No entry in cache, fetch it\n const promise = fetchRouteDataOnce(url)\n .then((value) => {\n setCacheEntry(key, { status: \"fulfilled\", value });\n return value;\n })\n .catch((error) => {\n console.error(\"[client][cache] Error fetching route data:\", error);\n dataCache.set(key, { status: \"rejected\", error });\n throw error;\n });\n\n dataCache.set(key, { status: \"pending\", promise });\n return promise;\n}\n","import { getRouteData } from \"../../react/cache/index\";\r\nimport { matchRouteClient } from \"./route-matcher\";\r\nimport { applyMetadata } from \"./metadata\";\r\nimport { setWindowData, getCurrentTheme } from \"./window-data\";\r\nimport type {\r\n ClientRouteLoaded,\r\n RouteViewState,\r\n InitialData,\r\n} from \"./types\";\r\n\r\nexport type NavigationHandlers = {\r\n setState: (state: RouteViewState) => void;\r\n routes: ClientRouteLoaded[];\r\n notFoundRoute: ClientRouteLoaded | null;\r\n errorRoute: ClientRouteLoaded | null;\r\n};\r\n\r\nasync function handleErrorRoute(\r\n nextUrl: string,\r\n json: any,\r\n errorRoute: ClientRouteLoaded,\r\n setState: (state: RouteViewState) => void\r\n): Promise<boolean> {\r\n try {\r\n const components = await errorRoute.load();\r\n \r\n // Get theme: prioritize cookie, then server, then window data, then default\r\n let theme: string = \"light\";\r\n if (typeof document !== \"undefined\") {\r\n const cookieMatch = document.cookie.match(/theme=([^;]+)/);\r\n if (cookieMatch) {\r\n theme = cookieMatch[1];\r\n } else if (json.theme) {\r\n theme = json.theme;\r\n } else {\r\n const currentTheme = getCurrentTheme();\r\n if (currentTheme) theme = currentTheme;\r\n }\r\n } else if (json.theme) {\r\n theme = json.theme;\r\n }\r\n \r\n const errorProps = {\r\n ...(json.props || {\r\n error: json.message || \"An error occurred\",\r\n }),\r\n theme,\r\n };\r\n\r\n const windowData: InitialData = {\r\n pathname: nextUrl,\r\n params: json.params || {},\r\n props: errorProps,\r\n metadata: json.metadata ?? null,\r\n theme,\r\n notFound: false,\r\n error: true,\r\n };\r\n\r\n setWindowData(windowData);\r\n\r\n setState({\r\n url: nextUrl,\r\n route: errorRoute,\r\n params: json.params || {},\r\n components,\r\n props: errorProps,\r\n });\r\n return true;\r\n } catch (loadError) {\r\n console.error(\r\n \"[client] Error loading error route components:\",\r\n loadError\r\n );\r\n window.location.href = nextUrl;\r\n return false;\r\n }\r\n}\r\n\r\nasync function handleNotFoundRoute(\r\n nextUrl: string,\r\n json: any,\r\n notFoundRoute: ClientRouteLoaded | null,\r\n setState: (state: RouteViewState) => void\r\n): Promise<void> {\r\n // Get theme: prioritize cookie, then server, then window data, then default\r\n let theme: string = \"light\";\r\n if (typeof document !== \"undefined\") {\r\n const cookieMatch = document.cookie.match(/theme=([^;]+)/);\r\n if (cookieMatch) {\r\n theme = cookieMatch[1];\r\n } else if (json.theme) {\r\n theme = json.theme;\r\n } else {\r\n const currentTheme = getCurrentTheme();\r\n if (currentTheme) theme = currentTheme;\r\n }\r\n } else if (json.theme) {\r\n theme = json.theme;\r\n }\r\n \r\n const notFoundProps = {\r\n ...(json.props ?? {}),\r\n theme,\r\n };\r\n\r\n const windowData: InitialData = {\r\n pathname: nextUrl,\r\n params: {},\r\n props: notFoundProps,\r\n metadata: json.metadata ?? null,\r\n theme,\r\n notFound: true,\r\n error: false,\r\n };\r\n\r\n setWindowData(windowData);\r\n\r\n if (notFoundRoute) {\r\n const components = await notFoundRoute.load();\r\n setState({\r\n url: nextUrl,\r\n route: notFoundRoute,\r\n params: {},\r\n components,\r\n props: notFoundProps,\r\n });\r\n } else {\r\n setState({\r\n url: nextUrl,\r\n route: null,\r\n params: {},\r\n components: null,\r\n props: {},\r\n });\r\n }\r\n}\r\n\r\nasync function handleNormalRoute(\r\n nextUrl: string,\r\n json: any,\r\n routes: ClientRouteLoaded[],\r\n setState: (state: RouteViewState) => void\r\n): Promise<boolean> {\r\n applyMetadata(json.metadata ?? null);\r\n \r\n // Get theme: prioritize cookie (source of truth), then server response, then window data, then default\r\n // Cookie is the source of truth because it persists across navigation\r\n let theme: string = \"light\"; // Default\r\n if (typeof document !== \"undefined\") {\r\n const cookieMatch = document.cookie.match(/theme=([^;]+)/);\r\n if (cookieMatch) {\r\n theme = cookieMatch[1];\r\n } else if (json.theme) {\r\n theme = json.theme;\r\n } else {\r\n const currentTheme = getCurrentTheme();\r\n if (currentTheme) {\r\n theme = currentTheme;\r\n }\r\n }\r\n } else if (json.theme) {\r\n theme = json.theme;\r\n }\r\n \r\n // Include theme in props so layouts receive it during SPA navigation\r\n const newProps = {\r\n ...(json.props ?? {}),\r\n theme, // Always include theme\r\n };\r\n\r\n const matched = matchRouteClient(nextUrl, routes);\r\n\r\n if (!matched) {\r\n window.location.href = nextUrl;\r\n return false;\r\n }\r\n\r\n const windowData: InitialData = {\r\n pathname: nextUrl,\r\n params: matched.params,\r\n props: newProps,\r\n metadata: json.metadata ?? null,\r\n theme,\r\n notFound: false,\r\n error: false,\r\n };\r\n\r\n setWindowData(windowData);\r\n\r\n const components = await matched.route.load();\r\n\r\n window.scrollTo({\r\n top: 0,\r\n behavior: \"smooth\",\r\n });\r\n\r\n setState({\r\n url: nextUrl,\r\n route: matched.route,\r\n params: matched.params,\r\n components,\r\n props: newProps,\r\n });\r\n\r\n return true;\r\n}\r\n\r\nexport type NavigateOptions = {\r\n /**\r\n * If true, forces revalidation of route data,\r\n * ignoring the cache and fetching fresh data from the server.\r\n * Similar to Next.js's `router.refresh()` behavior.\r\n */\r\n revalidate?: boolean;\r\n};\r\n\r\nexport async function navigate(\r\n nextUrl: string,\r\n handlers: NavigationHandlers,\r\n options?: NavigateOptions\r\n): Promise<void> {\r\n const { setState, routes, notFoundRoute, errorRoute } = handlers;\r\n\r\n try {\r\n const { ok, json } = await getRouteData(nextUrl, {\r\n revalidate: options?.revalidate,\r\n });\r\n\r\n if (json && json.error) {\r\n console.log(\"[client] Error detected in response:\", json);\r\n\r\n if (errorRoute) {\r\n const handled = await handleErrorRoute(\r\n nextUrl,\r\n json,\r\n errorRoute,\r\n setState\r\n );\r\n if (handled) return;\r\n } else {\r\n console.warn(\r\n \"[client] Error route not available, reloading page.\",\r\n errorRoute\r\n );\r\n window.location.href = nextUrl;\r\n return;\r\n }\r\n }\r\n\r\n // 🔴 HTTP error (404/500/etc)\r\n if (!ok) {\r\n if (json && (json as any).redirect) {\r\n window.location.href = (json as any).redirect.destination;\r\n return;\r\n }\r\n window.location.href = nextUrl;\r\n return;\r\n }\r\n\r\n // Redirección vía JSON\r\n if (json.redirect) {\r\n window.location.href = json.redirect.destination;\r\n return;\r\n }\r\n\r\n // Manejo de notFound\r\n if (json.notFound) {\r\n await handleNotFoundRoute(nextUrl, json, notFoundRoute, setState);\r\n return;\r\n }\r\n\r\n // Ruta normal\r\n await handleNormalRoute(nextUrl, json, routes, setState);\r\n } catch (err) {\r\n console.error(\"[client] Error fetching FW data:\", err);\r\n window.location.href = nextUrl;\r\n }\r\n}\r\n\r\nexport function createClickHandler(\r\n navigate: (url: string, options?: NavigateOptions) => void\r\n): (ev: MouseEvent) => void {\r\n return function handleClick(ev: MouseEvent) {\r\n // Verificar PRIMERO si es un elemento interactivo antes de hacer cualquier otra cosa\r\n // Usar composedPath para verificar todos los elementos en la cadena del evento\r\n const path = ev.composedPath();\r\n \r\n // Verificar si algún elemento en la cadena es interactivo\r\n for (const element of path) {\r\n if (!(element instanceof HTMLElement)) continue;\r\n \r\n const tagName = element.tagName.toLowerCase();\r\n if (\r\n tagName === \"input\" ||\r\n tagName === \"textarea\" ||\r\n tagName === \"button\" ||\r\n tagName === \"select\" ||\r\n element.isContentEditable ||\r\n (tagName === \"label\" && (element as HTMLLabelElement).control)\r\n ) {\r\n return; // Es un elemento interactivo, no procesar\r\n }\r\n }\r\n\r\n const target = ev.target as HTMLElement | null;\r\n if (!target) return;\r\n\r\n // Ahora verificar las otras condiciones\r\n if (ev.defaultPrevented) return;\r\n if (ev.button !== 0) return;\r\n if (ev.metaKey || ev.ctrlKey || ev.shiftKey || ev.altKey) return;\r\n\r\n const anchor = target.closest(\"a[href]\") as HTMLAnchorElement | null;\r\n if (!anchor) return;\r\n\r\n const href = anchor.getAttribute(\"href\");\r\n if (!href) return;\r\n if (href.startsWith(\"#\")) return;\r\n\r\n const url = new URL(href, window.location.href);\r\n if (url.origin !== window.location.origin) return;\r\n if (anchor.target && anchor.target !== \"_self\") return;\r\n\r\n ev.preventDefault();\r\n\r\n const nextUrl = url.pathname + url.search;\r\n const currentUrl = window.location.pathname + window.location.search;\r\n if (nextUrl === currentUrl) return;\r\n\r\n // Detectar si el link tiene data-revalidate para forzar revalidación\r\n const shouldRevalidate =\r\n anchor.hasAttribute(\"data-revalidate\") &&\r\n anchor.getAttribute(\"data-revalidate\") !== \"false\";\r\n\r\n window.history.pushState({}, \"\", nextUrl);\r\n navigate(nextUrl, shouldRevalidate ? { revalidate: true } : undefined);\r\n };\r\n}\r\n\r\nexport function createPopStateHandler(\r\n navigate: (url: string, options?: NavigateOptions) => void\r\n): () => void {\r\n return function handlePopState() {\r\n const nextUrl = window.location.pathname + window.location.search;\r\n navigate(nextUrl);\r\n };\r\n}\r\n\r\n"],"mappings":";AAAA,SAAS,mBAAmB;;;ACCrB,IAAM,kBAAkB;AACxB,IAAM,mBAAmB;;;ACCzB,SAAS,gBAAoC;AAClD,SAAS,OAAe,eAAe,KAAiC;AAC1E;AAEO,SAAS,cAAc,MAAyB;AACrD,EAAC,OAAe,eAAe,IAAI;AAInC,MAAI,OAAO,WAAW,aAAa;AACjC,WAAO;AAAA,MACL,IAAI,YAAY,mBAAmB;AAAA,QACjC,QAAQ,EAAE,KAAK;AAAA,MACjB,CAAC;AAAA,IACH;AAAA,EACF;AACF;AAEO,SAAS,kBAAiC;AAC/C,SAAO,cAAc,GAAG,SAAS;AACnC;;;ACrBO,SAAS,4BAA4B,SAAyB;AACnE,QAAM,WAAW,QAAQ,MAAM,GAAG,EAAE,OAAO,OAAO;AAClD,QAAM,aAAuB,CAAC;AAE9B,WAAS,IAAI,GAAG,IAAI,SAAS,QAAQ,KAAK;AACxC,UAAM,MAAM,SAAS,CAAC;AAGtB,QAAI,IAAI,WAAW,MAAM,KAAK,IAAI,SAAS,GAAG,GAAG;AAC/C,UAAI,MAAM,SAAS,SAAS,GAAG;AAC7B,cAAM,IAAI;AAAA,UACR,sBAAsB,GAAG,SAAS,OAAO;AAAA,QAC3C;AAAA,MACF;AACA,iBAAW,KAAK,MAAM;AACtB;AAAA,IACF;AAGA,QAAI,IAAI,WAAW,GAAG,KAAK,IAAI,SAAS,GAAG,GAAG;AAC5C,iBAAW,KAAK,SAAS;AACzB;AAAA,IACF;AAGA,UAAM,UAAU,IAAI,QAAQ,uBAAuB,MAAM;AACzD,eAAW,KAAK,OAAO;AAAA,EACzB;AAEA,QAAM,cAAc,OAAO,WAAW,KAAK,GAAG,IAAI;AAClD,SAAO,IAAI,OAAO,WAAW;AAC/B;AAEO,SAAS,iBACd,gBACA,QACyB;AACzB,QAAM,CAAC,QAAQ,IAAI,eAAe,MAAM,GAAG;AAC3C,aAAW,KAAK,QAAQ;AACtB,UAAM,QAAQ,4BAA4B,EAAE,OAAO;AACnD,UAAM,QAAQ,MAAM,KAAK,QAAQ;AACjC,QAAI,CAAC,MAAO;AAEZ,UAAM,SAAiC,CAAC;AACxC,MAAE,WAAW,QAAQ,CAAC,MAAM,QAAQ;AAClC,aAAO,IAAI,IAAI,mBAAmB,MAAM,MAAM,CAAC,KAAK,EAAE;AAAA,IACxD,CAAC;AAED,WAAO,EAAE,OAAO,GAAG,OAAO;AAAA,EAC5B;AACA,SAAO;AACT;;;ACrDO,SAAS,cACd,IACA;AACA,MAAI,CAAC,GAAI;AAET,MAAI,GAAG,OAAO;AACZ,aAAS,QAAQ,GAAG;AAAA,EACtB;AAEA,MAAI,GAAG,aAAa;AAClB,QAAI,OAAO,SAAS;AAAA,MAClB;AAAA,IACF;AAEA,QAAI,CAAC,MAAM;AACT,aAAO,SAAS,cAAc,MAAM;AACpC,WAAK,OAAO;AACZ,eAAS,KAAK,YAAY,IAAI;AAAA,IAChC;AAEA,SAAK,UAAU,GAAG;AAAA,EACpB;AACF;;;ACtBA,SAAS,WAAW,UAAU,cAAc;;;ACQjC;AANJ,SAAS,WAAW,EAAE,MAAM,GAA8B;AAC/D,MAAI,CAAC,MAAM,OAAO;AAEhB,QAAI,MAAM,eAAe,MAAM;AAC7B,aAAO;AAAA,IACT;AACA,WAAO,oBAAC,QAAG,mCAAqB;AAAA,EAClC;AAEA,MAAI,CAAC,MAAM,YAAY;AACrB,WAAO;AAAA,EACT;AAEA,QAAM,EAAE,MAAM,QAAQ,IAAI,MAAM;AAChC,QAAM,EAAE,QAAQ,MAAM,IAAI;AAE1B,MAAI,UAAU,oBAAC,QAAK,QAAiB,GAAG,OAAO;AAE/C,QAAM,cAAc,QAAQ,MAAM,EAAE,QAAQ;AAC5C,aAAW,UAAU,aAAa;AAChC,cACE,oBAAC,UAAO,QAAiB,GAAG,OACzB,mBACH;AAAA,EAEJ;AAEA,SAAO;AACT;;;ACjBA,IAAM,YAAY;AAGlB,IAAM,iBAAiB;AAQvB,SAAS,gBAA4B;AACnC,MAAI,OAAO,WAAW,aAAa;AACjC,QAAI,CAAE,OAAe,SAAS,GAAG;AAC/B,MAAC,OAAe,SAAS,IAAI;AAAA,QAC3B,MAAM,oBAAI,IAAwB;AAAA,QAClC,OAAO,oBAAI,IAAyB;AAAA,QACpC,KAAK,CAAC;AAAA,MACR;AAAA,IACF;AACA,WAAQ,OAAe,SAAS;AAAA,EAClC;AAEA,SAAO;AAAA,IACL,MAAM,oBAAI,IAAwB;AAAA,IAClC,OAAO,oBAAI,IAAyB;AAAA,IACpC,KAAK,CAAC;AAAA,EACR;AACF;AAEA,IAAM,aAAa,cAAc;AACjC,IAAM,YAAY,WAAW;AAC7B,IAAM,YAAY,WAAW;AAC7B,IAAM,MAAM,WAAW;AAOvB,SAAS,gBAAgB,KAAqB;AAC5C,SAAO,IAAI,MAAM,GAAG,EAAE,CAAC;AACzB;AAKA,SAAS,WAAW,KAAmB;AACrC,QAAM,WAAW,gBAAgB,GAAG;AACpC,MAAI,CAAC,UAAU,IAAI,QAAQ,GAAG;AAC5B,cAAU,IAAI,UAAU,oBAAI,IAAI,CAAC;AAAA,EACnC;AACA,YAAU,IAAI,QAAQ,EAAG,IAAI,GAAG;AAClC;AAKA,SAAS,gBAAgB,KAAmB;AAC1C,QAAM,WAAW,gBAAgB,GAAG;AACpC,QAAM,OAAO,UAAU,IAAI,QAAQ;AACnC,MAAI,MAAM;AACR,SAAK,OAAO,GAAG;AACf,QAAI,KAAK,SAAS,GAAG;AACnB,gBAAU,OAAO,QAAQ;AAAA,IAC3B;AAAA,EACF;AACF;AAKA,SAAS,UAAU,KAAmB;AACpC,QAAM,QAAQ,IAAI,QAAQ,GAAG;AAC7B,MAAI,UAAU,IAAI;AAChB,QAAI,OAAO,OAAO,CAAC;AAAA,EACrB;AACA,MAAI,KAAK,GAAG;AACd;AAKA,SAAS,cAAoB;AAC3B,SAAO,IAAI,UAAU,kBAAkB,IAAI,SAAS,GAAG;AACrD,UAAM,YAAY,IAAI,MAAM;AAC5B,cAAU,OAAO,SAAS;AAC1B,oBAAgB,SAAS;AAAA,EAC3B;AACF;AAKA,SAAS,cAAc,KAAa,OAAyB;AAC3D,QAAM,gBAAgB,UAAU,IAAI,GAAG;AACvC,QAAM,eAAe,eAAe,WAAW;AAE/C,YAAU,IAAI,KAAK,KAAK;AAGxB,MAAI,MAAM,WAAW,aAAa;AAEhC,QAAI,CAAC,cAAc;AACjB,iBAAW,GAAG;AAAA,IAChB;AACA,cAAU,GAAG;AACb,gBAAY;AAAA,EACd,WAAW,cAAc;AAEvB,oBAAgB,GAAG;AAAA,EACrB;AACF;AAKA,SAAS,iBAAiB,KAAmB;AAC3C,MAAI,UAAU,IAAI,GAAG,GAAG;AACtB,cAAU,OAAO,GAAG;AACpB,oBAAgB,GAAG;AACnB,UAAM,WAAW,IAAI,QAAQ,GAAG;AAChC,QAAI,aAAa,IAAI;AACnB,UAAI,OAAO,UAAU,CAAC;AAAA,IACxB;AAAA,EACF;AACF;AAEA,SAAS,aAAa,KAAqB;AACzC,SAAO,OAAO,IAAI,SAAS,GAAG,IAAI,MAAM,OAAO;AACjD;AAEA,eAAe,mBAAmB,KAAiC;AACjE,QAAM,UAAU,aAAa,GAAG;AAEhC,QAAM,MAAM,MAAM,MAAM,SAAS;AAAA,IAC/B,SAAS;AAAA,MACP,aAAa;AAAA,MACb,QAAQ;AAAA,IACV;AAAA,EACF,CAAC;AAED,MAAI,OAAY,CAAC;AAEjB,MAAI;AACF,UAAM,OAAO,MAAM,IAAI,KAAK;AAC5B,QAAI,MAAM;AACR,aAAO,KAAK,MAAM,IAAI;AAAA,IACxB;AAAA,EACF,SAAS,YAAY;AACnB,YAAQ;AAAA,MACN;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAEA,QAAM,SAAoB;AAAA,IACxB,IAAI,IAAI;AAAA,IACR,QAAQ,IAAI;AAAA,IACZ;AAAA,EACF;AAEA,SAAO;AACT;AAuKA,eAAsB,aACpB,KACA,SACoB;AACpB,QAAM,MAAM,aAAa,GAAG;AAG5B,MAAI,SAAS,YAAY;AACvB,qBAAiB,GAAG;AAAA,EACtB;AAEA,QAAM,QAAQ,UAAU,IAAI,GAAG;AAE/B,MAAI,OAAO;AACT,QAAI,MAAM,WAAW,aAAa;AAEhC,gBAAU,GAAG;AACb,aAAO,MAAM;AAAA,IACf;AACA,QAAI,MAAM,WAAW,WAAW;AAC9B,aAAO,MAAM;AAAA,IACf;AAAA,EACF;AAGA,QAAM,UAAU,mBAAmB,GAAG,EACnC,KAAK,CAAC,UAAU;AACf,kBAAc,KAAK,EAAE,QAAQ,aAAa,MAAM,CAAC;AACjD,WAAO;AAAA,EACT,CAAC,EACA,MAAM,CAAC,UAAU;AAChB,YAAQ,MAAM,8CAA8C,KAAK;AACjE,cAAU,IAAI,KAAK,EAAE,QAAQ,YAAY,MAAM,CAAC;AAChD,UAAM;AAAA,EACR,CAAC;AAEH,YAAU,IAAI,KAAK,EAAE,QAAQ,WAAW,QAAQ,CAAC;AACjD,SAAO;AACT;;;AC5WA,eAAe,iBACb,SACA,MACA,YACA,UACkB;AAClB,MAAI;AACF,UAAM,aAAa,MAAM,WAAW,KAAK;AAGzC,QAAI,QAAgB;AACpB,QAAI,OAAO,aAAa,aAAa;AACnC,YAAM,cAAc,SAAS,OAAO,MAAM,eAAe;AACzD,UAAI,aAAa;AACf,gBAAQ,YAAY,CAAC;AAAA,MACvB,WAAW,KAAK,OAAO;AACrB,gBAAQ,KAAK;AAAA,MACf,OAAO;AACL,cAAM,eAAe,gBAAgB;AACrC,YAAI,aAAc,SAAQ;AAAA,MAC5B;AAAA,IACF,WAAW,KAAK,OAAO;AACrB,cAAQ,KAAK;AAAA,IACf;AAEA,UAAM,aAAa;AAAA,MACjB,GAAI,KAAK,SAAS;AAAA,QAChB,OAAO,KAAK,WAAW;AAAA,MACzB;AAAA,MACA;AAAA,IACF;AAEA,UAAM,aAA0B;AAAA,MAC9B,UAAU;AAAA,MACV,QAAQ,KAAK,UAAU,CAAC;AAAA,MACxB,OAAO;AAAA,MACP,UAAU,KAAK,YAAY;AAAA,MAC3B;AAAA,MACA,UAAU;AAAA,MACV,OAAO;AAAA,IACT;AAEA,kBAAc,UAAU;AAExB,aAAS;AAAA,MACP,KAAK;AAAA,MACL,OAAO;AAAA,MACP,QAAQ,KAAK,UAAU,CAAC;AAAA,MACxB;AAAA,MACA,OAAO;AAAA,IACT,CAAC;AACD,WAAO;AAAA,EACT,SAAS,WAAW;AAClB,YAAQ;AAAA,MACN;AAAA,MACA;AAAA,IACF;AACA,WAAO,SAAS,OAAO;AACvB,WAAO;AAAA,EACT;AACF;AAEA,eAAe,oBACb,SACA,MACA,eACA,UACe;AAEf,MAAI,QAAgB;AACpB,MAAI,OAAO,aAAa,aAAa;AACnC,UAAM,cAAc,SAAS,OAAO,MAAM,eAAe;AACzD,QAAI,aAAa;AACf,cAAQ,YAAY,CAAC;AAAA,IACvB,WAAW,KAAK,OAAO;AACrB,cAAQ,KAAK;AAAA,IACf,OAAO;AACL,YAAM,eAAe,gBAAgB;AACrC,UAAI,aAAc,SAAQ;AAAA,IAC5B;AAAA,EACF,WAAW,KAAK,OAAO;AACrB,YAAQ,KAAK;AAAA,EACf;AAEA,QAAM,gBAAgB;AAAA,IACpB,GAAI,KAAK,SAAS,CAAC;AAAA,IACnB;AAAA,EACF;AAEA,QAAM,aAA0B;AAAA,IAC9B,UAAU;AAAA,IACV,QAAQ,CAAC;AAAA,IACT,OAAO;AAAA,IACP,UAAU,KAAK,YAAY;AAAA,IAC3B;AAAA,IACA,UAAU;AAAA,IACV,OAAO;AAAA,EACT;AAEA,gBAAc,UAAU;AAExB,MAAI,eAAe;AACjB,UAAM,aAAa,MAAM,cAAc,KAAK;AAC5C,aAAS;AAAA,MACP,KAAK;AAAA,MACL,OAAO;AAAA,MACP,QAAQ,CAAC;AAAA,MACT;AAAA,MACA,OAAO;AAAA,IACT,CAAC;AAAA,EACH,OAAO;AACL,aAAS;AAAA,MACP,KAAK;AAAA,MACL,OAAO;AAAA,MACP,QAAQ,CAAC;AAAA,MACT,YAAY;AAAA,MACZ,OAAO,CAAC;AAAA,IACV,CAAC;AAAA,EACH;AACF;AAEA,eAAe,kBACb,SACA,MACA,QACA,UACkB;AAClB,gBAAc,KAAK,YAAY,IAAI;AAInC,MAAI,QAAgB;AACpB,MAAI,OAAO,aAAa,aAAa;AACnC,UAAM,cAAc,SAAS,OAAO,MAAM,eAAe;AACzD,QAAI,aAAa;AACf,cAAQ,YAAY,CAAC;AAAA,IACvB,WAAW,KAAK,OAAO;AACrB,cAAQ,KAAK;AAAA,IACf,OAAO;AACL,YAAM,eAAe,gBAAgB;AACrC,UAAI,cAAc;AAChB,gBAAQ;AAAA,MACV;AAAA,IACF;AAAA,EACF,WAAW,KAAK,OAAO;AACrB,YAAQ,KAAK;AAAA,EACf;AAGA,QAAM,WAAW;AAAA,IACf,GAAI,KAAK,SAAS,CAAC;AAAA,IACnB;AAAA;AAAA,EACF;AAEA,QAAM,UAAU,iBAAiB,SAAS,MAAM;AAEhD,MAAI,CAAC,SAAS;AACZ,WAAO,SAAS,OAAO;AACvB,WAAO;AAAA,EACT;AAEA,QAAM,aAA0B;AAAA,IAC9B,UAAU;AAAA,IACV,QAAQ,QAAQ;AAAA,IAChB,OAAO;AAAA,IACP,UAAU,KAAK,YAAY;AAAA,IAC3B;AAAA,IACA,UAAU;AAAA,IACV,OAAO;AAAA,EACT;AAEA,gBAAc,UAAU;AAExB,QAAM,aAAa,MAAM,QAAQ,MAAM,KAAK;AAE5C,SAAO,SAAS;AAAA,IACd,KAAK;AAAA,IACL,UAAU;AAAA,EACZ,CAAC;AAED,WAAS;AAAA,IACP,KAAK;AAAA,IACL,OAAO,QAAQ;AAAA,IACf,QAAQ,QAAQ;AAAA,IAChB;AAAA,IACA,OAAO;AAAA,EACT,CAAC;AAED,SAAO;AACT;AAWA,eAAsB,SACpB,SACA,UACA,SACe;AACf,QAAM,EAAE,UAAU,QAAQ,eAAe,WAAW,IAAI;AAExD,MAAI;AACF,UAAM,EAAE,IAAI,KAAK,IAAI,MAAM,aAAa,SAAS;AAAA,MAC/C,YAAY,SAAS;AAAA,IACvB,CAAC;AAED,QAAI,QAAQ,KAAK,OAAO;AACtB,cAAQ,IAAI,wCAAwC,IAAI;AAExD,UAAI,YAAY;AACd,cAAM,UAAU,MAAM;AAAA,UACpB;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,QACF;AACA,YAAI,QAAS;AAAA,MACf,OAAO;AACL,gBAAQ;AAAA,UACN;AAAA,UACA;AAAA,QACF;AACA,eAAO,SAAS,OAAO;AACvB;AAAA,MACF;AAAA,IACF;AAGA,QAAI,CAAC,IAAI;AACP,UAAI,QAAS,KAAa,UAAU;AAClC,eAAO,SAAS,OAAQ,KAAa,SAAS;AAC9C;AAAA,MACF;AACA,aAAO,SAAS,OAAO;AACvB;AAAA,IACF;AAGA,QAAI,KAAK,UAAU;AACjB,aAAO,SAAS,OAAO,KAAK,SAAS;AACrC;AAAA,IACF;AAGA,QAAI,KAAK,UAAU;AACjB,YAAM,oBAAoB,SAAS,MAAM,eAAe,QAAQ;AAChE;AAAA,IACF;AAGA,UAAM,kBAAkB,SAAS,MAAM,QAAQ,QAAQ;AAAA,EACzD,SAAS,KAAK;AACZ,YAAQ,MAAM,oCAAoC,GAAG;AACrD,WAAO,SAAS,OAAO;AAAA,EACzB;AACF;AAEO,SAAS,mBACdA,WAC0B;AAC1B,SAAO,SAAS,YAAY,IAAgB;AAG1C,UAAM,OAAO,GAAG,aAAa;AAG7B,eAAW,WAAW,MAAM;AAC1B,UAAI,EAAE,mBAAmB,aAAc;AAEvC,YAAM,UAAU,QAAQ,QAAQ,YAAY;AAC5C,UACE,YAAY,WACZ,YAAY,cACZ,YAAY,YACZ,YAAY,YACZ,QAAQ,qBACP,YAAY,WAAY,QAA6B,SACtD;AACA;AAAA,MACF;AAAA,IACF;AAEA,UAAM,SAAS,GAAG;AAClB,QAAI,CAAC,OAAQ;AAGb,QAAI,GAAG,iBAAkB;AACzB,QAAI,GAAG,WAAW,EAAG;AACrB,QAAI,GAAG,WAAW,GAAG,WAAW,GAAG,YAAY,GAAG,OAAQ;AAE1D,UAAM,SAAS,OAAO,QAAQ,SAAS;AACvC,QAAI,CAAC,OAAQ;AAEb,UAAM,OAAO,OAAO,aAAa,MAAM;AACvC,QAAI,CAAC,KAAM;AACX,QAAI,KAAK,WAAW,GAAG,EAAG;AAE1B,UAAM,MAAM,IAAI,IAAI,MAAM,OAAO,SAAS,IAAI;AAC9C,QAAI,IAAI,WAAW,OAAO,SAAS,OAAQ;AAC3C,QAAI,OAAO,UAAU,OAAO,WAAW,QAAS;AAEhD,OAAG,eAAe;AAElB,UAAM,UAAU,IAAI,WAAW,IAAI;AACnC,UAAM,aAAa,OAAO,SAAS,WAAW,OAAO,SAAS;AAC9D,QAAI,YAAY,WAAY;AAG5B,UAAM,mBACJ,OAAO,aAAa,iBAAiB,KACrC,OAAO,aAAa,iBAAiB,MAAM;AAE7C,WAAO,QAAQ,UAAU,CAAC,GAAG,IAAI,OAAO;AACxC,IAAAA,UAAS,SAAS,mBAAmB,EAAE,YAAY,KAAK,IAAI,MAAS;AAAA,EACvE;AACF;AAEO,SAAS,sBACdA,WACY;AACZ,SAAO,SAAS,iBAAiB;AAC/B,UAAM,UAAU,OAAO,SAAS,WAAW,OAAO,SAAS;AAC3D,IAAAA,UAAS,OAAO;AAAA,EAClB;AACF;;;AHrRS,gBAAAC,YAAA;AAlDF,SAAS,SAAS;AAAA,EACvB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAAkB;AAChB,QAAM,CAAC,OAAO,QAAQ,IAAI,SAAyB,YAAY;AAC/D,QAAM,cAAc,OAA2B;AAAA,IAC7C;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,CAAC;AAGD,YAAU,MAAM;AACd,gBAAY,UAAU;AAAA,MACpB;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EACF,GAAG,CAAC,QAAQ,eAAe,UAAU,CAAC;AAEtC,YAAU,MAAM;AACd,mBAAe,eACb,SACA,SACA;AACA,YAAM,SAAS,SAAS,YAAY,SAAS,OAAO;AAAA,IACtD;AAEA,UAAM,cAAc,mBAAmB,cAAc;AACrD,UAAM,iBAAiB,sBAAsB,cAAc;AAG3D,WAAO,iBAAiB,SAAS,aAAa,EAAE,SAAS,MAAM,CAAC;AAChE,WAAO,iBAAiB,YAAY,gBAAgB,EAAE,SAAS,MAAM,CAAC;AAEtE,WAAO,MAAM;AACX,aAAO,oBAAoB,SAAS,aAAa,EAAE,SAAS,MAAM,CAAyB;AAC3F,aAAO,oBAAoB,YAAY,gBAAgB,EAAE,SAAS,MAAM,CAAyB;AAAA,IACnG;AAAA,EACF,GAAG,CAAC,CAAC;AAEL,QAAM,UAAU,MAAM,UAAU;AAChC,QAAM,aAAa,MAAM,UAAU;AACnC,QAAM,YAAY,UAAU,UAAU,aAAa,aAAa;AAChE,QAAM,WAAW,GAAG,MAAM,GAAG,IAAI,SAAS;AAE1C,SAAO,gBAAAA,KAAC,cAA0B,SAAV,QAAwB;AAClD;;;AL4BQ,gBAAAC,YAAA;AAvFR,eAAsB,iBACpB,YACA,aACA,QACA,eACA,YACyB;AACzB,QAAM,oBAAoB,aAAa,aAAa;AACpD,QAAM,iBAAiB,aAAa,UAAU;AAE9C,MAAI,eAAyC;AAC7C,MAAI,gBAAwC,CAAC;AAC7C,MAAI,oBAAoB;AAExB,MAAI,kBAAkB,YAAY;AAChC,mBAAe;AACf,oBAAgB,aAAa,UAAU,CAAC;AACxC,wBAAoB,MAAM,WAAW,KAAK;AAAA,EAC5C,WAAW,qBAAqB,eAAe;AAC7C,mBAAe;AACf,oBAAgB,CAAC;AACjB,wBAAoB,MAAM,cAAc,KAAK;AAAA,EAC/C,OAAO;AACL,UAAM,QAAQ,iBAAiB,YAAY,MAAM;AACjD,QAAI,OAAO;AACT,qBAAe,MAAM;AACrB,sBAAgB,MAAM;AACtB,0BAAoB,MAAM,MAAM,MAAM,KAAK;AAAA,IAC7C,WAAW,eAAe;AACxB,qBAAe;AACf,sBAAgB,CAAC;AACjB,0BAAoB,MAAM,cAAc,KAAK;AAAA,IAC/C,OAAO;AACL,cAAQ;AAAA,QACN,qCAAqC,UAAU;AAAA,QAC/C,OAAO,IAAI,CAAC,MAAM,EAAE,OAAO;AAAA,MAC7B;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AAAA,IACL,KAAK;AAAA,IACL,OAAO;AAAA,IACP,QAAQ;AAAA,IACR,YAAY;AAAA,IACZ,OAAO,aAAa,SAAS,CAAC;AAAA,EAChC;AACF;AASO,SAAS,gBACd,QACA,eACA,aAAuC,MACjC;AACN,GAAC,eAAe,YAAY;AAC1B,UAAM,YAAY,SAAS,eAAe,gBAAgB;AAC1D,UAAM,cAAc,cAAc;AAElC,QAAI,CAAC,WAAW;AACd,cAAQ,MAAM,cAAc,gBAAgB,0BAA0B;AACtE;AAAA,IACF;AAEA,UAAM,aAAa,OAAO,SAAS,WAAW,OAAO,SAAS;AAE9D,QAAI;AACF,YAAM,eAAe,MAAM;AAAA,QACzB;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAEA,UAAI,aAAa,UAAU;AACzB,sBAAc,YAAY,QAAQ;AAAA,MACpC;AAEA;AAAA,QACE;AAAA,QACA,gBAAAA;AAAA,UAAC;AAAA;AAAA,YACC;AAAA,YACA;AAAA,YACA;AAAA,YACA;AAAA;AAAA,QACF;AAAA,MACF;AAAA,IACF,SAAS,OAAO;AACd,cAAQ;AAAA,QACN;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAEA,aAAO,SAAS,OAAO;AAAA,IACzB;AAAA,EACF,GAAG;AACL;","names":["navigate","jsx","jsx"]}
|