diff --git a/server/src/__tests__/idle-admission.test.ts b/server/src/__tests__/idle-admission.test.ts index 3725230d93..19f30ad08c 100644 --- a/server/src/__tests__/idle-admission.test.ts +++ b/server/src/__tests__/idle-admission.test.ts @@ -1,6 +1,7 @@ import express from "express"; import request from "supertest"; import { request as httpRequest } from "node:http"; +import { createServer as createViteServer } from "vite"; import { afterEach, describe, expect, it } from "vitest"; import { idleAdmissionMiddleware, trackIdleRequestHandlers } from "../middleware/idle-admission.js"; import { idleWorkSnapshot, startTaskDrain, stopTaskDrain, readTaskDrain, beginIdleTrackedWork } from "../services/task-admission.js"; @@ -63,6 +64,25 @@ describe("idle admission", () => { expect(idleWorkSnapshot().active).toBe(0); }); + it("supports real Vite Connect route metadata without losing async work", async () => { + const vite = await createViteServer({ configFile: false, appType: "custom", + server: { middlewareMode: true, watch: null }, optimizeDeps: { noDiscovery: true, include: [] } }); + const server = app(), done = deferred(); + try { + server.use(vite.middlewares); + server.post("/work", async (_req, res) => { res.sendStatus(202); await done.promise; }); + expect(() => trackIdleRequestHandlers(server)).not.toThrow(); + await request(server).post("/work").expect(202); + expect(idleWorkSnapshot().active).toBe(1); + done.resolve(); + await new Promise(resolve => setImmediate(resolve)); + expect(idleWorkSnapshot().active).toBe(0); + } finally { + done.resolve(); + await vite.close(); + } + }); + it("does not mistake a client disconnect for completed accepted work", async () => { const server = app(); const entered = deferred(), done = deferred(), closed = deferred(); diff --git a/server/src/middleware/idle-admission.ts b/server/src/middleware/idle-admission.ts index 2016b9ca85..92b1e36aa9 100644 --- a/server/src/middleware/idle-admission.ts +++ b/server/src/middleware/idle-admission.ts @@ -60,7 +60,7 @@ export function trackIdleRequestHandlers(app: Application): void { for (const layer of stack) { if (seen.has(layer)) continue; seen.add(layer); - if (layer.route) { visit(layer.route.stack, true); continue; } + if (layer.route && Array.isArray(layer.route.stack)) { visit(layer.route.stack, true); continue; } if (Array.isArray(layer.handle.stack)) { visit(layer.handle.stack); continue; } const original = layer.handle; if (original === idleAdmissionMiddleware) continue;