#!/usr/bin/env node import fs from "node:fs"; import { spawn } from "node:child_process"; const appPort = 4322; const portHex = appPort.toString(16).toUpperCase().padStart(4, "0"); function listeningSocketInodes() { const inodes = new Set(); for (const file of ["/proc/net/tcp", "/proc/net/tcp6"]) { if (!fs.existsSync(file)) continue; for (const line of fs.readFileSync(file, "utf8").trim().split("\n").slice(1)) { const columns = line.trim().split(/\s+/); const localPort = columns[1]?.split(":").at(-1); const state = columns[3]; const inode = columns[9]; if (localPort === portHex && state === "0A" && inode) inodes.add(inode); } } return inodes; } function listenerPids(inodes) { const pids = []; for (const entry of fs.readdirSync("/proc").filter((value) => /^\d+$/.test(value))) { const directory = `/proc/${entry}/fd`; let descriptors; try { descriptors = fs.readdirSync(directory); } catch { continue; } const ownsSocket = descriptors.some((descriptor) => { try { const target = fs.readlinkSync(`${directory}/${descriptor}`); return [...inodes].some((inode) => target === `socket:[${inode}]`); } catch { return false; } }); if (ownsSocket && Number(entry) !== process.pid) pids.push(Number(entry)); } return pids; } const delay = (milliseconds) => new Promise((resolve) => setTimeout(resolve, milliseconds)); const alive = (pid) => { try { process.kill(pid, 0); return true; } catch { return false; } }; const pids = listenerPids(listeningSocketInodes()); for (const pid of pids) process.kill(pid, "SIGTERM"); for (let attempt = 0; attempt < 20 && pids.some(alive); attempt += 1) await delay(100); for (const pid of pids.filter(alive)) process.kill(pid, "SIGKILL"); const npmCommand = process.platform === "win32" ? "npm.cmd" : "npm"; const child = spawn(npmCommand, ["run", "dev:app"], { stdio: "inherit" }); for (const signal of ["SIGINT", "SIGTERM"]) process.on(signal, () => child.kill(signal)); child.on("exit", (code, signal) => process.exit(signal ? 1 : (code ?? 1)));