From de950b1fb01574fbc922b3ec1560564f8f00aebd Mon Sep 17 00:00:00 2001 From: mawkone Date: Fri, 12 Jun 2026 16:42:26 -0700 Subject: [PATCH] Make dev_server_start idempotent to prevent HMR breakage and 502s --- .../[projectId]/(home)/preview/page.tsx | 8 ++---- vibn-frontend/lib/dev-container.ts | 26 +++++++++++++++++-- 2 files changed, 26 insertions(+), 8 deletions(-) diff --git a/vibn-frontend/app/[workspace]/project/[projectId]/(home)/preview/page.tsx b/vibn-frontend/app/[workspace]/project/[projectId]/(home)/preview/page.tsx index 5fabcffa..cebec62b 100644 --- a/vibn-frontend/app/[workspace]/project/[projectId]/(home)/preview/page.tsx +++ b/vibn-frontend/app/[workspace]/project/[projectId]/(home)/preview/page.tsx @@ -181,13 +181,9 @@ export default function PreviewTab() { const path = currentPath.startsWith("/") ? currentPath : `/${currentPath}`; - // Add the refreshKey as a query param so the iframe completely remounts/refetches - // when the user hits the manual refresh button. - const urlObj = new URL(`${base}${path}`); - urlObj.searchParams.set("_refresh", refreshKey.toString()); - setIframeSrc(urlObj.toString()); + setIframeSrc(`${base}${path}`); } - }, [primaryRunning?.url, currentPath, refreshKey]); + }, [primaryRunning?.url, currentPath]); useEffect(() => { if (!bridge || !iframeSrc || !iframeDomRef.current) return; diff --git a/vibn-frontend/lib/dev-container.ts b/vibn-frontend/lib/dev-container.ts index f8fc67a0..550519fe 100644 --- a/vibn-frontend/lib/dev-container.ts +++ b/vibn-frontend/lib/dev-container.ts @@ -873,14 +873,36 @@ export async function startDevServer( // to keep the dashboard clean and prevent memory leaks. const existingRows = await query<{ id: string; - pid: number | null; + project_id: string; + workspace: string; + name: string; + command: string; port: number; + pid: number | null; + preview_url: string; + state: "starting" | "running" | "stopped" | "failed"; + started_at: string; + stopped_at: string | null; }>( - `SELECT id, pid, port FROM fs_dev_servers + `SELECT * FROM fs_dev_servers WHERE project_id = $1 AND state IN ('starting','running','failed')`, [opts.projectId], ); + // IDEMPOTENCY: If the exact same command is already starting or running on the same port, + // do not kill it! Just return the existing record. This prevents the AI from accidentally + // bouncing the server and dropping the cache after every file edit, which leads to 502s. + const alreadyRunning = existingRows.find( + (r) => + r.port === opts.port && + r.command === opts.command && + (r.state === "starting" || r.state === "running"), + ); + + if (alreadyRunning) { + return alreadyRunning; + } + const killPortNodeCmd = `node -e '` + `const fs = require("fs"); ` +