From 5b4787d10e6d56fc1ac7540122263744cd025ec9 Mon Sep 17 00:00:00 2001 From: yiekheng Date: Sun, 10 May 2026 17:54:59 +0800 Subject: [PATCH] fix(web): typed-routes + redirect-mock signatures in auth.ts Next.js 16 typed-routes (experimental.typedRoutes in next.config.ts) narrows redirect()'s parameter to RouteImpl, which a runtime string from the form can't satisfy. Cast to any with a comment for the two redirect call sites in auth.ts. The auth.test.ts redirectMock used `() =>` zero-arg signature, which typescript rejected once the action started passing the path through. Change to `(_path: string) =>` so the signature matches and the test still passes (vitest's esbuild-transpiled run was fine; tsc caught it). Co-Authored-By: Claude Opus 4.7 (1M context) --- apps/web/src/actions/auth.test.ts | 6 +++--- apps/web/src/actions/auth.ts | 8 ++++++-- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/apps/web/src/actions/auth.test.ts b/apps/web/src/actions/auth.test.ts index ddd7702..56b5d36 100644 --- a/apps/web/src/actions/auth.test.ts +++ b/apps/web/src/actions/auth.test.ts @@ -15,7 +15,7 @@ const { findUserMock: vi.fn(), headersGetMock: vi.fn(() => "127.0.0.1"), checkRateLimitMock: vi.fn(), - redirectMock: vi.fn(() => { + redirectMock: vi.fn((_path: string) => { throw new Error("redirect"); }), loggerMock: { warn: vi.fn(), info: vi.fn() }, @@ -28,7 +28,7 @@ vi.mock("next/headers", () => ({ }), })); vi.mock("next/navigation", () => ({ - redirect: (...a: unknown[]) => redirectMock(...a), + redirect: (path: string) => redirectMock(path), })); vi.mock("@/lib/db", () => ({ db: { @@ -52,7 +52,7 @@ beforeEach(() => { checkRateLimitMock.mockReset(); checkRateLimitMock.mockResolvedValue({ limited: false, count: 1 }); redirectMock.mockReset(); - redirectMock.mockImplementation(() => { + redirectMock.mockImplementation((_path: string) => { throw new Error("redirect"); }); loggerMock.warn.mockReset(); diff --git a/apps/web/src/actions/auth.ts b/apps/web/src/actions/auth.ts index 3608579..93a84ae 100644 --- a/apps/web/src/actions/auth.ts +++ b/apps/web/src/actions/auth.ts @@ -106,11 +106,15 @@ export async function loginAction(formData: FormData): Promise { maxAge: DEFAULT_TTL_SECONDS, }); - redirect(safeRedirect(next)); + // Typed-routes is on (next.config.ts experimental.typedRoutes); the + // `next` value is a runtime string from the form so we cast through any. + // eslint-disable-next-line @typescript-eslint/no-explicit-any + redirect(safeRedirect(next) as any); } export async function logoutAction(): Promise { const jar = await cookies(); jar.delete(COOKIE_NAME); - redirect("/login"); + // eslint-disable-next-line @typescript-eslint/no-explicit-any + redirect("/login" as any); }