@fictjs/router 0.2.2 → 0.3.0

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.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/components.tsx","../src/context.ts","../src/utils.ts","../src/history.ts","../src/scroll.ts","../src/link.tsx","../src/data.ts","../src/lazy.tsx"],"sourcesContent":["/**\n * @fileoverview Router components for @fictjs/router\n *\n * This module provides the main Router, Routes, Route, and Outlet components.\n * These integrate with Fict's reactive system for fine-grained updates.\n */\n\nimport {\n createEffect,\n onCleanup,\n createMemo,\n batch,\n untrack,\n startTransition,\n Fragment,\n Suspense,\n ErrorBoundary,\n type FictNode,\n type Component,\n} from '@fictjs/runtime'\nimport { createSignal } from '@fictjs/runtime/advanced'\n\nimport {\n RouterContext,\n RouteContext,\n BeforeLeaveContext,\n RouteErrorContext,\n useRouter,\n useRoute,\n type BeforeLeaveContextValue,\n} from './context'\nimport {\n createBrowserHistory,\n createHashHistory,\n createMemoryHistory,\n createStaticHistory,\n} from './history'\nimport type {\n History,\n Location,\n RouteDefinition,\n RouteMatch,\n RouterContextValue,\n RouteContextValue,\n NavigateFunction,\n NavigateOptions,\n To,\n Params,\n BeforeLeaveHandler,\n BeforeLeaveEventArgs,\n MemoryRouterOptions,\n HashRouterOptions,\n RouterOptions,\n} from './types'\nimport {\n compileRoute,\n createBranches,\n matchRoutes,\n resolvePath,\n createLocation,\n normalizePath,\n isBrowser,\n stripBasePath,\n prependBasePath,\n locationsAreEqual,\n} from './utils'\nimport { getScrollRestoration } from './scroll'\n\n// Use Fict's signal for reactive state\n\n// ============================================================================\n// Internal State Types\n// ============================================================================\n\ninterface RouterState {\n location: Location\n matches: RouteMatch[]\n isRouting: boolean\n pendingLocation: Location | null\n}\n\nconst isDevEnv =\n (typeof import.meta !== 'undefined' &&\n (import.meta as { env?: { DEV?: boolean } }).env?.DEV === true) ||\n (typeof process !== 'undefined' && process.env?.NODE_ENV !== 'production')\n\nlet didWarnBaseMismatch = false\n\nfunction hasBasePrefix(pathname: string, base: string): boolean {\n if (!base) return true\n return pathname === base || pathname.startsWith(base + '/')\n}\n\nfunction stripBaseOrWarn(pathname: string, base: string): string | null {\n if (!base) return pathname\n if (!hasBasePrefix(pathname, base)) {\n if (isDevEnv && !didWarnBaseMismatch) {\n didWarnBaseMismatch = true\n console.warn(\n `[fict-router] Location \"${pathname}\" does not start with base \"${base}\". No routes matched.`,\n )\n }\n return null\n }\n return stripBasePath(pathname, base)\n}\n\nfunction stripBaseIfPresent(pathname: string, base: string): string {\n if (!base) return pathname\n if (hasBasePrefix(pathname, base)) {\n return stripBasePath(pathname, base)\n }\n return pathname\n}\n\n// ============================================================================\n// createRouter - Core router factory\n// ============================================================================\n\n/**\n * Create a router instance with the given history and routes\n */\nfunction createRouterState(\n history: History,\n routes: RouteDefinition[],\n base = '',\n): {\n state: () => RouterState\n navigate: NavigateFunction\n beforeLeave: BeforeLeaveContextValue\n cleanup: () => void\n normalizedBase: string\n} {\n // Normalize the base path\n const normalizedBase = normalizePath(base)\n const baseForStrip = normalizedBase === '/' ? '' : normalizedBase\n\n // Compile routes into branches for efficient matching\n const compiledRoutes = routes.map(r => compileRoute(r))\n const branches = createBranches(compiledRoutes)\n\n // Helper to match with base path stripped\n const matchWithBase = (pathname: string): RouteMatch[] => {\n const strippedPath = stripBaseOrWarn(pathname, baseForStrip)\n if (strippedPath == null) return []\n return matchRoutes(branches, strippedPath) || []\n }\n\n // Initial state\n const initialLocation = history.location\n const initialMatches = matchWithBase(initialLocation.pathname)\n\n // Reactive state using signals\n const locationSignal = createSignal<Location>(initialLocation)\n const matchesSignal = createSignal<RouteMatch[]>(initialMatches)\n const isRoutingSignal = createSignal<boolean>(false)\n const pendingLocationSignal = createSignal<Location | null>(null)\n\n // BeforeLeave handlers and navigation token for async ordering\n const beforeLeaveHandlers = new Set<BeforeLeaveHandler>()\n let navigationToken = 0\n\n const beforeLeave: BeforeLeaveContextValue = {\n addHandler(handler: BeforeLeaveHandler) {\n beforeLeaveHandlers.add(handler)\n return () => beforeLeaveHandlers.delete(handler)\n },\n async confirm(to: Location, from: Location): Promise<boolean> {\n if (beforeLeaveHandlers.size === 0) return true\n\n // Capture current token for this navigation\n const currentToken = ++navigationToken\n\n let defaultPrevented = false\n let retryRequested = false\n let forceRetry = false\n\n const event: BeforeLeaveEventArgs = {\n to,\n from,\n get defaultPrevented() {\n return defaultPrevented\n },\n preventDefault() {\n defaultPrevented = true\n },\n retry(force?: boolean) {\n retryRequested = true\n forceRetry = force ?? false\n },\n }\n\n for (const handler of beforeLeaveHandlers) {\n await handler(event)\n\n // Check if this navigation is still current (not superseded by newer navigation)\n if (currentToken !== navigationToken) {\n // This navigation was superseded, ignore its result\n return false\n }\n\n if (defaultPrevented && !retryRequested) {\n return false\n }\n if (retryRequested && forceRetry) {\n return true\n }\n }\n\n // Final check that this navigation is still current\n if (currentToken !== navigationToken) {\n return false\n }\n\n return !defaultPrevented || retryRequested\n },\n }\n\n // Navigation function\n const navigate: NavigateFunction = (toOrDelta: To | number, options?: NavigateOptions) => {\n if (typeof toOrDelta === 'number') {\n history.go(toOrDelta)\n return\n }\n\n const currentLocation = locationSignal()\n const to = toOrDelta\n\n // Extract pathname, search, and hash from string without normalizing pathname\n // This preserves relative paths like 'settings' vs '/settings'\n let toPathname: string\n let toSearch = ''\n let toHash = ''\n\n if (typeof to === 'string') {\n // Extract hash first\n let remaining = to\n const hashIndex = remaining.indexOf('#')\n if (hashIndex >= 0) {\n toHash = remaining.slice(hashIndex)\n remaining = remaining.slice(0, hashIndex)\n }\n // Extract search\n const searchIndex = remaining.indexOf('?')\n if (searchIndex >= 0) {\n toSearch = remaining.slice(searchIndex)\n remaining = remaining.slice(0, searchIndex)\n }\n // Remaining is the pathname (keep empty string for search/hash-only navigation)\n toPathname = remaining\n } else {\n toPathname = to.pathname || ''\n toSearch = to.search || ''\n toHash = to.hash || ''\n }\n\n // Resolve the target path (relative to current path, without base)\n let targetPath: string\n const currentPathWithoutBase = stripBaseOrWarn(currentLocation.pathname, baseForStrip) || '/'\n\n if (typeof to === 'string') {\n // Empty pathname means search/hash-only navigation - keep current path\n if (toPathname === '') {\n targetPath = currentPathWithoutBase\n } else if (options?.relative === 'route') {\n // Resolve relative to current route\n const matches = matchesSignal()\n const currentMatch = matches[matches.length - 1]\n const currentRoutePath = currentMatch?.pathname || currentPathWithoutBase\n targetPath = resolvePath(currentRoutePath, toPathname)\n } else {\n // Resolve relative to current pathname\n // Only strip base if it's an absolute path\n targetPath = toPathname.startsWith('/')\n ? stripBaseIfPresent(toPathname, baseForStrip)\n : resolvePath(currentPathWithoutBase, toPathname)\n }\n } else {\n const rawTargetPath = toPathname || currentPathWithoutBase\n targetPath = stripBaseIfPresent(rawTargetPath, baseForStrip)\n }\n\n // Create the full target location, preserving to.state and to.key\n // options.state overrides to.state if provided\n const toState = typeof to === 'object' ? to.state : undefined\n const toKey = typeof to === 'object' ? to.key : undefined\n const finalState = options?.state !== undefined ? options.state : toState\n\n // Build location object, only including key if defined\n const targetPathWithBase = prependBasePath(targetPath, baseForStrip)\n const locationSpec: Partial<Location> = {\n pathname: targetPathWithBase,\n search: toSearch,\n hash: toHash,\n }\n if (finalState !== undefined) {\n locationSpec.state = finalState\n }\n if (toKey !== undefined) {\n locationSpec.key = toKey\n }\n\n const targetLocation = createLocation(locationSpec, finalState, toKey)\n\n // Check beforeLeave handlers\n untrack(async () => {\n const canNavigate = await beforeLeave.confirm(targetLocation, currentLocation)\n if (!canNavigate) {\n pendingLocationSignal(null)\n return\n }\n\n // Start routing indicator and set pending location\n batch(() => {\n isRoutingSignal(true)\n pendingLocationSignal(targetLocation)\n })\n\n // Use transition for smooth updates\n // Note: We only push/replace to history here.\n // The actual signal updates happen in history.listen to avoid duplicates.\n startTransition(() => {\n const prevLocation = history.location\n if (options?.replace) {\n history.replace(targetLocation, finalState)\n } else {\n history.push(targetLocation, finalState)\n }\n\n // Scroll handling for programmatic navigation\n if (options?.scroll !== false && isBrowser()) {\n const scrollRestoration = getScrollRestoration()\n scrollRestoration.handleNavigation(\n prevLocation,\n history.location,\n options?.replace ? 'REPLACE' : 'PUSH',\n )\n }\n\n // If navigation was blocked or no-op, reset routing state\n if (locationsAreEqual(prevLocation, history.location)) {\n batch(() => {\n isRoutingSignal(false)\n pendingLocationSignal(null)\n })\n }\n })\n })\n }\n\n // Listen for history changes (browser back/forward AND navigate calls)\n // This is the single source of truth for location/matches updates\n const unlisten = history.listen(({ action, location: newLocation }) => {\n const prevLocation = locationSignal()\n\n batch(() => {\n locationSignal(newLocation)\n const newMatches = matchWithBase(newLocation.pathname)\n matchesSignal(newMatches)\n isRoutingSignal(false)\n pendingLocationSignal(null)\n })\n\n // Handle scroll restoration for POP navigation (back/forward)\n if (action === 'POP' && isBrowser()) {\n const scrollRestoration = getScrollRestoration()\n scrollRestoration.handleNavigation(prevLocation, newLocation, 'POP')\n }\n })\n\n // State accessor\n const state = () => ({\n location: locationSignal(),\n matches: matchesSignal(),\n isRouting: isRoutingSignal(),\n pendingLocation: pendingLocationSignal(),\n })\n\n return {\n state,\n navigate,\n beforeLeave,\n cleanup: unlisten,\n normalizedBase: baseForStrip,\n }\n}\n\n// ============================================================================\n// Router Component\n// ============================================================================\n\ninterface BaseRouterProps {\n children?: FictNode\n base?: string\n}\n\ninterface BrowserRouterProps extends BaseRouterProps, RouterOptions {}\ninterface HashRouterProps extends BaseRouterProps, HashRouterOptions {}\ninterface MemoryRouterProps extends BaseRouterProps, MemoryRouterOptions {}\ninterface StaticRouterProps extends BaseRouterProps {\n url: string\n}\n\n/**\n * Internal router component that sets up the context\n */\nfunction RouterProvider(props: {\n history: History\n routes: RouteDefinition[]\n base?: string | undefined\n children?: FictNode\n}) {\n const { state, navigate, beforeLeave, cleanup, normalizedBase } = createRouterState(\n props.history,\n props.routes,\n props.base,\n )\n\n onCleanup(cleanup)\n\n const routerContext: RouterContextValue = {\n location: () => state().location,\n params: () => {\n const matches = state().matches\n // Use Record<string, string | undefined> for type precision\n const allParams: Record<string, string | undefined> = {}\n for (const match of matches) {\n Object.assign(allParams, match.params)\n }\n return allParams as Params\n },\n matches: () => state().matches,\n navigate,\n isRouting: () => state().isRouting,\n pendingLocation: () => state().pendingLocation,\n base: normalizedBase,\n resolvePath: (to: To) => {\n // Resolve path relative to current location (without base)\n const location = state().location\n const currentPathWithoutBase = stripBaseOrWarn(location.pathname, normalizedBase) || '/'\n const rawTargetPath = typeof to === 'string' ? to : to.pathname || '/'\n const targetPath = rawTargetPath.startsWith('/')\n ? stripBaseIfPresent(rawTargetPath, normalizedBase)\n : rawTargetPath\n return resolvePath(currentPathWithoutBase, targetPath)\n },\n }\n\n return (\n <RouterContext.Provider value={routerContext}>\n <BeforeLeaveContext.Provider value={beforeLeave}>\n {props.children}\n </BeforeLeaveContext.Provider>\n </RouterContext.Provider>\n )\n}\n\n/**\n * Browser Router - uses the History API\n */\nexport function Router(props: BrowserRouterProps & { children?: FictNode }) {\n const history = props.history || createBrowserHistory()\n const routes = extractRoutes(props.children)\n\n return (\n <RouterProvider history={history} routes={routes} base={props.base}>\n <Routes>{props.children}</Routes>\n </RouterProvider>\n )\n}\n\n/**\n * Hash Router - uses the URL hash\n */\nexport function HashRouter(props: HashRouterProps & { children?: FictNode }) {\n const hashOptions = props.hashType ? { hashType: props.hashType } : undefined\n const history = createHashHistory(hashOptions)\n const routes = extractRoutes(props.children)\n\n return (\n <RouterProvider history={history} routes={routes} base={props.base}>\n <Routes>{props.children}</Routes>\n </RouterProvider>\n )\n}\n\n/**\n * Memory Router - keeps history in memory (for testing/SSR)\n */\nexport function MemoryRouter(props: MemoryRouterProps & { children?: FictNode }) {\n const memoryOptions: { initialEntries?: string[]; initialIndex?: number } = {}\n if (props.initialEntries !== undefined) {\n memoryOptions.initialEntries = props.initialEntries\n }\n if (props.initialIndex !== undefined) {\n memoryOptions.initialIndex = props.initialIndex\n }\n const history = createMemoryHistory(\n Object.keys(memoryOptions).length > 0 ? memoryOptions : undefined,\n )\n const routes = extractRoutes(props.children)\n\n return (\n <RouterProvider history={history} routes={routes} base={props.base}>\n <Routes>{props.children}</Routes>\n </RouterProvider>\n )\n}\n\n/**\n * Static Router - for server-side rendering\n */\nexport function StaticRouter(props: StaticRouterProps & { children?: FictNode }) {\n const history = createStaticHistory(props.url)\n const routes = extractRoutes(props.children)\n\n return (\n <RouterProvider history={history} routes={routes} base={props.base}>\n <Routes>{props.children}</Routes>\n </RouterProvider>\n )\n}\n\n// ============================================================================\n// Routes Component\n// ============================================================================\n\ninterface RoutesProps {\n children?: FictNode\n}\n\n/**\n * Routes component - renders the matched route\n */\nexport function Routes(props: RoutesProps) {\n const router = useRouter()\n const parentRoute = useRoute()\n\n // Get routes from children\n const routes = extractRoutes(props.children)\n\n // Compile routes for matching\n const compiledRoutes = routes.map(r => compileRoute(r))\n const branches = createBranches(compiledRoutes)\n\n // Create reactive memo for current matches\n const currentMatches = createMemo(() => {\n const location = router.location()\n const parentMatch = parentRoute.match()\n const locationPath = stripBaseOrWarn(location.pathname, router.base)\n if (locationPath == null) return []\n\n // Calculate the remaining path after parent route\n let basePath = '/'\n if (parentMatch) {\n basePath = parentMatch.pathname\n }\n\n // Get path relative to parent\n const relativePath = locationPath.startsWith(basePath)\n ? locationPath.slice(basePath.length) || '/'\n : locationPath\n\n return matchRoutes(branches, relativePath) || []\n })\n\n // Render the matched routes\n return <>{renderMatches(currentMatches(), 0)}</>\n}\n\n/**\n * Route data state for preloading\n */\ninterface RouteDataState<T = unknown> {\n data: T | undefined\n error: unknown\n loading: boolean\n}\n\n/**\n * Render route matches recursively with data loading support\n */\nfunction renderMatches(matches: RouteMatch[], index: number): FictNode {\n if (index >= matches.length) {\n return null\n }\n\n const match = matches[index]!\n const route = match.route\n const router = useRouter()\n\n // Create signals for route data\n const dataState = createSignal<RouteDataState>({\n data: undefined,\n error: undefined,\n loading: !!route.preload,\n })\n\n // Token to prevent stale preload results from overwriting newer ones\n let preloadToken = 0\n\n // Load data if preload is defined\n if (route.preload) {\n // Trigger preload on initial render and when location changes\n createEffect(() => {\n const location = router.location()\n const preloadArgs = {\n params: match.params,\n location,\n intent: 'navigate' as const,\n }\n\n // Increment token to invalidate any pending preloads\n const currentToken = ++preloadToken\n\n dataState({ data: undefined, error: undefined, loading: true })\n\n Promise.resolve(route.preload!(preloadArgs))\n .then(result => {\n // Only apply result if this preload is still current\n if (currentToken === preloadToken) {\n dataState({ data: result, error: undefined, loading: false })\n }\n })\n .catch(error => {\n // Only apply error if this preload is still current\n if (currentToken === preloadToken) {\n dataState({ data: undefined, error, loading: false })\n }\n })\n })\n }\n\n // Create route context for this level\n const routeContext: RouteContextValue = {\n match: () => match,\n data: () => dataState().data,\n error: () => dataState().error,\n outlet: () => renderMatches(matches, index + 1),\n resolvePath: (to: To) => {\n const basePath = match.pathname\n const targetPath = typeof to === 'string' ? to : to.pathname || '/'\n return resolvePath(basePath, targetPath)\n },\n }\n\n // Determine what to render\n const renderContent = (): FictNode => {\n const state = dataState()\n\n // If there's an error and an errorElement, render it\n if (state.error !== undefined && route.errorElement) {\n return route.errorElement\n }\n\n // If loading and there's a loadingElement, render it\n if (state.loading && route.loadingElement) {\n return route.loadingElement\n }\n\n // Render the normal content\n if (route.component) {\n const Component = route.component\n return (\n <Component params={match.params} location={router.location()} data={state.data}>\n <Outlet />\n </Component>\n )\n } else if (route.element) {\n return route.element\n } else if (route.children) {\n // Layout route without component - just render outlet\n return <Outlet />\n }\n\n return null\n }\n\n // Build the route content with context provider\n let content: FictNode = (\n <RouteContext.Provider value={routeContext}>{renderContent()}</RouteContext.Provider>\n )\n\n // Always wrap with ErrorBoundary if errorElement is defined\n // This catches both preload errors (handled in renderContent) AND render errors from components\n // Use a function fallback to pass the error via RouteErrorContext for useRouteError()\n if (route.errorElement) {\n content = (\n <ErrorBoundary\n fallback={(err: unknown, reset?: () => void) => (\n <RouteErrorContext.Provider value={{ error: err, reset }}>\n {route.errorElement}\n </RouteErrorContext.Provider>\n )}\n >\n {content}\n </ErrorBoundary>\n )\n }\n\n // If route has loadingElement and component uses Suspense internally\n if (route.loadingElement) {\n content = <Suspense fallback={route.loadingElement}>{content}</Suspense>\n }\n\n return content\n}\n\n// ============================================================================\n// Route Component\n// ============================================================================\n\ninterface RouteJSXProps {\n path?: string | undefined\n component?: Component<any> | undefined\n element?: FictNode\n children?: FictNode\n index?: boolean | undefined\n key?: string | undefined\n preload?:\n | ((args: {\n params: Params\n location: Location\n intent: 'initial' | 'navigate' | 'native' | 'preload'\n }) => unknown | Promise<unknown>)\n | undefined\n errorElement?: FictNode\n loadingElement?: FictNode\n}\n\n/**\n * Route component - defines a route\n * This is a configuration component, it doesn't render anything directly.\n */\nexport function Route(_props: RouteJSXProps): FictNode {\n // Route components are declarative - they're processed by Routes/extractRoutes\n // They don't render anything themselves\n return null\n}\n\n// ============================================================================\n// Outlet Component\n// ============================================================================\n\n/**\n * Outlet component - renders the child route\n */\nexport function Outlet(): FictNode {\n const route = useRoute()\n return <>{route.outlet()}</>\n}\n\n// ============================================================================\n// Navigate Component\n// ============================================================================\n\ninterface NavigateComponentProps {\n to: To\n replace?: boolean\n state?: unknown\n}\n\n/**\n * Navigate component - declarative navigation\n * Navigates immediately when rendered.\n */\nexport function Navigate(props: NavigateComponentProps): FictNode {\n const router = useRouter()\n\n // Navigate on mount\n createEffect(() => {\n router.navigate(props.to, {\n replace: props.replace ?? true,\n state: props.state,\n })\n })\n\n return null\n}\n\n// ============================================================================\n// Redirect Component\n// ============================================================================\n\ninterface RedirectProps {\n /** Target path to redirect to */\n to: To\n /** Path pattern that triggers this redirect (optional, for declarative redirects) */\n from?: string\n /** State to pass with the redirect */\n state?: unknown\n /** Whether to replace or push to history (default: true) */\n push?: boolean\n}\n\n/**\n * Redirect component - declarative redirect\n *\n * Unlike Navigate, Redirect is specifically for redirect scenarios:\n * - Always replaces by default (unless push=true)\n * - Can be used in route definitions with a `from` pattern\n * - Semantically indicates a redirect rather than navigation\n *\n * @example\n * ```tsx\n * // Basic redirect (replaces current entry)\n * <Redirect to=\"/login\" />\n *\n * // Redirect with state\n * <Redirect to=\"/login\" state={{ from: location.pathname }} />\n *\n * // Push instead of replace\n * <Redirect to=\"/new-page\" push />\n *\n * // In route definitions (redirect old paths)\n * <Route path=\"/old-path\" element={<Redirect to=\"/new-path\" />} />\n * ```\n */\nexport function Redirect(props: RedirectProps): FictNode {\n const router = useRouter()\n\n // Redirect on mount\n createEffect(() => {\n router.navigate(props.to, {\n replace: props.push !== true, // Replace by default, push only if explicitly requested\n state: props.state,\n })\n })\n\n return null\n}\n\n// ============================================================================\n// Utility Functions\n// ============================================================================\n\n/**\n * Extract route definitions from JSX children\n */\nfunction extractRoutes(children: FictNode): RouteDefinition[] {\n const routes: RouteDefinition[] = []\n\n if (children == null) return routes\n\n const childArray = Array.isArray(children) ? children : [children]\n\n for (const child of childArray) {\n if (child == null || typeof child !== 'object') continue\n\n // Check if it's a Route element\n const vnode = child as { type?: unknown; props?: Record<string, unknown> }\n\n if (vnode.type === Route) {\n const props = vnode.props || {}\n const routeDef: RouteDefinition = {}\n if (props.path !== undefined) routeDef.path = props.path as string\n if (props.component !== undefined) routeDef.component = props.component as Component<any>\n if (props.element !== undefined) routeDef.element = props.element as FictNode\n if (props.index !== undefined) routeDef.index = props.index as boolean\n if (props.preload !== undefined)\n routeDef.preload = props.preload as NonNullable<RouteDefinition['preload']>\n if (props.errorElement !== undefined) routeDef.errorElement = props.errorElement as FictNode\n if (props.loadingElement !== undefined)\n routeDef.loadingElement = props.loadingElement as FictNode\n if (props.children) routeDef.children = extractRoutes(props.children as FictNode)\n routes.push(routeDef)\n } else if (vnode.type === Fragment && vnode.props?.children) {\n // Handle fragments\n routes.push(...extractRoutes(vnode.props.children as FictNode))\n }\n }\n\n return routes\n}\n\n// ============================================================================\n// Programmatic Route Definition\n// ============================================================================\n\n/**\n * Create routes from a configuration array (alternative to JSX)\n */\nexport function createRoutes(routes: RouteDefinition[]): RouteDefinition[] {\n return routes\n}\n\n/**\n * Create a router with programmatic routes\n */\nexport function createRouter(\n routes: RouteDefinition[],\n options?: RouterOptions,\n): {\n Router: Component<{ children?: FictNode }>\n} {\n return {\n Router: (props: { children?: FictNode }) => {\n const history = options?.history || createBrowserHistory()\n\n return (\n <RouterProvider history={history} routes={routes} base={options?.base}>\n {props.children || <Routes>{routesToElements(routes)}</Routes>}\n </RouterProvider>\n )\n },\n }\n}\n\n/**\n * Convert route definitions to Route elements\n */\nfunction routesToElements(routes: RouteDefinition[]): FictNode {\n return (\n <>\n {routes.map((route, i) => {\n const routeProps: RouteJSXProps = { key: route.key || `route-${i}` }\n if (route.path !== undefined) routeProps.path = route.path\n if (route.component !== undefined) routeProps.component = route.component\n if (route.element !== undefined) routeProps.element = route.element\n if (route.index !== undefined) routeProps.index = route.index\n if (route.children) routeProps.children = routesToElements(route.children)\n return <Route {...routeProps} />\n })}\n </>\n )\n}\n","/**\n * @fileoverview Router and Route contexts for @fictjs/router\n *\n * This module provides the context system that allows components to access\n * routing state without prop drilling. Uses Fict's context API.\n */\n\nimport { createContext, useContext } from '@fictjs/runtime'\n\nimport type {\n RouterContextValue,\n RouteContextValue,\n Location,\n Params,\n RouteMatch,\n NavigateFunction,\n To,\n BeforeLeaveHandler,\n} from './types'\nimport { stripBasePath, prependBasePath } from './utils'\n\n// ============================================================================\n// Router Context\n// ============================================================================\n\n/**\n * Default router context value (used when no router is present)\n */\nconst defaultRouterContext: RouterContextValue = {\n location: () => ({\n pathname: '/',\n search: '',\n hash: '',\n state: null,\n key: 'default',\n }),\n params: () => ({}),\n matches: () => [],\n navigate: () => {\n console.warn('[fict-router] No router found. Wrap your app in a <Router>')\n },\n isRouting: () => false,\n pendingLocation: () => null,\n base: '',\n resolvePath: (to: To) => (typeof to === 'string' ? to : to.pathname || '/'),\n}\n\n/**\n * Router context - provides access to router state\n */\nexport const RouterContext = createContext<RouterContextValue>(defaultRouterContext)\nRouterContext.displayName = 'RouterContext'\n\n/**\n * Use the router context\n */\nexport function useRouter(): RouterContextValue {\n return useContext(RouterContext)\n}\n\n// ============================================================================\n// Route Context\n// ============================================================================\n\n/**\n * Default route context value (used when not inside a route)\n */\nconst defaultRouteContext: RouteContextValue = {\n match: () => undefined,\n data: () => undefined,\n outlet: () => null,\n resolvePath: (to: To) => (typeof to === 'string' ? to : to.pathname || '/'),\n}\n\n/**\n * Route context - provides access to current route match and data\n */\nexport const RouteContext = createContext<RouteContextValue>(defaultRouteContext)\nRouteContext.displayName = 'RouteContext'\n\n/**\n * Use the route context\n */\nexport function useRoute(): RouteContextValue {\n return useContext(RouteContext)\n}\n\n// ============================================================================\n// BeforeLeave Context\n// ============================================================================\n\n/**\n * BeforeLeave context for route guards\n */\nexport interface BeforeLeaveContextValue {\n addHandler: (handler: BeforeLeaveHandler) => () => void\n confirm: (to: Location, from: Location) => Promise<boolean>\n}\n\nconst defaultBeforeLeaveContext: BeforeLeaveContextValue = {\n addHandler: () => () => {},\n confirm: async () => true,\n}\n\nexport const BeforeLeaveContext = createContext<BeforeLeaveContextValue>(defaultBeforeLeaveContext)\nBeforeLeaveContext.displayName = 'BeforeLeaveContext'\n\n/**\n * Use the beforeLeave context\n */\nexport function useBeforeLeaveContext(): BeforeLeaveContextValue {\n return useContext(BeforeLeaveContext)\n}\n\n// ============================================================================\n// Route Error Context (for ErrorBoundary-caught errors)\n// ============================================================================\n\n/**\n * Context for passing render errors caught by ErrorBoundary to errorElement components.\n * This allows useRouteError() to access errors from both preload and render phases.\n */\nexport interface RouteErrorContextValue {\n error: unknown\n reset: (() => void) | undefined\n}\n\nconst defaultRouteErrorContext: RouteErrorContextValue = {\n error: undefined,\n reset: undefined,\n}\n\nexport const RouteErrorContext = createContext<RouteErrorContextValue>(defaultRouteErrorContext)\nRouteErrorContext.displayName = 'RouteErrorContext'\n\n// ============================================================================\n// Navigation Hooks\n// ============================================================================\n\n/**\n * Get the navigate function\n */\nexport function useNavigate(): NavigateFunction {\n const router = useRouter()\n return router.navigate\n}\n\n/**\n * Get the current location\n */\nexport function useLocation(): () => Location {\n const router = useRouter()\n return router.location\n}\n\n/**\n * Get the current route parameters\n */\nexport function useParams<P extends string = string>(): () => Params<P> {\n const router = useRouter()\n return router.params as () => Params<P>\n}\n\n/**\n * Get the current search parameters\n */\nexport function useSearchParams(): [\n () => URLSearchParams,\n (params: URLSearchParams | Record<string, string>, options?: { replace?: boolean }) => void,\n] {\n const router = useRouter()\n\n const getSearchParams = () => {\n const location = router.location()\n return new URLSearchParams(location.search)\n }\n\n const setSearchParams = (\n params: URLSearchParams | Record<string, string>,\n options?: { replace?: boolean },\n ) => {\n const searchParams = params instanceof URLSearchParams ? params : new URLSearchParams(params)\n const search = searchParams.toString()\n\n const location = router.location()\n router.navigate(\n {\n pathname: location.pathname,\n search: search ? '?' + search : '',\n hash: location.hash,\n },\n { replace: options?.replace },\n )\n }\n\n return [getSearchParams, setSearchParams]\n}\n\n/**\n * Get the current route matches\n */\nexport function useMatches(): () => RouteMatch[] {\n const router = useRouter()\n return router.matches\n}\n\n/**\n * Check if currently routing (loading new route)\n */\nexport function useIsRouting(): () => boolean {\n const router = useRouter()\n return router.isRouting\n}\n\n/**\n * Get the pending navigation location (if any)\n */\nexport function usePendingLocation(): () => Location | null {\n const router = useRouter()\n return router.pendingLocation\n}\n\n/**\n * Get the preloaded data for the current route\n */\nexport function useRouteData<T = unknown>(): () => T | undefined {\n const route = useRoute()\n return route.data as () => T | undefined\n}\n\n/**\n * Get route error (for use in errorElement components)\n * This hook is used within an error boundary to access the caught error.\n * It returns errors from both preload phase (via route context) and\n * render phase (via ErrorBoundary context).\n *\n * @example\n * ```tsx\n * function RouteErrorPage() {\n * const error = useRouteError()\n * return (\n * <div>\n * <h1>Error</h1>\n * <p>{error?.message || 'Unknown error'}</p>\n * </div>\n * )\n * }\n * ```\n */\nexport function useRouteError(): unknown {\n // First check RouteErrorContext for render errors caught by ErrorBoundary\n const errorContext = useContext(RouteErrorContext)\n if (errorContext.error !== undefined) {\n return errorContext.error\n }\n\n // Fall back to route context for preload errors\n const route = useRoute()\n return (route as any).error?.()\n}\n\n/**\n * Resolve a path relative to the current route\n */\nexport function useResolvedPath(to: To | (() => To)): () => string {\n const route = useRoute()\n\n return () => {\n const target = typeof to === 'function' ? to() : to\n return route.resolvePath(target)\n }\n}\n\n/**\n * Check if a path matches the current location\n */\nexport function useMatch(path: string | (() => string)): () => RouteMatch | null {\n const router = useRouter()\n\n return () => {\n const targetPath = typeof path === 'function' ? path() : path\n const matches = router.matches()\n\n // Check if any match's pattern matches the target path\n for (const match of matches) {\n if (match.pattern === targetPath || match.pathname === targetPath) {\n return match\n }\n }\n\n return null\n }\n}\n\n// ============================================================================\n// Helper Hooks\n// ============================================================================\n\n/**\n * Get the href for a given path (useful for SSR)\n */\nexport function useHref(to: To | (() => To)): () => string {\n const router = useRouter()\n\n return () => {\n const target = typeof to === 'function' ? to() : to\n\n // Extract pathname, search, and hash from target\n // For strings, we must extract WITHOUT normalizing to preserve relative paths\n let pathname: string\n let search = ''\n let hash = ''\n\n if (typeof target === 'string') {\n // Extract hash first\n let remaining = target\n const hashIndex = remaining.indexOf('#')\n if (hashIndex >= 0) {\n hash = remaining.slice(hashIndex)\n remaining = remaining.slice(0, hashIndex)\n }\n // Extract search\n const searchIndex = remaining.indexOf('?')\n if (searchIndex >= 0) {\n search = remaining.slice(searchIndex)\n remaining = remaining.slice(0, searchIndex)\n }\n // Keep empty string for search/hash-only targets\n pathname = remaining\n } else {\n // Keep empty string for search/hash-only targets\n pathname = target.pathname || ''\n search = target.search || ''\n hash = target.hash || ''\n }\n\n // For empty pathname (search/hash-only), use current location's pathname\n // Otherwise resolve the pathname (handles relative paths)\n let resolved: string\n if (pathname === '') {\n // Use current path for search/hash-only hrefs\n const currentPathname = router.location().pathname\n const normalizedBase = router.base === '/' || router.base === '' ? '' : router.base\n\n // Check if current location is within the router's base\n if (normalizedBase && !currentPathname.startsWith(normalizedBase)) {\n // Current location is outside the base - return raw pathname + search/hash\n // without base manipulation to avoid generating incorrect hrefs\n return currentPathname + search + hash\n }\n\n resolved = stripBasePath(currentPathname, router.base)\n } else {\n resolved = router.resolvePath(pathname)\n }\n // Prepend base to get the full href, then append search/hash\n const baseHref = prependBasePath(resolved, router.base)\n return baseHref + search + hash\n }\n}\n\n/**\n * Check if a path is active (matches current location)\n */\nexport function useIsActive(\n to: To | (() => To),\n options?: { end?: boolean | undefined },\n): () => boolean {\n const router = useRouter()\n\n return () => {\n const target = typeof to === 'function' ? to() : to\n\n // Resolve the target path relative to current location (handles relative paths)\n const resolvedTargetPath = router.resolvePath(target)\n\n // Strip base from current location pathname for comparison\n const currentPath = router.location().pathname\n if (router.base && currentPath !== router.base && !currentPath.startsWith(router.base + '/')) {\n return false\n }\n const currentPathWithoutBase = stripBasePath(currentPath, router.base)\n\n if (options?.end) {\n return currentPathWithoutBase === resolvedTargetPath\n }\n\n return (\n currentPathWithoutBase === resolvedTargetPath ||\n currentPathWithoutBase.startsWith(resolvedTargetPath + '/')\n )\n }\n}\n\n/**\n * Register a beforeLeave handler for the current route\n */\nexport function useBeforeLeave(handler: BeforeLeaveHandler): void {\n const context = useBeforeLeaveContext()\n const _cleanup = context.addHandler(handler)\n\n // Note: In Fict, cleanup happens automatically when the component unmounts\n // via the RootContext cleanup system\n}\n","/**\n * @fileoverview Path matching and utility functions for @fictjs/router\n *\n * This module provides path parsing, matching, and scoring utilities.\n * Based on patterns from Solid Router with optimizations for Fict.\n */\n\nimport type {\n Location,\n To,\n Params,\n RouteMatch,\n RouteDefinition,\n CompiledRoute,\n RouteBranch,\n MatchFilter,\n} from './types'\n\n// ============================================================================\n// Path Normalization\n// ============================================================================\n\n/**\n * Normalize a path by removing trailing slashes and ensuring leading slash\n */\nexport function normalizePath(path: string): string {\n // Handle empty or root path\n if (!path || path === '/') return '/'\n\n // Ensure leading slash\n let normalized = path.startsWith('/') ? path : '/' + path\n\n // Remove trailing slash (except for root)\n if (normalized.length > 1 && normalized.endsWith('/')) {\n normalized = normalized.slice(0, -1)\n }\n\n return normalized\n}\n\n/**\n * Join path segments together\n */\nexport function joinPaths(...paths: (string | undefined)[]): string {\n return normalizePath(\n paths\n .filter((p): p is string => p != null && p !== '')\n .join('/')\n .replace(/\\/+/g, '/'),\n )\n}\n\n/**\n * Resolve a relative path against a base path\n */\nexport function resolvePath(base: string, to: To): string {\n const toPath = typeof to === 'string' ? to : to.pathname || ''\n\n // Absolute path\n if (toPath.startsWith('/')) {\n return normalizePath(toPath)\n }\n\n // Relative path resolution\n const baseSegments = base.split('/').filter(Boolean)\n\n // Handle special relative segments\n const toSegments = toPath.split('/').filter(Boolean)\n\n for (const segment of toSegments) {\n if (segment === '..') {\n baseSegments.pop()\n } else if (segment !== '.') {\n baseSegments.push(segment)\n }\n }\n\n return '/' + baseSegments.join('/')\n}\n\n// ============================================================================\n// Location Utilities\n// ============================================================================\n\n/**\n * Create a Location object from a To value\n */\nexport function createLocation(to: To, state?: unknown, key?: string): Location {\n if (typeof to === 'string') {\n const url = parseURL(to)\n return {\n pathname: url.pathname,\n search: url.search,\n hash: url.hash,\n state: state ?? null,\n key: key ?? createKey(),\n }\n }\n\n return {\n pathname: to.pathname || '/',\n search: to.search || '',\n hash: to.hash || '',\n state: state ?? to.state ?? null,\n key: key ?? to.key ?? createKey(),\n }\n}\n\n/**\n * Parse a URL string into its components\n */\nexport function parseURL(url: string): { pathname: string; search: string; hash: string } {\n // Handle hash first\n const hashIndex = url.indexOf('#')\n let hash = ''\n if (hashIndex >= 0) {\n hash = url.slice(hashIndex)\n url = url.slice(0, hashIndex)\n }\n\n // Handle search\n const searchIndex = url.indexOf('?')\n let search = ''\n if (searchIndex >= 0) {\n search = url.slice(searchIndex)\n url = url.slice(0, searchIndex)\n }\n\n return {\n pathname: normalizePath(url || '/'),\n search,\n hash,\n }\n}\n\n/**\n * Create a URL string from a Location object\n */\nexport function createURL(location: Partial<Location>): string {\n const pathname = location.pathname || '/'\n const search = location.search || ''\n const hash = location.hash || ''\n return pathname + search + hash\n}\n\n/**\n * Generate a unique key for location entries\n */\nlet keyIndex = 0\nexport function createKey(): string {\n return String(++keyIndex)\n}\n\n// ============================================================================\n// Path Matching\n// ============================================================================\n\n/**\n * Segment types for pattern matching\n */\ntype SegmentType = 'static' | 'dynamic' | 'optional' | 'splat'\n\ninterface PathSegment {\n type: SegmentType\n value: string\n paramName?: string\n}\n\n/**\n * Parse a path pattern into segments\n */\nexport function parsePathPattern(pattern: string): PathSegment[] {\n const segments: PathSegment[] = []\n const parts = pattern.split('/').filter(Boolean)\n\n for (const part of parts) {\n if (part === '*' || part.startsWith('*')) {\n // Splat/catch-all segment\n const paramName = part.length > 1 ? part.slice(1) : '*'\n segments.push({ type: 'splat', value: part, paramName })\n } else if (part.startsWith(':')) {\n // Dynamic or optional segment\n const isOptional = part.endsWith('?')\n const paramName = isOptional ? part.slice(1, -1) : part.slice(1)\n segments.push({\n type: isOptional ? 'optional' : 'dynamic',\n value: part,\n paramName,\n })\n } else {\n // Static segment\n segments.push({ type: 'static', value: part.toLowerCase() })\n }\n }\n\n return segments\n}\n\n/**\n * Calculate the score for a route pattern.\n * Higher score = more specific route.\n *\n * Scoring:\n * - Static segment: 3 points\n * - Dynamic segment: 2 points\n * - Optional segment: 1 point\n * - Splat segment: 0.5 points\n * - Index route bonus: 0.5 points\n */\nexport function scoreRoute(pattern: string, isIndex = false): number {\n const segments = parsePathPattern(pattern)\n let score = 0\n\n for (const segment of segments) {\n switch (segment.type) {\n case 'static':\n score += 3\n break\n case 'dynamic':\n score += 2\n break\n case 'optional':\n score += 1\n break\n case 'splat':\n score += 0.5\n break\n }\n }\n\n // Index route gets a small bonus\n if (isIndex) {\n score += 0.5\n }\n\n return score\n}\n\n/**\n * Create a matcher function for a path pattern\n */\nexport function createMatcher(\n pattern: string,\n matchFilters?: Record<string, MatchFilter>,\n): (pathname: string) => RouteMatch | null {\n const segments = parsePathPattern(pattern)\n const normalizedPattern = normalizePath(pattern)\n\n return (pathname: string): RouteMatch | null => {\n const pathSegments = pathname.split('/').filter(Boolean)\n const params: Record<string, string> = {}\n let matchedPath = ''\n let pathIndex = 0\n\n for (let i = 0; i < segments.length; i++) {\n const segment = segments[i]!\n const pathSegment = pathSegments[pathIndex]\n\n switch (segment.type) {\n case 'static':\n // Must match exactly (case-insensitive)\n if (!pathSegment || pathSegment.toLowerCase() !== segment.value) {\n return null\n }\n matchedPath += '/' + pathSegment\n pathIndex++\n break\n\n case 'dynamic':\n // Must have a value\n if (!pathSegment) {\n return null\n }\n // Validate with filter if provided\n if (matchFilters && segment.paramName && matchFilters[segment.paramName]) {\n if (!validateParam(pathSegment, matchFilters[segment.paramName]!)) {\n return null\n }\n }\n params[segment.paramName!] = decodeURIComponent(pathSegment)\n matchedPath += '/' + pathSegment\n pathIndex++\n break\n\n case 'optional': {\n // May or may not have a value\n if (pathSegment) {\n // Look ahead: if next pattern segment is static and matches current path segment,\n // skip this optional to allow the static to match\n const nextSegment = segments[i + 1]\n if (\n nextSegment &&\n nextSegment.type === 'static' &&\n pathSegment.toLowerCase() === nextSegment.value\n ) {\n // Skip this optional - don't consume the path segment\n // so the next iteration can match it as static\n break\n }\n\n // Validate with filter if provided\n if (matchFilters && segment.paramName && matchFilters[segment.paramName]) {\n if (!validateParam(pathSegment, matchFilters[segment.paramName]!)) {\n // Optional segment doesn't match filter, treat as not provided\n break\n }\n }\n params[segment.paramName!] = decodeURIComponent(pathSegment)\n matchedPath += '/' + pathSegment\n pathIndex++\n }\n break\n }\n\n case 'splat': {\n // Capture remaining path\n // Decode each segment individually to handle encoded slashes correctly\n const remainingSegments = pathSegments.slice(pathIndex)\n const decodedSegments = remainingSegments.map(seg => {\n try {\n return decodeURIComponent(seg)\n } catch {\n // If decoding fails (malformed URI), use the original segment\n return seg\n }\n })\n params[segment.paramName!] = decodedSegments.join('/')\n matchedPath += remainingSegments.length > 0 ? '/' + remainingSegments.join('/') : ''\n pathIndex = pathSegments.length\n break\n }\n }\n }\n\n // If we haven't consumed all path segments, this is not a match\n // (unless the last segment was a splat)\n if (pathIndex < pathSegments.length) {\n return null\n }\n\n return {\n route: {} as RouteDefinition, // Will be filled in by caller\n pathname: matchedPath || '/',\n params: params as Params,\n pattern: normalizedPattern,\n }\n }\n}\n\n/**\n * Validate a parameter value against a filter\n */\nfunction validateParam(value: string, filter: MatchFilter): boolean {\n if (filter instanceof RegExp) {\n return filter.test(value)\n }\n if (Array.isArray(filter)) {\n return filter.includes(value)\n }\n if (typeof filter === 'function') {\n return filter(value)\n }\n return true\n}\n\n// ============================================================================\n// Route Compilation\n// ============================================================================\n\nlet routeKeyCounter = 0\n\n/**\n * Compile a route definition into a CompiledRoute\n */\nexport function compileRoute(route: RouteDefinition, parentPattern = ''): CompiledRoute {\n const pattern = normalizePath(\n joinPaths(parentPattern, route.path || (route.index ? '' : undefined)),\n )\n\n const compiled: CompiledRoute = {\n route,\n pattern,\n matcher: createMatcher(pattern, route.matchFilters as Record<string, MatchFilter>),\n score: scoreRoute(pattern, route.index),\n key: route.key || `route-${++routeKeyCounter}`,\n }\n\n if (route.children && route.children.length > 0) {\n compiled.children = route.children.map(child =>\n compileRoute(child, route.index ? parentPattern : pattern),\n )\n }\n\n return compiled\n}\n\n/**\n * Create branches from compiled routes for efficient matching.\n * A branch represents a complete path from root to leaf.\n */\nexport function createBranches(routes: CompiledRoute[]): RouteBranch[] {\n const branches: RouteBranch[] = []\n\n function buildBranches(route: CompiledRoute, parentRoutes: CompiledRoute[] = []): void {\n const currentRoutes = [...parentRoutes, route]\n\n if (route.children && route.children.length > 0) {\n for (const child of route.children) {\n buildBranches(child, currentRoutes)\n }\n } else {\n // Leaf route - create a branch\n const score = currentRoutes.reduce((sum, r) => sum + r.score, 0)\n\n const branchMatcher = (pathname: string): RouteMatch[] | null => {\n const matches: RouteMatch[] = []\n let remainingPath = pathname\n let accumulatedParams: Record<string, string | undefined> = {}\n\n for (const compiledRoute of currentRoutes) {\n const match = compiledRoute.matcher(remainingPath)\n if (!match) {\n return null\n }\n\n // Accumulate params\n accumulatedParams = { ...accumulatedParams, ...match.params }\n\n matches.push({\n ...match,\n route: compiledRoute.route,\n params: { ...accumulatedParams } as Params,\n })\n\n // For nested routes, the remaining path should be after the matched portion\n // But only if this isn't the leaf route\n if (compiledRoute !== currentRoutes[currentRoutes.length - 1]) {\n if (match.pathname !== '/') {\n remainingPath = remainingPath.slice(match.pathname.length) || '/'\n }\n }\n }\n\n return matches\n }\n\n branches.push({\n routes: currentRoutes,\n score,\n matcher: branchMatcher,\n })\n }\n }\n\n for (const route of routes) {\n buildBranches(route)\n }\n\n // Sort branches by score (highest first)\n branches.sort((a, b) => b.score - a.score)\n\n return branches\n}\n\n/**\n * Match a pathname against route branches\n */\nexport function matchRoutes(branches: RouteBranch[], pathname: string): RouteMatch[] | null {\n const normalizedPath = normalizePath(pathname)\n\n for (const branch of branches) {\n const matches = branch.matcher(normalizedPath)\n if (matches) {\n return matches\n }\n }\n\n return null\n}\n\n// ============================================================================\n// Search Params Utilities\n// ============================================================================\n\n/**\n * Parse search params from a search string\n */\nexport function parseSearchParams(search: string): URLSearchParams {\n return new URLSearchParams(search)\n}\n\n/**\n * Stringify search params to a search string\n */\nexport function stringifySearchParams(params: URLSearchParams | Record<string, string>): string {\n const searchParams = params instanceof URLSearchParams ? params : new URLSearchParams(params)\n\n const str = searchParams.toString()\n return str ? '?' + str : ''\n}\n\n// ============================================================================\n// Misc Utilities\n// ============================================================================\n\n/**\n * Check if two locations are equal\n */\nexport function locationsAreEqual(a: Location, b: Location): boolean {\n return a.pathname === b.pathname && a.search === b.search && a.hash === b.hash\n}\n\n/**\n * Strip the base path from a pathname\n */\nexport function stripBasePath(pathname: string, basePath: string): string {\n if (basePath === '/' || basePath === '') {\n return pathname\n }\n\n const normalizedBase = normalizePath(basePath)\n if (pathname.startsWith(normalizedBase)) {\n const stripped = pathname.slice(normalizedBase.length)\n return stripped || '/'\n }\n\n return pathname\n}\n\n/**\n * Prepend the base path to a pathname\n */\nexport function prependBasePath(pathname: string, basePath: string): string {\n if (basePath === '/' || basePath === '') {\n return pathname\n }\n\n return joinPaths(basePath, pathname)\n}\n\n/**\n * Generate a stable hash for route params (for caching)\n */\nexport function hashParams(params: Record<string, unknown>): string {\n const entries = Object.entries(params).sort(([a], [b]) => a.localeCompare(b))\n return JSON.stringify(entries)\n}\n\n/**\n * Scroll to top of the page\n */\nexport function scrollToTop(): void {\n if (typeof window !== 'undefined') {\n window.scrollTo(0, 0)\n }\n}\n\n/**\n * Check if code is running on the server\n */\nexport function isServer(): boolean {\n return typeof window === 'undefined'\n}\n\n/**\n * Check if code is running in the browser\n */\nexport function isBrowser(): boolean {\n return typeof window !== 'undefined'\n}\n","/**\n * @fileoverview History implementations for @fictjs/router\n *\n * Provides browser history, hash history, and memory history implementations.\n * These handle the low-level navigation state management.\n */\n\nimport type { History, HistoryAction, HistoryListener, Location, To, Blocker } from './types'\nimport { createLocation, createURL, parseURL, createKey, normalizePath } from './utils'\n\n// ============================================================================\n// Shared Utilities\n// ============================================================================\n\n/**\n * Create a history state object\n */\nfunction createHistoryState(\n location: Location,\n index: number,\n): { usr: unknown; key: string; idx: number } {\n return {\n usr: location.state,\n key: location.key,\n idx: index,\n }\n}\n\n/**\n * Read a location from window.history.state\n */\nfunction readLocation(\n state: { usr?: unknown; key?: string; idx?: number } | null,\n url: string,\n): Location {\n const { pathname, search, hash } = parseURL(url)\n return {\n pathname,\n search,\n hash,\n state: state?.usr ?? null,\n key: state?.key ?? createKey(),\n }\n}\n\n// ============================================================================\n// Browser History\n// ============================================================================\n\n/**\n * Create a browser history instance that uses the History API.\n * This is the standard history for most web applications.\n *\n * @throws Error if called in a non-browser environment (SSR)\n */\nexport function createBrowserHistory(): History {\n // SSR guard: throw clear error if window is not available\n if (typeof window === 'undefined') {\n throw new Error(\n '[fict-router] createBrowserHistory cannot be used in a server environment. ' +\n 'Use createMemoryHistory or createStaticHistory for SSR.',\n )\n }\n\n const listeners = new Set<HistoryListener>()\n const blockers = new Set<Blocker>()\n\n let action: HistoryAction = 'POP'\n let location = readLocation(\n window.history.state,\n window.location.pathname + window.location.search + window.location.hash,\n )\n let index = (window.history.state?.idx as number) ?? 0\n\n // Handle popstate (back/forward navigation)\n function handlePopState(event: PopStateEvent) {\n const nextLocation = readLocation(\n event.state,\n window.location.pathname + window.location.search + window.location.hash,\n )\n const nextAction: HistoryAction = 'POP'\n const nextIndex = (event.state?.idx as number) ?? 0\n\n // Check blockers\n if (blockers.size > 0) {\n let blocked = false\n const retry = () => {\n // Re-trigger the navigation\n window.history.go(nextIndex - index)\n }\n\n for (const blocker of blockers) {\n blocker({\n action: nextAction,\n location: nextLocation,\n retry,\n })\n blocked = true\n break\n }\n\n if (blocked) {\n // Restore the previous state by going back\n window.history.go(index - nextIndex)\n return\n }\n }\n\n action = nextAction\n location = nextLocation\n index = nextIndex\n\n notifyListeners()\n }\n\n window.addEventListener('popstate', handlePopState)\n\n function notifyListeners() {\n for (const listener of listeners) {\n listener({ action, location })\n }\n }\n\n function push(to: To, state?: unknown) {\n const nextLocation = createLocation(to, state)\n const nextAction: HistoryAction = 'PUSH'\n\n // Check blockers\n if (blockers.size > 0) {\n let blocked = false\n const retry = () => push(to, state)\n\n for (const blocker of blockers) {\n blocker({\n action: nextAction,\n location: nextLocation,\n retry,\n })\n blocked = true\n break\n }\n\n if (blocked) return\n }\n\n action = nextAction\n location = nextLocation\n index++\n\n const historyState = createHistoryState(location, index)\n window.history.pushState(historyState, '', createURL(location))\n\n notifyListeners()\n }\n\n function replace(to: To, state?: unknown) {\n const nextLocation = createLocation(to, state)\n const nextAction: HistoryAction = 'REPLACE'\n\n // Check blockers\n if (blockers.size > 0) {\n let blocked = false\n const retry = () => replace(to, state)\n\n for (const blocker of blockers) {\n blocker({\n action: nextAction,\n location: nextLocation,\n retry,\n })\n blocked = true\n break\n }\n\n if (blocked) return\n }\n\n action = nextAction\n location = nextLocation\n\n const historyState = createHistoryState(location, index)\n window.history.replaceState(historyState, '', createURL(location))\n\n notifyListeners()\n }\n\n function go(delta: number) {\n window.history.go(delta)\n }\n\n // Initialize history state if not set\n if (window.history.state === null) {\n const historyState = createHistoryState(location, index)\n window.history.replaceState(historyState, '', createURL(location))\n }\n\n return {\n get action() {\n return action\n },\n get location() {\n return location\n },\n push,\n replace,\n go,\n back() {\n go(-1)\n },\n forward() {\n go(1)\n },\n listen(listener: HistoryListener) {\n listeners.add(listener)\n return () => listeners.delete(listener)\n },\n createHref(to: To) {\n const loc = typeof to === 'string' ? parseURL(to) : to\n return createURL(loc as Location)\n },\n block(blocker: Blocker) {\n blockers.add(blocker)\n\n // Set up beforeunload handler if this is the first blocker\n if (blockers.size === 1) {\n window.addEventListener('beforeunload', handleBeforeUnload)\n }\n\n return () => {\n blockers.delete(blocker)\n if (blockers.size === 0) {\n window.removeEventListener('beforeunload', handleBeforeUnload)\n }\n }\n },\n }\n}\n\nfunction handleBeforeUnload(event: BeforeUnloadEvent) {\n event.preventDefault()\n // Modern browsers ignore the return value, but we set it anyway\n event.returnValue = ''\n}\n\n// ============================================================================\n// Hash History\n// ============================================================================\n\n/**\n * Create a hash history instance that uses the URL hash.\n * Useful for static file hosting or when you can't configure server-side routing.\n *\n * @throws Error if called in a non-browser environment (SSR)\n */\nexport function createHashHistory(options: { hashType?: 'slash' | 'noslash' } = {}): History {\n // SSR guard: throw clear error if window is not available\n if (typeof window === 'undefined') {\n throw new Error(\n '[fict-router] createHashHistory cannot be used in a server environment. ' +\n 'Use createMemoryHistory or createStaticHistory for SSR.',\n )\n }\n\n const { hashType = 'slash' } = options\n const listeners = new Set<HistoryListener>()\n const blockers = new Set<Blocker>()\n\n let action: HistoryAction = 'POP'\n let location = readHashLocation()\n let index = 0\n\n function readHashLocation(): Location {\n let hash = window.location.hash.slice(1) // Remove the #\n\n // Handle hash type\n if (hashType === 'slash' && !hash.startsWith('/')) {\n hash = '/' + hash\n } else if (hashType === 'noslash' && hash.startsWith('/')) {\n hash = hash.slice(1)\n }\n\n const { pathname, search, hash: innerHash } = parseURL(hash || '/')\n\n return {\n pathname: normalizePath(pathname),\n search,\n hash: innerHash,\n state: window.history.state?.usr ?? null,\n key: window.history.state?.key ?? createKey(),\n }\n }\n\n function createHashHref(location: Location): string {\n const url = createURL(location)\n if (hashType === 'noslash') {\n return '#' + url.slice(1) // Remove leading /\n }\n return '#' + url\n }\n\n function handleHashChange() {\n const nextLocation = readHashLocation()\n const nextAction: HistoryAction = 'POP'\n\n // Check blockers\n if (blockers.size > 0) {\n let blocked = false\n const retry = () => {\n window.location.hash = createHashHref(nextLocation)\n }\n\n for (const blocker of blockers) {\n blocker({\n action: nextAction,\n location: nextLocation,\n retry,\n })\n blocked = true\n break\n }\n\n if (blocked) {\n // Restore the previous hash\n window.location.hash = createHashHref(location)\n return\n }\n }\n\n action = nextAction\n location = nextLocation\n\n notifyListeners()\n }\n\n window.addEventListener('hashchange', handleHashChange)\n\n function notifyListeners() {\n for (const listener of listeners) {\n listener({ action, location })\n }\n }\n\n function push(to: To, state?: unknown) {\n const nextLocation = createLocation(to, state)\n const nextAction: HistoryAction = 'PUSH'\n\n // Check blockers\n if (blockers.size > 0) {\n let blocked = false\n const retry = () => push(to, state)\n\n for (const blocker of blockers) {\n blocker({\n action: nextAction,\n location: nextLocation,\n retry,\n })\n blocked = true\n break\n }\n\n if (blocked) return\n }\n\n action = nextAction\n location = nextLocation\n index++\n\n const historyState = createHistoryState(location, index)\n window.history.pushState(historyState, '', createHashHref(location))\n\n notifyListeners()\n }\n\n function replace(to: To, state?: unknown) {\n const nextLocation = createLocation(to, state)\n const nextAction: HistoryAction = 'REPLACE'\n\n // Check blockers\n if (blockers.size > 0) {\n let blocked = false\n const retry = () => replace(to, state)\n\n for (const blocker of blockers) {\n blocker({\n action: nextAction,\n location: nextLocation,\n retry,\n })\n blocked = true\n break\n }\n\n if (blocked) return\n }\n\n action = nextAction\n location = nextLocation\n\n const historyState = createHistoryState(location, index)\n window.history.replaceState(historyState, '', createHashHref(location))\n\n notifyListeners()\n }\n\n function go(delta: number) {\n window.history.go(delta)\n }\n\n return {\n get action() {\n return action\n },\n get location() {\n return location\n },\n push,\n replace,\n go,\n back() {\n go(-1)\n },\n forward() {\n go(1)\n },\n listen(listener: HistoryListener) {\n listeners.add(listener)\n return () => listeners.delete(listener)\n },\n createHref(to: To) {\n const loc = createLocation(to)\n return createHashHref(loc)\n },\n block(blocker: Blocker) {\n blockers.add(blocker)\n\n if (blockers.size === 1) {\n window.addEventListener('beforeunload', handleBeforeUnload)\n }\n\n return () => {\n blockers.delete(blocker)\n if (blockers.size === 0) {\n window.removeEventListener('beforeunload', handleBeforeUnload)\n }\n }\n },\n }\n}\n\n// ============================================================================\n// Memory History\n// ============================================================================\n\n/**\n * Create a memory history instance that keeps history in memory.\n * Useful for testing and server-side rendering.\n */\nexport function createMemoryHistory(\n options: {\n initialEntries?: string[]\n initialIndex?: number\n } = {},\n): History {\n const { initialEntries = ['/'], initialIndex } = options\n const listeners = new Set<HistoryListener>()\n const blockers = new Set<Blocker>()\n\n // Initialize entries\n const entries: Location[] = initialEntries.map((entry, i) => createLocation(entry, null, `${i}`))\n\n let index = initialIndex ?? entries.length - 1\n let action: HistoryAction = 'POP'\n\n // Clamp index to valid range\n index = Math.max(0, Math.min(index, entries.length - 1))\n\n function notifyListeners() {\n const location = entries[index]!\n for (const listener of listeners) {\n listener({ action, location })\n }\n }\n\n function push(to: To, state?: unknown) {\n const nextLocation = createLocation(to, state)\n const nextAction: HistoryAction = 'PUSH'\n\n // Check blockers\n if (blockers.size > 0) {\n let blocked = false\n const retry = () => push(to, state)\n\n for (const blocker of blockers) {\n blocker({\n action: nextAction,\n location: nextLocation,\n retry,\n })\n blocked = true\n break\n }\n\n if (blocked) return\n }\n\n action = nextAction\n\n // Remove any entries after the current index\n entries.splice(index + 1)\n\n // Add the new entry\n entries.push(nextLocation)\n index = entries.length - 1\n\n notifyListeners()\n }\n\n function replace(to: To, state?: unknown) {\n const nextLocation = createLocation(to, state)\n const nextAction: HistoryAction = 'REPLACE'\n\n // Check blockers\n if (blockers.size > 0) {\n let blocked = false\n const retry = () => replace(to, state)\n\n for (const blocker of blockers) {\n blocker({\n action: nextAction,\n location: nextLocation,\n retry,\n })\n blocked = true\n break\n }\n\n if (blocked) return\n }\n\n action = nextAction\n entries[index] = nextLocation\n\n notifyListeners()\n }\n\n function go(delta: number) {\n const nextIndex = Math.max(0, Math.min(index + delta, entries.length - 1))\n\n if (nextIndex === index) return\n\n const nextLocation = entries[nextIndex]!\n const nextAction: HistoryAction = 'POP'\n\n // Check blockers\n if (blockers.size > 0) {\n let blocked = false\n const retry = () => go(delta)\n\n for (const blocker of blockers) {\n blocker({\n action: nextAction,\n location: nextLocation,\n retry,\n })\n blocked = true\n break\n }\n\n if (blocked) return\n }\n\n action = nextAction\n index = nextIndex\n\n notifyListeners()\n }\n\n return {\n get action() {\n return action\n },\n get location() {\n return entries[index]!\n },\n push,\n replace,\n go,\n back() {\n go(-1)\n },\n forward() {\n go(1)\n },\n listen(listener: HistoryListener) {\n listeners.add(listener)\n return () => listeners.delete(listener)\n },\n createHref(to: To) {\n const loc = typeof to === 'string' ? parseURL(to) : to\n return createURL(loc as Location)\n },\n block(blocker: Blocker) {\n blockers.add(blocker)\n return () => blockers.delete(blocker)\n },\n }\n}\n\n// ============================================================================\n// Static History (for SSR)\n// ============================================================================\n\n/**\n * Create a static history for server-side rendering.\n * This history doesn't support navigation and always returns the initial location.\n */\nexport function createStaticHistory(url: string): History {\n const location = createLocation(url)\n\n return {\n get action(): HistoryAction {\n return 'POP'\n },\n get location() {\n return location\n },\n push() {\n // No-op on server\n console.warn('[fict-router] Cannot push on static history (SSR)')\n },\n replace() {\n // No-op on server\n console.warn('[fict-router] Cannot replace on static history (SSR)')\n },\n go() {\n // No-op on server\n console.warn('[fict-router] Cannot go on static history (SSR)')\n },\n back() {\n // No-op on server\n },\n forward() {\n // No-op on server\n },\n listen() {\n // No-op on server\n return () => {}\n },\n createHref(to: To) {\n const loc = typeof to === 'string' ? parseURL(to) : to\n return createURL(loc as Location)\n },\n block() {\n // No-op on server\n return () => {}\n },\n }\n}\n","/**\n * @fileoverview Scroll restoration utilities for @fictjs/router\n *\n * This module provides scroll position management including:\n * - Saving scroll positions per location key\n * - Restoring scroll on back/forward navigation\n * - Scrolling to top on new navigation\n * - Hash scrolling support (#section)\n */\n\nimport type { Location } from './types'\nimport { isBrowser } from './utils'\n\n// ============================================================================\n// Scroll Position Storage\n// ============================================================================\n\n/** Stored scroll positions keyed by location key */\nconst scrollPositions = new Map<string, { x: number; y: number }>()\n\n/** Maximum number of positions to store to prevent memory leaks */\nconst MAX_STORED_POSITIONS = 100\n\n/**\n * Save the current scroll position for a location\n */\nexport function saveScrollPosition(key: string): void {\n if (!isBrowser()) return\n\n scrollPositions.set(key, {\n x: window.scrollX,\n y: window.scrollY,\n })\n\n // Evict oldest entries if we exceed the limit\n if (scrollPositions.size > MAX_STORED_POSITIONS) {\n const firstKey = scrollPositions.keys().next().value\n if (firstKey) {\n scrollPositions.delete(firstKey)\n }\n }\n}\n\n/**\n * Get the saved scroll position for a location\n */\nexport function getSavedScrollPosition(key: string): { x: number; y: number } | undefined {\n return scrollPositions.get(key)\n}\n\n/**\n * Clear saved scroll position for a location\n */\nexport function clearScrollPosition(key: string): void {\n scrollPositions.delete(key)\n}\n\n/**\n * Clear all saved scroll positions\n */\nexport function clearAllScrollPositions(): void {\n scrollPositions.clear()\n}\n\n// ============================================================================\n// Scroll Actions\n// ============================================================================\n\n/**\n * Scroll to a specific position\n */\nexport function scrollTo(x: number, y: number, behavior: ScrollBehavior = 'auto'): void {\n if (!isBrowser()) return\n\n window.scrollTo({\n left: x,\n top: y,\n behavior,\n })\n}\n\n/**\n * Scroll to top of the page\n */\nexport function scrollToTop(behavior: ScrollBehavior = 'auto'): void {\n scrollTo(0, 0, behavior)\n}\n\n/**\n * Scroll to an element by ID (hash navigation)\n */\nexport function scrollToHash(hash: string, behavior: ScrollBehavior = 'auto'): boolean {\n if (!isBrowser() || !hash) return false\n\n // Remove the leading #\n const id = hash.startsWith('#') ? hash.slice(1) : hash\n if (!id) return false\n\n const element = document.getElementById(id)\n if (element) {\n element.scrollIntoView({ behavior })\n return true\n }\n\n return false\n}\n\n/**\n * Restore scroll position for a location\n * Returns true if position was restored\n */\nexport function restoreScrollPosition(key: string): boolean {\n if (!isBrowser()) return false\n\n const position = scrollPositions.get(key)\n if (position) {\n // Use requestAnimationFrame to ensure DOM has updated\n requestAnimationFrame(() => {\n scrollTo(position.x, position.y)\n })\n return true\n }\n\n return false\n}\n\n// ============================================================================\n// Scroll Restoration Manager\n// ============================================================================\n\nexport interface ScrollRestorationOptions {\n /** Whether scroll restoration is enabled */\n enabled?: boolean\n /** Whether to restore scroll on back/forward navigation */\n restoreOnPop?: boolean\n /** Whether to scroll to top on push navigation */\n scrollToTopOnPush?: boolean\n /** Default scroll behavior */\n behavior?: ScrollBehavior\n}\n\nconst defaultOptions: Required<ScrollRestorationOptions> = {\n enabled: true,\n restoreOnPop: true,\n scrollToTopOnPush: true,\n behavior: 'auto',\n}\n\n/**\n * Create a scroll restoration manager\n */\nexport function createScrollRestoration(options: ScrollRestorationOptions = {}) {\n const config = { ...defaultOptions, ...options }\n\n // Disable browser's native scroll restoration\n if (isBrowser() && 'scrollRestoration' in history) {\n history.scrollRestoration = 'manual'\n }\n\n /**\n * Handle navigation to save/restore scroll\n */\n function handleNavigation(\n from: Location | null,\n to: Location,\n action: 'PUSH' | 'REPLACE' | 'POP',\n ): void {\n if (!config.enabled || !isBrowser()) return\n\n // Save current position before navigating\n if (from?.key) {\n saveScrollPosition(from.key)\n }\n\n // Determine what scroll action to take\n if (action === 'POP' && config.restoreOnPop) {\n // Back/forward navigation - try to restore position\n if (!restoreScrollPosition(to.key)) {\n // No saved position, handle hash or scroll to top\n if (to.hash) {\n requestAnimationFrame(() => {\n if (!scrollToHash(to.hash, config.behavior)) {\n scrollToTop(config.behavior)\n }\n })\n } else {\n scrollToTop(config.behavior)\n }\n }\n } else if ((action === 'PUSH' || action === 'REPLACE') && config.scrollToTopOnPush) {\n // New navigation - handle hash or scroll to top\n requestAnimationFrame(() => {\n if (to.hash) {\n if (!scrollToHash(to.hash, config.behavior)) {\n scrollToTop(config.behavior)\n }\n } else {\n scrollToTop(config.behavior)\n }\n })\n }\n }\n\n /**\n * Reset scroll restoration to browser defaults\n */\n function reset(): void {\n if (isBrowser() && 'scrollRestoration' in history) {\n history.scrollRestoration = 'auto'\n }\n clearAllScrollPositions()\n }\n\n return {\n handleNavigation,\n saveScrollPosition,\n restoreScrollPosition,\n scrollToTop: () => scrollToTop(config.behavior),\n scrollToHash: (hash: string) => scrollToHash(hash, config.behavior),\n reset,\n config,\n }\n}\n\n/**\n * Default scroll restoration instance\n */\nlet defaultScrollRestoration: ReturnType<typeof createScrollRestoration> | null = null\n\n/**\n * Get or create the default scroll restoration instance\n */\nexport function getScrollRestoration(): ReturnType<typeof createScrollRestoration> {\n if (!defaultScrollRestoration) {\n defaultScrollRestoration = createScrollRestoration()\n }\n return defaultScrollRestoration\n}\n\n/**\n * Configure the default scroll restoration\n */\nexport function configureScrollRestoration(options: ScrollRestorationOptions): void {\n defaultScrollRestoration = createScrollRestoration(options)\n}\n","/**\n * @fileoverview Link components for @fictjs/router\n *\n * This module provides Link and NavLink components for declarative navigation.\n * Integrates with Fict's reactive system for active state tracking.\n */\n\nimport { createMemo, type FictNode, type JSX, type StyleProp } from '@fictjs/runtime'\n\nimport { useRouter, useIsActive, useHref, usePendingLocation } from './context'\nimport type { To, NavigateOptions } from './types'\nimport { parseURL, stripBasePath } from './utils'\n\n// CSS Properties type for styles\ntype CSSProperties = StyleProp\n\n// ============================================================================\n// Link Component\n// ============================================================================\n\nexport interface LinkProps extends Omit<JSX.IntrinsicElements['a'], 'href'> {\n /** Navigation target */\n to: To\n /** Replace history entry instead of pushing */\n replace?: boolean\n /** State to pass with navigation */\n state?: unknown\n /** Scroll to top after navigation */\n scroll?: boolean\n /** Relative path resolution mode */\n relative?: 'route' | 'path'\n /** Force full page reload */\n reloadDocument?: boolean\n /** Preload behavior */\n prefetch?: 'none' | 'intent' | 'render'\n /** Prevent navigation (render as text) */\n disabled?: boolean\n /** Custom click handler (called before navigation) */\n onClick?: (event: MouseEvent) => void\n children?: FictNode\n}\n\n/**\n * Link component for navigation\n *\n * @example\n * ```tsx\n * <Link to=\"/about\">About</Link>\n * <Link to=\"/users/123\" replace>View User</Link>\n * <Link to={{ pathname: \"/search\", search: \"?q=test\" }}>Search</Link>\n * ```\n */\nexport function Link(props: LinkProps): FictNode {\n const router = useRouter()\n const href = useHref(() => props.to)\n let preloadTriggered = false\n\n const handleClick = (event: MouseEvent) => {\n // Call custom onClick handler first\n if (props.onClick) {\n props.onClick(event)\n }\n\n // Don't handle if default was prevented\n if (event.defaultPrevented) return\n\n // Don't handle modifier keys (open in new tab, etc.)\n if (event.metaKey || event.altKey || event.ctrlKey || event.shiftKey) return\n\n // Don't handle right-clicks\n if (event.button !== 0) return\n\n // Don't handle if reloadDocument is set\n if (props.reloadDocument) return\n\n // Don't handle if disabled\n if (props.disabled) return\n\n // Don't handle external links\n const target = (event.currentTarget as HTMLAnchorElement).target\n if (target && target !== '_self') return\n\n // Prevent default browser navigation\n event.preventDefault()\n\n // Navigate using the router\n const options: NavigateOptions = {\n replace: props.replace,\n state: props.state,\n scroll: props.scroll,\n relative: props.relative,\n }\n\n router.navigate(props.to, options)\n }\n\n // Preload handler for hover/focus\n const triggerPreload = () => {\n if (preloadTriggered || props.disabled || props.prefetch === 'none') return\n preloadTriggered = true\n\n // Emit a preload event that can be handled by route preloaders\n const hrefValue = href()\n if (typeof window !== 'undefined' && window.dispatchEvent) {\n window.dispatchEvent(\n new CustomEvent('fict-router:preload', {\n detail: { href: hrefValue, to: props.to },\n }),\n )\n }\n }\n\n const handleMouseEnter = (event: MouseEvent) => {\n if (props.prefetch === 'intent' || props.prefetch === undefined) {\n triggerPreload()\n }\n // Call original handler if provided\n const onMouseEnter = (props as any).onMouseEnter\n if (onMouseEnter) onMouseEnter(event)\n }\n\n const handleFocus = (event: FocusEvent) => {\n if (props.prefetch === 'intent' || props.prefetch === undefined) {\n triggerPreload()\n }\n // Call original handler if provided\n const onFocus = (props as any).onFocus\n if (onFocus) onFocus(event)\n }\n\n // Extract link-specific props, pass rest to anchor\n const {\n to: _to,\n replace: _replace,\n state: _state,\n scroll: _scroll,\n relative: _relative,\n reloadDocument: _reloadDocument,\n prefetch,\n disabled,\n onClick: _onClick,\n children,\n ...anchorProps\n } = props\n\n if (disabled) {\n // Render as span when disabled\n return <span {...(anchorProps as any)}>{children}</span>\n }\n\n // Trigger preload immediately if prefetch='render'\n if (prefetch === 'render') {\n triggerPreload()\n }\n\n return (\n <a\n {...anchorProps}\n href={href()}\n onClick={handleClick}\n onMouseEnter={handleMouseEnter}\n onFocus={handleFocus}\n >\n {children}\n </a>\n )\n}\n\n// ============================================================================\n// NavLink Component\n// ============================================================================\n\nexport interface NavLinkRenderProps {\n /** Whether the link is active */\n isActive: boolean\n /** Whether a navigation to this link is pending */\n isPending: boolean\n /** Whether a view transition is in progress */\n isTransitioning: boolean\n}\n\nexport interface NavLinkProps extends Omit<LinkProps, 'className' | 'style' | 'children'> {\n /** Class name - can be a function that receives render props */\n className?: string | ((props: NavLinkRenderProps) => string | undefined)\n /** Style - can be a function that receives render props */\n style?: CSSProperties | ((props: NavLinkRenderProps) => CSSProperties | undefined)\n /** Children - can be a function that receives render props */\n children?: FictNode | ((props: NavLinkRenderProps) => FictNode)\n /** Only match if path is exactly equal (not a prefix) */\n end?: boolean\n /** Case-sensitive matching */\n caseSensitive?: boolean\n /** Custom active class name */\n activeClassName?: string\n /** Custom pending class name */\n pendingClassName?: string\n /** Custom active style */\n activeStyle?: CSSProperties\n /** Custom pending style */\n pendingStyle?: CSSProperties\n /** aria-current value when active */\n 'aria-current'?: 'page' | 'step' | 'location' | 'date' | 'time' | 'true' | 'false'\n}\n\n/**\n * NavLink component for navigation with active state\n *\n * @example\n * ```tsx\n * <NavLink to=\"/about\" activeClassName=\"active\">About</NavLink>\n *\n * <NavLink to=\"/users\" end>\n * {({ isActive }) => (\n * <span className={isActive ? 'active' : ''}>Users</span>\n * )}\n * </NavLink>\n *\n * <NavLink\n * to=\"/dashboard\"\n * className={({ isActive }) => isActive ? 'nav-active' : 'nav-link'}\n * >\n * Dashboard\n * </NavLink>\n * ```\n */\nexport function NavLink(props: NavLinkProps): FictNode {\n const router = useRouter()\n const isActive = useIsActive(() => props.to, { end: props.end })\n const href = useHref(() => props.to)\n const pendingLocation = usePendingLocation()\n\n // Compute isPending by comparing pending location with this link's target\n const computeIsPending = (): boolean => {\n const pending = pendingLocation()\n if (!pending) return false\n\n // Get the resolved path for this link\n const resolvedHref = href()\n const baseToStrip = router.base === '/' ? '' : router.base\n\n // Strip base from pending location to compare\n const pendingPathWithoutBase = stripBasePath(pending.pathname, baseToStrip)\n\n // Parse the resolved href to get pathname\n const parsed = parseURL(resolvedHref)\n const targetPathWithoutBase = stripBasePath(parsed.pathname, baseToStrip)\n\n // Check if the pending navigation is to this link's destination\n if (props.end) {\n return pendingPathWithoutBase === targetPathWithoutBase\n }\n\n return (\n pendingPathWithoutBase === targetPathWithoutBase ||\n pendingPathWithoutBase.startsWith(targetPathWithoutBase + '/')\n )\n }\n\n // Compute render props\n const getRenderProps = (): NavLinkRenderProps => ({\n isActive: isActive(),\n isPending: computeIsPending(),\n isTransitioning: router.isRouting(),\n })\n\n // Compute className\n const computedClassName = createMemo(() => {\n const renderProps = getRenderProps()\n const classes: string[] = []\n\n // Base className\n if (typeof props.className === 'function') {\n const result = props.className(renderProps)\n if (result) classes.push(result)\n } else if (props.className) {\n classes.push(props.className)\n }\n\n // Active className\n if (renderProps.isActive && props.activeClassName) {\n classes.push(props.activeClassName)\n }\n\n // Pending className\n if (renderProps.isPending && props.pendingClassName) {\n classes.push(props.pendingClassName)\n }\n\n return classes.join(' ') || undefined\n })\n\n // Compute style\n const computedStyle = createMemo(() => {\n const renderProps = getRenderProps()\n const style: CSSProperties = {}\n\n // Base style\n if (typeof props.style === 'function') {\n const result = props.style(renderProps)\n if (result) Object.assign(style, result)\n } else if (props.style) {\n Object.assign(style, props.style)\n }\n\n // Active style\n if (renderProps.isActive && props.activeStyle) {\n Object.assign(style, props.activeStyle)\n }\n\n // Pending style\n if (renderProps.isPending && props.pendingStyle) {\n Object.assign(style, props.pendingStyle)\n }\n\n return Object.keys(style).length > 0 ? style : undefined\n })\n\n // Compute children\n const computedChildren = createMemo(() => {\n const renderProps = getRenderProps()\n\n if (typeof props.children === 'function') {\n return props.children(renderProps)\n }\n\n return props.children\n })\n\n // Compute aria-current\n const ariaCurrent = createMemo(() => {\n const renderProps = getRenderProps()\n if (!renderProps.isActive) return undefined\n return props['aria-current'] || 'page'\n })\n\n const handleClick = (event: MouseEvent) => {\n // Call custom onClick handler first\n if (props.onClick) {\n props.onClick(event)\n }\n\n // Don't handle if default was prevented\n if (event.defaultPrevented) return\n\n // Don't handle modifier keys\n if (event.metaKey || event.altKey || event.ctrlKey || event.shiftKey) return\n\n // Don't handle right-clicks\n if (event.button !== 0) return\n\n // Don't handle if reloadDocument is set\n if (props.reloadDocument) return\n\n // Don't handle if disabled\n if (props.disabled) return\n\n // Don't handle external links\n const target = (event.currentTarget as HTMLAnchorElement).target\n if (target && target !== '_self') return\n\n // Prevent default browser navigation\n event.preventDefault()\n\n // Navigate using the router\n router.navigate(props.to, {\n replace: props.replace,\n state: props.state,\n scroll: props.scroll,\n relative: props.relative,\n })\n }\n\n // Extract NavLink-specific props\n const {\n to: _to,\n replace: _replace,\n state: _state,\n scroll: _scroll,\n relative: _relative,\n reloadDocument: _reloadDocument,\n prefetch: _prefetch,\n disabled,\n onClick: _onClick,\n children: _children,\n className: _className,\n style: _style,\n end: _end,\n caseSensitive: _caseSensitive,\n activeClassName: _activeClassName,\n pendingClassName: _pendingClassName,\n activeStyle: _activeStyle,\n pendingStyle: _pendingStyle,\n 'aria-current': _ariaCurrent,\n ...anchorProps\n } = props\n\n if (disabled) {\n return (\n <span {...(anchorProps as any)} className={computedClassName()} style={computedStyle()}>\n {computedChildren()}\n </span>\n )\n }\n\n const finalClassName = computedClassName()\n const finalStyle = computedStyle()\n const finalAriaCurrent = ariaCurrent()\n\n return (\n <a\n {...anchorProps}\n href={href()}\n {...(finalClassName !== undefined ? { className: finalClassName } : {})}\n {...(finalStyle !== undefined ? { style: finalStyle } : {})}\n {...(finalAriaCurrent !== undefined ? { 'aria-current': finalAriaCurrent } : {})}\n onClick={handleClick}\n >\n {computedChildren()}\n </a>\n )\n}\n\n// ============================================================================\n// Form Component (for actions)\n// ============================================================================\n\nexport interface FormProps extends Omit<JSX.IntrinsicElements['form'], 'action' | 'method'> {\n /** Form action URL */\n action?: string\n /** HTTP method */\n method?: 'get' | 'post' | 'put' | 'patch' | 'delete'\n /** Replace history entry */\n replace?: boolean\n /** Relative path resolution */\n relative?: 'route' | 'path'\n /** Prevent navigation */\n preventScrollReset?: boolean\n /** Navigate on submit */\n navigate?: boolean\n /** Fetch mode */\n fetcherKey?: string\n children?: FictNode\n onSubmit?: (event: SubmitEvent) => void\n}\n\n/**\n * Form component for action submissions\n *\n * @example\n * ```tsx\n * <Form action=\"/api/submit\" method=\"post\">\n * <input name=\"email\" type=\"email\" />\n * <button type=\"submit\">Submit</button>\n * </Form>\n * ```\n */\nexport function Form(props: FormProps): FictNode {\n const router = useRouter()\n\n const handleSubmit = (event: SubmitEvent) => {\n // Call custom onSubmit\n if (props.onSubmit) {\n props.onSubmit(event)\n }\n\n // Don't handle if prevented\n if (event.defaultPrevented) return\n\n const form = event.currentTarget as HTMLFormElement\n\n // Don't handle if form has a target that opens in a new window/frame\n const target = form.target\n if (target && target !== '_self') return\n\n // Prevent default form submission\n event.preventDefault()\n\n const formData = new FormData(form)\n const method = props.method?.toUpperCase() || 'GET'\n\n const actionUrl = props.action || router.location().pathname\n\n if (method === 'GET') {\n // For GET, navigate with search params\n const searchParams = new URLSearchParams()\n formData.forEach((value, key) => {\n if (typeof value === 'string') {\n searchParams.append(key, value)\n }\n })\n\n router.navigate(\n {\n pathname: actionUrl,\n search: '?' + searchParams.toString(),\n },\n { replace: props.replace },\n )\n } else {\n // For POST/PUT/PATCH/DELETE, submit via fetch\n submitFormAction(form, actionUrl, method, formData, {\n navigate: props.navigate !== false,\n replace: props.replace ?? false,\n router,\n })\n }\n }\n\n /**\n * Submit form data via fetch for non-GET methods\n */\n async function submitFormAction(\n formElement: HTMLFormElement,\n url: string,\n method: string,\n formData: FormData,\n options: {\n navigate: boolean\n replace: boolean\n router: typeof router\n },\n ) {\n try {\n const response = await fetch(url, {\n method,\n body: formData,\n headers: {\n // Let the browser set Content-Type for FormData (includes boundary)\n Accept: 'application/json',\n },\n })\n\n if (!response.ok) {\n throw new Error(`HTTP ${response.status}: ${response.statusText}`)\n }\n\n // Try to parse JSON response\n const contentType = response.headers.get('Content-Type')\n let data: unknown = null\n if (contentType?.includes('application/json')) {\n data = await response.json()\n }\n\n // If navigate is enabled and response includes a redirect location\n const redirectUrl = response.headers.get('X-Redirect') || response.headers.get('Location')\n if (options.navigate && redirectUrl) {\n options.router.navigate(redirectUrl, { replace: options.replace })\n }\n\n // Emit a custom event for the form submission result on the actual form element\n formElement.dispatchEvent(\n new CustomEvent('formsubmit', {\n bubbles: true,\n detail: { data, response },\n }),\n )\n\n return { data, response }\n } catch (error) {\n // Emit error event on the actual form element\n formElement.dispatchEvent(\n new CustomEvent('formerror', {\n bubbles: true,\n detail: { error },\n }),\n )\n\n console.error('[fict-router] Form submission failed:', error)\n throw error\n }\n }\n\n const {\n action,\n method,\n replace: _replace,\n relative: _relative,\n preventScrollReset: _preventScrollReset,\n navigate: _navigate,\n fetcherKey: _fetcherKey,\n children,\n onSubmit: _onSubmit,\n ...formProps\n } = props\n\n // Only use standard form methods (get, post) for the HTML attribute\n // Other methods (put, patch, delete) are handled via fetch in handleSubmit\n const htmlMethod =\n method && ['get', 'post'].includes(method) ? (method as 'get' | 'post') : undefined\n\n return (\n <form\n {...formProps}\n {...(action !== undefined ? { action } : {})}\n {...(htmlMethod !== undefined ? { method: htmlMethod } : {})}\n onSubmit={handleSubmit}\n >\n {children}\n </form>\n )\n}\n","/**\n * @fileoverview Data loading utilities for @fictjs/router\n *\n * This module provides utilities for loading data in routes,\n * including query caching, actions, and preloading.\n */\n\nimport { createEffect, batch } from '@fictjs/runtime'\nimport { createSignal } from '@fictjs/runtime/advanced'\n\nimport type {\n QueryFunction,\n QueryCacheEntry,\n ActionFunction,\n Action,\n Submission,\n NavigationIntent,\n Params,\n} from './types'\nimport { hashParams } from './utils'\n\n// ============================================================================\n// Query Cache\n// ============================================================================\n\n/** Cache duration in milliseconds (default: 3 minutes) */\nconst CACHE_DURATION = 3 * 60 * 1000\n\n/** Preload cache duration in milliseconds (default: 5 seconds) */\nconst PRELOAD_CACHE_DURATION = 5 * 1000\n\n/** Maximum cache size to prevent memory spikes */\nconst MAX_CACHE_SIZE = 500\n\n/** Cleanup interval when cache is small (60 seconds) */\nconst NORMAL_CLEANUP_INTERVAL = 60 * 1000\n\n/** Cleanup interval when cache is large (10 seconds) */\nconst FAST_CLEANUP_INTERVAL = 10 * 1000\n\n/** Global query cache */\nconst queryCache = new Map<string, QueryCacheEntry<unknown>>()\n\n/** Cache cleanup timer */\nlet cacheCleanupTimer: ReturnType<typeof setInterval> | undefined\n\n/** Current cleanup interval */\nlet currentCleanupInterval = NORMAL_CLEANUP_INTERVAL\n\n/**\n * Evict oldest entries when cache exceeds max size\n */\nfunction evictOldestEntries() {\n if (queryCache.size <= MAX_CACHE_SIZE) return\n\n // Sort entries by timestamp (oldest first)\n const entries = Array.from(queryCache.entries()).sort((a, b) => a[1].timestamp - b[1].timestamp)\n\n // Remove oldest entries until we're under the limit\n const toRemove = queryCache.size - MAX_CACHE_SIZE\n for (let i = 0; i < toRemove && i < entries.length; i++) {\n queryCache.delete(entries[i]![0])\n }\n}\n\n/**\n * Run cache cleanup\n */\nfunction runCacheCleanup() {\n const now = Date.now()\n\n for (const [key, entry] of queryCache) {\n const maxAge = entry.intent === 'preload' ? PRELOAD_CACHE_DURATION : CACHE_DURATION\n\n if (now - entry.timestamp > maxAge) {\n queryCache.delete(key)\n }\n }\n\n // Adjust cleanup interval based on cache size\n const newInterval =\n queryCache.size > MAX_CACHE_SIZE / 2 ? FAST_CLEANUP_INTERVAL : NORMAL_CLEANUP_INTERVAL\n\n if (newInterval !== currentCleanupInterval) {\n currentCleanupInterval = newInterval\n // Restart with new interval\n stopCacheCleanup()\n startCacheCleanup()\n }\n}\n\n/**\n * Start the cache cleanup interval\n */\nfunction startCacheCleanup() {\n if (cacheCleanupTimer) return\n\n cacheCleanupTimer = setInterval(runCacheCleanup, currentCleanupInterval)\n}\n\n/**\n * Stop the cache cleanup interval\n */\nfunction stopCacheCleanup() {\n if (cacheCleanupTimer) {\n clearInterval(cacheCleanupTimer)\n cacheCleanupTimer = undefined\n }\n}\n\n// ============================================================================\n// Query Function\n// ============================================================================\n\n/**\n * Create a cached query function\n *\n * @example\n * ```tsx\n * const getUser = query(\n * async (id: string) => {\n * const response = await fetch(`/api/users/${id}`)\n * return response.json()\n * },\n * 'getUser'\n * )\n *\n * // In a component\n * function UserProfile({ id }) {\n * const user = getUser(id)\n * return <div>{user()?.name}</div>\n * }\n * ```\n */\nexport function query<T, Args extends unknown[]>(\n fn: QueryFunction<T, Args>,\n name: string,\n): (...args: Args) => () => T | undefined {\n startCacheCleanup()\n\n return (...args: Args) => {\n const cacheKey = `${name}:${hashParams(args as unknown as Record<string, unknown>)}`\n\n // Check cache\n const cached = queryCache.get(cacheKey) as QueryCacheEntry<T> | undefined\n if (cached && cached.result !== undefined) {\n // Check if cache is still valid\n const maxAge = cached.intent === 'preload' ? PRELOAD_CACHE_DURATION : CACHE_DURATION\n\n if (Date.now() - cached.timestamp < maxAge) {\n return () => cached.result\n }\n }\n\n // Create reactive signal for the result\n const resultSignal = createSignal<T | undefined>(cached?.result)\n const errorSignal = createSignal<unknown>(undefined)\n const loadingSignal = createSignal<boolean>(true)\n\n // Fetch the data\n const promise = Promise.resolve(fn(...args))\n .then(result => {\n // Update cache\n const entry: QueryCacheEntry<T> = {\n timestamp: Date.now(),\n promise,\n result,\n intent: 'navigate',\n }\n queryCache.set(cacheKey, entry)\n evictOldestEntries()\n\n // Update signals\n batch(() => {\n resultSignal(result)\n loadingSignal(false)\n })\n\n return result\n })\n .catch(error => {\n batch(() => {\n errorSignal(error)\n loadingSignal(false)\n })\n throw error\n })\n\n // Store promise in cache immediately for deduplication\n if (!cached) {\n queryCache.set(cacheKey, {\n timestamp: Date.now(),\n promise: promise as Promise<unknown>,\n intent: 'navigate',\n })\n }\n\n return () => resultSignal()\n }\n}\n\n/**\n * Invalidate cached queries by key pattern\n */\nexport function revalidate(keys?: string | string[] | RegExp): void {\n if (!keys) {\n // Invalidate all\n queryCache.clear()\n return\n }\n\n if (typeof keys === 'string') {\n // Single key or prefix\n for (const cacheKey of queryCache.keys()) {\n if (cacheKey.startsWith(keys)) {\n queryCache.delete(cacheKey)\n }\n }\n return\n }\n\n if (Array.isArray(keys)) {\n // Multiple keys\n for (const key of keys) {\n for (const cacheKey of queryCache.keys()) {\n if (cacheKey.startsWith(key)) {\n queryCache.delete(cacheKey)\n }\n }\n }\n return\n }\n\n if (keys instanceof RegExp) {\n // Regex pattern\n for (const cacheKey of queryCache.keys()) {\n if (keys.test(cacheKey)) {\n queryCache.delete(cacheKey)\n }\n }\n }\n}\n\n// ============================================================================\n// Action Function\n// ============================================================================\n\n/** Global action registry */\nconst actionRegistry = new Map<string, ActionFunction<unknown>>()\n\n/** Submission counter for unique keys */\nlet submissionCounter = 0\n\n/**\n * Create an action for form submissions\n *\n * @example\n * ```tsx\n * const createUser = action(\n * async (formData, { params }) => {\n * const response = await fetch('/api/users', {\n * method: 'POST',\n * body: formData,\n * })\n * return response.json()\n * },\n * 'createUser'\n * )\n *\n * // In a component\n * <Form action={createUser}>\n * <input name=\"name\" />\n * <button type=\"submit\">Create</button>\n * </Form>\n * ```\n */\nexport function action<T>(fn: ActionFunction<T>, name?: string): Action<T> {\n const actionName = name || `action-${++submissionCounter}`\n const actionUrl = `/_action/${actionName}`\n\n // Register the action\n actionRegistry.set(actionUrl, fn as ActionFunction<unknown>)\n\n return {\n url: actionUrl,\n name: actionName,\n submit: async (formData: FormData): Promise<T> => {\n // Create a mock request with a base URL for Node.js/jsdom compatibility\n const baseUrl =\n typeof window !== 'undefined' && window.location\n ? window.location.origin\n : 'http://localhost'\n const request = new Request(new URL(actionUrl, baseUrl).href, {\n method: 'POST',\n body: formData,\n })\n\n return fn(formData, { params: {}, request }) as Promise<T>\n },\n }\n}\n\n/**\n * Get a registered action by URL\n */\nexport function getAction(url: string): ActionFunction<unknown> | undefined {\n return actionRegistry.get(url)\n}\n\n// ============================================================================\n// Submission Tracking\n// ============================================================================\n\n/** Active submissions */\nconst activeSubmissions = createSignal<Map<string, Submission<unknown>>>(new Map())\n\n/**\n * Use submission state for an action\n */\nexport function useSubmission<T>(actionOrUrl: Action<T> | string): () => Submission<T> | undefined {\n const url = typeof actionOrUrl === 'string' ? actionOrUrl : actionOrUrl.url\n\n return () => {\n const submissions = activeSubmissions()\n return submissions.get(url) as Submission<T> | undefined\n }\n}\n\n/**\n * Use all active submissions\n */\nexport function useSubmissions(): () => Submission<unknown>[] {\n return () => Array.from(activeSubmissions().values())\n}\n\n/**\n * Submit an action and track the submission\n */\nexport async function submitAction<T>(\n action: Action<T>,\n formData: FormData,\n params: Params = {},\n): Promise<T> {\n const key = `submission-${++submissionCounter}`\n\n // Create submission object\n const submission: Submission<T> = {\n key,\n formData,\n state: 'submitting',\n clear: () => {\n const submissions = new Map(activeSubmissions())\n submissions.delete(action.url)\n activeSubmissions(submissions)\n },\n retry: () => {\n submitAction(action, formData, params)\n },\n }\n\n // Add to active submissions\n const submissions = new Map(activeSubmissions())\n submissions.set(action.url, submission as Submission<unknown>)\n activeSubmissions(submissions)\n\n try {\n // Execute the action\n const result = await action.submit(formData)\n\n // Update submission with result\n submission.result = result\n submission.state = 'idle'\n\n // Update active submissions\n const updatedSubmissions = new Map(activeSubmissions())\n updatedSubmissions.set(action.url, submission as Submission<unknown>)\n activeSubmissions(updatedSubmissions)\n\n return result\n } catch (error) {\n // Update submission with error\n submission.error = error\n submission.state = 'idle'\n\n // Update active submissions\n const updatedSubmissions = new Map(activeSubmissions())\n updatedSubmissions.set(action.url, submission as Submission<unknown>)\n activeSubmissions(updatedSubmissions)\n\n throw error\n }\n}\n\n// ============================================================================\n// Preloading\n// ============================================================================\n\n/**\n * Preload a query for faster navigation\n */\nexport function preloadQuery<T, Args extends unknown[]>(\n queryFn: (...args: Args) => () => T | undefined,\n ...args: Args\n): void {\n // The query function handles caching internally\n queryFn(...args)\n}\n\n/**\n * Create a preload function for a route\n */\nexport function createPreload<T>(\n fn: (args: { params: Params; intent: NavigationIntent }) => T | Promise<T>,\n): (args: { params: Params; intent: NavigationIntent }) => Promise<T> {\n return async args => {\n return fn(args)\n }\n}\n\n// ============================================================================\n// Resource (async data with Suspense support)\n// ============================================================================\n\n/**\n * Resource state\n */\nexport interface Resource<T> {\n /** Get current data (undefined during loading or on error) */\n (): T | undefined\n /** Whether the resource is currently loading */\n loading: () => boolean\n /** Error if the fetch failed, undefined otherwise */\n error: () => unknown\n /** Latest successfully loaded value (persists during reloads) */\n latest: () => T | undefined\n /** Trigger a refetch, returns the result or undefined on error */\n refetch: () => Promise<T | undefined>\n}\n\n/**\n * Create a resource for async data loading\n * Integrates with Suspense for loading states\n *\n * @example\n * ```tsx\n * const userResource = createResource(\n * () => userId,\n * async (id) => fetch(`/api/users/${id}`).then(r => r.json())\n * )\n *\n * function UserProfile() {\n * const user = userResource()\n * return <div>{user?.name}</div>\n * }\n * ```\n */\nexport function createResource<T, S = unknown>(\n source: () => S,\n fetcher: (source: S) => T | Promise<T>,\n): Resource<T> {\n const dataSignal = createSignal<T | undefined>(undefined)\n const loadingSignal = createSignal<boolean>(true)\n const errorSignal = createSignal<unknown>(undefined)\n const latestSignal = createSignal<T | undefined>(undefined)\n\n let currentSource: S\n let fetchId = 0 // Used to prevent race conditions\n\n /**\n * Internal fetch function with race condition protection\n * Returns T on success, undefined on error (error is stored in errorSignal)\n */\n const doFetch = async (s: S, id: number): Promise<T | undefined> => {\n loadingSignal(true)\n errorSignal(undefined)\n\n try {\n const result = await fetcher(s)\n\n // Only apply results if this fetch is still current\n // (prevents race conditions when source changes rapidly)\n if (id === fetchId) {\n batch(() => {\n dataSignal(result)\n latestSignal(result)\n loadingSignal(false)\n })\n return result\n }\n\n // This fetch was superseded, return undefined\n return undefined\n } catch (err) {\n // Only apply error if this fetch is still current\n if (id === fetchId) {\n batch(() => {\n errorSignal(err)\n loadingSignal(false)\n })\n }\n\n // Return undefined on error - error is accessible via resource.error()\n return undefined\n }\n }\n\n // Initial fetch and tracking\n createEffect(() => {\n const s = source()\n\n // Only refetch if source changed\n if (s !== currentSource) {\n currentSource = s\n // Increment fetchId to invalidate any pending fetches\n const currentFetchId = ++fetchId\n doFetch(s, currentFetchId)\n }\n })\n\n const resource = (() => dataSignal()) as Resource<T>\n\n resource.loading = () => loadingSignal()\n resource.error = () => errorSignal()\n resource.latest = () => latestSignal()\n resource.refetch = () => {\n const currentFetchId = ++fetchId\n return doFetch(currentSource, currentFetchId)\n }\n\n return resource\n}\n\n// ============================================================================\n// Cleanup\n// ============================================================================\n\n/**\n * Clean up router data utilities\n */\nexport function cleanupDataUtilities(): void {\n stopCacheCleanup()\n queryCache.clear()\n actionRegistry.clear()\n activeSubmissions(new Map())\n}\n","/**\n * @fileoverview Lazy loading utilities for @fictjs/router\n *\n * This module provides code splitting support via lazy loading of route components.\n * Works with Fict's Suspense for loading states.\n */\n\nimport { type FictNode, type Component } from '@fictjs/runtime'\nimport { createSignal } from '@fictjs/runtime/advanced'\n\nimport type { RouteComponentProps, RouteDefinition } from './types'\n\n// ============================================================================\n// Lazy Component\n// ============================================================================\n\n/**\n * State for lazy-loaded component\n */\ninterface LazyState<T> {\n component: T | null\n error: unknown\n loading: boolean\n}\n\n/**\n * Create a lazy-loaded component\n *\n * @example\n * ```tsx\n * // Create a lazy component\n * const LazyUserProfile = lazy(() => import('./pages/UserProfile'))\n *\n * // Use in routes\n * <Route path=\"/users/:id\" component={LazyUserProfile} />\n *\n * // Or with Suspense fallback\n * <Suspense fallback={<Loading />}>\n * <LazyUserProfile />\n * </Suspense>\n * ```\n */\nexport function lazy<T extends Component<any>>(\n loader: () => Promise<{ default: T } | T>,\n): Component<any> {\n let cachedComponent: T | null = null\n let loadPromise: Promise<T> | null = null\n\n // Create a wrapper component that handles lazy loading\n const LazyComponent: Component<any> = props => {\n const state = createSignal<LazyState<T>>({\n component: cachedComponent,\n error: null,\n loading: !cachedComponent,\n })\n\n // If already cached, render immediately\n if (cachedComponent) {\n const CachedComponent = cachedComponent\n return <CachedComponent {...props} />\n }\n\n // Start loading if not already in progress\n if (!loadPromise) {\n loadPromise = loader().then(module => {\n // Handle both { default: Component } and direct Component exports\n const component = 'default' in module ? module.default : module\n cachedComponent = component\n return component\n })\n }\n\n // Load the component\n loadPromise\n .then(component => {\n state({ component, error: null, loading: false })\n })\n .catch(error => {\n state({ component: null, error, loading: false })\n })\n\n // Render based on state\n const currentState = state()\n\n if (currentState.error) {\n throw currentState.error\n }\n\n if (currentState.loading || !currentState.component) {\n // Return null while loading - Suspense will handle the fallback\n // For this to work properly with Suspense, we need to throw a promise\n throw loadPromise\n }\n\n const LoadedComponent = currentState.component\n return <LoadedComponent {...props} />\n }\n\n // Mark as lazy for identification\n ;(LazyComponent as any).__lazy = true\n ;(LazyComponent as any).__preload = () => {\n if (!loadPromise) {\n loadPromise = loader().then(module => {\n const component = 'default' in module ? module.default : module\n cachedComponent = component\n return component\n })\n }\n return loadPromise\n }\n\n return LazyComponent\n}\n\n/**\n * Preload a lazy component\n * Useful for preloading on hover/focus\n */\nexport function preloadLazy(component: Component<any>): Promise<void> {\n const lazyComp = component as any\n if (lazyComp.__lazy && lazyComp.__preload) {\n return lazyComp.__preload()\n }\n return Promise.resolve()\n}\n\n/**\n * Check if a component is a lazy component\n */\nexport function isLazyComponent(component: unknown): boolean {\n return !!(component && typeof component === 'function' && (component as any).__lazy)\n}\n\n// ============================================================================\n// Lazy Route\n// ============================================================================\n\n/**\n * Create a lazy route definition\n *\n * @example\n * ```tsx\n * const routes = [\n * lazyRoute({\n * path: '/users/:id',\n * component: () => import('./pages/UserProfile'),\n * }),\n * lazyRoute({\n * path: '/settings',\n * component: () => import('./pages/Settings'),\n * loadingElement: <Loading />,\n * errorElement: <Error />,\n * }),\n * ]\n * ```\n */\nexport function lazyRoute<P extends string = string>(config: {\n path?: string\n component: () => Promise<\n { default: Component<RouteComponentProps<P>> } | Component<RouteComponentProps<P>>\n >\n loadingElement?: FictNode\n errorElement?: FictNode\n preload?: RouteDefinition<P>['preload']\n children?: RouteDefinition[]\n index?: boolean\n key?: string\n}): RouteDefinition<P> {\n const LazyComponent = lazy(config.component)\n\n // Build the route definition, only including defined properties\n const routeDef: RouteDefinition<P> = {\n component: LazyComponent as Component<RouteComponentProps<P>>,\n }\n\n if (config.path !== undefined) routeDef.path = config.path\n if (config.loadingElement !== undefined) routeDef.loadingElement = config.loadingElement\n if (config.errorElement !== undefined) routeDef.errorElement = config.errorElement\n if (config.preload !== undefined) routeDef.preload = config.preload\n if (config.children !== undefined) routeDef.children = config.children\n if (config.index !== undefined) routeDef.index = config.index\n if (config.key !== undefined) routeDef.key = config.key\n\n return routeDef\n}\n\n// ============================================================================\n// Lazy Loading Helpers\n// ============================================================================\n\n/**\n * Create multiple lazy routes from a glob import pattern\n * Useful for file-system based routing\n *\n * @example\n * ```tsx\n * // In a Vite project\n * const pages = import.meta.glob('./pages/*.tsx')\n *\n * const routes = createLazyRoutes(pages, {\n * // Map file path to route path\n * pathTransform: (filePath) => {\n * // ./pages/UserProfile.tsx -> /user-profile\n * return filePath\n * .replace('./pages/', '/')\n * .replace('.tsx', '')\n * .toLowerCase()\n * .replace(/([A-Z])/g, '-$1')\n * },\n * })\n * ```\n */\nexport function createLazyRoutes(\n modules: Record<string, () => Promise<{ default: Component<any> }>>,\n options: {\n pathTransform?: (filePath: string) => string\n loadingElement?: FictNode\n errorElement?: FictNode\n } = {},\n): RouteDefinition[] {\n const routes: RouteDefinition[] = []\n\n for (const [filePath, loader] of Object.entries(modules)) {\n const path = options.pathTransform\n ? options.pathTransform(filePath)\n : filePath\n .replace(/^\\.\\/pages/, '')\n .replace(/\\.(tsx?|jsx?)$/, '')\n .toLowerCase()\n\n routes.push(\n lazyRoute({\n path,\n component: loader,\n loadingElement: options.loadingElement,\n errorElement: options.errorElement,\n }),\n )\n }\n\n return routes\n}\n"],"mappings":"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","names":["action","location","action","Fragment","history","action","createMemo","jsx","createMemo","method","action","createEffect","batch","createSignal","createSignal","batch","action","submissions","createEffect","createSignal","jsx"]}
package/package.json CHANGED
@@ -1,11 +1,20 @@
1
1
  {
2
2
  "name": "@fictjs/router",
3
- "version": "0.2.2",
4
- "description": "Router for Fict",
3
+ "version": "0.3.0",
4
+ "description": "Reactive router for Fict applications",
5
5
  "publishConfig": {
6
6
  "access": "public",
7
7
  "provenance": true
8
8
  },
9
+ "keywords": [
10
+ "fict",
11
+ "router",
12
+ "routing",
13
+ "spa",
14
+ "reactive"
15
+ ],
16
+ "author": "Michael Lin",
17
+ "license": "MIT",
9
18
  "repository": {
10
19
  "type": "git",
11
20
  "url": "https://github.com/fictjs/fict.git",
@@ -22,19 +31,36 @@
22
31
  "require": "./dist/index.cjs"
23
32
  }
24
33
  },
