/** * Run the server and the web dev server together, with one Ctrl+C to stop both. * * A tiny spawner instead of a dependency like concurrently: two fewer packages to * install, and it behaves predictably on Windows, which is where this demo runs. */ import { spawn } from 'node:child_process'; import { fileURLToPath } from 'node:url'; import path from 'node:path'; const root = path.dirname(path.dirname(fileURLToPath(import.meta.url))); const npm = process.platform === 'win32' ? 'npm.cmd' : 'npm'; const targets = [ { name: 'server', args: ['run', 'dev', '-w', 'server'], color: '\x1b[36m' }, { name: 'web', args: ['run', 'dev', '-w', 'web'], color: '\x1b[35m' }, ]; const children = []; let shuttingDown = false; for (const t of targets) { const child = spawn(npm, t.args, { cwd: root, shell: true, stdio: ['ignore', 'pipe', 'pipe'] }); children.push(child); const prefix = `${t.color}[${t.name}]\x1b[0m `; const pipe = (stream) => { let buf = ''; stream.on('data', (chunk) => { buf += chunk.toString(); let i; while ((i = buf.indexOf('\n')) >= 0) { const line = buf.slice(0, i); buf = buf.slice(i + 1); if (line.trim()) process.stdout.write(prefix + line + '\n'); } }); }; pipe(child.stdout); pipe(child.stderr); child.on('exit', (code) => { if (shuttingDown) return; console.log(`${prefix}exited with code ${code}. Shutting down.`); shutdown(); }); } function shutdown() { if (shuttingDown) return; shuttingDown = true; for (const c of children) { if (!c.killed) c.kill(); } setTimeout(() => process.exit(0), 300); } process.on('SIGINT', shutdown); process.on('SIGTERM', shutdown); console.log('\n Digital twin demo starting…'); console.log(' Dashboard: \x1b[1mhttp://localhost:5173\x1b[0m'); console.log(' API: http://localhost:8787\n');