import { createServer } from "node:http"; import { readFile, stat } from "node:fs/promises"; import { resolve, sep } from "node:path"; const types = { ".html": "text/html; charset=utf-8", ".css": "text/css; charset=utf-8", ".js": "text/javascript; charset=utf-8", ".svg": "image/svg+xml", ".md": "text/plain; charset=utf-8", ".json": "application/json; charset=utf-8" }; /** Loopback-only static server, explicitly scoped to the supplied directory roots. */ export async function serve(root, { port = 0, mounts = {} } = {}) { root = resolve(root); const server = createServer(async (req, res) => { try { const path = decodeURIComponent(new URL(req.url, "http://localhost").pathname); const mount = Object.keys(mounts).find(prefix => path.startsWith(prefix)); const base = mount ? resolve(mounts[mount]) : root; let file = resolve(base, `.${mount ? path.slice(mount.length - 1) : path}`); if (file !== base && !file.startsWith(base + sep)) throw new Error("Outside served directory"); if ((await stat(file)).isDirectory()) file = resolve(file, "index.html"); const body = await readFile(file); const ext = /\.[^.]+$/.exec(file)?.[0]; res.writeHead(200, { "content-type": types[ext] ?? "application/octet-stream", "cache-control": "no-store" }); res.end(body); } catch { res.writeHead(404); res.end("Not found"); } }); await new Promise((done, reject) => { server.once("error", reject); server.listen(port, "127.0.0.1", done); }); return { server, url: `http://127.0.0.1:${server.address().port}/`, close: () => new Promise(done => server.close(done)) }; }