diff --git a/packages/vinext/src/index.ts b/packages/vinext/src/index.ts index f59c80d5..f8a94687 100644 --- a/packages/vinext/src/index.ts +++ b/packages/vinext/src/index.ts @@ -3044,6 +3044,7 @@ hydrate(); ) { const apiRoutes = await apiRouter(pagesDir, nextConfig?.pageExtensions, fileMatcher); const handled = await handleApiRoute( + getPagesRunner(), server, req, res, @@ -3082,7 +3083,7 @@ hydrate(); return; } - const handler = createSSRHandler(server, routes, pagesDir, nextConfig?.i18n, fileMatcher); + const handler = createSSRHandler(getPagesRunner(), server, routes, pagesDir, nextConfig?.i18n, fileMatcher); const mwStatus = (req as any).__vinextRewriteStatus as number | undefined; // Try rendering the resolved URL diff --git a/packages/vinext/src/server/api-handler.ts b/packages/vinext/src/server/api-handler.ts index 4a63d3cc..ca775102 100644 --- a/packages/vinext/src/server/api-handler.ts +++ b/packages/vinext/src/server/api-handler.ts @@ -8,6 +8,7 @@ * Next.js extensions: req.query, req.body, res.json(), res.status(), etc. */ import type { ViteDevServer } from "vite"; +import type { ModuleRunner } from "vite/module-runner"; import type { IncomingMessage, ServerResponse } from "node:http"; import { type Route, matchRoute } from "../routing/pages-router.js"; import { reportRequestError } from "./instrumentation.js"; @@ -163,6 +164,7 @@ function enhanceApiObjects( * Returns true if the request was handled, false if no API route matched. */ export async function handleApiRoute( + runner: ModuleRunner, server: ViteDevServer, req: IncomingMessage, res: ServerResponse, @@ -176,7 +178,7 @@ export async function handleApiRoute( try { // Load the API route module through Vite - const apiModule = await server.ssrLoadModule(route.filePath); + const apiModule = await runner.import(route.filePath); const handler = apiModule.default; if (typeof handler !== "function") { @@ -206,7 +208,7 @@ export async function handleApiRoute( await handler(apiReq, apiRes); return true; } catch (e) { - server.ssrFixStacktrace(e as Error); + server.ssrFixStacktrace?.(e as Error); console.error(e); reportRequestError( e instanceof Error ? e : new Error(String(e)), diff --git a/packages/vinext/src/server/dev-server.ts b/packages/vinext/src/server/dev-server.ts index 70aae84f..b4439cfc 100644 --- a/packages/vinext/src/server/dev-server.ts +++ b/packages/vinext/src/server/dev-server.ts @@ -1,4 +1,5 @@ import type { ViteDevServer } from "vite"; +import type { ModuleRunner } from "vite/module-runner"; import type { IncomingMessage, ServerResponse } from "node:http"; import type { Route } from "../routing/pages-router.js"; import { matchRoute, patternToNextFormat } from "../routing/pages-router.js"; @@ -64,7 +65,7 @@ async function streamPageToResponse( element: React.ReactElement, options: { url: string; - server: ViteDevServer; + server: ViteDevServer; // needed only for transformIndexHtml fontHeadHTML: string; scripts: string; DocumentComponent: React.ComponentType | null; @@ -272,6 +273,7 @@ export function parseCookieLocale( * 5. Wrap in _document shell and send response */ export function createSSRHandler( + runner: ModuleRunner, server: ViteDevServer, routes: Route[], pagesDir: string, @@ -344,7 +346,7 @@ export function createSSRHandler( if (!match) { // No route matched — try to render custom 404 page - await renderErrorPage(server, req, res, url, pagesDir, 404, undefined, matcher); + await renderErrorPage(runner, server, req, res, url, pagesDir, 404, undefined, matcher); return; } @@ -360,7 +362,7 @@ export function createSSRHandler( try { // Set SSR context for the router shim so useRouter() returns // the correct URL and params during server-side rendering. - const routerShim = await server.ssrLoadModule("next/router"); + const routerShim = await runner.import("next/router"); if (typeof routerShim.setSSRContext === "function") { routerShim.setSSRContext({ pathname: localeStrippedUrl.split("?")[0], @@ -381,7 +383,7 @@ export function createSSRHandler( // Load the page module through Vite's SSR pipeline // This gives us HMR and transform support for free - const pageModule = await server.ssrLoadModule(route.filePath); + const pageModule = await runner.import(route.filePath); // Mark end of compile phase: everything from here is rendering. _compileEnd = now(); @@ -422,7 +424,7 @@ export function createSSRHandler( }); if (!isValidPath) { - await renderErrorPage(server, req, res, url, pagesDir, 404, routerShim.wrapWithRouterContext, matcher); + await renderErrorPage(runner, server, req, res, url, pagesDir, 404, routerShim.wrapWithRouterContext as ((el: React.ReactElement) => React.ReactElement) | undefined, matcher); return; } } @@ -483,7 +485,7 @@ export function createSSRHandler( return; } if (result && "notFound" in result && result.notFound) { - await renderErrorPage(server, req, res, url, pagesDir, 404, routerShim.wrapWithRouterContext); + await renderErrorPage(runner, server, req, res, url, pagesDir, 404, routerShim.wrapWithRouterContext as ((el: React.ReactElement) => React.ReactElement) | undefined); return; } // Preserve any status code set by gSSP (e.g. res.statusCode = 201). @@ -511,11 +513,11 @@ export function createSSRHandler( let earlyFontLinkHeader = ""; try { const earlyPreloads: Array<{ href: string; type: string }> = []; - const fontGoogleEarly = await server.ssrLoadModule("next/font/google"); + const fontGoogleEarly = await runner.import("next/font/google"); if (typeof fontGoogleEarly.getSSRFontPreloads === "function") { earlyPreloads.push(...fontGoogleEarly.getSSRFontPreloads()); } - const fontLocalEarly = await server.ssrLoadModule("next/font/local"); + const fontLocalEarly = await runner.import("next/font/local"); if (typeof fontLocalEarly.getSSRFontPreloads === "function") { earlyPreloads.push(...fontLocalEarly.getSSRFontPreloads()); } @@ -607,7 +609,7 @@ export function createSSRHandler( return; } if (result && "notFound" in result && result.notFound) { - await renderErrorPage(server, req, res, url, pagesDir, 404, routerShim.wrapWithRouterContext); + await renderErrorPage(runner, server, req, res, url, pagesDir, 404, routerShim.wrapWithRouterContext as ((el: React.ReactElement) => React.ReactElement) | undefined); return; } @@ -623,7 +625,7 @@ export function createSSRHandler( const appPath = path.join(pagesDir, "_app"); if (findFileWithExtensions(appPath, matcher)) { try { - const appModule = await server.ssrLoadModule(appPath); + const appModule = await runner.import(appPath); AppComponent = appModule.default ?? null; } catch { // _app exists but failed to load @@ -654,13 +656,13 @@ export function createSSRHandler( } // Reset SSR head collector before rendering so tags are captured - const headShim = await server.ssrLoadModule("next/head"); + const headShim = await runner.import("next/head"); if (typeof headShim.resetSSRHead === "function") { headShim.resetSSRHead(); } // Flush any pending dynamic() preloads so components are ready - const dynamicShim = await server.ssrLoadModule("next/dynamic"); + const dynamicShim = await runner.import("next/dynamic"); if (typeof dynamicShim.flushPreloads === "function") { await dynamicShim.flushPreloads(); } @@ -674,7 +676,7 @@ export function createSSRHandler( const allFontStyles: string[] = []; const allFontPreloads: Array<{ href: string; type: string }> = []; try { - const fontGoogle = await server.ssrLoadModule("next/font/google"); + const fontGoogle = await runner.import("next/font/google"); if (typeof fontGoogle.getSSRFontLinks === "function") { const fontUrls = fontGoogle.getSSRFontLinks(); for (const fontUrl of fontUrls) { @@ -693,7 +695,7 @@ export function createSSRHandler( // next/font/google not used — skip } try { - const fontLocal = await server.ssrLoadModule("next/font/local"); + const fontLocal = await runner.import("next/font/local"); if (typeof fontLocal.getSSRFontStyles === "function") { allFontStyles.push(...fontLocal.getSSRFontStyles()); } @@ -777,7 +779,7 @@ hydrate(); let DocumentComponent: any = null; if (findFileWithExtensions(docPath, matcher)) { try { - const docModule = await server.ssrLoadModule(docPath); + const docModule = await runner.import(docPath); DocumentComponent = docModule.default ?? null; } catch { // _document exists but failed to load @@ -865,7 +867,7 @@ hydrate(); ).catch(() => { /* ignore reporting errors */ }); // Try to render custom 500 error page try { - await renderErrorPage(server, req, res, url, pagesDir, 500, undefined, matcher); + await renderErrorPage(runner, server, req, res, url, pagesDir, 500, undefined, matcher); } catch (fallbackErr) { // If error page itself fails, fall back to plain text. // This is a dev-only code path (prod uses prod-server.ts), so @@ -894,6 +896,7 @@ hydrate(); * - other: pages/_error.tsx -> default */ async function renderErrorPage( + runner: ModuleRunner, server: ViteDevServer, _req: IncomingMessage, res: ServerResponse, @@ -917,7 +920,7 @@ async function renderErrorPage( const candidatePath = path.join(pagesDir, candidate); if (!findFileWithExtensions(candidatePath, matcher)) continue; - const errorModule = await server.ssrLoadModule(candidatePath); + const errorModule = await runner.import(candidatePath); const ErrorComponent = errorModule.default; if (!ErrorComponent) continue; @@ -926,7 +929,7 @@ async function renderErrorPage( const appPathErr = path.join(pagesDir, "_app"); if (findFileWithExtensions(appPathErr, matcher)) { try { - const appModule = await server.ssrLoadModule(appPathErr); + const appModule = await runner.import(appPathErr); AppComponent = appModule.default ?? null; } catch { // _app exists but failed to load @@ -941,7 +944,7 @@ async function renderErrorPage( let wrapFn = wrapWithRouterContext; if (!wrapFn) { try { - const errRouterShim = await server.ssrLoadModule("next/router"); + const errRouterShim = await runner.import("next/router"); wrapFn = errRouterShim.wrapWithRouterContext; } catch { // router shim not available — continue without it @@ -970,7 +973,7 @@ async function renderErrorPage( const docPathErr = path.join(pagesDir, "_document"); if (findFileWithExtensions(docPathErr, matcher)) { try { - const docModule = await server.ssrLoadModule(docPathErr); + const docModule = await runner.import(docPathErr); DocumentComponent = docModule.default ?? null; } catch { // _document exists but failed to load diff --git a/tests/api-handler.test.ts b/tests/api-handler.test.ts index f6d7cca8..3875bb24 100644 --- a/tests/api-handler.test.ts +++ b/tests/api-handler.test.ts @@ -8,7 +8,7 @@ * * Since parseBody, parseCookies, and enhanceApiObjects are not exported, * all behavior is tested indirectly through handleApiRoute with a mocked - * ViteDevServer. + * ModuleRunner and ViteDevServer. */ import { describe, it, expect, vi } from "vitest"; import { PassThrough } from "node:stream"; @@ -16,6 +16,7 @@ import http from "node:http"; import { handleApiRoute } from "../packages/vinext/src/server/api-handler.js"; import type { Route } from "../packages/vinext/src/routing/pages-router.js"; import type { ViteDevServer } from "vite"; +import type { ModuleRunner } from "vite/module-runner"; // ── Helpers ────────────────────────────────────────────────────────────── @@ -121,13 +122,21 @@ function route(pattern: string, filePath = "/fake/api/handler.ts"): Route { } /** - * Build a minimal mock ViteDevServer with configurable ssrLoadModule behavior. + * Build a minimal mock ModuleRunner with configurable import behavior. */ -function mockServer( +function mockRunner( moduleExport: Record, -): ViteDevServer { +): ModuleRunner { + return { + import: vi.fn().mockResolvedValue(moduleExport), + } as unknown as ModuleRunner; +} + +/** + * Build a minimal mock ViteDevServer (only needs ssrFixStacktrace for error handling). + */ +function mockServer(): ViteDevServer { return { - ssrLoadModule: vi.fn().mockResolvedValue(moduleExport), ssrFixStacktrace: vi.fn(), } as unknown as ViteDevServer; } @@ -140,11 +149,13 @@ describe("handleApiRoute", () => { describe("route matching", () => { it("returns false when no route matches", async () => { const handler = vi.fn(); - const server = mockServer({ default: handler }); + const runner = mockRunner({ default: handler }); + const server = mockServer(); const req = mockReq("GET", "/api/nonexistent"); const res = mockRes(); const handled = await handleApiRoute( + runner, server, req, res, @@ -158,11 +169,13 @@ describe("handleApiRoute", () => { it("returns true when a route matches", async () => { const handler = vi.fn(); - const server = mockServer({ default: handler }); + const runner = mockRunner({ default: handler }); + const server = mockServer(); const req = mockReq("GET", "/api/users"); const res = mockRes(); const handled = await handleApiRoute( + runner, server, req, res, @@ -183,14 +196,15 @@ describe("handleApiRoute", () => { const handler = vi.fn((req: any) => { capturedBody = req.body; }); - const server = mockServer({ default: handler }); + const runner = mockRunner({ default: handler }); + const server = mockServer(); const payload = JSON.stringify({ name: "Alice", age: 30 }); const req = mockReq("POST", "/api/users", payload, { "content-type": "application/json", }); const res = mockRes(); - await handleApiRoute(server, req, res, "/api/users", [ + await handleApiRoute(runner, server, req, res, "/api/users", [ route("/api/users"), ]); @@ -202,13 +216,14 @@ describe("handleApiRoute", () => { const handler = vi.fn((req: any) => { capturedBody = req.body; }); - const server = mockServer({ default: handler }); + const runner = mockRunner({ default: handler }); + const server = mockServer(); const req = mockReq("POST", "/api/users", "{not json", { "content-type": "application/json", }); const res = mockRes(); - await handleApiRoute(server, req, res, "/api/users", [ + await handleApiRoute(runner, server, req, res, "/api/users", [ route("/api/users"), ]); @@ -220,13 +235,14 @@ describe("handleApiRoute", () => { const handler = vi.fn((req: any) => { capturedBody = req.body; }); - const server = mockServer({ default: handler }); + const runner = mockRunner({ default: handler }); + const server = mockServer(); const req = mockReq("POST", "/api/users", "name=Alice&role=admin", { "content-type": "application/x-www-form-urlencoded", }); const res = mockRes(); - await handleApiRoute(server, req, res, "/api/users", [ + await handleApiRoute(runner, server, req, res, "/api/users", [ route("/api/users"), ]); @@ -238,13 +254,14 @@ describe("handleApiRoute", () => { const handler = vi.fn((req: any) => { capturedBody = req.body; }); - const server = mockServer({ default: handler }); + const runner = mockRunner({ default: handler }); + const server = mockServer(); const req = mockReq("POST", "/api/users", "plain text body", { "content-type": "text/plain", }); const res = mockRes(); - await handleApiRoute(server, req, res, "/api/users", [ + await handleApiRoute(runner, server, req, res, "/api/users", [ route("/api/users"), ]); @@ -256,11 +273,12 @@ describe("handleApiRoute", () => { const handler = vi.fn((req: any) => { capturedBody = req.body; }); - const server = mockServer({ default: handler }); + const runner = mockRunner({ default: handler }); + const server = mockServer(); const req = mockReq("GET", "/api/users"); const res = mockRes(); - await handleApiRoute(server, req, res, "/api/users", [ + await handleApiRoute(runner, server, req, res, "/api/users", [ route("/api/users"), ]); @@ -272,11 +290,12 @@ describe("handleApiRoute", () => { const handler = vi.fn((req: any) => { capturedBody = req.body; }); - const server = mockServer({ default: handler }); + const runner = mockRunner({ default: handler }); + const server = mockServer(); const req = mockReq("POST", "/api/users", "some data"); const res = mockRes(); - await handleApiRoute(server, req, res, "/api/users", [ + await handleApiRoute(runner, server, req, res, "/api/users", [ route("/api/users"), ]); @@ -289,7 +308,8 @@ describe("handleApiRoute", () => { describe("MAX_BODY_SIZE enforcement", () => { it("rejects bodies exceeding 1 MB with 413 status", async () => { const handler = vi.fn(); - const server = mockServer({ default: handler }); + const runner = mockRunner({ default: handler }); + const server = mockServer(); // Create a stream that pushes > 1 MB. // Do NOT override destroy — let PassThrough's native destroy work @@ -329,7 +349,7 @@ describe("handleApiRoute", () => { } }); - await handleApiRoute(server, req, res, "/api/upload", [ + await handleApiRoute(runner, server, req, res, "/api/upload", [ route("/api/upload"), ]); @@ -343,7 +363,8 @@ describe("handleApiRoute", () => { const handler = vi.fn((req: any) => { capturedBody = req.body; }); - const server = mockServer({ default: handler }); + const runner = mockRunner({ default: handler }); + const server = mockServer(); // Send exactly 512 KB — well within the 1 MB limit const body = "x".repeat(512 * 1024); @@ -352,7 +373,7 @@ describe("handleApiRoute", () => { }); const res = mockRes(); - await handleApiRoute(server, req, res, "/api/upload", [ + await handleApiRoute(runner, server, req, res, "/api/upload", [ route("/api/upload"), ]); @@ -369,13 +390,14 @@ describe("handleApiRoute", () => { const handler = vi.fn((req: any) => { capturedCookies = req.cookies; }); - const server = mockServer({ default: handler }); + const runner = mockRunner({ default: handler }); + const server = mockServer(); const req = mockReq("GET", "/api/users", undefined, { cookie: "session=abc123", }); const res = mockRes(); - await handleApiRoute(server, req, res, "/api/users", [ + await handleApiRoute(runner, server, req, res, "/api/users", [ route("/api/users"), ]); @@ -387,13 +409,14 @@ describe("handleApiRoute", () => { const handler = vi.fn((req: any) => { capturedCookies = req.cookies; }); - const server = mockServer({ default: handler }); + const runner = mockRunner({ default: handler }); + const server = mockServer(); const req = mockReq("GET", "/api/users", undefined, { cookie: "session=abc123; theme=dark; lang=en", }); const res = mockRes(); - await handleApiRoute(server, req, res, "/api/users", [ + await handleApiRoute(runner, server, req, res, "/api/users", [ route("/api/users"), ]); @@ -409,13 +432,14 @@ describe("handleApiRoute", () => { const handler = vi.fn((req: any) => { capturedCookies = req.cookies; }); - const server = mockServer({ default: handler }); + const runner = mockRunner({ default: handler }); + const server = mockServer(); const req = mockReq("GET", "/api/users", undefined, { cookie: "token=abc=def=ghi", }); const res = mockRes(); - await handleApiRoute(server, req, res, "/api/users", [ + await handleApiRoute(runner, server, req, res, "/api/users", [ route("/api/users"), ]); @@ -427,11 +451,12 @@ describe("handleApiRoute", () => { const handler = vi.fn((req: any) => { capturedCookies = req.cookies; }); - const server = mockServer({ default: handler }); + const runner = mockRunner({ default: handler }); + const server = mockServer(); const req = mockReq("GET", "/api/users"); const res = mockRes(); - await handleApiRoute(server, req, res, "/api/users", [ + await handleApiRoute(runner, server, req, res, "/api/users", [ route("/api/users"), ]); @@ -448,11 +473,12 @@ describe("handleApiRoute", () => { // Should return res for chaining returned.json({ ok: true }); }); - const server = mockServer({ default: handler }); + const runner = mockRunner({ default: handler }); + const server = mockServer(); const req = mockReq("POST", "/api/users"); const res = mockRes(); - await handleApiRoute(server, req, res, "/api/users", [ + await handleApiRoute(runner, server, req, res, "/api/users", [ route("/api/users"), ]); @@ -467,11 +493,12 @@ describe("handleApiRoute", () => { const handler = vi.fn((_req: any, res: any) => { res.json({ message: "hello" }); }); - const server = mockServer({ default: handler }); + const runner = mockRunner({ default: handler }); + const server = mockServer(); const req = mockReq("GET", "/api/users"); const res = mockRes(); - await handleApiRoute(server, req, res, "/api/users", [ + await handleApiRoute(runner, server, req, res, "/api/users", [ route("/api/users"), ]); @@ -484,11 +511,12 @@ describe("handleApiRoute", () => { const handler = vi.fn((_req: any, res: any) => { res.json(data); }); - const server = mockServer({ default: handler }); + const runner = mockRunner({ default: handler }); + const server = mockServer(); const req = mockReq("GET", "/api/users"); const res = mockRes(); - await handleApiRoute(server, req, res, "/api/users", [ + await handleApiRoute(runner, server, req, res, "/api/users", [ route("/api/users"), ]); @@ -501,11 +529,12 @@ describe("handleApiRoute", () => { const handler = vi.fn((_req: any, res: any) => { res.send({ key: "value" }); }); - const server = mockServer({ default: handler }); + const runner = mockRunner({ default: handler }); + const server = mockServer(); const req = mockReq("GET", "/api/users"); const res = mockRes(); - await handleApiRoute(server, req, res, "/api/users", [ + await handleApiRoute(runner, server, req, res, "/api/users", [ route("/api/users"), ]); @@ -517,11 +546,12 @@ describe("handleApiRoute", () => { const handler = vi.fn((_req: any, res: any) => { res.send("hello world"); }); - const server = mockServer({ default: handler }); + const runner = mockRunner({ default: handler }); + const server = mockServer(); const req = mockReq("GET", "/api/users"); const res = mockRes(); - await handleApiRoute(server, req, res, "/api/users", [ + await handleApiRoute(runner, server, req, res, "/api/users", [ route("/api/users"), ]); @@ -533,11 +563,12 @@ describe("handleApiRoute", () => { const handler = vi.fn((_req: any, res: any) => { res.send(42); }); - const server = mockServer({ default: handler }); + const runner = mockRunner({ default: handler }); + const server = mockServer(); const req = mockReq("GET", "/api/users"); const res = mockRes(); - await handleApiRoute(server, req, res, "/api/users", [ + await handleApiRoute(runner, server, req, res, "/api/users", [ route("/api/users"), ]); @@ -550,11 +581,12 @@ describe("handleApiRoute", () => { res.setHeader("Content-Type", "text/html"); res.send("

Hello

"); }); - const server = mockServer({ default: handler }); + const runner = mockRunner({ default: handler }); + const server = mockServer(); const req = mockReq("GET", "/api/page"); const res = mockRes(); - await handleApiRoute(server, req, res, "/api/page", [ + await handleApiRoute(runner, server, req, res, "/api/page", [ route("/api/page"), ]); @@ -568,11 +600,12 @@ describe("handleApiRoute", () => { const handler = vi.fn((_req: any, res: any) => { res.send(null); }); - const server = mockServer({ default: handler }); + const runner = mockRunner({ default: handler }); + const server = mockServer(); const req = mockReq("GET", "/api/users"); const res = mockRes(); - await handleApiRoute(server, req, res, "/api/users", [ + await handleApiRoute(runner, server, req, res, "/api/users", [ route("/api/users"), ]); @@ -586,11 +619,12 @@ describe("handleApiRoute", () => { const handler = vi.fn((_req: any, res: any) => { res.redirect("/dashboard"); }); - const server = mockServer({ default: handler }); + const runner = mockRunner({ default: handler }); + const server = mockServer(); const req = mockReq("GET", "/api/login"); const res = mockRes(); - await handleApiRoute(server, req, res, "/api/login", [ + await handleApiRoute(runner, server, req, res, "/api/login", [ route("/api/login"), ]); @@ -603,11 +637,12 @@ describe("handleApiRoute", () => { const handler = vi.fn((_req: any, res: any) => { res.redirect(301, "/new-location"); }); - const server = mockServer({ default: handler }); + const runner = mockRunner({ default: handler }); + const server = mockServer(); const req = mockReq("GET", "/api/old"); const res = mockRes(); - await handleApiRoute(server, req, res, "/api/old", [ + await handleApiRoute(runner, server, req, res, "/api/old", [ route("/api/old"), ]); @@ -619,11 +654,12 @@ describe("handleApiRoute", () => { const handler = vi.fn((_req: any, res: any) => { res.redirect(302, "https://external.com"); }); - const server = mockServer({ default: handler }); + const runner = mockRunner({ default: handler }); + const server = mockServer(); const req = mockReq("GET", "/api/external"); const res = mockRes(); - await handleApiRoute(server, req, res, "/api/external", [ + await handleApiRoute(runner, server, req, res, "/api/external", [ route("/api/external"), ]); @@ -640,11 +676,12 @@ describe("handleApiRoute", () => { const handler = vi.fn((req: any) => { capturedQuery = req.query; }); - const server = mockServer({ default: handler }); + const runner = mockRunner({ default: handler }); + const server = mockServer(); const req = mockReq("GET", "/api/users?page=2&limit=10"); const res = mockRes(); - await handleApiRoute(server, req, res, "/api/users?page=2&limit=10", [ + await handleApiRoute(runner, server, req, res, "/api/users?page=2&limit=10", [ route("/api/users"), ]); @@ -657,11 +694,12 @@ describe("handleApiRoute", () => { const handler = vi.fn((req: any) => { capturedQuery = req.query; }); - const server = mockServer({ default: handler }); + const runner = mockRunner({ default: handler }); + const server = mockServer(); const req = mockReq("GET", "/api/users/42"); const res = mockRes(); - await handleApiRoute(server, req, res, "/api/users/42", [ + await handleApiRoute(runner, server, req, res, "/api/users/42", [ route("/api/users/:id"), ]); @@ -673,11 +711,13 @@ describe("handleApiRoute", () => { const handler = vi.fn((req: any) => { capturedQuery = req.query; }); - const server = mockServer({ default: handler }); + const runner = mockRunner({ default: handler }); + const server = mockServer(); const req = mockReq("GET", "/api/users/42?fields=name,email"); const res = mockRes(); await handleApiRoute( + runner, server, req, res, @@ -694,11 +734,12 @@ describe("handleApiRoute", () => { const handler = vi.fn((req: any) => { capturedQuery = req.query; }); - const server = mockServer({ default: handler }); + const runner = mockRunner({ default: handler }); + const server = mockServer(); const req = mockReq("GET", "/api/users?tag=a&tag=b"); const res = mockRes(); - await handleApiRoute(server, req, res, "/api/users?tag=a&tag=b", [ + await handleApiRoute(runner, server, req, res, "/api/users?tag=a&tag=b", [ route("/api/users"), ]); @@ -710,11 +751,12 @@ describe("handleApiRoute", () => { const handler = vi.fn((req: any) => { capturedQuery = req.query; }); - const server = mockServer({ default: handler }); + const runner = mockRunner({ default: handler }); + const server = mockServer(); const req = mockReq("GET", "/api/users"); const res = mockRes(); - await handleApiRoute(server, req, res, "/api/users", [ + await handleApiRoute(runner, server, req, res, "/api/users", [ route("/api/users"), ]); @@ -726,11 +768,12 @@ describe("handleApiRoute", () => { describe("error handling", () => { it("returns 500 when module has no default export", async () => { - const server = mockServer({ notDefault: () => {} }); + const runner = mockRunner({ notDefault: () => {} }); + const server = mockServer(); const req = mockReq("GET", "/api/users"); const res = mockRes(); - const handled = await handleApiRoute(server, req, res, "/api/users", [ + const handled = await handleApiRoute(runner, server, req, res, "/api/users", [ route("/api/users"), ]); @@ -740,11 +783,12 @@ describe("handleApiRoute", () => { }); it("returns 500 when default export is not a function", async () => { - const server = mockServer({ default: "not a function" }); + const runner = mockRunner({ default: "not a function" }); + const server = mockServer(); const req = mockReq("GET", "/api/users"); const res = mockRes(); - const handled = await handleApiRoute(server, req, res, "/api/users", [ + const handled = await handleApiRoute(runner, server, req, res, "/api/users", [ route("/api/users"), ]); @@ -757,11 +801,12 @@ describe("handleApiRoute", () => { const handler = vi.fn(() => { throw new Error("something broke"); }); - const server = mockServer({ default: handler }); + const runner = mockRunner({ default: handler }); + const server = mockServer(); const req = mockReq("GET", "/api/users"); const res = mockRes(); - const handled = await handleApiRoute(server, req, res, "/api/users", [ + const handled = await handleApiRoute(runner, server, req, res, "/api/users", [ route("/api/users"), ]); @@ -775,11 +820,12 @@ describe("handleApiRoute", () => { const handler = vi.fn(() => { throw error; }); - const server = mockServer({ default: handler }); + const runner = mockRunner({ default: handler }); + const server = mockServer(); const req = mockReq("GET", "/api/users"); const res = mockRes(); - await handleApiRoute(server, req, res, "/api/users", [ + await handleApiRoute(runner, server, req, res, "/api/users", [ route("/api/users"), ]);