Big News: Socket raises $60M Series C at a $1B valuation to secure software supply chains for AI-driven development.Announcement
Sign In

@expo/spawn-async

Package Overview
Dependencies
Maintainers
12
Versions
11
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@expo/spawn-async - npm Package Compare versions

Comparing version
1.7.2
to
1.8.0
+1
-0
build/spawnAsync.d.ts

@@ -6,2 +6,3 @@ /// <reference types="node" />

ignoreStdio?: boolean;
maxBuffer?: number;
}

@@ -8,0 +9,0 @@ interface SpawnPromise<T> extends Promise<T> {

+119
-49

@@ -5,77 +5,147 @@ "use strict";

};
const buffer_1 = require("buffer");
const cross_spawn_1 = __importDefault(require("cross-spawn"));
const DEFAULT_MAX_BUFFER = buffer_1.constants.MAX_STRING_LENGTH;
function spawnAsync(command, args, options = {}) {
const stubError = new Error();
const callerStack = stubError.stack ? stubError.stack.replace(/^.*/, ' ...') : null;
let child;
const { ignoreStdio: optionsIgnoreStdio, maxBuffer: optionsMaxBuffer, ...nodeOptions } = options;
// NOTE(@kitten): When `maxBuffer` is set explicitly, we enforce it strictly
// and don't produce a result without it being strictly enforced
const enforceMaxBufferStrictly = optionsMaxBuffer != null;
const ignoreStdio = !!optionsIgnoreStdio;
const maxBuffer = Math.min(optionsMaxBuffer !== null && optionsMaxBuffer !== void 0 ? optionsMaxBuffer : DEFAULT_MAX_BUFFER, buffer_1.constants.MAX_STRING_LENGTH);
let child = (0, cross_spawn_1.default)(command, args, nodeOptions);
let promise = new Promise((resolve, reject) => {
let { ignoreStdio, ...nodeOptions } = options;
// @ts-ignore: cross-spawn declares "args" to be a regular array instead of a read-only one
child = (0, cross_spawn_1.default)(command, args, nodeOptions);
let stdout = '';
let stderr = '';
if (!ignoreStdio) {
if (child.stdout) {
child.stdout.on('data', (data) => {
stdout += data;
});
var _a, _b;
const stdoutChunks = { buffer: [], maxExceeded: false };
const stderrChunks = { buffer: [], maxExceeded: false };
function makeHandler(chunks) {
let length = 0;
return (chunk) => {
chunks.buffer.push(chunk);
length += typeof chunk === 'string' ? Buffer.byteLength(chunk) : chunk.byteLength;
while (chunks.buffer.length > 0 && length > maxBuffer) {
chunks.maxExceeded = true;
chunk = chunks.buffer[0];
const chunkLength = typeof chunk === 'string' ? Buffer.byteLength(chunk) : chunk.byteLength;
if (length - chunkLength < maxBuffer) {
const replacement = typeof chunk === 'string' ? Buffer.from(chunk) : chunk;
const excess = length - maxBuffer;
chunks.buffer[0] = replacement.subarray(excess);
length -= excess;
break;
}
else {
chunks.buffer.shift();
length -= chunkLength;
}
}
};
}
function attachResult(target, assign, stdoutChunks, stderrChunks, skipMaxBufferCheck) {
function makeMaxBufferError() {
const argumentString = args && args.length > 0 ? ` ${args.join(' ')}` : '';
const error = new Error(`${command}${argumentString} exceeded maxBuffer of ${maxBuffer} bytes`);
error.code = 'ERR_CHILD_PROCESS_STDIO_MAXBUFFER';
return attachResult(error, assign, stdoutChunks, stderrChunks, true);
}
if (child.stderr) {
child.stderr.on('data', (data) => {
stderr += data;
});
let _stdout;
let _stderr;
const map = {
stdout: {
enumerable: true,
configurable: true,
get() {
if (!skipMaxBufferCheck && stdoutChunks.maxExceeded) {
throw makeMaxBufferError();
}
else if (_stdout === undefined) {
_stdout = Buffer.concat(stdoutChunks.buffer.map((chunk) => typeof chunk === 'string' ? Buffer.from(chunk) : chunk)).toString('utf8');
}
return _stdout;
},
},
stderr: {
enumerable: true,
configurable: true,
get() {
if (!skipMaxBufferCheck && stderrChunks.maxExceeded) {
throw makeMaxBufferError();
}
else if (_stderr === undefined) {
_stderr = Buffer.concat(stderrChunks.buffer.map((chunk) => typeof chunk === 'string' ? Buffer.from(chunk) : chunk)).toString('utf8');
}
return _stderr;
},
},
output: {
enumerable: true,
configurable: true,
get: () => [target.stdout, target.stderr],
},
};
for (const key in assign) {
map[key] = {
value: assign[key],
enumerable: true,
writable: true,
configurable: true,
};
}
Object.defineProperties(target, map);
return target;
}
if (!ignoreStdio) {
(_a = child.stdout) === null || _a === void 0 ? void 0 : _a.on('data', makeHandler(stdoutChunks));
(_b = child.stderr) === null || _b === void 0 ? void 0 : _b.on('data', makeHandler(stderrChunks));
}
// Use 'exit' instead of 'close' when there are no piped stdio streams for us to drain;
// 'close' can be deferred past 'exit' when the child has grandchildren that inherit its
// stdio fds, so waiting on it without anything to read just stalls
const completionEvent = ignoreStdio || (!child.stdout && !child.stderr) ? 'exit' : 'close';
let completionListener = (code, signal) => {
child.removeListener('error', errorListener);
let result = {
const argumentString = args && args.length > 0 ? ` ${args.join(' ')}` : '';
let error = null;
if (code !== 0) {
error = signal
? new Error(`${command}${argumentString} exited with signal: ${signal}`)
: new Error(`${command}${argumentString} exited with non-zero code: ${code}`);
}
const assignResult = {
pid: child.pid,
output: [stdout, stderr],
stdout,
stderr,
status: code,
signal,
};
if (code !== 0) {
let argumentString = args && args.length > 0 ? ` ${args.join(' ')}` : '';
let error = signal
? new Error(`${command}${argumentString} exited with signal: ${signal}`)
: new Error(`${command}${argumentString} exited with non-zero code: ${code}`);
if (error.stack && callerStack) {
if (error) {
if (error.stack && callerStack)
error.stack += `\n${callerStack}`;
}
Object.assign(error, result);
reject(error);
// When we're already rejecting, we don't enforce the max buffer error, and accept that we
// may truncate stderr/stdout
reject(attachResult(error, assignResult, stdoutChunks, stderrChunks, true));
}
else if (enforceMaxBufferStrictly && (stdoutChunks.maxExceeded || stderrChunks.maxExceeded)) {
// When a `maxBuffer` is passed, we enforce the maximum on stdout and stderr strictly
const error = new Error(`${command}${argumentString} exceeded maxBuffer of ${maxBuffer} bytes`);
error.code = 'ERR_CHILD_PROCESS_STDIO_MAXBUFFER';
reject(attachResult(error, assignResult, stdoutChunks, stderrChunks, true));
}
else {
resolve(result);
const result = {};
resolve(attachResult(result, assignResult, stdoutChunks, stderrChunks));
}
};
let errorListener = (error) => {
if (ignoreStdio) {
child.removeListener('exit', completionListener);
}
else {
child.removeListener('close', completionListener);
}
Object.assign(error, {
child.removeListener(completionEvent, completionListener);
const assignResult = {
pid: child.pid,
output: [stdout, stderr],
stdout,
stderr,
status: null,
signal: null,
});
reject(error);
};
reject(attachResult(error, assignResult, stdoutChunks, stderrChunks));
};
if (ignoreStdio) {
child.once('exit', completionListener);
}
else {
child.once('close', completionListener);
}
child.once(completionEvent, completionListener);
child.once('error', errorListener);
});
// @ts-ignore: TypeScript isn't aware the Promise constructor argument runs synchronously and
// thinks `child` is not yet defined
promise.child = child;

@@ -82,0 +152,0 @@ return promise;

@@ -1,1 +0,1 @@

{"version":3,"file":"spawnAsync.js","sourceRoot":"","sources":["../src/spawnAsync.ts"],"names":[],"mappings":"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","sourcesContent":["import { ChildProcess, SpawnOptions as NodeSpawnOptions } from 'child_process';\nimport spawn from 'cross-spawn';\n\nnamespace spawnAsync {\n export interface SpawnOptions extends NodeSpawnOptions {\n ignoreStdio?: boolean;\n }\n\n export interface SpawnPromise<T> extends Promise<T> {\n child: ChildProcess;\n }\n\n export interface SpawnResult {\n pid?: number;\n output: string[];\n stdout: string;\n stderr: string;\n status: number | null;\n signal: string | null;\n }\n}\n\nfunction spawnAsync(\n command: string,\n args?: ReadonlyArray<string>,\n options: spawnAsync.SpawnOptions = {}\n): spawnAsync.SpawnPromise<spawnAsync.SpawnResult> {\n const stubError = new Error();\n const callerStack = stubError.stack ? stubError.stack.replace(/^.*/, ' ...') : null;\n\n let child: ChildProcess;\n let promise = new Promise((resolve, reject) => {\n let { ignoreStdio, ...nodeOptions } = options;\n // @ts-ignore: cross-spawn declares \"args\" to be a regular array instead of a read-only one\n child = spawn(command, args, nodeOptions);\n let stdout = '';\n let stderr = '';\n\n if (!ignoreStdio) {\n if (child.stdout) {\n child.stdout.on('data', (data) => {\n stdout += data;\n });\n }\n\n if (child.stderr) {\n child.stderr.on('data', (data) => {\n stderr += data;\n });\n }\n }\n\n let completionListener = (code: number | null, signal: string | null) => {\n child.removeListener('error', errorListener);\n let result: spawnAsync.SpawnResult = {\n pid: child.pid,\n output: [stdout, stderr],\n stdout,\n stderr,\n status: code,\n signal,\n };\n if (code !== 0) {\n let argumentString = args && args.length > 0 ? ` ${args.join(' ')}` : '';\n let error = signal\n ? new Error(`${command}${argumentString} exited with signal: ${signal}`)\n : new Error(`${command}${argumentString} exited with non-zero code: ${code}`);\n if (error.stack && callerStack) {\n error.stack += `\\n${callerStack}`;\n }\n Object.assign(error, result);\n reject(error);\n } else {\n resolve(result);\n }\n };\n\n let errorListener = (error: Error) => {\n if (ignoreStdio) {\n child.removeListener('exit', completionListener);\n } else {\n child.removeListener('close', completionListener);\n }\n Object.assign(error, {\n pid: child.pid,\n output: [stdout, stderr],\n stdout,\n stderr,\n status: null,\n signal: null,\n });\n reject(error);\n };\n\n if (ignoreStdio) {\n child.once('exit', completionListener);\n } else {\n child.once('close', completionListener);\n }\n child.once('error', errorListener);\n }) as spawnAsync.SpawnPromise<spawnAsync.SpawnResult>;\n // @ts-ignore: TypeScript isn't aware the Promise constructor argument runs synchronously and\n // thinks `child` is not yet defined\n promise.child = child;\n return promise;\n}\n\nexport = spawnAsync;\n"]}
{"version":3,"file":"spawnAsync.js","sourceRoot":"","sources":["../src/spawnAsync.ts"],"names":[],"mappings":"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","sourcesContent":["import { ChildProcess, SpawnOptions as NodeSpawnOptions } from 'child_process';\nimport { constants as bufferConstants } from 'buffer';\nimport spawn from 'cross-spawn';\n\nnamespace spawnAsync {\n export interface SpawnOptions extends NodeSpawnOptions {\n ignoreStdio?: boolean;\n maxBuffer?: number;\n }\n\n export interface SpawnPromise<T> extends Promise<T> {\n child: ChildProcess;\n }\n\n export interface SpawnResult {\n pid?: number;\n output: string[];\n stdout: string;\n stderr: string;\n status: number | null;\n signal: string | null;\n }\n}\n\ntype IOChunk = string | Buffer;\n\ninterface IOChunksState {\n buffer: IOChunk[];\n maxExceeded: boolean;\n}\n\nconst DEFAULT_MAX_BUFFER = bufferConstants.MAX_STRING_LENGTH;\n\nfunction spawnAsync(\n command: string,\n args?: ReadonlyArray<string>,\n options: spawnAsync.SpawnOptions = {}\n): spawnAsync.SpawnPromise<spawnAsync.SpawnResult> {\n const stubError = new Error();\n const callerStack = stubError.stack ? stubError.stack.replace(/^.*/, ' ...') : null;\n\n const {\n ignoreStdio: optionsIgnoreStdio,\n maxBuffer: optionsMaxBuffer,\n ...nodeOptions\n } = options;\n\n // NOTE(@kitten): When `maxBuffer` is set explicitly, we enforce it strictly\n // and don't produce a result without it being strictly enforced\n const enforceMaxBufferStrictly = optionsMaxBuffer != null;\n\n const ignoreStdio = !!optionsIgnoreStdio;\n const maxBuffer = Math.min(\n optionsMaxBuffer ?? DEFAULT_MAX_BUFFER,\n bufferConstants.MAX_STRING_LENGTH,\n );\n\n let child: ChildProcess = spawn(command, args, nodeOptions);\n let promise = new Promise((resolve, reject) => {\n const stdoutChunks: IOChunksState = { buffer: [], maxExceeded: false };\n const stderrChunks: IOChunksState = { buffer: [], maxExceeded: false };\n\n function makeHandler(chunks: IOChunksState) {\n let length = 0;\n return (chunk: IOChunk) => {\n chunks.buffer.push(chunk);\n length += typeof chunk === 'string' ? Buffer.byteLength(chunk) : chunk.byteLength;\n while (chunks.buffer.length > 0 && length > maxBuffer) {\n chunks.maxExceeded = true;\n chunk = chunks.buffer[0];\n const chunkLength = typeof chunk === 'string' ? Buffer.byteLength(chunk) : chunk.byteLength;\n if (length - chunkLength < maxBuffer) {\n const replacement = typeof chunk === 'string' ? Buffer.from(chunk) : chunk;\n const excess = length - maxBuffer;\n chunks.buffer[0] = replacement.subarray(excess);\n length -= excess;\n break;\n } else {\n chunks.buffer.shift();\n length -= chunkLength;\n }\n }\n };\n }\n\n function attachResult<T extends spawnAsync.SpawnResult | (Error & Partial<spawnAsync.SpawnResult>)>(\n target: T,\n assign: Partial<spawnAsync.SpawnResult>,\n stdoutChunks: IOChunksState,\n stderrChunks: IOChunksState,\n skipMaxBufferCheck?: boolean,\n ): T {\n function makeMaxBufferError() {\n const argumentString = args && args.length > 0 ? ` ${args.join(' ')}` : '';\n const error: Error & { code?: string } = new Error(`${command}${argumentString} exceeded maxBuffer of ${maxBuffer} bytes`);\n error.code = 'ERR_CHILD_PROCESS_STDIO_MAXBUFFER';\n return attachResult(error, assign, stdoutChunks, stderrChunks, true);\n }\n\n let _stdout: string | undefined;\n let _stderr: string | undefined;\n const map: PropertyDescriptorMap = {\n stdout: {\n enumerable: true,\n configurable: true,\n get() {\n if (!skipMaxBufferCheck && stdoutChunks.maxExceeded) {\n throw makeMaxBufferError();\n } else if (_stdout === undefined) {\n _stdout = Buffer.concat(\n stdoutChunks.buffer.map((chunk) => typeof chunk === 'string' ? Buffer.from(chunk) : chunk)\n ).toString('utf8');\n }\n return _stdout;\n },\n },\n stderr: {\n enumerable: true,\n configurable: true,\n get() {\n if (!skipMaxBufferCheck && stderrChunks.maxExceeded) {\n throw makeMaxBufferError();\n } else if (_stderr === undefined) {\n _stderr = Buffer.concat(\n stderrChunks.buffer.map((chunk) => typeof chunk === 'string' ? Buffer.from(chunk) : chunk)\n ).toString('utf8');\n }\n return _stderr;\n },\n },\n output: {\n enumerable: true,\n configurable: true,\n get: () => [target.stdout, target.stderr],\n },\n };\n for (const key in assign) {\n map[key] = {\n value: assign[key as keyof spawnAsync.SpawnResult],\n enumerable: true,\n writable: true,\n configurable: true,\n };\n }\n Object.defineProperties(target, map);\n return target;\n }\n\n if (!ignoreStdio) {\n child.stdout?.on('data', makeHandler(stdoutChunks));\n child.stderr?.on('data', makeHandler(stderrChunks));\n }\n\n // Use 'exit' instead of 'close' when there are no piped stdio streams for us to drain;\n // 'close' can be deferred past 'exit' when the child has grandchildren that inherit its\n // stdio fds, so waiting on it without anything to read just stalls\n const completionEvent =\n ignoreStdio || (!child.stdout && !child.stderr) ? 'exit' : 'close';\n\n let completionListener = (code: number | null, signal: string | null) => {\n child.removeListener('error', errorListener);\n const argumentString = args && args.length > 0 ? ` ${args.join(' ')}` : '';\n let error: (Error & { code?: string }) | null = null;\n if (code !== 0) {\n error = signal\n ? new Error(`${command}${argumentString} exited with signal: ${signal}`)\n : new Error(`${command}${argumentString} exited with non-zero code: ${code}`);\n }\n const assignResult: Partial<spawnAsync.SpawnResult> = {\n pid: child.pid,\n status: code,\n signal,\n };\n if (error) {\n if (error.stack && callerStack) error.stack += `\\n${callerStack}`;\n // When we're already rejecting, we don't enforce the max buffer error, and accept that we\n // may truncate stderr/stdout\n reject(attachResult(error, assignResult, stdoutChunks, stderrChunks, true));\n } else if (enforceMaxBufferStrictly && (stdoutChunks.maxExceeded || stderrChunks.maxExceeded)) {\n // When a `maxBuffer` is passed, we enforce the maximum on stdout and stderr strictly\n const error: Error & { code?: string } = new Error(`${command}${argumentString} exceeded maxBuffer of ${maxBuffer} bytes`);\n error.code = 'ERR_CHILD_PROCESS_STDIO_MAXBUFFER';\n reject(attachResult(error, assignResult, stdoutChunks, stderrChunks, true));\n } else {\n const result = {} as spawnAsync.SpawnResult;\n resolve(attachResult(result, assignResult, stdoutChunks, stderrChunks));\n }\n };\n\n let errorListener = (error: Error) => {\n child.removeListener(completionEvent, completionListener);\n const assignResult: Partial<spawnAsync.SpawnResult> = {\n pid: child.pid,\n status: null,\n signal: null,\n };\n reject(attachResult(error, assignResult, stdoutChunks, stderrChunks));\n };\n\n child.once(completionEvent, completionListener);\n child.once('error', errorListener);\n }) as spawnAsync.SpawnPromise<spawnAsync.SpawnResult>;\n\n promise.child = child;\n return promise;\n}\n\nexport = spawnAsync;\n"]}
{
"name": "@expo/spawn-async",
"version": "1.7.2",
"version": "1.8.0",
"description": "A Promise-based interface into processes created by child_process.spawn",

@@ -43,3 +43,3 @@ "main": "./build/spawnAsync.js",

"dependencies": {
"cross-spawn": "^7.0.3"
"cross-spawn": "^7.0.6"
},

@@ -46,0 +46,0 @@ "devDependencies": {

@@ -33,2 +33,3 @@ # spawn-async [![Tests](https://github.com/expo/spawn-async/actions/workflows/main.yml/badge.svg)](https://github.com/expo/spawn-async/actions/workflows/main.yml)

- `ignoreStdio`: whether to ignore waiting for the child process's stdio streams to close before resolving the result promise. When ignoring stdio, the returned values for `stdout` and `stderr` will be empty strings. The default value of this option is `false`.
- `maxBuffer`: the maximum bytes retained from `stdout` and `stderr` (independently). Output is collected with a sliding window. When set explicitly, exceeding it rejects the promise with an error whose `code` is `ERR_CHILD_PROCESS_STDIO_MAXBUFFER` and whose `stdout`/`stderr` carry the truncated tail. When omitted, the default is `buffer.constants.MAX_STRING_LENGTH` (~512 MiB).

@@ -68,1 +69,11 @@ It returns a promise whose result is an object with these properties:

```
## Notes
### `maxBuffer`
`maxBuffer` is a later addition to the API. Set it when child output could exhaust memory and crash the parent process, or when the command or arguments are influenced by untrusted input — an attacker can otherwise force unbounded output to crash the parent.
The default of `buffer.constants.MAX_STRING_LENGTH` (~512 MiB) is a crash-safe floor, not a memory bound: at that size the materialized string itself can still exhaust process memory.
When `maxBuffer` is set explicitly, exceeding it rejects the promise immediately with `ERR_CHILD_PROCESS_STDIO_MAXBUFFER`. When left at the default, exceeding it doesn't reject; the sliding-window tail is still readable, but reading `stdout`/`stderr` throws `ERR_CHILD_PROCESS_STDIO_MAXBUFFER` with the truncated tail attached.