import { createReadStream, statSync } from "node:fs"; import { createServer } from "node:http"; import { spawn } from "node:child_process"; import { dirname, extname, join, normalize } from "node:path"; import { fileURLToPath } from "node:url"; const projectRoot = dirname(fileURLToPath(import.meta.url)); const webRoot = join(projectRoot, "web"); const shouldOpenBrowser = !process.argv.includes("--no-open"); const contentTypes = { ".css": "text/css; charset=utf-8", ".html": "text/html; charset=utf-8", ".ico": "image/x-icon", ".js": "text/javascript; charset=utf-8", ".json": "application/json; charset=utf-8", ".pak": "application/octet-stream", ".png": "image/png", ".wasm": "application/wasm", ".woff2": "font/woff2", }; function localPath(url) { const pathname = decodeURIComponent(new URL(url, "http://127.0.0.1").pathname); const requested = pathname === "/" ? "/apps/ep-sample-tool/" : pathname; const relative = normalize(requested).replace(/^([/\\])+/, ""); const candidate = join(webRoot, relative); if (!candidate.startsWith(webRoot)) return null; try { return statSync(candidate).isDirectory() ? join(candidate, "index.html") : candidate; } catch { return candidate; } } const server = createServer((request, response) => { // The upstream app reports errors to /_api/err. Keep that diagnostic local. if (request.url?.startsWith("/_api/err")) { response.writeHead(204).end(); return; } const path = localPath(request.url ?? "/"); if (!path) { response.writeHead(403).end("Forbidden"); return; } try { const { size } = statSync(path); response.writeHead(200, { "Cache-Control": "no-store", "Content-Length": size, "Content-Type": contentTypes[extname(path)] ?? "application/octet-stream", "Cross-Origin-Opener-Policy": "same-origin", "X-Content-Type-Options": "nosniff", }); createReadStream(path).pipe(response); } catch { response.writeHead(404, { "Content-Type": "text/plain; charset=utf-8" }); response.end("Not found"); } }); server.listen(0, "127.0.0.1", () => { const address = server.address(); if (!address || typeof address === "string") throw new Error("Could not start local server"); const url = `http://127.0.0.1:${address.port}/apps/ep-sample-tool/`; if (!shouldOpenBrowser) { console.log(`READY ${url}`); return; } const candidates = [ "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome", "/Applications/Chromium.app/Contents/MacOS/Chromium", "/Applications/Microsoft Edge.app/Contents/MacOS/Microsoft Edge", ]; const browser = candidates.find((candidate) => { try { return statSync(candidate).isFile(); } catch { return false; } }); if (!browser) { console.error("Google Chrome, Chromium, or Microsoft Edge is required for Web MIDI."); console.error(`Open this URL in a Web MIDI browser: ${url}`); return; } spawn(browser, [`--app=${url}`, "--new-window"], { detached: true, stdio: "ignore", }).unref(); console.log("EP sample tool is running fully locally."); console.log(`Local address: ${url}`); console.log("Keep this window open while using the tool. Press Control-C to stop."); }); for (const signal of ["SIGINT", "SIGTERM"]) { process.on(signal, () => server.close(() => process.exit(0))); }