/** * Dependency-free local development server for XZBT. * * It deliberately binds only to loopback. It is not part of the distributed * XZBT artifact and cannot establish direct-file Phase 0 feasibility results. */ import { createReadStream, statSync } from 'node:fs'; import { createServer } from 'node:http'; import { extname, normalize, resolve, sep } from 'node:path'; import { fileURLToPath } from 'node:url'; const root = resolve(fileURLToPath(new URL('..', import.meta.url))); const host = '127.0.0.1'; const loopbackAddresses = ['127.0.0.1', '::1']; const port = Number.parseInt(process.env.XZBT_DEV_PORT ?? '5173', 10); if (!Number.isInteger(port) || port < 1 || port > 65535) { throw new Error('XZBT_DEV_PORT must be an integer from 1 through 65535.'); } const mimeTypes = new Map([ ['.css', 'text/css; charset=utf-8'], ['.html', 'text/html; charset=utf-8'], ['.js', 'text/javascript; charset=utf-8'], ['.json', 'application/json; charset=utf-8'], ['.mjs', 'text/javascript; charset=utf-8'], ['.xzbt', 'application/json; charset=utf-8'], ]); function requestedPath(requestUrl) { const pathname = new URL(requestUrl, `http://${host}:${port}`).pathname; const decodedPath = decodeURIComponent(pathname); if (decodedPath.includes('\0')) return null; if (decodedPath.split('/').some((segment) => segment.startsWith('.'))) return null; const relativePath = decodedPath === '/' ? 'prototypes/phase0/XZBT-phase0-probe.html' : decodedPath.replace(/^\/+/, ''); const candidate = resolve(root, normalize(relativePath)); return candidate === root || candidate.startsWith(`${root}${sep}`) ? candidate : null; } function handleRequest(request, response) { if (!['GET', 'HEAD'].includes(request.method ?? '')) { response.writeHead(405, { Allow: 'GET, HEAD' }).end(); return; } let path; try { path = requestedPath(request.url ?? '/'); } catch { response.writeHead(400).end('Malformed request path.'); return; } if (!path) { response.writeHead(403).end('Path is outside the XZBT workspace.'); return; } let stats; try { stats = statSync(path); } catch { response.writeHead(404).end('Not found.'); return; } if (!stats.isFile()) { response.writeHead(404).end('Not found.'); return; } response.writeHead(200, { 'Content-Length': stats.size, 'Content-Type': mimeTypes.get(extname(path).toLowerCase()) ?? 'application/octet-stream', 'Cache-Control': 'no-store', 'X-Content-Type-Options': 'nosniff', }); if (request.method === 'HEAD') response.end(); else createReadStream(path).pipe(response); } for (const address of loopbackAddresses) { const server = createServer(handleRequest); server.listen(port, address, () => { console.log(`XZBT development server: http://${address}:${port}/`); }); } console.log(`Serving workspace root: ${root}`);