From 302ff389f3209b11dc96c23c0963f5872b499fac Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 28 Oct 2026 11:33:54 +1100 Subject: [PATCH] fix(frontend): send signed-out users to /auth with a full page load (#133) Opening / or /audit without a session threw a client-side redirect from beforeLoad while the first page was still hydrating. The router then rendered a match that no longer existed ("reading 'routeId'"), and React logged #520 and #422 before recovering. The guard now redirects with reloadDocument, so the browser loads /auth afresh. Production build in Chromium: 8 console errors before, 0 after. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_019YPoXzBSj89ZHgw7nfTmbB --- csv-table-hub-main/src/__tests__/session.test.ts | 5 ++++- csv-table-hub-main/src/lib/session.ts | 6 ++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/csv-table-hub-main/src/__tests__/session.test.ts b/csv-table-hub-main/src/__tests__/session.test.ts index d5fbecd..6037403 100644 --- a/csv-table-hub-main/src/__tests__/session.test.ts +++ b/csv-table-hub-main/src/__tests__/session.test.ts @@ -130,7 +130,10 @@ describe("requireSession (route guard)", () => { thrown = e; } expect(isRedirect(thrown)).toBe(true); - expect((thrown as { options: { to: string } }).options.to).toBe("/auth"); + const { options } = thrown as { options: { to: string; reloadDocument?: boolean } }; + expect(options.to).toBe("/auth"); + // #133: a client-side redirect during hydration crashed the router (React #520/#422). + expect(options.reloadDocument).toBe(true); }); it("lets a signed-in user through", () => { diff --git a/csv-table-hub-main/src/lib/session.ts b/csv-table-hub-main/src/lib/session.ts index 3fd75a9..a791a38 100644 --- a/csv-table-hub-main/src/lib/session.ts +++ b/csv-table-hub-main/src/lib/session.ts @@ -98,9 +98,11 @@ export function authHeaders(): Record { return s ? { Authorization: `Bearer ${s.token}` } : {}; } -/** Route guard (#96): send anyone without a live session to the sign-in page. */ +/** Route guard (#96): send anyone without a live session to the sign-in page. + * A full page load, not a client-side redirect: redirecting while the first + * page hydrates broke the router's match tree (React #520/#422, #133). */ export function requireSession(): void { - if (!getSession()) throw redirect({ to: "/auth" }); + if (!getSession()) throw redirect({ to: "/auth", reloadDocument: true }); } /** Change your own password (#117). This session stays; the server ends all others. */