@lolyjs/core 0.2.0-alpha.2 → 0.2.0-alpha.3
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/LICENCE.md +9 -0
- package/dist/cli.cjs +5 -1
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +5 -1
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +24 -9
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +20 -5
- package/dist/index.js.map +1 -1
- package/dist/react/hooks.cjs +134 -2
- package/dist/react/hooks.cjs.map +1 -1
- package/dist/react/hooks.d.mts +79 -1
- package/dist/react/hooks.d.ts +79 -1
- package/dist/react/hooks.js +132 -1
- package/dist/react/hooks.js.map +1 -1
- package/dist/runtime.cjs +24 -9
- package/dist/runtime.cjs.map +1 -1
- package/dist/runtime.js +20 -5
- package/dist/runtime.js.map +1 -1
- package/package.json +3 -1
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 * Sets up hot reload via Server-Sent Events (SSE) in development mode.\r\n * Listens for file changes and reloads the page when needed.\r\n */\r\nfunction setupHotReload(): void {\r\n // Only enable hot reload in development mode\r\n // In production, process.env.NODE_ENV is replaced by DefinePlugin with \"production\"\r\n // @ts-ignore - process.env.NODE_ENV is replaced by DefinePlugin at build time\r\n const nodeEnv: string = (typeof process !== \"undefined\" && process?.env?.NODE_ENV) || \"production\";\r\n const isDev = nodeEnv !== \"production\";\r\n \r\n if (!isDev) {\r\n return; // Skip hot reload in production\r\n }\r\n\r\n try {\r\n console.log(\"[hot-reload] Attempting to connect to /__fw/hot...\");\r\n const eventSource = new EventSource(\"/__fw/hot\");\r\n let reloadTimeout: ReturnType<typeof setTimeout> | null = null;\r\n\r\n eventSource.addEventListener(\"message\", (event) => {\r\n const data = event.data;\r\n if (data && data.startsWith(\"reload:\")) {\r\n const filePath = data.slice(7);\r\n console.log(`[hot-reload] File changed: ${filePath}`);\r\n\r\n // Clear any pending reload\r\n if (reloadTimeout) {\r\n clearTimeout(reloadTimeout);\r\n }\r\n\r\n // Wait a bit for the bundler to finish compiling and files to be written\r\n // Increased timeout to ensure everything is ready\r\n reloadTimeout = setTimeout(() => {\r\n console.log(\"[hot-reload] Reloading page...\");\r\n // Force reload without cache to ensure we get the latest files\r\n window.location.reload();\r\n }, 500);\r\n }\r\n });\r\n\r\n eventSource.addEventListener(\"ping\", () => {\r\n console.log(\"[hot-reload] ✓ Connected to hot reload server\");\r\n });\r\n\r\n eventSource.onopen = () => {\r\n console.log(\"[hot-reload] ✓ SSE connection opened\");\r\n };\r\n\r\n eventSource.onerror = (error) => {\r\n // Log connection state for debugging\r\n const states = [\"CONNECTING\", \"OPEN\", \"CLOSED\"];\r\n const state = states[eventSource.readyState] || \"UNKNOWN\";\r\n \r\n if (eventSource.readyState === EventSource.CONNECTING) {\r\n // Still connecting, might be normal\r\n console.log(\"[hot-reload] Connecting...\");\r\n } else if (eventSource.readyState === EventSource.OPEN) {\r\n console.warn(\"[hot-reload] Connection error (but connection is open):\", error);\r\n } else {\r\n // Connection closed - might be production mode or server not running\r\n console.log(\"[hot-reload] Connection closed (readyState:\", state, \")\");\r\n }\r\n // EventSource automatically reconnects, so we don't need to do anything\r\n };\r\n } catch (error) {\r\n // Fail silently if EventSource is not supported\r\n console.log(\"[hot-reload] EventSource not supported or error:\", error);\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 // Set up hot reload in development mode\r\n console.log(\"[client] Bootstrap starting, setting up hot reload...\");\r\n setupHotReload();\r\n\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 // Flag para evitar múltiples listeners (por si React Strict Mode ejecuta dos veces)\r\n let isMounted = true;\r\n\r\n async function handleNavigate(\r\n nextUrl: string,\r\n options?: { revalidate?: boolean }\r\n ) {\r\n if (!isMounted) return;\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, false);\r\n window.addEventListener(\"popstate\", handlePopState, false);\r\n\r\n return () => {\r\n isMounted = false;\r\n window.removeEventListener(\"click\", handleClick, false);\r\n window.removeEventListener(\"popstate\", handlePopState, false);\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 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 try {\r\n // Salir temprano si el evento ya fue prevenido\r\n if (ev.defaultPrevented) return;\r\n \r\n // Verificar que sea un evento de mouse real (no sintético o de teclado)\r\n if (ev.type !== \"click\") return;\r\n if (ev.button !== 0) return;\r\n if (ev.metaKey || ev.ctrlKey || ev.shiftKey || ev.altKey) return;\r\n \r\n // Verificar que el evento tenga coordenadas válidas (eventos de mouse reales las tienen)\r\n const target = ev.target as HTMLElement | null;\r\n if (ev.clientX === 0 && ev.clientY === 0 && ev.detail === 0) {\r\n // Podría ser un evento sintético, ser más cauteloso\r\n if (target) {\r\n const tagName = target.tagName.toLowerCase();\r\n if (tagName === \"input\" || tagName === \"textarea\" || tagName === \"button\" || tagName === \"select\") {\r\n return; // Es un input, no procesar eventos sintéticos\r\n }\r\n }\r\n }\r\n\r\n if (!target) return;\r\n\r\n // Verificar PRIMERO si el target es un elemento interactivo (más rápido)\r\n const tagName = target.tagName.toLowerCase();\r\n if (\r\n tagName === \"input\" ||\r\n tagName === \"textarea\" ||\r\n tagName === \"button\" ||\r\n tagName === \"select\" ||\r\n target.isContentEditable ||\r\n target.getAttribute(\"contenteditable\") === \"true\"\r\n ) {\r\n return; // Es un elemento interactivo, no procesar\r\n }\r\n\r\n // Verificar si está dentro de un elemento interactivo usando closest (más eficiente que composedPath)\r\n const interactiveParent = target.closest(\"input, textarea, button, select, [contenteditable], label\");\r\n if (interactiveParent) {\r\n // Si el parent es un label, verificar si tiene un control asociado\r\n if (interactiveParent.tagName.toLowerCase() === \"label\") {\r\n const label = interactiveParent as HTMLLabelElement;\r\n if (label.control) {\r\n return; // El label tiene un control asociado (input, etc)\r\n }\r\n } else {\r\n return; // Está dentro de un elemento interactivo\r\n }\r\n }\r\n\r\n // Solo buscar anchor si no es un elemento interactivo\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 } catch (error) {\r\n // Silenciar errores para evitar bloquear el navegador\r\n console.error(\"[navigation] Error in click handler:\", error);\r\n }\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","tagName","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","../modules/runtime/client/RouterContext.tsx"],"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 * Sets up hot reload via Server-Sent Events (SSE) in development mode.\r\n * Listens for file changes and reloads the page when needed.\r\n */\r\nfunction setupHotReload(): void {\r\n // Only enable hot reload in development mode\r\n // In production, process.env.NODE_ENV is replaced by DefinePlugin with \"production\"\r\n // @ts-ignore - process.env.NODE_ENV is replaced by DefinePlugin at build time\r\n const nodeEnv: string = (typeof process !== \"undefined\" && process?.env?.NODE_ENV) || \"production\";\r\n const isDev = nodeEnv !== \"production\";\r\n \r\n if (!isDev) {\r\n return; // Skip hot reload in production\r\n }\r\n\r\n try {\r\n console.log(\"[hot-reload] Attempting to connect to /__fw/hot...\");\r\n const eventSource = new EventSource(\"/__fw/hot\");\r\n let reloadTimeout: ReturnType<typeof setTimeout> | null = null;\r\n\r\n eventSource.addEventListener(\"message\", (event) => {\r\n const data = event.data;\r\n if (data && data.startsWith(\"reload:\")) {\r\n const filePath = data.slice(7);\r\n console.log(`[hot-reload] File changed: ${filePath}`);\r\n\r\n // Clear any pending reload\r\n if (reloadTimeout) {\r\n clearTimeout(reloadTimeout);\r\n }\r\n\r\n // Wait a bit for the bundler to finish compiling and files to be written\r\n // Increased timeout to ensure everything is ready\r\n reloadTimeout = setTimeout(() => {\r\n console.log(\"[hot-reload] Reloading page...\");\r\n // Force reload without cache to ensure we get the latest files\r\n window.location.reload();\r\n }, 500);\r\n }\r\n });\r\n\r\n eventSource.addEventListener(\"ping\", () => {\r\n console.log(\"[hot-reload] ✓ Connected to hot reload server\");\r\n });\r\n\r\n eventSource.onopen = () => {\r\n console.log(\"[hot-reload] ✓ SSE connection opened\");\r\n };\r\n\r\n eventSource.onerror = (error) => {\r\n // Log connection state for debugging\r\n const states = [\"CONNECTING\", \"OPEN\", \"CLOSED\"];\r\n const state = states[eventSource.readyState] || \"UNKNOWN\";\r\n \r\n if (eventSource.readyState === EventSource.CONNECTING) {\r\n // Still connecting, might be normal\r\n console.log(\"[hot-reload] Connecting...\");\r\n } else if (eventSource.readyState === EventSource.OPEN) {\r\n console.warn(\"[hot-reload] Connection error (but connection is open):\", error);\r\n } else {\r\n // Connection closed - might be production mode or server not running\r\n console.log(\"[hot-reload] Connection closed (readyState:\", state, \")\");\r\n }\r\n // EventSource automatically reconnects, so we don't need to do anything\r\n };\r\n } catch (error) {\r\n // Fail silently if EventSource is not supported\r\n console.log(\"[hot-reload] EventSource not supported or error:\", error);\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 // Set up hot reload in development mode\r\n console.log(\"[client] Bootstrap starting, setting up hot reload...\");\r\n setupHotReload();\r\n\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 if (typeof window === \"undefined\") {\r\n return null;\r\n }\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, useCallback } 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 { RouterContext } from \"./RouterContext\";\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 // Create navigate function for router context\r\n const handleNavigate = useCallback(\r\n async (\r\n nextUrl: string,\r\n options?: { revalidate?: boolean; replace?: boolean }\r\n ) => {\r\n await navigate(nextUrl, handlersRef.current, {\r\n revalidate: options?.revalidate,\r\n });\r\n },\r\n []\r\n );\r\n\r\n useEffect(() => {\r\n // Flag para evitar múltiples listeners (por si React Strict Mode ejecuta dos veces)\r\n let isMounted = true;\r\n\r\n async function handleNavigateInternal(\r\n nextUrl: string,\r\n options?: { revalidate?: boolean }\r\n ) {\r\n if (!isMounted) return;\r\n await navigate(nextUrl, handlersRef.current, options);\r\n }\r\n\r\n const handleClick = createClickHandler(handleNavigateInternal);\r\n const handlePopState = createPopStateHandler(handleNavigateInternal);\r\n\r\n // Usar capture: false (burbujeo) para que los eventos del input se manejen primero\r\n window.addEventListener(\"click\", handleClick, false);\r\n window.addEventListener(\"popstate\", handlePopState, false);\r\n\r\n return () => {\r\n isMounted = false;\r\n window.removeEventListener(\"click\", handleClick, false);\r\n window.removeEventListener(\"popstate\", handlePopState, false);\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 (\r\n <RouterContext.Provider value={{ navigate: handleNavigate }}>\r\n <RouterView key={routeKey} state={state} />\r\n </RouterContext.Provider>\r\n );\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 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 try {\r\n // Salir temprano si el evento ya fue prevenido\r\n if (ev.defaultPrevented) return;\r\n \r\n // Verificar que sea un evento de mouse real (no sintético o de teclado)\r\n if (ev.type !== \"click\") return;\r\n if (ev.button !== 0) return;\r\n if (ev.metaKey || ev.ctrlKey || ev.shiftKey || ev.altKey) return;\r\n \r\n // Verificar que el evento tenga coordenadas válidas (eventos de mouse reales las tienen)\r\n const target = ev.target as HTMLElement | null;\r\n if (ev.clientX === 0 && ev.clientY === 0 && ev.detail === 0) {\r\n // Podría ser un evento sintético, ser más cauteloso\r\n if (target) {\r\n const tagName = target.tagName.toLowerCase();\r\n if (tagName === \"input\" || tagName === \"textarea\" || tagName === \"button\" || tagName === \"select\") {\r\n return; // Es un input, no procesar eventos sintéticos\r\n }\r\n }\r\n }\r\n\r\n if (!target) return;\r\n\r\n // Verificar PRIMERO si el target es un elemento interactivo (más rápido)\r\n const tagName = target.tagName.toLowerCase();\r\n if (\r\n tagName === \"input\" ||\r\n tagName === \"textarea\" ||\r\n tagName === \"button\" ||\r\n tagName === \"select\" ||\r\n target.isContentEditable ||\r\n target.getAttribute(\"contenteditable\") === \"true\"\r\n ) {\r\n return; // Es un elemento interactivo, no procesar\r\n }\r\n\r\n // Verificar si está dentro de un elemento interactivo usando closest (más eficiente que composedPath)\r\n const interactiveParent = target.closest(\"input, textarea, button, select, [contenteditable], label\");\r\n if (interactiveParent) {\r\n // Si el parent es un label, verificar si tiene un control asociado\r\n if (interactiveParent.tagName.toLowerCase() === \"label\") {\r\n const label = interactiveParent as HTMLLabelElement;\r\n if (label.control) {\r\n return; // El label tiene un control asociado (input, etc)\r\n }\r\n } else {\r\n return; // Está dentro de un elemento interactivo\r\n }\r\n }\r\n\r\n // Solo buscar anchor si no es un elemento interactivo\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 } catch (error) {\r\n // Silenciar errores para evitar bloquear el navegador\r\n console.error(\"[navigation] Error in click handler:\", error);\r\n }\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","import { createContext, useContext } from \"react\";\r\n\r\nexport type NavigateFunction = (\r\n url: string,\r\n options?: { revalidate?: boolean; replace?: boolean }\r\n) => Promise<void>;\r\n\r\nexport interface RouterContextValue {\r\n navigate: NavigateFunction;\r\n}\r\n\r\nexport const RouterContext = createContext<RouterContextValue | null>(null);\r\n\r\nexport function useRouterContext(): RouterContextValue {\r\n const context = useContext(RouterContext);\r\n if (!context) {\r\n throw new Error(\r\n \"useRouter must be used within a RouterProvider. Make sure you're using it inside a Loly app.\"\r\n );\r\n }\r\n return context;\r\n}\r\n"],"mappings":"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","names":["import_react","navigate","tagName","import_jsx_runtime","import_jsx_runtime"]}
|
package/dist/runtime.js
CHANGED
|
@@ -7,6 +7,9 @@ var APP_CONTAINER_ID = "__app";
|
|
|
7
7
|
|
|
8
8
|
// modules/runtime/client/window-data.ts
|
|
9
9
|
function getWindowData() {
|
|
10
|
+
if (typeof window === "undefined") {
|
|
11
|
+
return null;
|
|
12
|
+
}
|
|
10
13
|
return window[WINDOW_DATA_KEY] ?? null;
|
|
11
14
|
}
|
|
12
15
|
function setWindowData(data) {
|
|
@@ -83,7 +86,7 @@ function applyMetadata(md) {
|
|
|
83
86
|
}
|
|
84
87
|
|
|
85
88
|
// modules/runtime/client/AppShell.tsx
|
|
86
|
-
import { useEffect, useState, useRef } from "react";
|
|
89
|
+
import { useEffect, useState, useRef, useCallback } from "react";
|
|
87
90
|
|
|
88
91
|
// modules/runtime/client/RouterView.tsx
|
|
89
92
|
import { jsx } from "react/jsx-runtime";
|
|
@@ -502,6 +505,10 @@ function createPopStateHandler(navigate2) {
|
|
|
502
505
|
};
|
|
503
506
|
}
|
|
504
507
|
|
|
508
|
+
// modules/runtime/client/RouterContext.tsx
|
|
509
|
+
import { createContext, useContext } from "react";
|
|
510
|
+
var RouterContext = createContext(null);
|
|
511
|
+
|
|
505
512
|
// modules/runtime/client/AppShell.tsx
|
|
506
513
|
import { jsx as jsx2 } from "react/jsx-runtime";
|
|
507
514
|
function AppShell({
|
|
@@ -525,14 +532,22 @@ function AppShell({
|
|
|
525
532
|
errorRoute
|
|
526
533
|
};
|
|
527
534
|
}, [routes, notFoundRoute, errorRoute]);
|
|
535
|
+
const handleNavigate = useCallback(
|
|
536
|
+
async (nextUrl, options) => {
|
|
537
|
+
await navigate(nextUrl, handlersRef.current, {
|
|
538
|
+
revalidate: options?.revalidate
|
|
539
|
+
});
|
|
540
|
+
},
|
|
541
|
+
[]
|
|
542
|
+
);
|
|
528
543
|
useEffect(() => {
|
|
529
544
|
let isMounted = true;
|
|
530
|
-
async function
|
|
545
|
+
async function handleNavigateInternal(nextUrl, options) {
|
|
531
546
|
if (!isMounted) return;
|
|
532
547
|
await navigate(nextUrl, handlersRef.current, options);
|
|
533
548
|
}
|
|
534
|
-
const handleClick = createClickHandler(
|
|
535
|
-
const handlePopState = createPopStateHandler(
|
|
549
|
+
const handleClick = createClickHandler(handleNavigateInternal);
|
|
550
|
+
const handlePopState = createPopStateHandler(handleNavigateInternal);
|
|
536
551
|
window.addEventListener("click", handleClick, false);
|
|
537
552
|
window.addEventListener("popstate", handlePopState, false);
|
|
538
553
|
return () => {
|
|
@@ -545,7 +560,7 @@ function AppShell({
|
|
|
545
560
|
const isNotFound = state.route === notFoundRoute;
|
|
546
561
|
const routeType = isError ? "error" : isNotFound ? "notfound" : "normal";
|
|
547
562
|
const routeKey = `${state.url}:${routeType}`;
|
|
548
|
-
return /* @__PURE__ */ jsx2(RouterView, { state }, routeKey);
|
|
563
|
+
return /* @__PURE__ */ jsx2(RouterContext.Provider, { value: { navigate: handleNavigate }, children: /* @__PURE__ */ jsx2(RouterView, { state }, routeKey) });
|
|
549
564
|
}
|
|
550
565
|
|
|
551
566
|
// modules/runtime/client/bootstrap.tsx
|
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 * Sets up hot reload via Server-Sent Events (SSE) in development mode.\r\n * Listens for file changes and reloads the page when needed.\r\n */\r\nfunction setupHotReload(): void {\r\n // Only enable hot reload in development mode\r\n // In production, process.env.NODE_ENV is replaced by DefinePlugin with \"production\"\r\n // @ts-ignore - process.env.NODE_ENV is replaced by DefinePlugin at build time\r\n const nodeEnv: string = (typeof process !== \"undefined\" && process?.env?.NODE_ENV) || \"production\";\r\n const isDev = nodeEnv !== \"production\";\r\n \r\n if (!isDev) {\r\n return; // Skip hot reload in production\r\n }\r\n\r\n try {\r\n console.log(\"[hot-reload] Attempting to connect to /__fw/hot...\");\r\n const eventSource = new EventSource(\"/__fw/hot\");\r\n let reloadTimeout: ReturnType<typeof setTimeout> | null = null;\r\n\r\n eventSource.addEventListener(\"message\", (event) => {\r\n const data = event.data;\r\n if (data && data.startsWith(\"reload:\")) {\r\n const filePath = data.slice(7);\r\n console.log(`[hot-reload] File changed: ${filePath}`);\r\n\r\n // Clear any pending reload\r\n if (reloadTimeout) {\r\n clearTimeout(reloadTimeout);\r\n }\r\n\r\n // Wait a bit for the bundler to finish compiling and files to be written\r\n // Increased timeout to ensure everything is ready\r\n reloadTimeout = setTimeout(() => {\r\n console.log(\"[hot-reload] Reloading page...\");\r\n // Force reload without cache to ensure we get the latest files\r\n window.location.reload();\r\n }, 500);\r\n }\r\n });\r\n\r\n eventSource.addEventListener(\"ping\", () => {\r\n console.log(\"[hot-reload] ✓ Connected to hot reload server\");\r\n });\r\n\r\n eventSource.onopen = () => {\r\n console.log(\"[hot-reload] ✓ SSE connection opened\");\r\n };\r\n\r\n eventSource.onerror = (error) => {\r\n // Log connection state for debugging\r\n const states = [\"CONNECTING\", \"OPEN\", \"CLOSED\"];\r\n const state = states[eventSource.readyState] || \"UNKNOWN\";\r\n \r\n if (eventSource.readyState === EventSource.CONNECTING) {\r\n // Still connecting, might be normal\r\n console.log(\"[hot-reload] Connecting...\");\r\n } else if (eventSource.readyState === EventSource.OPEN) {\r\n console.warn(\"[hot-reload] Connection error (but connection is open):\", error);\r\n } else {\r\n // Connection closed - might be production mode or server not running\r\n console.log(\"[hot-reload] Connection closed (readyState:\", state, \")\");\r\n }\r\n // EventSource automatically reconnects, so we don't need to do anything\r\n };\r\n } catch (error) {\r\n // Fail silently if EventSource is not supported\r\n console.log(\"[hot-reload] EventSource not supported or error:\", error);\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 // Set up hot reload in development mode\r\n console.log(\"[client] Bootstrap starting, setting up hot reload...\");\r\n setupHotReload();\r\n\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 // Flag para evitar múltiples listeners (por si React Strict Mode ejecuta dos veces)\r\n let isMounted = true;\r\n\r\n async function handleNavigate(\r\n nextUrl: string,\r\n options?: { revalidate?: boolean }\r\n ) {\r\n if (!isMounted) return;\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, false);\r\n window.addEventListener(\"popstate\", handlePopState, false);\r\n\r\n return () => {\r\n isMounted = false;\r\n window.removeEventListener(\"click\", handleClick, false);\r\n window.removeEventListener(\"popstate\", handlePopState, false);\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 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 try {\r\n // Salir temprano si el evento ya fue prevenido\r\n if (ev.defaultPrevented) return;\r\n \r\n // Verificar que sea un evento de mouse real (no sintético o de teclado)\r\n if (ev.type !== \"click\") return;\r\n if (ev.button !== 0) return;\r\n if (ev.metaKey || ev.ctrlKey || ev.shiftKey || ev.altKey) return;\r\n \r\n // Verificar que el evento tenga coordenadas válidas (eventos de mouse reales las tienen)\r\n const target = ev.target as HTMLElement | null;\r\n if (ev.clientX === 0 && ev.clientY === 0 && ev.detail === 0) {\r\n // Podría ser un evento sintético, ser más cauteloso\r\n if (target) {\r\n const tagName = target.tagName.toLowerCase();\r\n if (tagName === \"input\" || tagName === \"textarea\" || tagName === \"button\" || tagName === \"select\") {\r\n return; // Es un input, no procesar eventos sintéticos\r\n }\r\n }\r\n }\r\n\r\n if (!target) return;\r\n\r\n // Verificar PRIMERO si el target es un elemento interactivo (más rápido)\r\n const tagName = target.tagName.toLowerCase();\r\n if (\r\n tagName === \"input\" ||\r\n tagName === \"textarea\" ||\r\n tagName === \"button\" ||\r\n tagName === \"select\" ||\r\n target.isContentEditable ||\r\n target.getAttribute(\"contenteditable\") === \"true\"\r\n ) {\r\n return; // Es un elemento interactivo, no procesar\r\n }\r\n\r\n // Verificar si está dentro de un elemento interactivo usando closest (más eficiente que composedPath)\r\n const interactiveParent = target.closest(\"input, textarea, button, select, [contenteditable], label\");\r\n if (interactiveParent) {\r\n // Si el parent es un label, verificar si tiene un control asociado\r\n if (interactiveParent.tagName.toLowerCase() === \"label\") {\r\n const label = interactiveParent as HTMLLabelElement;\r\n if (label.control) {\r\n return; // El label tiene un control asociado (input, etc)\r\n }\r\n } else {\r\n return; // Está dentro de un elemento interactivo\r\n }\r\n }\r\n\r\n // Solo buscar anchor si no es un elemento interactivo\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 } catch (error) {\r\n // Silenciar errores para evitar bloquear el navegador\r\n console.error(\"[navigation] Error in click handler:\", error);\r\n }\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","tagName","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","../modules/runtime/client/RouterContext.tsx"],"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 * Sets up hot reload via Server-Sent Events (SSE) in development mode.\r\n * Listens for file changes and reloads the page when needed.\r\n */\r\nfunction setupHotReload(): void {\r\n // Only enable hot reload in development mode\r\n // In production, process.env.NODE_ENV is replaced by DefinePlugin with \"production\"\r\n // @ts-ignore - process.env.NODE_ENV is replaced by DefinePlugin at build time\r\n const nodeEnv: string = (typeof process !== \"undefined\" && process?.env?.NODE_ENV) || \"production\";\r\n const isDev = nodeEnv !== \"production\";\r\n \r\n if (!isDev) {\r\n return; // Skip hot reload in production\r\n }\r\n\r\n try {\r\n console.log(\"[hot-reload] Attempting to connect to /__fw/hot...\");\r\n const eventSource = new EventSource(\"/__fw/hot\");\r\n let reloadTimeout: ReturnType<typeof setTimeout> | null = null;\r\n\r\n eventSource.addEventListener(\"message\", (event) => {\r\n const data = event.data;\r\n if (data && data.startsWith(\"reload:\")) {\r\n const filePath = data.slice(7);\r\n console.log(`[hot-reload] File changed: ${filePath}`);\r\n\r\n // Clear any pending reload\r\n if (reloadTimeout) {\r\n clearTimeout(reloadTimeout);\r\n }\r\n\r\n // Wait a bit for the bundler to finish compiling and files to be written\r\n // Increased timeout to ensure everything is ready\r\n reloadTimeout = setTimeout(() => {\r\n console.log(\"[hot-reload] Reloading page...\");\r\n // Force reload without cache to ensure we get the latest files\r\n window.location.reload();\r\n }, 500);\r\n }\r\n });\r\n\r\n eventSource.addEventListener(\"ping\", () => {\r\n console.log(\"[hot-reload] ✓ Connected to hot reload server\");\r\n });\r\n\r\n eventSource.onopen = () => {\r\n console.log(\"[hot-reload] ✓ SSE connection opened\");\r\n };\r\n\r\n eventSource.onerror = (error) => {\r\n // Log connection state for debugging\r\n const states = [\"CONNECTING\", \"OPEN\", \"CLOSED\"];\r\n const state = states[eventSource.readyState] || \"UNKNOWN\";\r\n \r\n if (eventSource.readyState === EventSource.CONNECTING) {\r\n // Still connecting, might be normal\r\n console.log(\"[hot-reload] Connecting...\");\r\n } else if (eventSource.readyState === EventSource.OPEN) {\r\n console.warn(\"[hot-reload] Connection error (but connection is open):\", error);\r\n } else {\r\n // Connection closed - might be production mode or server not running\r\n console.log(\"[hot-reload] Connection closed (readyState:\", state, \")\");\r\n }\r\n // EventSource automatically reconnects, so we don't need to do anything\r\n };\r\n } catch (error) {\r\n // Fail silently if EventSource is not supported\r\n console.log(\"[hot-reload] EventSource not supported or error:\", error);\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 // Set up hot reload in development mode\r\n console.log(\"[client] Bootstrap starting, setting up hot reload...\");\r\n setupHotReload();\r\n\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 if (typeof window === \"undefined\") {\r\n return null;\r\n }\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, useCallback } 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 { RouterContext } from \"./RouterContext\";\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 // Create navigate function for router context\r\n const handleNavigate = useCallback(\r\n async (\r\n nextUrl: string,\r\n options?: { revalidate?: boolean; replace?: boolean }\r\n ) => {\r\n await navigate(nextUrl, handlersRef.current, {\r\n revalidate: options?.revalidate,\r\n });\r\n },\r\n []\r\n );\r\n\r\n useEffect(() => {\r\n // Flag para evitar múltiples listeners (por si React Strict Mode ejecuta dos veces)\r\n let isMounted = true;\r\n\r\n async function handleNavigateInternal(\r\n nextUrl: string,\r\n options?: { revalidate?: boolean }\r\n ) {\r\n if (!isMounted) return;\r\n await navigate(nextUrl, handlersRef.current, options);\r\n }\r\n\r\n const handleClick = createClickHandler(handleNavigateInternal);\r\n const handlePopState = createPopStateHandler(handleNavigateInternal);\r\n\r\n // Usar capture: false (burbujeo) para que los eventos del input se manejen primero\r\n window.addEventListener(\"click\", handleClick, false);\r\n window.addEventListener(\"popstate\", handlePopState, false);\r\n\r\n return () => {\r\n isMounted = false;\r\n window.removeEventListener(\"click\", handleClick, false);\r\n window.removeEventListener(\"popstate\", handlePopState, false);\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 (\r\n <RouterContext.Provider value={{ navigate: handleNavigate }}>\r\n <RouterView key={routeKey} state={state} />\r\n </RouterContext.Provider>\r\n );\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 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 try {\r\n // Salir temprano si el evento ya fue prevenido\r\n if (ev.defaultPrevented) return;\r\n \r\n // Verificar que sea un evento de mouse real (no sintético o de teclado)\r\n if (ev.type !== \"click\") return;\r\n if (ev.button !== 0) return;\r\n if (ev.metaKey || ev.ctrlKey || ev.shiftKey || ev.altKey) return;\r\n \r\n // Verificar que el evento tenga coordenadas válidas (eventos de mouse reales las tienen)\r\n const target = ev.target as HTMLElement | null;\r\n if (ev.clientX === 0 && ev.clientY === 0 && ev.detail === 0) {\r\n // Podría ser un evento sintético, ser más cauteloso\r\n if (target) {\r\n const tagName = target.tagName.toLowerCase();\r\n if (tagName === \"input\" || tagName === \"textarea\" || tagName === \"button\" || tagName === \"select\") {\r\n return; // Es un input, no procesar eventos sintéticos\r\n }\r\n }\r\n }\r\n\r\n if (!target) return;\r\n\r\n // Verificar PRIMERO si el target es un elemento interactivo (más rápido)\r\n const tagName = target.tagName.toLowerCase();\r\n if (\r\n tagName === \"input\" ||\r\n tagName === \"textarea\" ||\r\n tagName === \"button\" ||\r\n tagName === \"select\" ||\r\n target.isContentEditable ||\r\n target.getAttribute(\"contenteditable\") === \"true\"\r\n ) {\r\n return; // Es un elemento interactivo, no procesar\r\n }\r\n\r\n // Verificar si está dentro de un elemento interactivo usando closest (más eficiente que composedPath)\r\n const interactiveParent = target.closest(\"input, textarea, button, select, [contenteditable], label\");\r\n if (interactiveParent) {\r\n // Si el parent es un label, verificar si tiene un control asociado\r\n if (interactiveParent.tagName.toLowerCase() === \"label\") {\r\n const label = interactiveParent as HTMLLabelElement;\r\n if (label.control) {\r\n return; // El label tiene un control asociado (input, etc)\r\n }\r\n } else {\r\n return; // Está dentro de un elemento interactivo\r\n }\r\n }\r\n\r\n // Solo buscar anchor si no es un elemento interactivo\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 } catch (error) {\r\n // Silenciar errores para evitar bloquear el navegador\r\n console.error(\"[navigation] Error in click handler:\", error);\r\n }\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","import { createContext, useContext } from \"react\";\r\n\r\nexport type NavigateFunction = (\r\n url: string,\r\n options?: { revalidate?: boolean; replace?: boolean }\r\n) => Promise<void>;\r\n\r\nexport interface RouterContextValue {\r\n navigate: NavigateFunction;\r\n}\r\n\r\nexport const RouterContext = createContext<RouterContextValue | null>(null);\r\n\r\nexport function useRouterContext(): RouterContextValue {\r\n const context = useContext(RouterContext);\r\n if (!context) {\r\n throw new Error(\r\n \"useRouter must be used within a RouterProvider. Make sure you're using it inside a Loly app.\"\r\n );\r\n }\r\n return context;\r\n}\r\n"],"mappings":"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","names":["navigate","tagName","jsx","jsx"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lolyjs/core",
|
|
3
|
-
"
|
|
3
|
+
"author": "LolyJS",
|
|
4
|
+
"version": "0.2.0-alpha.3",
|
|
4
5
|
"main": "dist/index.cjs",
|
|
5
6
|
"module": "dist/index.js",
|
|
6
7
|
"types": "dist/index.d.ts",
|
|
@@ -94,6 +95,7 @@
|
|
|
94
95
|
"access": "public",
|
|
95
96
|
"tag": "alpha"
|
|
96
97
|
},
|
|
98
|
+
"license": "MIT",
|
|
97
99
|
"scripts": {
|
|
98
100
|
"build": "tsup",
|
|
99
101
|
"dev": "node ./dev/dev-server.js"
|