34
+ "sideEffects": false,
25
35
  "files": [
26
- "dist"
36
+ "dist",
37
+ "src"
27
38
  ],
39
+ "dependencies": {
40
+ "@fictjs/runtime": "0.3.0"
41
+ },
28
42
  "devDependencies": {
29
- "tsup": "^8.5.1"
43
+ "jsdom": "^27.4.0",
44
+ "tsup": "^8.5.1",
45
+ "@fictjs/vite-plugin": "0.3.0",
46
+ "fict": "0.3.0"
47
+ },
48
+ "peerDependencies": {
49
+ "fict": "0.3.0"
50
+ },
51
+ "peerDependenciesMeta": {
52
+ "fict": {
53
+ "optional": true
54
+ }
30
55
  },
31
56
  "scripts": {
32
- "build": "tsup src/index.ts --format cjs,esm --dts",
33
- "dev": "tsup src/index.ts --format cjs,esm --dts --watch",
57
+ "build": "tsup",
58
+ "dev": "tsup --watch",
34
59
  "test": "vitest run",
60
+ "test:watch": "vitest",
35
61
  "test:coverage": "vitest run --coverage",
36
62
  "lint": "eslint src",
37
63
  "typecheck": "tsc --noEmit",
38
- "clean": "rm -rf dist"
64
+ "clean": "rm -rf dist coverage"
39
65
  }
40
66
  }