import { createContext, type ReactNode, Suspense, useContext, useEffect, useMemo, useRef, useState, } from "react"; import { createBrowserRouter, RouterProvider, type RouteObject, useLocation, useNavigate, useParams, useSearchParams, } from "react-router-dom"; import { getRoute, ROUTE_REGISTRY, } from "../../features/installed-feature-contracts.js"; import type { RouteDefinition } from "../../contracts/routes.js"; import { FeatureBoundary, RouteBoundary, } from "../boundaries/render-error-boundary.jsx"; import { ChunkRecoveryBoundary } from "../boundaries/chunk-recovery-boundary.js"; import { Button, PageHeader } from "../design-system/index.js"; import { LocaleProvider, useLocale } from "../i18n/index.js"; import { AppShell } from "../layouts/app-shell.jsx"; import { useApplication } from "../providers/application-provider.js"; import { SessionProvider, useSession } from "../providers/session-provider.jsx"; import { ThemeProvider } from "../providers/theme-provider.jsx"; import { createRedirectLoopGuard, decideRouteAccess, } from "./navigation-policy.js"; import { buildRouteUrl, parseRouteInput, type ParsedRouteInput, type RouteId, } from "./route-codecs.js"; import { ROUTE_RUNTIME } from "../../features/installed-feature-runtimes.js"; const RouteInputContext = createContext(null); export function useRouteInput(): ParsedRouteInput { const input = useContext(RouteInputContext); if (!input) throw new Error("Registered route input is required"); return input; } function RouteLoadingSurface({ definition }: { definition: RouteDefinition }) { const { message, resolve } = useLocale(); const title = resolve(`route.${definition.routeId}.title`); return (
); } function RouteFailureSurface({ definition, }: { definition?: RouteDefinition; }) { const { message } = useLocale(); return (
); } function InvalidRouteSurface({ code }: { code: string }) { const { message } = useLocale(); return (

{message("route.invalid.action")}

); } function RouteLifecycle({ definition, buildId, }: { definition: RouteDefinition; buildId: string; }) { const location = useLocation(); const { message, resolve } = useLocale(); const { diagnostics } = useApplication(); useEffect(() => { document.title = message("route.documentTitle", { title: resolve(`route.${definition.routeId}.title`), appName: message("common.appName"), }); const main = document.getElementById("main-content"); main?.focus({ preventScroll: true }); try { if (!navigator.userAgent.toLowerCase().includes("jsdom")) { window.scrollTo({ top: 0, left: 0, behavior: "auto" }); } } catch { // Non-browser test hosts may not implement scrolling. } diagnostics.reportRouteChanged({ routeId: definition.routeId, buildId, }); }, [ buildId, definition, diagnostics, location.key, location.pathname, message, resolve, ]); return null; } function CanonicalRouteRedirect({ input, }: { input: ParsedRouteInput; }) { const location = useLocation(); const navigate = useNavigate(); const guard = useRef(createRedirectLoopGuard(3)); useEffect(() => { if (input.routeId === "NOT_FOUND") return; const source = `${location.pathname}${location.search}`; const target = buildRouteUrl(input.routeId, { params: input.params, search: input.search, }); if (source !== target && guard.current.allow(source, target)) { void navigate(target, { replace: true }); } }, [input, location.pathname, location.search, navigate]); return null; } function ProtectedRoute({ routeId, children, }: { routeId: RouteId; children: ReactNode; }) { const location = useLocation(); const { sessionState, beginSignIn, recover } = useSession(); const { message } = useLocale(); const [pending, setPending] = useState(false); const [failed, setFailed] = useState(false); const decision = decideRouteAccess(routeId, sessionState); async function continueSession() { setPending(true); setFailed(false); try { if (decision.action === "wait-for-session") { await recover(); } else { await beginSignIn( `${location.pathname}${location.search}${location.hash}`, ); } } catch { setFailed(true); } finally { setPending(false); } } if (decision.allowed) return children; if (sessionState === "integration-failed") { return (
); } const recovering = decision.action === "wait-for-session"; return (
{failed ? (

{message("shell.session.actionFailed")}

) : null}
); } function RegisteredRoute({ routeId, buildId, }: { routeId: RouteId; buildId: string; }) { const definition = getRoute(routeId); const runtime = ROUTE_RUNTIME[routeId]; const params = useParams(); const [search] = useSearchParams(); const location = useLocation(); const { diagnostics, recovery } = useApplication(); const parsed = parseRouteInput(routeId, params, search); if (!parsed.success) return ; const content = ( }> ); const protectedContent = definition.access === "public" ? ( content ) : ( {content} ); const boundaryProps = { routeId, buildId, resetKey: `${location.pathname}${location.search}`, onRenderFailure: diagnostics.reportRenderFailure, fallback: , children: protectedContent, }; return definition.errorSurface === "feature-boundary" ? ( ) : ( ); } function createRegisteredRoutes(buildId: string): RouteObject[] { const children = Object.values(ROUTE_REGISTRY).map((definition) => { const routeId = definition.routeId as RouteId; if (definition.path === "/") { return { id: routeId, index: true, element: , }; } return { id: routeId, path: definition.path === "*" ? "*" : definition.path.replace(/^\//, ""), element: , }; }); return [ { id: "APP_SHELL", path: "/", element: , errorElement: , children, }, ]; } export function AppRouter({ basename = "/", buildId = "local-build", }: Readonly<{ basename?: string; buildId?: string }>) { const router = useMemo( () => createBrowserRouter(createRegisteredRoutes(buildId), { basename, }), [basename, buildId], ); return ( ); }