// Read-only fictional HTTP fixtures and a bounded loopback static server. import http from'node:http';import fs from'node:fs/promises';import path from'node:path';import{fileURLToPath}from'node:url'; export function createLabServer(root){ const base=path.resolve(root),requests=[]; const server=http.createServer(async(req,res)=>{ try{ if(req.method!=='GET'){res.writeHead(405).end('Read-only fixture');return;} const url=new URL(req.url,'http://127.0.0.1');requests.push({url:url.pathname+url.search,desk:req.headers['x-desk']??null}); const json=(data,status=200)=>{if(!res.destroyed){res.writeHead(status,{'Content-Type':'application/json','Cache-Control':'no-store'});res.end(JSON.stringify(data));}}; if(url.pathname==='/api/tool'){json({name:'Hand drill'});return;} if(url.pathname==='/api/echo'){json({desk:req.headers['x-desk']||'none'});return;} if(url.pathname==='/api/failure'){json({message:'Local fixture failure'},503);return;} if(url.pathname==='/api/search'){const slow=url.searchParams.get('q')==='slow';setTimeout(()=>json({label:slow?'Slow result':'Fast result'}),slow?240:10);return;} const relative=decodeURIComponent(url.pathname).replace(/^\/+/,''),target=path.resolve(base,relative||'index.html'); if(target!==base&&!target.startsWith(base+path.sep)){res.writeHead(403).end();return;} let file=target;if(!path.extname(file))file=path.join(base,'index.html'); const body=await fs.readFile(file);const types={'.html':'text/html; charset=utf-8','.js':'text/javascript; charset=utf-8','.css':'text/css; charset=utf-8','.json':'application/json','.svg':'image/svg+xml'}; res.writeHead(200,{'Content-Type':types[path.extname(file)]||'application/octet-stream','Cache-Control':'no-store'});res.end(body); }catch{if(!res.headersSent)res.writeHead(404);res.end('Fixture file not found');} }); return {server,requests}; } if(process.argv[1]&&path.resolve(process.argv[1])===fileURLToPath(import.meta.url)){ const {server}=createLabServer(path.resolve('out/browser')); server.listen(8135,'127.0.0.1',()=>console.log('Local lesson: http://127.0.0.1:8135')); }