UNPKG

teen_process

Version:
158 lines 6.11 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.exec = exec; const node_child_process_1 = require("node:child_process"); const shell_quote_1 = require("shell-quote"); const helpers_1 = require("./helpers"); const circular_buffer_1 = require("./circular-buffer"); /** * Spawns a child process and collects its output. * * This is a promisified version of Node's spawn that collects stdout and stderr, * handles timeouts, and provides error context. * * @template T - The options type extending TeenProcessExecOptions * @param cmd - The command to execute * @param args - Array of arguments to pass to the command (default: []) * @param originalOpts - Execution options including timeout, encoding, environment, etc. * @returns Promise resolving to an object with stdout, stderr, and exit code * * @throws {ExecError} When the process exits with non-zero code or times out * * @example * ```typescript * // Simple execution * const {stdout, stderr, code} = await exec('ls', ['-la']); * * // With timeout and custom encoding * const result = await exec('long-running-cmd', [], { * timeout: 5000, * encoding: 'utf8', * cwd: '/custom/path' * }); * * // Return output as Buffer * const {stdout} = await exec('cat', ['image.png'], {isBuffer: true}); * ``` */ async function exec(cmd, args = [], originalOpts = {}) { // get a quoted representation of the command for error strings const rep = (0, shell_quote_1.quote)([cmd, ...args]); const defaults = { timeout: undefined, encoding: 'utf8', killSignal: 'SIGTERM', cwd: undefined, env: process.env, ignoreOutput: false, stdio: 'inherit', isBuffer: false, shell: undefined, logger: undefined, maxStdoutBufferSize: circular_buffer_1.MAX_BUFFER_SIZE, maxStderrBufferSize: circular_buffer_1.MAX_BUFFER_SIZE, }; const opts = applyDefaults(defaults, originalOpts); const isBuffer = Boolean(opts.isBuffer); const spawnOpts = buildSpawnOptions(opts, originalOpts); return await new Promise((resolve, reject) => { const proc = (0, node_child_process_1.spawn)(cmd, args, spawnOpts); const stdoutBuffer = new circular_buffer_1.CircularBuffer(opts.maxStdoutBufferSize); const stderrBuffer = new circular_buffer_1.CircularBuffer(opts.maxStderrBufferSize); let timer = null; proc.on('error', async (err) => { let error = err; if (error.code === 'ENOENT') { error = await (0, helpers_1.formatEnoent)(error, cmd, opts.cwd?.toString()); } reject(error); }); if (proc.stdin) { proc.stdin.on('error', (err) => { reject(new Error(`Standard input '${err.syscall}' error: ${err.stack}`)); }); } const handleStream = (streamType, buffer) => { const stream = proc[streamType]; if (!stream) { return; } stream.on('error', (err) => { const capitalizedStreamType = streamType.charAt(0).toUpperCase() + streamType.slice(1).toLowerCase(); reject(new Error(`${capitalizedStreamType} '${err.syscall}' error: ${err.stack}`)); }); if (opts.ignoreOutput) { // https://github.com/nodejs/node/issues/4236 stream.on('data', () => { }); return; } stream.on('data', (chunk) => { buffer.add(chunk); if (typeof opts.logger?.debug === 'function') { opts.logger.debug(chunk.toString()); } }); }; handleStream('stdout', stdoutBuffer); handleStream('stderr', stderrBuffer); function getStdio(wantBuffer) { const stdout = wantBuffer ? stdoutBuffer.value() : stdoutBuffer.value().toString(opts.encoding); const stderr = wantBuffer ? stderrBuffer.value() : stderrBuffer.value().toString(opts.encoding); return { stdout, stderr }; } proc.on('close', (code) => { if (timer) { clearTimeout(timer); } const { stdout, stderr } = getStdio(isBuffer); if (code === 0) { resolve({ stdout, stderr, code }); } else { const err = Object.assign(new Error(`Command '${rep}' exited with code ${code}`), { stdout, stderr, code, }); reject(err); } }); if (opts.timeout) { timer = setTimeout(() => { const { stdout, stderr } = getStdio(isBuffer); const err = Object.assign(new Error(`Command '${rep}' timed out after ${opts.timeout}ms`), { stdout, stderr, code: null, }); reject(err); proc.kill(opts.killSignal ?? 'SIGTERM'); }, opts.timeout); } }); } function applyDefaults(defaults, originalOpts) { const normalizedOriginalOpts = originalOpts !== null && typeof originalOpts === 'object' ? originalOpts : {}; const definedOriginalOpts = Object.fromEntries(Object.entries(normalizedOriginalOpts).filter(([, value]) => value !== undefined)); return { ...defaults, ...definedOriginalOpts }; } function buildSpawnOptions(opts, originalOpts) { return { cwd: opts.cwd, env: opts.env, shell: opts.shell, argv0: opts.argv0, uid: opts.uid, gid: opts.gid, detached: opts.detached, windowsHide: opts.windowsHide, windowsVerbatimArguments: opts.windowsVerbatimArguments, signal: opts.signal, ...(Object.prototype.hasOwnProperty.call(originalOpts, 'stdio') ? { stdio: opts.stdio } : {}), }; } //# sourceMappingURL=exec.js.map