import http from "node:http"; import { readFile, stat } from "node:fs/promises"; import path from "node:path"; import { fileURLToPath } from "node:url"; const scriptDir = path.dirname(fileURLToPath(import.meta.url)); const rootDir = path.resolve(scriptDir, "../.."); const port = Number(process.env.HEROLL_MVP_PORT || 4173); const parentPidFlag = process.argv.indexOf("--parent-pid"); const parentPid = parentPidFlag >= 0 ? Number(process.argv[parentPidFlag + 1]) : 0; const mime = new Map([ [".html", "text/html; charset=utf-8"], [".js", "text/javascript; charset=utf-8"], [".mjs", "text/javascript; charset=utf-8"], [".css", "text/css; charset=utf-8"], [".json", "application/json; charset=utf-8"], [".csv", "text/csv; charset=utf-8"], [".png", "image/png"], [".jpg", "image/jpeg"], [".jpeg", "image/jpeg"], [".svg", "image/svg+xml"], [".wav", "audio/wav"], [".ogg", "audio/ogg"] ]); function safePath(urlPath) { const decoded = decodeURIComponent(urlPath.split("?")[0] || "/"); const relative = decoded === "/" ? "mvp/index.html" : decoded.replace(/^\/+/, ""); const resolved = path.resolve(rootDir, relative); if (!resolved.startsWith(rootDir + path.sep) && resolved !== rootDir) return null; return resolved; } const server = http.createServer(async (req, res) => { try { let target = safePath(req.url || "/"); if (!target) { res.writeHead(403).end("Forbidden"); return; } const info = await stat(target); if (info.isDirectory()) target = path.join(target, "index.html"); const body = await readFile(target); res.writeHead(200, { "Content-Type": mime.get(path.extname(target).toLowerCase()) || "application/octet-stream", "Cache-Control": "no-store", "Cross-Origin-Resource-Policy": "same-origin" }); res.end(body); } catch (error) { res.writeHead(404, { "Content-Type": "text/plain; charset=utf-8" }); res.end(`Not found\n${String(error)}`); } }); server.listen(port, "127.0.0.1", () => { console.log(`HEROLL MVP running at http://127.0.0.1:${port}/mvp/`); console.log("Press Ctrl+C to stop."); }); if (Number.isInteger(parentPid) && parentPid > 0) { const parentWatch = setInterval(() => { try { process.kill(parentPid, 0); } catch { clearInterval(parentWatch); server.close(() => process.exit(0)); } }, 1000); parentWatch.unref(); }