Make dev_server_start idempotent to prevent HMR breakage and 502s

This commit is contained in:
2026-06-12 16:42:26 -07:00
parent 76c0241bd1
commit de950b1fb0
2 changed files with 26 additions and 8 deletions

View File

@@ -181,13 +181,9 @@ export default function PreviewTab() {
const path = currentPath.startsWith("/") const path = currentPath.startsWith("/")
? currentPath ? currentPath
: `/${currentPath}`; : `/${currentPath}`;
// Add the refreshKey as a query param so the iframe completely remounts/refetches setIframeSrc(`${base}${path}`);
// when the user hits the manual refresh button.
const urlObj = new URL(`${base}${path}`);
urlObj.searchParams.set("_refresh", refreshKey.toString());
setIframeSrc(urlObj.toString());
} }
}, [primaryRunning?.url, currentPath, refreshKey]); }, [primaryRunning?.url, currentPath]);
useEffect(() => { useEffect(() => {
if (!bridge || !iframeSrc || !iframeDomRef.current) return; if (!bridge || !iframeSrc || !iframeDomRef.current) return;

View File

@@ -873,14 +873,36 @@ export async function startDevServer(
// to keep the dashboard clean and prevent memory leaks. // to keep the dashboard clean and prevent memory leaks.
const existingRows = await query<{ const existingRows = await query<{
id: string; id: string;
pid: number | null; project_id: string;
workspace: string;
name: string;
command: string;
port: number; 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')`, WHERE project_id = $1 AND state IN ('starting','running','failed')`,
[opts.projectId], [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 = const killPortNodeCmd =
`node -e '` + `node -e '` +
`const fs = require("fs"); ` + `const fs = require("fs"); ` +