Files
ep-133-sample-tool-offline/launcher.mjs
2026-07-12 11:02:46 +02:00

108 lines
3.3 KiB
JavaScript

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)));
}