#!/usr/bin/env node // Serves companion/dist/ over HTTPS using the mkcert-issued test.locqr.dev // cert (dev.md — same domain as the test website, different port, so the // companion stub gets a secure context for getUserMedia()). Requires // `npm run build` in companion/ first — this serves the built output, not // the TypeScript source, mirroring how the extension is loaded from // extension/dist/. // // Plain Node https + fs — same shape as serve-website.js. import { createServer } from 'node:https'; import { readFile, stat } from 'node:fs/promises'; import { extname, join, normalize } from 'node:path'; import { dirname } from 'node:path'; import { fileURLToPath } from 'node:url'; const ROOT = dirname(dirname(fileURLToPath(import.meta.url))); const COMPANION_DIST_DIR = join(ROOT, 'companion', 'dist'); const PORT = 5174; const MIME = { '.html': 'text/html; charset=utf-8', '.js': 'text/javascript; charset=utf-8', '.css': 'text/css; charset=utf-8', '.json': 'application/json', }; async function resolveFile(urlPath) { const safePath = normalize(urlPath === '/' ? '/index.html' : urlPath).replace(/^(\.\.[/\\])+/, ''); const filePath = join(COMPANION_DIST_DIR, safePath); if (!filePath.startsWith(COMPANION_DIST_DIR)) return null; // path traversal guard try { const stats = await stat(filePath); return stats.isFile() ? filePath : null; } catch { return null; } } const options = { cert: await readFile(join(ROOT, 'dev', 'certs', 'test.locqr.dev.pem')), key: await readFile(join(ROOT, 'dev', 'certs', 'test.locqr.dev-key.pem')), }; createServer(options, async (req, res) => { const url = new URL(req.url ?? '/', 'https://test.locqr.dev'); const filePath = await resolveFile(url.pathname); if (!filePath) { res.writeHead(404).end('Not found — did you run `npm run build` in companion/?'); return; } const body = await readFile(filePath); // No caching, ever — this is a dev stub rebuilt on every source edit, // and mobile browsers in particular are prone to heuristically caching // a same-URL response across page reloads when the server sends no // explicit Cache-Control at all, silently serving a stale bundle. res.writeHead(200, { 'Content-Type': MIME[extname(filePath)] ?? 'application/octet-stream', 'Cache-Control': 'no-store' }); res.end(body); }).listen(PORT, () => { console.log(`Serving companion/dist/ -> https://test.locqr.dev:${PORT}`); });