diff --git a/lib/arguments/options.js b/lib/arguments/options.js index ec785349a0..d08d65907f 100644 --- a/lib/arguments/options.js +++ b/lib/arguments/options.js @@ -3,6 +3,7 @@ import process from 'node:process'; import crossSpawn from 'cross-spawn'; import {npmRunPathEnv} from 'npm-run-path'; import {normalizeForceKillAfterDelay} from '../terminate/kill.js'; +import {normalizeKillSignal} from '../terminate/signal.js'; import {validateTimeout} from '../terminate/timeout.js'; import {handleNodeOption} from '../methods/node.js'; import {validateEncoding, BINARY_ENCODINGS} from './encoding-option.js'; @@ -24,6 +25,7 @@ export const normalizeOptions = (filePath, rawArguments, rawOptions) => { validateEncoding(options); options.shell = normalizeFileUrl(options.shell); options.env = getEnv(options); + options.killSignal = normalizeKillSignal(options.killSignal); options.forceKillAfterDelay = normalizeForceKillAfterDelay(options.forceKillAfterDelay); options.lines = options.lines.map((lines, fdNumber) => lines && !BINARY_ENCODINGS.has(options.encoding) && options.buffer[fdNumber]); diff --git a/lib/terminate/kill.js b/lib/terminate/kill.js index 0dccfa3f83..a31f5a3b4b 100644 --- a/lib/terminate/kill.js +++ b/lib/terminate/kill.js @@ -1,6 +1,6 @@ -import os from 'node:os'; import {setTimeout} from 'node:timers/promises'; import {isErrorInstance} from '../return/final-error.js'; +import {normalizeSignalArgument} from './signal.js'; // Normalize the `forceKillAfterDelay` option export const normalizeForceKillAfterDelay = forceKillAfterDelay => { @@ -46,15 +46,15 @@ const parseKillArguments = (signalOrError, errorArgument, killSignal) => { ? [undefined, signalOrError] : [signalOrError, errorArgument]; - if (typeof signal !== 'string' && typeof signal !== 'number') { - throw new TypeError(`The first argument must be an error instance or a signal name string/number: ${signal}`); + if (typeof signal !== 'string' && !Number.isInteger(signal)) { + throw new TypeError(`The first argument must be an error instance or a signal name string/integer: ${String(signal)}`); } if (error !== undefined && !isErrorInstance(error)) { throw new TypeError(`The second argument is optional. If specified, it must be an error instance: ${error}`); } - return {signal, error}; + return {signal: normalizeSignalArgument(signal), error}; }; // Fails right away when calling `subprocess.kill(error)`. @@ -77,11 +77,6 @@ const setKillTimeout = async ({kill, signal, forceKillAfterDelay, killSignal, ki } catch {} }; -const shouldForceKill = (signal, forceKillAfterDelay, killSignal, killResult) => - normalizeSignal(signal) === normalizeSignal(killSignal) +const shouldForceKill = (signal, forceKillAfterDelay, killSignal, killResult) => signal === killSignal && forceKillAfterDelay !== false && killResult; - -const normalizeSignal = signal => typeof signal === 'string' - ? os.constants.signals[signal.toUpperCase()] - : signal; diff --git a/lib/terminate/signal.js b/lib/terminate/signal.js new file mode 100644 index 0000000000..a243f64132 --- /dev/null +++ b/lib/terminate/signal.js @@ -0,0 +1,66 @@ +import os from 'node:os'; + +// Normalize signals for comparison purpose. +// Also validate the signal exists. +export const normalizeKillSignal = killSignal => { + const optionName = 'option `killSignal`'; + if (killSignal === 0) { + throw new TypeError(`Invalid ${optionName}: 0 cannot be used.`); + } + + return normalizeSignal(killSignal, optionName); +}; + +export const normalizeSignalArgument = signal => signal === 0 + ? signal + : normalizeSignal(signal, '`subprocess.kill()`\'s argument'); + +const normalizeSignal = (signalNameOrInteger, optionName) => { + if (Number.isInteger(signalNameOrInteger)) { + return normalizeSignalInteger(signalNameOrInteger, optionName); + } + + if (typeof signalNameOrInteger === 'string') { + return normalizeSignalName(signalNameOrInteger, optionName); + } + + throw new TypeError(`Invalid ${optionName} ${String(signalNameOrInteger)}: it must be a string or an integer.\n${getAvailableSignals()}`); +}; + +const normalizeSignalInteger = (signalInteger, optionName) => { + if (signalsIntegerToName.has(signalInteger)) { + return signalsIntegerToName.get(signalInteger); + } + + throw new TypeError(`Invalid ${optionName} ${signalInteger}: this signal integer does not exist.\n${getAvailableSignals()}`); +}; + +const getSignalsIntegerToName = () => new Map(Object.entries(os.constants.signals) + .reverse() + .map(([signalName, signalInteger]) => [signalInteger, signalName])); + +const signalsIntegerToName = getSignalsIntegerToName(); + +const normalizeSignalName = (signalName, optionName) => { + if (signalName in os.constants.signals) { + return signalName; + } + + if (signalName.toUpperCase() in os.constants.signals) { + throw new TypeError(`Invalid ${optionName} '${signalName}': please rename it to '${signalName.toUpperCase()}'.`); + } + + throw new TypeError(`Invalid ${optionName} '${signalName}': this signal name does not exist.\n${getAvailableSignals()}`); +}; + +const getAvailableSignals = () => `Available signal names: ${getAvailableSignalNames()}. +Available signal numbers: ${getAvailableSignalIntegers()}.`; + +const getAvailableSignalNames = () => Object.keys(os.constants.signals) + .sort() + .map(signalName => `'${signalName}'`) + .join(', '); + +const getAvailableSignalIntegers = () => [...new Set(Object.values(os.constants.signals) + .sort((signalInteger, signalIntegerTwo) => signalInteger - signalIntegerTwo))] + .join(', '); diff --git a/package.json b/package.json index 17f02c76c6..7ae3c6fb3f 100644 --- a/package.json +++ b/package.json @@ -52,7 +52,7 @@ "cross-spawn": "^7.0.3", "figures": "^6.1.0", "get-stream": "^9.0.0", - "human-signals": "^6.0.0", + "human-signals": "^7.0.0", "is-plain-obj": "^4.1.0", "is-stream": "^4.0.1", "npm-run-path": "^5.2.0", diff --git a/test-d/arguments/options.test-d.ts b/test-d/arguments/options.test-d.ts index 683a6b8f3b..cd37c5efa0 100644 --- a/test-d/arguments/options.test-d.ts +++ b/test-d/arguments/options.test-d.ts @@ -135,6 +135,18 @@ await execa('unicorns', {killSignal: 9}); execaSync('unicorns', {killSignal: 9}); expectError(await execa('unicorns', {killSignal: false})); expectError(execaSync('unicorns', {killSignal: false})); +expectError(await execa('unicorns', {killSignal: 'Sigterm'})); +expectError(execaSync('unicorns', {killSignal: 'Sigterm'})); +expectError(await execa('unicorns', {killSignal: 'sigterm'})); +expectError(execaSync('unicorns', {killSignal: 'sigterm'})); +expectError(await execa('unicorns', {killSignal: 'SIGOTHER'})); +expectError(execaSync('unicorns', {killSignal: 'SIGOTHER'})); +expectError(await execa('unicorns', {killSignal: 'SIGEMT'})); +expectError(execaSync('unicorns', {killSignal: 'SIGEMT'})); +expectError(await execa('unicorns', {killSignal: 'SIGCLD'})); +expectError(execaSync('unicorns', {killSignal: 'SIGCLD'})); +expectError(await execa('unicorns', {killSignal: 'SIGRT1'})); +expectError(execaSync('unicorns', {killSignal: 'SIGRT1'})); await execa('unicorns', {forceKillAfterDelay: false}); expectError(execaSync('unicorns', {forceKillAfterDelay: false})); diff --git a/test-d/return/result-main.test-d.ts b/test-d/return/result-main.test-d.ts index bc35b98d8d..fcf6c1bf94 100644 --- a/test-d/return/result-main.test-d.ts +++ b/test-d/return/result-main.test-d.ts @@ -28,7 +28,7 @@ expectType(unicornsResult.timedOut); expectType(unicornsResult.isCanceled); expectType(unicornsResult.isTerminated); expectType(unicornsResult.isMaxBuffer); -expectType(unicornsResult.signal); +expectType(unicornsResult.signal); expectType(unicornsResult.signalDescription); expectType(unicornsResult.cwd); expectType(unicornsResult.durationMs); @@ -44,7 +44,7 @@ expectType(unicornsResultSync.timedOut); expectType(unicornsResultSync.isCanceled); expectType(unicornsResultSync.isTerminated); expectType(unicornsResultSync.isMaxBuffer); -expectType(unicornsResultSync.signal); +expectType(unicornsResultSync.signal); expectType(unicornsResultSync.signalDescription); expectType(unicornsResultSync.cwd); expectType(unicornsResultSync.durationMs); @@ -61,7 +61,7 @@ if (error instanceof ExecaError) { expectType(error.isCanceled); expectType(error.isTerminated); expectType(error.isMaxBuffer); - expectType(error.signal); + expectType(error.signal); expectType(error.signalDescription); expectType(error.cwd); expectType(error.durationMs); @@ -83,7 +83,7 @@ if (errorSync instanceof ExecaSyncError) { expectType(errorSync.isCanceled); expectType(errorSync.isTerminated); expectType(errorSync.isMaxBuffer); - expectType(errorSync.signal); + expectType(errorSync.signal); expectType(errorSync.signalDescription); expectType(errorSync.cwd); expectType(errorSync.durationMs); diff --git a/test-d/subprocess/subprocess.test-d.ts b/test-d/subprocess/subprocess.test-d.ts index 8770003543..0492c284ba 100644 --- a/test-d/subprocess/subprocess.test-d.ts +++ b/test-d/subprocess/subprocess.test-d.ts @@ -14,6 +14,12 @@ subprocess.kill('SIGKILL', new Error('test')); subprocess.kill(undefined, new Error('test')); expectError(subprocess.kill(null)); expectError(subprocess.kill(0n)); +expectError(subprocess.kill('Sigkill')); +expectError(subprocess.kill('sigkill')); +expectError(subprocess.kill('SIGOTHER')); +expectError(subprocess.kill('SIGEMT')); +expectError(subprocess.kill('SIGCLD')); +expectError(subprocess.kill('SIGRT1')); expectError(subprocess.kill([new Error('test')])); expectError(subprocess.kill({message: 'test'})); expectError(subprocess.kill(undefined, {})); diff --git a/test/helpers/early-error.js b/test/helpers/early-error.js index 430658316a..aca36c18df 100644 --- a/test/helpers/early-error.js +++ b/test/helpers/early-error.js @@ -1,7 +1,9 @@ import {execa, execaSync} from '../../index.js'; -export const earlyErrorOptions = {killSignal: false}; +export const earlyErrorOptions = {cancelSignal: false}; export const getEarlyErrorSubprocess = options => execa('empty.js', {...earlyErrorOptions, ...options}); -export const getEarlyErrorSubprocessSync = options => execaSync('empty.js', {...earlyErrorOptions, ...options}); +export const earlyErrorOptionsSync = {maxBuffer: false}; +export const getEarlyErrorSubprocessSync = options => execaSync('empty.js', {...earlyErrorOptionsSync, ...options}); export const expectedEarlyError = {code: 'ERR_INVALID_ARG_TYPE'}; +export const expectedEarlyErrorSync = {code: 'ERR_OUT_OF_RANGE'}; diff --git a/test/io/max-buffer.js b/test/io/max-buffer.js index 3464f3b6f4..37b63c7b98 100644 --- a/test/io/max-buffer.js +++ b/test/io/max-buffer.js @@ -4,7 +4,7 @@ import getStream from 'get-stream'; import {execa, execaSync} from '../../index.js'; import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; import {fullStdio} from '../helpers/stdio.js'; -import {getEarlyErrorSubprocess, getEarlyErrorSubprocessSync} from '../helpers/early-error.js'; +import {getEarlyErrorSubprocess} from '../helpers/early-error.js'; import {maxBuffer, assertErrorMessage} from '../helpers/max-buffer.js'; setFixtureDirectory(); @@ -260,11 +260,9 @@ test('abort stream when hitting maxBuffer with stdout', testMaxBufferAbort, 1); test('abort stream when hitting maxBuffer with stderr', testMaxBufferAbort, 2); test('abort stream when hitting maxBuffer with stdio[*]', testMaxBufferAbort, 3); -const testEarlyError = async (t, getSubprocess) => { - const {failed, isMaxBuffer} = await getSubprocess({reject: false, maxBuffer: 1}); +test('error.isMaxBuffer is false on early errors', async t => { + const {failed, isMaxBuffer} = await getEarlyErrorSubprocess({reject: false, maxBuffer: 1}); t.true(failed); t.false(isMaxBuffer); -}; +}); -test('error.isMaxBuffer is false on early errors', testEarlyError, getEarlyErrorSubprocess); -test('error.isMaxBuffer is false on early errors, sync', testEarlyError, getEarlyErrorSubprocessSync); diff --git a/test/return/early-error.js b/test/return/early-error.js index dbee21d691..959a111524 100644 --- a/test/return/early-error.js +++ b/test/return/early-error.js @@ -8,6 +8,7 @@ import { getEarlyErrorSubprocess, getEarlyErrorSubprocessSync, expectedEarlyError, + expectedEarlyErrorSync, } from '../helpers/early-error.js'; setFixtureDirectory(); @@ -43,7 +44,7 @@ test('child_process.spawn() early errors are returned', async t => { }); test('child_process.spawnSync() early errors are propagated with a correct shape', t => { - t.throws(getEarlyErrorSubprocessSync, expectedEarlyError); + t.throws(getEarlyErrorSubprocessSync, expectedEarlyErrorSync); }); test('child_process.spawnSync() early errors are propagated with a correct shape - reject false', t => { diff --git a/test/terminate/kill-force.js b/test/terminate/kill-force.js index 1a72544977..b62c9b91b1 100644 --- a/test/terminate/kill-force.js +++ b/test/terminate/kill-force.js @@ -90,7 +90,9 @@ if (isWindows) { test('`forceKillAfterDelay: undefined` should kill after a timeout', testForceKill, undefined); test('`forceKillAfterDelay` should kill after a timeout with SIGTERM', testForceKill, 50, 'SIGTERM'); test('`forceKillAfterDelay` should kill after a timeout with the killSignal string', testForceKill, 50, 'SIGINT', {killSignal: 'SIGINT'}); + test('`forceKillAfterDelay` should kill after a timeout with the killSignal string, mixed', testForceKill, 50, 'SIGINT', {killSignal: constants.signals.SIGINT}); test('`forceKillAfterDelay` should kill after a timeout with the killSignal number', testForceKill, 50, constants.signals.SIGINT, {killSignal: constants.signals.SIGINT}); + test('`forceKillAfterDelay` should kill after a timeout with the killSignal number, mixed', testForceKill, 50, constants.signals.SIGINT, {killSignal: 'SIGINT'}); test('`forceKillAfterDelay` should kill after a timeout with an error', testForceKill, 50, new Error('test')); test('`forceKillAfterDelay` should kill after a timeout with an error and a killSignal', testForceKill, 50, new Error('test'), {killSignal: 'SIGINT'}); diff --git a/test/terminate/kill-signal.js b/test/terminate/kill-signal.js index ef84b9d2c5..273a4f22a7 100644 --- a/test/terminate/kill-signal.js +++ b/test/terminate/kill-signal.js @@ -1,8 +1,9 @@ import {once} from 'node:events'; import {platform, version} from 'node:process'; +import {constants} from 'node:os'; import {setImmediate} from 'node:timers/promises'; import test from 'ava'; -import {execa} from '../../index.js'; +import {execa, execaSync} from '../../index.js'; import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; setFixtureDirectory(); @@ -10,6 +11,50 @@ setFixtureDirectory(); const isWindows = platform === 'win32'; const majorNodeVersion = Number(version.split('.')[0].slice(1)); +const testKillSignal = async (t, killSignal) => { + const {isTerminated, signal} = await t.throwsAsync(execa('forever.js', {killSignal, timeout: 1})); + t.true(isTerminated); + t.is(signal, 'SIGINT'); +}; + +test('Can use killSignal: "SIGINT"', testKillSignal, 'SIGINT'); +test('Can use killSignal: 2', testKillSignal, constants.signals.SIGINT); + +const testKillSignalSync = (t, killSignal) => { + const {isTerminated, signal} = t.throws(() => { + execaSync('forever.js', {killSignal, timeout: 1}); + }); + t.true(isTerminated); + t.is(signal, 'SIGINT'); +}; + +test('Can use killSignal: "SIGINT", sync', testKillSignalSync, 'SIGINT'); +test('Can use killSignal: 2, sync', testKillSignalSync, constants.signals.SIGINT); + +test('Can call .kill("SIGTERM")', async t => { + const subprocess = execa('forever.js'); + subprocess.kill('SIGTERM'); + const {isTerminated, signal} = await t.throwsAsync(subprocess); + t.true(isTerminated); + t.is(signal, 'SIGTERM'); +}); + +test('Can call .kill(15)', async t => { + const subprocess = execa('forever.js'); + subprocess.kill(constants.signals.SIGTERM); + const {isTerminated, signal} = await t.throwsAsync(subprocess); + t.true(isTerminated); + t.is(signal, 'SIGTERM'); +}); + +test('Can call .kill(0)', async t => { + const subprocess = execa('forever.js'); + t.true(subprocess.kill(0)); + subprocess.kill(); + await t.throwsAsync(subprocess); + t.false(subprocess.kill(0)); +}); + test('Can call `.kill()` multiple times', async t => { const subprocess = execa('forever.js'); subprocess.kill(); @@ -50,9 +95,6 @@ const testInvalidKillArgument = async (t, killArgument, secondKillArgument) => { await subprocess; }; -test('Cannot call .kill(null)', testInvalidKillArgument, null); -test('Cannot call .kill(0n)', testInvalidKillArgument, 0n); -test('Cannot call .kill(true)', testInvalidKillArgument, true); test('Cannot call .kill(errorObject)', testInvalidKillArgument, {name: '', message: '', stack: ''}); test('Cannot call .kill(errorArray)', testInvalidKillArgument, [new Error('test')]); test('Cannot call .kill(undefined, true)', testInvalidKillArgument, undefined, true); diff --git a/test/terminate/signal.js b/test/terminate/signal.js new file mode 100644 index 0000000000..96d5114b7a --- /dev/null +++ b/test/terminate/signal.js @@ -0,0 +1,87 @@ +import test from 'ava'; +import {execa, execaSync} from '../../index.js'; +import {setFixtureDirectory} from '../helpers/fixtures-directory.js'; + +setFixtureDirectory(); + +const VALIDATION_MESSAGES = { + string: 'this signal name does not exist', + integer: 'this signal integer does not exist', + other: 'it must be a string or an integer', + rename: 'please rename it to', + zero: '0 cannot be used', +}; + +const validateMessage = (t, message, type) => { + t.true(message.includes(VALIDATION_MESSAGES[type])); + + if (type !== 'rename' && type !== 'zero') { + t.true(message.includes('Available signal names: \'SIGABRT\', ')); + t.true(message.includes('Available signal numbers: 1, ')); + } +}; + +const testInvalidKillSignal = (t, killSignal, type, execaMethod) => { + const {message} = t.throws(() => { + execaMethod('empty.js', {killSignal}); + }); + t.true(message.includes('Invalid option `killSignal`')); + validateMessage(t, message, type); +}; + +test('Cannot use killSignal: "SIGOTHER"', testInvalidKillSignal, 'SIGOTHER', 'string', execa); +test('Cannot use killSignal: "Sigterm"', testInvalidKillSignal, 'Sigterm', 'rename', execa); +test('Cannot use killSignal: "sigterm"', testInvalidKillSignal, 'sigterm', 'rename', execa); +test('Cannot use killSignal: -1', testInvalidKillSignal, -1, 'integer', execa); +test('Cannot use killSignal: 200', testInvalidKillSignal, 200, 'integer', execa); +test('Cannot use killSignal: 1n', testInvalidKillSignal, 1n, 'other', execa); +test('Cannot use killSignal: 1.5', testInvalidKillSignal, 1.5, 'other', execa); +test('Cannot use killSignal: Infinity', testInvalidKillSignal, Number.POSITIVE_INFINITY, 'other', execa); +test('Cannot use killSignal: NaN', testInvalidKillSignal, Number.NaN, 'other', execa); +test('Cannot use killSignal: false', testInvalidKillSignal, false, 'other', execa); +test('Cannot use killSignal: null', testInvalidKillSignal, null, 'other', execa); +test('Cannot use killSignal: symbol', testInvalidKillSignal, Symbol('test'), 'other', execa); +test('Cannot use killSignal: {}', testInvalidKillSignal, {}, 'other', execa); +test('Cannot use killSignal: 0', testInvalidKillSignal, 0, 'zero', execa); +test('Cannot use killSignal: "SIGOTHER", sync', testInvalidKillSignal, 'SIGOTHER', 'string', execaSync); +test('Cannot use killSignal: "Sigterm", sync', testInvalidKillSignal, 'Sigterm', 'rename', execaSync); +test('Cannot use killSignal: "sigterm", sync', testInvalidKillSignal, 'sigterm', 'rename', execaSync); +test('Cannot use killSignal: -1, sync', testInvalidKillSignal, -1, 'integer', execaSync); +test('Cannot use killSignal: 200, sync', testInvalidKillSignal, 200, 'integer', execaSync); +test('Cannot use killSignal: 1.5, sync', testInvalidKillSignal, 1.5, 'other', execaSync); +test('Cannot use killSignal: Infinity, sync', testInvalidKillSignal, Number.POSITIVE_INFINITY, 'other', execaSync); +test('Cannot use killSignal: NaN, sync', testInvalidKillSignal, Number.NaN, 'other', execaSync); +test('Cannot use killSignal: null, sync', testInvalidKillSignal, null, 'other', execaSync); +test('Cannot use killSignal: symbol, sync', testInvalidKillSignal, Symbol('test'), 'other', execaSync); +test('Cannot use killSignal: {}, sync', testInvalidKillSignal, {}, 'other', execaSync); +test('Cannot use killSignal: 0, sync', testInvalidKillSignal, 0, 'zero', execaSync); + +const testInvalidSignalArgument = async (t, signal, type) => { + const subprocess = execa('empty.js'); + const {message} = t.throws(() => { + subprocess.kill(signal); + }); + + if (type === 'other') { + t.true(message.includes('must be an error instance or a signal name string/integer')); + } else { + t.true(message.includes('Invalid `subprocess.kill()`\'s argument')); + validateMessage(t, message, type); + } + + await subprocess; +}; + +test('Cannot use subprocess.kill("SIGOTHER")', testInvalidSignalArgument, 'SIGOTHER', 'string'); +test('Cannot use subprocess.kill("Sigterm")', testInvalidSignalArgument, 'Sigterm', 'rename'); +test('Cannot use subprocess.kill("sigterm")', testInvalidSignalArgument, 'sigterm', 'rename'); +test('Cannot use subprocess.kill(-1)', testInvalidSignalArgument, -1, 'integer'); +test('Cannot use subprocess.kill(200)', testInvalidSignalArgument, 200, 'integer'); +test('Cannot use subprocess.kill(1n)', testInvalidSignalArgument, 1n, 'other'); +test('Cannot use subprocess.kill(1.5)', testInvalidSignalArgument, 1.5, 'other'); +test('Cannot use subprocess.kill(Infinity)', testInvalidSignalArgument, Number.POSITIVE_INFINITY, 'other'); +test('Cannot use subprocess.kill(NaN)', testInvalidSignalArgument, Number.NaN, 'other'); +test('Cannot use subprocess.kill(false)', testInvalidSignalArgument, false, 'other'); +test('Cannot use subprocess.kill(null)', testInvalidSignalArgument, null, 'other'); +test('Cannot use subprocess.kill(symbol)', testInvalidSignalArgument, Symbol('test'), 'other'); +test('Cannot use subprocess.kill({})', testInvalidSignalArgument, {}, 'other'); diff --git a/types/arguments/options.d.ts b/types/arguments/options.d.ts index e3993be4b8..6067c8313d 100644 --- a/types/arguments/options.d.ts +++ b/types/arguments/options.d.ts @@ -278,7 +278,7 @@ export type CommonOptions = { @default 'SIGTERM' */ - readonly killSignal?: string | number; + readonly killSignal?: NodeJS.Signals | number; /** Run the subprocess independently from the current process. diff --git a/types/return/result.d.ts b/types/return/result.d.ts index 3b9afaf9cf..5dca7ff0a4 100644 --- a/types/return/result.d.ts +++ b/types/return/result.d.ts @@ -117,7 +117,7 @@ export declare abstract class CommonResult< If a signal terminated the subprocess, this property is defined and included in the error message. Otherwise it is `undefined`. */ - signal?: string; + signal?: NodeJS.Signals; /** A human-friendly description of the signal that was used to terminate the subprocess. diff --git a/types/subprocess/subprocess.d.ts b/types/subprocess/subprocess.d.ts index bf3c6f8906..c8ea7549d0 100644 --- a/types/subprocess/subprocess.d.ts +++ b/types/subprocess/subprocess.d.ts @@ -86,8 +86,8 @@ type ExecaCustomSubprocess = { [More info.](https://nodejs.org/api/child_process.html#subprocesskillsignal) */ - kill(signal: Parameters[0], error?: Error): ReturnType; - kill(error?: Error): ReturnType; + kill(signal?: NodeJS.Signals | number, error?: Error): boolean; + kill(error?: Error): boolean; /** Subprocesses are [async iterables](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol/asyncIterator). They iterate over each output line.