{"version":3,"file":"AppRouter.cjs","names":[],"sources":["../../src/router/AppRouter.tsx"],"sourcesContent":["import { Suspense, useMemo } from \"react\";\nimport type { ComponentType, ReactNode } from \"react\";\nimport { BrowserRouter, HashRouter, MemoryRouter, Routes, Route } from \"react-router\";\nimport { lazyWithRetry } from \"../auth/lazy-with-retry\";\nimport { RouteGuard } from \"./RouteGuard\";\nimport type { RouteGuardResult, RouterKind, TempestRouteObject } from \"./types\";\n\n/**\n * Identity helper that types a declarative route tree. Use it so editors give\n * autocomplete and type-checking on every node; the array is returned as-is.\n *\n * @example\n * export const routes = defineRoutes([\n *     { path: \"/\", element: <Layout />, children: [\n *         { index: true, lazy: () => import(\"./pages/Home\") },\n *         { path: \"settings\", lazy: () => import(\"./pages/Settings\"),\n *           guard: () => useAuth.getState().isAuthenticated, redirectTo: \"/login\" },\n *     ] },\n * ]);\n */\nexport function defineRoutes(routes: TempestRouteObject[]): TempestRouteObject[] {\n    return routes;\n}\n\nconst lazyCache = new WeakMap<TempestRouteObject, ComponentType<unknown>>();\n\nfunction resolveLazy(route: TempestRouteObject): ComponentType<unknown> {\n    let comp = lazyCache.get(route);\n    if (!comp) {\n        comp = lazyWithRetry(route.lazy!);\n        lazyCache.set(route, comp);\n    }\n    return comp;\n}\n\n/**\n * Runs one route's `guard` and renders the outcome.\n *\n * The guard is called during this component's render, which is what lets it\n * call hooks — `useCan` returning `{ allowed, isLoading }` maps straight onto\n * the three outcomes. See {@link guardKey} for why each guarded route gets its\n * own instance.\n */\nfunction GuardedElement({\n    guard,\n    guardFallback,\n    redirectTo,\n    children,\n}: {\n    guard: boolean | (() => RouteGuardResult);\n    guardFallback?: ReactNode;\n    redirectTo?: string;\n    children: ReactNode;\n}) {\n    const allowed = typeof guard === \"function\" ? guard() : guard;\n    return (\n        <RouteGuard when={allowed} fallback={guardFallback} redirectTo={redirectTo}>\n            {children}\n        </RouteGuard>\n    );\n}\n\nconst guardKeys = new WeakMap<TempestRouteObject, string>();\nlet guardKeySeq = 0;\n\n/**\n * A key unique to each route object, so React remounts `GuardedElement` when\n * the matched route changes instead of reusing the instance.\n *\n * Load-bearing once guards may call hooks. React Router renders the matched\n * route's element at the same position in the tree, so without a differing key\n * React reconciles two different routes onto the same `GuardedElement`\n * instance and the previous route's guard state survives into the next one's —\n * a guard reading `useState(\"from-a\")` still reads `\"from-a\"` after navigating\n * to a route whose guard initialises it to `\"from-b\"`. Path is not enough on\n * its own: two routes in different branches can share one.\n */\nfunction guardKey(route: TempestRouteObject): string {\n    let key = guardKeys.get(route);\n    if (!key) {\n        key = `guard-${++guardKeySeq}`;\n        guardKeys.set(route, key);\n    }\n    return key;\n}\n\nfunction toRouteElements(routes: TempestRouteObject[], fallback: ReactNode): ReactNode {\n    return routes.map((route, i) => {\n        let element: ReactNode = route.element;\n        if (route.lazy) {\n            const LazyComponent = resolveLazy(route);\n            element = <LazyComponent />;\n        }\n        if (route.guard !== undefined) {\n            element = (\n                <GuardedElement\n                    key={guardKey(route)}\n                    guard={route.guard}\n                    guardFallback={route.guardFallback ?? fallback}\n                    redirectTo={route.redirectTo}\n                >\n                    {element}\n                </GuardedElement>\n            );\n        }\n\n        if (route.index) {\n            return <Route key={`index-${i}`} index element={element} />;\n        }\n\n        return (\n            <Route\n                key={route.path ?? `route-${i}`}\n                path={route.path}\n                element={element}\n                caseSensitive={route.caseSensitive}\n            >\n                {route.children ? toRouteElements(route.children, fallback) : null}\n            </Route>\n        );\n    });\n}\n\nconst ROUTERS: Record<RouterKind, typeof BrowserRouter> = {\n    browser: BrowserRouter,\n    hash: HashRouter,\n    memory: MemoryRouter,\n};\n\nexport interface AppRouterProps {\n    /** Declarative route tree, ideally built with {@link defineRoutes}. */\n    routes: TempestRouteObject[];\n    /** History strategy (default: `\"browser\"`). */\n    router?: RouterKind;\n    /** App-wide base path forwarded to the underlying router. */\n    basename?: string;\n    /** Initial URLs for the `\"memory\"` router (tests / non-DOM hosts). */\n    initialEntries?: string[];\n    /**\n     * Suspense fallback shown while a `lazy` route chunk loads, and the default\n     * for a route whose `guard` answers `\"pending\"` — one spinner covers both\n     * kinds of \"not ready yet\". A route overrides it with its own\n     * `guardFallback`.\n     */\n    fallback?: ReactNode;\n}\n\n/**\n * Render a full React Router (v7 or v8, declarative mode) from a\n * {@link defineRoutes}\n * tree: picks the history strategy, wraps everything in a `<Suspense>` boundary\n * for `lazy` routes, and applies per-route `guard` redirects. This is the\n * single entry point apps mount at their root.\n *\n * @example\n * <AppRouter routes={routes} fallback={<Spinner />} />\n */\nexport function AppRouter({\n    routes,\n    router = \"browser\",\n    basename,\n    initialEntries,\n    fallback = null,\n}: AppRouterProps) {\n    const tree = useMemo(() => toRouteElements(routes, fallback), [routes, fallback]);\n    const Router = ROUTERS[router];\n    const routerProps = router === \"memory\" ? { basename, initialEntries } : { basename };\n\n    return (\n        <Router {...routerProps}>\n            <Suspense fallback={fallback}>\n                <Routes>{tree}</Routes>\n            </Suspense>\n        </Router>\n    );\n}\n"],"mappings":"6JAoBA,SAAgB,EAAa,EAAoD,CAC7E,OAAO,CACX,CAEA,IAAM,EAAY,IAAI,QAEtB,SAAS,EAAY,EAAmD,CACpE,IAAI,EAAO,EAAU,IAAI,CAAK,EAK9B,OAJK,IACD,EAAO,EAAA,cAAc,EAAM,IAAK,EAChC,EAAU,IAAI,EAAO,CAAI,GAEtB,CACX,CAUA,SAAS,EAAe,CACpB,QACA,gBACA,aACA,YAMD,CACC,IAAM,EAAU,OAAO,GAAU,WAAa,EAAM,EAAI,EACxD,OACI,EAAA,EAAA,IAAA,CAAC,EAAA,WAAD,CAAY,KAAM,EAAS,SAAU,EAA2B,aAC3D,UACO,CAAA,CAEpB,CAEA,IAAM,EAAY,IAAI,QAClB,EAAc,EAclB,SAAS,EAAS,EAAmC,CACjD,IAAI,EAAM,EAAU,IAAI,CAAK,EAK7B,OAJK,IACD,EAAM,SAAS,EAAE,IACjB,EAAU,IAAI,EAAO,CAAG,GAErB,CACX,CAEA,SAAS,EAAgB,EAA8B,EAAgC,CACnF,OAAO,EAAO,KAAK,EAAO,IAAM,CAC5B,IAAI,EAAqB,EAAM,QAC/B,GAAI,EAAM,KAAM,CACZ,IAAM,EAAgB,EAAY,CAAK,EACvC,GAAU,EAAA,EAAA,IAAA,CAAC,EAAD,CAAgB,CAAA,CAC9B,CAkBA,OAjBI,EAAM,QAAU,IAAA,KAChB,GACI,EAAA,EAAA,IAAA,CAAC,EAAD,CAEI,MAAO,EAAM,MACb,cAAe,EAAM,eAAiB,EACtC,WAAY,EAAM,WAEjB,SAAA,CACW,EANP,EAAS,CAAK,CAMP,GAIpB,EAAM,OACC,EAAA,EAAA,IAAA,CAAC,EAAA,MAAD,CAA0B,MAAA,GAAe,SAAU,EAAvC,SAAS,GAA8B,GAI1D,EAAA,EAAA,IAAA,CAAC,EAAA,MAAD,CAEI,KAAM,EAAM,KACH,UACT,cAAe,EAAM,cAEpB,SAAA,EAAM,SAAW,EAAgB,EAAM,SAAU,CAAQ,EAAI,IAC3D,EANE,EAAM,MAAQ,SAAS,GAMzB,CAEf,CAAC,CACL,CAEA,IAAM,EAAoD,CACtD,QAAS,EAAA,cACT,KAAM,EAAA,WACN,OAAQ,EAAA,YACZ,EA8BA,SAAgB,EAAU,CACtB,SACA,SAAS,UACT,WACA,iBACA,WAAW,MACI,CACf,IAAM,GAAA,EAAO,EAAA,QAAA,KAAc,EAAgB,EAAQ,CAAQ,EAAG,CAAC,EAAQ,CAAQ,CAAC,EAC1E,EAAS,EAAQ,GAGvB,OACI,EAAA,EAAA,IAAA,CAAC,EAAD,CAAQ,GAHQ,IAAW,SAAW,CAAE,WAAU,gBAAe,EAAI,CAAE,UAAS,EAI5E,UAAA,EAAA,EAAA,IAAA,CAAC,EAAA,SAAD,CAAoB,WAChB,UAAA,EAAA,EAAA,IAAA,CAAC,EAAA,OAAD,CAAA,SAAS,CAAa,CAAA,CAChB,CAAA,CACN,CAAA,CAEhB"}