/* Local development helper · BINX Forge · MIT. Never use as a public production server. */ import {createServer} from 'node:http'; import {readFileSync} from 'node:fs'; import {resolve} from 'node:path'; import {pathToFileURL} from 'node:url'; import {exampleFiles} from './files.mjs'; const types={html:'text/html; charset=utf-8',css:'text/css; charset=utf-8',mjs:'text/javascript; charset=utf-8',md:'text/plain; charset=utf-8',txt:'text/plain; charset=utf-8'}; export function createExampleServer(){return createServer((req,res)=>{ const url=new URL(req.url,'http://127.0.0.1'),path=url.pathname==='/'?'index.html':url.pathname.slice(1); res.setHeader('X-Content-Type-Options','nosniff');res.setHeader('Cache-Control','no-store');res.setHeader('Content-Security-Policy',"default-src 'self'; script-src 'self'; style-src 'self'; object-src 'none'; base-uri 'none'; frame-ancestors 'none'"); if(!['GET','HEAD'].includes(req.method)){res.setHeader('Allow','GET, HEAD');res.writeHead(405);res.end('Use GET or HEAD.');return;} if(url.search||!exampleFiles.includes(path)){res.writeHead(404);res.end('Not an example file.');return;} try{const bytes=readFileSync(new URL(path,import.meta.url));res.setHeader('Content-Type',types[path.split('.').pop()]);res.setHeader('Content-Length',bytes.length);res.writeHead(200);res.end(req.method==='HEAD'?undefined:bytes);}catch{res.writeHead(404);res.end('Keep all example files together.');} });} if(process.argv[1]&&pathToFileURL(resolve(process.argv[1])).href===import.meta.url){ const port=Number(process.argv[2]??8080);if(!Number.isInteger(port)||port<1||port>65535)throw Error('Choose a port from 1 to 65535.'); const server=createExampleServer();server.on('error',error=>{console.error(error.code==='EADDRINUSE'?'Port in use. Try node serve.mjs 8081.':'Local server could not start.');process.exitCode=1;});server.listen(port,'127.0.0.1',()=>console.log('Open http://127.0.0.1:'+port+' — stop with Ctrl+C.')); }