UNPKG

@sentzunhat/zacatl

Version:

A modular, high-performance TypeScript microservice framework for Node.js, featuring layered architecture, dependency injection, and robust validation for building scalable APIs and distributed systems.

78 lines 2.64 kB
import { spawn } from 'child_process'; import { validateCommandSpec } from './policy.js'; import { commandSpecSchema } from './types.js'; const SIGKILL_GRACE_MS = 2_000; export const runCommand = (spec, policy) => { return new Promise((resolve, reject) => { const parsed = commandSpecSchema.safeParse(spec); if (!parsed.success) { reject(parsed.error); return; } try { validateCommandSpec(parsed.data, policy); } catch (err) { reject(err); return; } const startMs = Date.now(); let stdout = ''; let stderr = ''; let timedOut = false; let outputBytes = 0; const env = policy.inheritEnv ? { ...process.env, ...(parsed.data.env ?? {}) } : { ...(parsed.data.env ?? {}) }; const child = spawn(parsed.data.cmd, parsed.data.args, { shell: false, cwd: parsed.data.cwd, env, stdio: ['ignore', 'pipe', 'pipe'], }); const killTimer = setTimeout(() => { timedOut = true; child.kill('SIGTERM'); const forceKill = setTimeout(() => child.kill('SIGKILL'), SIGKILL_GRACE_MS); if (typeof forceKill.unref === 'function') forceKill.unref(); }, policy.timeoutMs); child.stdout.on('data', (chunk) => { outputBytes += chunk.byteLength; if (outputBytes <= policy.maxOutputBytes) { stdout += chunk.toString('utf-8'); } }); child.stderr.on('data', (chunk) => { outputBytes += chunk.byteLength; if (outputBytes <= policy.maxOutputBytes) { stderr += chunk.toString('utf-8'); } }); child.on('close', (code) => { clearTimeout(killTimer); resolve({ cmd: parsed.data.cmd, args: parsed.data.args, exitCode: code, stdout, stderr, timedOut, durationMs: Date.now() - startMs, }); }); child.on('error', (err) => { clearTimeout(killTimer); resolve({ cmd: parsed.data.cmd, args: parsed.data.args, exitCode: null, stdout, stderr: stderr ? `${stderr}\n${err.message}` : err.message, timedOut, durationMs: Date.now() - startMs, }); }); }); }; //# sourceMappingURL=runner.js.map