@karmaniverous/get-dotenv 4.4.3 → 4.4.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/getdotenv.cli.mjs +1865 -678
- package/dist/index.cjs +1861 -674
- package/dist/index.mjs +1865 -678
- package/package.json +15 -11
package/dist/index.mjs
CHANGED
|
@@ -1,17 +1,18 @@
|
|
|
1
|
-
import require$$0, { once,
|
|
1
|
+
import require$$0, { once, addAbortListener, EventEmitter as EventEmitter$1, on, setMaxListeners } from 'node:events';
|
|
2
2
|
import require$$1, { ChildProcess, spawnSync as spawnSync$1, spawn as spawn$1 } from 'node:child_process';
|
|
3
3
|
import path$s, { resolve, basename } from 'node:path';
|
|
4
4
|
import fs$v, { writeFileSync as writeFileSync$1, statSync as statSync$1, readFileSync as readFileSync$1, appendFileSync, createReadStream, createWriteStream } from 'node:fs';
|
|
5
|
-
import process$3, {
|
|
5
|
+
import process$3, { platform as platform$1, hrtime, execPath, execArgv } from 'node:process';
|
|
6
6
|
import { fileURLToPath } from 'node:url';
|
|
7
7
|
import { StringDecoder } from 'node:string_decoder';
|
|
8
8
|
import tty from 'node:tty';
|
|
9
|
-
import { debuglog, stripVTControlCharacters,
|
|
9
|
+
import { debuglog, stripVTControlCharacters, inspect, promisify as promisify$1, callbackify, aborted } from 'node:util';
|
|
10
10
|
import require$$0$3 from 'child_process';
|
|
11
11
|
import require$$0$2, { join } from 'path';
|
|
12
12
|
import require$$0$1 from 'fs';
|
|
13
|
-
import { setTimeout as setTimeout$1, setImmediate as setImmediate$1 } from 'node:timers/promises';
|
|
13
|
+
import { setTimeout as setTimeout$1, scheduler, setImmediate as setImmediate$1 } from 'node:timers/promises';
|
|
14
14
|
import { constants as constants$6 } from 'node:os';
|
|
15
|
+
import { serialize } from 'node:v8';
|
|
15
16
|
import { Transform, getDefaultHighWaterMark, PassThrough as PassThrough$1, Readable, Writable, Duplex } from 'node:stream';
|
|
16
17
|
import { Buffer as Buffer$1 } from 'node:buffer';
|
|
17
18
|
import { finished } from 'node:stream/promises';
|
|
@@ -4461,64 +4462,6 @@ const fallbackSymbols = {...common$7, ...specialFallbackSymbols};
|
|
|
4461
4462
|
const shouldUseMain = isUnicodeSupported();
|
|
4462
4463
|
const figures = shouldUseMain ? mainSymbols : fallbackSymbols;
|
|
4463
4464
|
|
|
4464
|
-
// Write synchronously to ensure lines are properly ordered and not interleaved with `stdout`
|
|
4465
|
-
const verboseLog = (string, verboseId, icon, color) => {
|
|
4466
|
-
const prefixedLines = addPrefix(string, verboseId, icon, color);
|
|
4467
|
-
writeFileSync$1(STDERR_FD, `${prefixedLines}\n`);
|
|
4468
|
-
};
|
|
4469
|
-
|
|
4470
|
-
const STDERR_FD = 2;
|
|
4471
|
-
|
|
4472
|
-
const addPrefix = (string, verboseId, icon, color) => string.includes('\n')
|
|
4473
|
-
? string
|
|
4474
|
-
.split('\n')
|
|
4475
|
-
.map(line => addPrefixToLine(line, verboseId, icon, color))
|
|
4476
|
-
.join('\n')
|
|
4477
|
-
: addPrefixToLine(string, verboseId, icon, color);
|
|
4478
|
-
|
|
4479
|
-
const addPrefixToLine = (line, verboseId, icon, color = identity$1) => [
|
|
4480
|
-
gray(`[${getTimestamp()}]`),
|
|
4481
|
-
gray(`[${verboseId}]`),
|
|
4482
|
-
color(ICONS[icon]),
|
|
4483
|
-
color(line),
|
|
4484
|
-
].join(' ');
|
|
4485
|
-
|
|
4486
|
-
const identity$1 = string => string;
|
|
4487
|
-
|
|
4488
|
-
// Prepending the timestamp allows debugging the slow paths of a subprocess
|
|
4489
|
-
const getTimestamp = () => {
|
|
4490
|
-
const date = new Date();
|
|
4491
|
-
return `${padField(date.getHours(), 2)}:${padField(date.getMinutes(), 2)}:${padField(date.getSeconds(), 2)}.${padField(date.getMilliseconds(), 3)}`;
|
|
4492
|
-
};
|
|
4493
|
-
|
|
4494
|
-
const padField = (field, padding) => String(field).padStart(padding, '0');
|
|
4495
|
-
|
|
4496
|
-
const ICONS = {
|
|
4497
|
-
command: '$',
|
|
4498
|
-
pipedCommand: '|',
|
|
4499
|
-
output: ' ',
|
|
4500
|
-
error: figures.cross,
|
|
4501
|
-
warning: figures.warning,
|
|
4502
|
-
success: figures.tick,
|
|
4503
|
-
};
|
|
4504
|
-
|
|
4505
|
-
// When `verbose` is `short|full`, print each command
|
|
4506
|
-
const logCommand = (escapedCommand, {verbose, verboseId}, {piped = false}) => {
|
|
4507
|
-
if (!isVerbose(verbose)) {
|
|
4508
|
-
return;
|
|
4509
|
-
}
|
|
4510
|
-
|
|
4511
|
-
const icon = piped ? 'pipedCommand' : 'command';
|
|
4512
|
-
verboseLog(escapedCommand, verboseId, icon, bold);
|
|
4513
|
-
};
|
|
4514
|
-
|
|
4515
|
-
// Start counting time before spawning the subprocess
|
|
4516
|
-
const getStartTime = () => hrtime.bigint();
|
|
4517
|
-
|
|
4518
|
-
// Compute duration after the subprocess ended.
|
|
4519
|
-
// Printed by the `verbose` option.
|
|
4520
|
-
const getDurationMs = startTime => Number(hrtime.bigint() - startTime) / 1e6;
|
|
4521
|
-
|
|
4522
4465
|
// Compute `result.command` and `result.escapedCommand`
|
|
4523
4466
|
const joinCommand = (filePath, rawArguments) => {
|
|
4524
4467
|
const fileAndArguments = [filePath, ...rawArguments];
|
|
@@ -4589,6 +4532,75 @@ const quoteString = escapedArgument => {
|
|
|
4589
4532
|
|
|
4590
4533
|
const NO_ESCAPE_REGEXP = /^[\w./-]+$/;
|
|
4591
4534
|
|
|
4535
|
+
// Write synchronously to ensure lines are properly ordered and not interleaved with `stdout`
|
|
4536
|
+
const verboseLog = (string, verboseId, icon, color) => {
|
|
4537
|
+
const prefixedLines = addPrefix(string, verboseId, icon, color);
|
|
4538
|
+
writeFileSync$1(STDERR_FD, `${prefixedLines}\n`);
|
|
4539
|
+
};
|
|
4540
|
+
|
|
4541
|
+
const STDERR_FD = 2;
|
|
4542
|
+
|
|
4543
|
+
const addPrefix = (string, verboseId, icon, color) => string.includes('\n')
|
|
4544
|
+
? string
|
|
4545
|
+
.split('\n')
|
|
4546
|
+
.map(line => addPrefixToLine(line, verboseId, icon, color))
|
|
4547
|
+
.join('\n')
|
|
4548
|
+
: addPrefixToLine(string, verboseId, icon, color);
|
|
4549
|
+
|
|
4550
|
+
const addPrefixToLine = (line, verboseId, icon, color = identity$1) => [
|
|
4551
|
+
gray(`[${getTimestamp()}]`),
|
|
4552
|
+
gray(`[${verboseId}]`),
|
|
4553
|
+
color(ICONS[icon]),
|
|
4554
|
+
color(line),
|
|
4555
|
+
].join(' ');
|
|
4556
|
+
|
|
4557
|
+
const identity$1 = string => string;
|
|
4558
|
+
|
|
4559
|
+
// Prepending the timestamp allows debugging the slow paths of a subprocess
|
|
4560
|
+
const getTimestamp = () => {
|
|
4561
|
+
const date = new Date();
|
|
4562
|
+
return `${padField(date.getHours(), 2)}:${padField(date.getMinutes(), 2)}:${padField(date.getSeconds(), 2)}.${padField(date.getMilliseconds(), 3)}`;
|
|
4563
|
+
};
|
|
4564
|
+
|
|
4565
|
+
const padField = (field, padding) => String(field).padStart(padding, '0');
|
|
4566
|
+
|
|
4567
|
+
const ICONS = {
|
|
4568
|
+
command: '$',
|
|
4569
|
+
pipedCommand: '|',
|
|
4570
|
+
output: ' ',
|
|
4571
|
+
ipc: '*',
|
|
4572
|
+
error: figures.cross,
|
|
4573
|
+
warning: figures.warning,
|
|
4574
|
+
success: figures.tick,
|
|
4575
|
+
};
|
|
4576
|
+
|
|
4577
|
+
// Serialize any type to a line string, for logging
|
|
4578
|
+
const serializeLogMessage = message => {
|
|
4579
|
+
const messageString = typeof message === 'string' ? message : inspect(message);
|
|
4580
|
+
const escapedMessage = escapeLines(messageString);
|
|
4581
|
+
return escapedMessage.replaceAll('\t', ' '.repeat(TAB_SIZE));
|
|
4582
|
+
};
|
|
4583
|
+
|
|
4584
|
+
// Same as `util.inspect()`
|
|
4585
|
+
const TAB_SIZE = 2;
|
|
4586
|
+
|
|
4587
|
+
// When `verbose` is `short|full`, print each command
|
|
4588
|
+
const logCommand = (escapedCommand, {verbose, verboseId}, {piped = false}) => {
|
|
4589
|
+
if (!isVerbose(verbose)) {
|
|
4590
|
+
return;
|
|
4591
|
+
}
|
|
4592
|
+
|
|
4593
|
+
const icon = piped ? 'pipedCommand' : 'command';
|
|
4594
|
+
verboseLog(escapedCommand, verboseId, icon, bold);
|
|
4595
|
+
};
|
|
4596
|
+
|
|
4597
|
+
// Start counting time before spawning the subprocess
|
|
4598
|
+
const getStartTime = () => hrtime.bigint();
|
|
4599
|
+
|
|
4600
|
+
// Compute duration after the subprocess ended.
|
|
4601
|
+
// Printed by the `verbose` option.
|
|
4602
|
+
const getDurationMs = startTime => Number(hrtime.bigint() - startTime) / 1e6;
|
|
4603
|
+
|
|
4592
4604
|
const isStandardStream = stream => STANDARD_STREAMS.includes(stream);
|
|
4593
4605
|
const STANDARD_STREAMS = [process$3.stdin, process$3.stdout, process$3.stderr];
|
|
4594
4606
|
const STANDARD_STREAMS_ALIASES = ['stdin', 'stdout', 'stderr'];
|
|
@@ -4608,7 +4620,7 @@ const normalizeFdSpecificOptions = options => {
|
|
|
4608
4620
|
};
|
|
4609
4621
|
|
|
4610
4622
|
const normalizeFdSpecificOption = (options, optionName) => {
|
|
4611
|
-
const optionBaseArray = Array.from({length: getStdioLength(options)});
|
|
4623
|
+
const optionBaseArray = Array.from({length: getStdioLength(options) + 1});
|
|
4612
4624
|
const optionArray = normalizeFdSpecificValue(options[optionName], optionBaseArray, optionName);
|
|
4613
4625
|
return addDefaultValue$1(optionArray, optionName);
|
|
4614
4626
|
};
|
|
@@ -4643,10 +4655,14 @@ const getFdNameOrder = fdName => {
|
|
|
4643
4655
|
};
|
|
4644
4656
|
|
|
4645
4657
|
const parseFdName = (fdName, optionName, optionArray) => {
|
|
4658
|
+
if (fdName === 'ipc') {
|
|
4659
|
+
return [optionArray.length - 1];
|
|
4660
|
+
}
|
|
4661
|
+
|
|
4646
4662
|
const fdNumber = parseFd(fdName);
|
|
4647
4663
|
if (fdNumber === undefined || fdNumber === 0) {
|
|
4648
4664
|
throw new TypeError(`"${optionName}.${fdName}" is invalid.
|
|
4649
|
-
It must be "${optionName}.stdout", "${optionName}.stderr", "${optionName}.all", or "${optionName}.fd3", "${optionName}.fd4" (and so on).`);
|
|
4665
|
+
It must be "${optionName}.stdout", "${optionName}.stderr", "${optionName}.all", "${optionName}.ipc", or "${optionName}.fd3", "${optionName}.fd4" (and so on).`);
|
|
4650
4666
|
}
|
|
4651
4667
|
|
|
4652
4668
|
if (fdNumber >= optionArray.length) {
|
|
@@ -5440,6 +5456,386 @@ setErrorName(ExecaError, ExecaError.name);
|
|
|
5440
5456
|
class ExecaSyncError extends Error {}
|
|
5441
5457
|
setErrorName(ExecaSyncError, ExecaSyncError.name);
|
|
5442
5458
|
|
|
5459
|
+
const getRealtimeSignals=()=>{
|
|
5460
|
+
const length=SIGRTMAX-SIGRTMIN+1;
|
|
5461
|
+
return Array.from({length},getRealtimeSignal)
|
|
5462
|
+
};
|
|
5463
|
+
|
|
5464
|
+
const getRealtimeSignal=(value,index)=>({
|
|
5465
|
+
name:`SIGRT${index+1}`,
|
|
5466
|
+
number:SIGRTMIN+index,
|
|
5467
|
+
action:"terminate",
|
|
5468
|
+
description:"Application-specific signal (realtime)",
|
|
5469
|
+
standard:"posix"
|
|
5470
|
+
});
|
|
5471
|
+
|
|
5472
|
+
const SIGRTMIN=34;
|
|
5473
|
+
const SIGRTMAX=64;
|
|
5474
|
+
|
|
5475
|
+
const SIGNALS=[
|
|
5476
|
+
{
|
|
5477
|
+
name:"SIGHUP",
|
|
5478
|
+
number:1,
|
|
5479
|
+
action:"terminate",
|
|
5480
|
+
description:"Terminal closed",
|
|
5481
|
+
standard:"posix"
|
|
5482
|
+
},
|
|
5483
|
+
{
|
|
5484
|
+
name:"SIGINT",
|
|
5485
|
+
number:2,
|
|
5486
|
+
action:"terminate",
|
|
5487
|
+
description:"User interruption with CTRL-C",
|
|
5488
|
+
standard:"ansi"
|
|
5489
|
+
},
|
|
5490
|
+
{
|
|
5491
|
+
name:"SIGQUIT",
|
|
5492
|
+
number:3,
|
|
5493
|
+
action:"core",
|
|
5494
|
+
description:"User interruption with CTRL-\\",
|
|
5495
|
+
standard:"posix"
|
|
5496
|
+
},
|
|
5497
|
+
{
|
|
5498
|
+
name:"SIGILL",
|
|
5499
|
+
number:4,
|
|
5500
|
+
action:"core",
|
|
5501
|
+
description:"Invalid machine instruction",
|
|
5502
|
+
standard:"ansi"
|
|
5503
|
+
},
|
|
5504
|
+
{
|
|
5505
|
+
name:"SIGTRAP",
|
|
5506
|
+
number:5,
|
|
5507
|
+
action:"core",
|
|
5508
|
+
description:"Debugger breakpoint",
|
|
5509
|
+
standard:"posix"
|
|
5510
|
+
},
|
|
5511
|
+
{
|
|
5512
|
+
name:"SIGABRT",
|
|
5513
|
+
number:6,
|
|
5514
|
+
action:"core",
|
|
5515
|
+
description:"Aborted",
|
|
5516
|
+
standard:"ansi"
|
|
5517
|
+
},
|
|
5518
|
+
{
|
|
5519
|
+
name:"SIGIOT",
|
|
5520
|
+
number:6,
|
|
5521
|
+
action:"core",
|
|
5522
|
+
description:"Aborted",
|
|
5523
|
+
standard:"bsd"
|
|
5524
|
+
},
|
|
5525
|
+
{
|
|
5526
|
+
name:"SIGBUS",
|
|
5527
|
+
number:7,
|
|
5528
|
+
action:"core",
|
|
5529
|
+
description:
|
|
5530
|
+
"Bus error due to misaligned, non-existing address or paging error",
|
|
5531
|
+
standard:"bsd"
|
|
5532
|
+
},
|
|
5533
|
+
{
|
|
5534
|
+
name:"SIGEMT",
|
|
5535
|
+
number:7,
|
|
5536
|
+
action:"terminate",
|
|
5537
|
+
description:"Command should be emulated but is not implemented",
|
|
5538
|
+
standard:"other"
|
|
5539
|
+
},
|
|
5540
|
+
{
|
|
5541
|
+
name:"SIGFPE",
|
|
5542
|
+
number:8,
|
|
5543
|
+
action:"core",
|
|
5544
|
+
description:"Floating point arithmetic error",
|
|
5545
|
+
standard:"ansi"
|
|
5546
|
+
},
|
|
5547
|
+
{
|
|
5548
|
+
name:"SIGKILL",
|
|
5549
|
+
number:9,
|
|
5550
|
+
action:"terminate",
|
|
5551
|
+
description:"Forced termination",
|
|
5552
|
+
standard:"posix",
|
|
5553
|
+
forced:true
|
|
5554
|
+
},
|
|
5555
|
+
{
|
|
5556
|
+
name:"SIGUSR1",
|
|
5557
|
+
number:10,
|
|
5558
|
+
action:"terminate",
|
|
5559
|
+
description:"Application-specific signal",
|
|
5560
|
+
standard:"posix"
|
|
5561
|
+
},
|
|
5562
|
+
{
|
|
5563
|
+
name:"SIGSEGV",
|
|
5564
|
+
number:11,
|
|
5565
|
+
action:"core",
|
|
5566
|
+
description:"Segmentation fault",
|
|
5567
|
+
standard:"ansi"
|
|
5568
|
+
},
|
|
5569
|
+
{
|
|
5570
|
+
name:"SIGUSR2",
|
|
5571
|
+
number:12,
|
|
5572
|
+
action:"terminate",
|
|
5573
|
+
description:"Application-specific signal",
|
|
5574
|
+
standard:"posix"
|
|
5575
|
+
},
|
|
5576
|
+
{
|
|
5577
|
+
name:"SIGPIPE",
|
|
5578
|
+
number:13,
|
|
5579
|
+
action:"terminate",
|
|
5580
|
+
description:"Broken pipe or socket",
|
|
5581
|
+
standard:"posix"
|
|
5582
|
+
},
|
|
5583
|
+
{
|
|
5584
|
+
name:"SIGALRM",
|
|
5585
|
+
number:14,
|
|
5586
|
+
action:"terminate",
|
|
5587
|
+
description:"Timeout or timer",
|
|
5588
|
+
standard:"posix"
|
|
5589
|
+
},
|
|
5590
|
+
{
|
|
5591
|
+
name:"SIGTERM",
|
|
5592
|
+
number:15,
|
|
5593
|
+
action:"terminate",
|
|
5594
|
+
description:"Termination",
|
|
5595
|
+
standard:"ansi"
|
|
5596
|
+
},
|
|
5597
|
+
{
|
|
5598
|
+
name:"SIGSTKFLT",
|
|
5599
|
+
number:16,
|
|
5600
|
+
action:"terminate",
|
|
5601
|
+
description:"Stack is empty or overflowed",
|
|
5602
|
+
standard:"other"
|
|
5603
|
+
},
|
|
5604
|
+
{
|
|
5605
|
+
name:"SIGCHLD",
|
|
5606
|
+
number:17,
|
|
5607
|
+
action:"ignore",
|
|
5608
|
+
description:"Child process terminated, paused or unpaused",
|
|
5609
|
+
standard:"posix"
|
|
5610
|
+
},
|
|
5611
|
+
{
|
|
5612
|
+
name:"SIGCLD",
|
|
5613
|
+
number:17,
|
|
5614
|
+
action:"ignore",
|
|
5615
|
+
description:"Child process terminated, paused or unpaused",
|
|
5616
|
+
standard:"other"
|
|
5617
|
+
},
|
|
5618
|
+
{
|
|
5619
|
+
name:"SIGCONT",
|
|
5620
|
+
number:18,
|
|
5621
|
+
action:"unpause",
|
|
5622
|
+
description:"Unpaused",
|
|
5623
|
+
standard:"posix",
|
|
5624
|
+
forced:true
|
|
5625
|
+
},
|
|
5626
|
+
{
|
|
5627
|
+
name:"SIGSTOP",
|
|
5628
|
+
number:19,
|
|
5629
|
+
action:"pause",
|
|
5630
|
+
description:"Paused",
|
|
5631
|
+
standard:"posix",
|
|
5632
|
+
forced:true
|
|
5633
|
+
},
|
|
5634
|
+
{
|
|
5635
|
+
name:"SIGTSTP",
|
|
5636
|
+
number:20,
|
|
5637
|
+
action:"pause",
|
|
5638
|
+
description:"Paused using CTRL-Z or \"suspend\"",
|
|
5639
|
+
standard:"posix"
|
|
5640
|
+
},
|
|
5641
|
+
{
|
|
5642
|
+
name:"SIGTTIN",
|
|
5643
|
+
number:21,
|
|
5644
|
+
action:"pause",
|
|
5645
|
+
description:"Background process cannot read terminal input",
|
|
5646
|
+
standard:"posix"
|
|
5647
|
+
},
|
|
5648
|
+
{
|
|
5649
|
+
name:"SIGBREAK",
|
|
5650
|
+
number:21,
|
|
5651
|
+
action:"terminate",
|
|
5652
|
+
description:"User interruption with CTRL-BREAK",
|
|
5653
|
+
standard:"other"
|
|
5654
|
+
},
|
|
5655
|
+
{
|
|
5656
|
+
name:"SIGTTOU",
|
|
5657
|
+
number:22,
|
|
5658
|
+
action:"pause",
|
|
5659
|
+
description:"Background process cannot write to terminal output",
|
|
5660
|
+
standard:"posix"
|
|
5661
|
+
},
|
|
5662
|
+
{
|
|
5663
|
+
name:"SIGURG",
|
|
5664
|
+
number:23,
|
|
5665
|
+
action:"ignore",
|
|
5666
|
+
description:"Socket received out-of-band data",
|
|
5667
|
+
standard:"bsd"
|
|
5668
|
+
},
|
|
5669
|
+
{
|
|
5670
|
+
name:"SIGXCPU",
|
|
5671
|
+
number:24,
|
|
5672
|
+
action:"core",
|
|
5673
|
+
description:"Process timed out",
|
|
5674
|
+
standard:"bsd"
|
|
5675
|
+
},
|
|
5676
|
+
{
|
|
5677
|
+
name:"SIGXFSZ",
|
|
5678
|
+
number:25,
|
|
5679
|
+
action:"core",
|
|
5680
|
+
description:"File too big",
|
|
5681
|
+
standard:"bsd"
|
|
5682
|
+
},
|
|
5683
|
+
{
|
|
5684
|
+
name:"SIGVTALRM",
|
|
5685
|
+
number:26,
|
|
5686
|
+
action:"terminate",
|
|
5687
|
+
description:"Timeout or timer",
|
|
5688
|
+
standard:"bsd"
|
|
5689
|
+
},
|
|
5690
|
+
{
|
|
5691
|
+
name:"SIGPROF",
|
|
5692
|
+
number:27,
|
|
5693
|
+
action:"terminate",
|
|
5694
|
+
description:"Timeout or timer",
|
|
5695
|
+
standard:"bsd"
|
|
5696
|
+
},
|
|
5697
|
+
{
|
|
5698
|
+
name:"SIGWINCH",
|
|
5699
|
+
number:28,
|
|
5700
|
+
action:"ignore",
|
|
5701
|
+
description:"Terminal window size changed",
|
|
5702
|
+
standard:"bsd"
|
|
5703
|
+
},
|
|
5704
|
+
{
|
|
5705
|
+
name:"SIGIO",
|
|
5706
|
+
number:29,
|
|
5707
|
+
action:"terminate",
|
|
5708
|
+
description:"I/O is available",
|
|
5709
|
+
standard:"other"
|
|
5710
|
+
},
|
|
5711
|
+
{
|
|
5712
|
+
name:"SIGPOLL",
|
|
5713
|
+
number:29,
|
|
5714
|
+
action:"terminate",
|
|
5715
|
+
description:"Watched event",
|
|
5716
|
+
standard:"other"
|
|
5717
|
+
},
|
|
5718
|
+
{
|
|
5719
|
+
name:"SIGINFO",
|
|
5720
|
+
number:29,
|
|
5721
|
+
action:"ignore",
|
|
5722
|
+
description:"Request for process information",
|
|
5723
|
+
standard:"other"
|
|
5724
|
+
},
|
|
5725
|
+
{
|
|
5726
|
+
name:"SIGPWR",
|
|
5727
|
+
number:30,
|
|
5728
|
+
action:"terminate",
|
|
5729
|
+
description:"Device running out of power",
|
|
5730
|
+
standard:"systemv"
|
|
5731
|
+
},
|
|
5732
|
+
{
|
|
5733
|
+
name:"SIGSYS",
|
|
5734
|
+
number:31,
|
|
5735
|
+
action:"core",
|
|
5736
|
+
description:"Invalid system call",
|
|
5737
|
+
standard:"other"
|
|
5738
|
+
},
|
|
5739
|
+
{
|
|
5740
|
+
name:"SIGUNUSED",
|
|
5741
|
+
number:31,
|
|
5742
|
+
action:"terminate",
|
|
5743
|
+
description:"Invalid system call",
|
|
5744
|
+
standard:"other"
|
|
5745
|
+
}];
|
|
5746
|
+
|
|
5747
|
+
const getSignals=()=>{
|
|
5748
|
+
const realtimeSignals=getRealtimeSignals();
|
|
5749
|
+
const signals=[...SIGNALS,...realtimeSignals].map(normalizeSignal$1);
|
|
5750
|
+
return signals
|
|
5751
|
+
};
|
|
5752
|
+
|
|
5753
|
+
|
|
5754
|
+
|
|
5755
|
+
|
|
5756
|
+
|
|
5757
|
+
|
|
5758
|
+
|
|
5759
|
+
const normalizeSignal$1=({
|
|
5760
|
+
name,
|
|
5761
|
+
number:defaultNumber,
|
|
5762
|
+
description,
|
|
5763
|
+
action,
|
|
5764
|
+
forced=false,
|
|
5765
|
+
standard
|
|
5766
|
+
})=>{
|
|
5767
|
+
const{
|
|
5768
|
+
signals:{[name]:constantSignal}
|
|
5769
|
+
}=constants$6;
|
|
5770
|
+
const supported=constantSignal!==undefined;
|
|
5771
|
+
const number=supported?constantSignal:defaultNumber;
|
|
5772
|
+
return {name,number,description,supported,action,forced,standard}
|
|
5773
|
+
};
|
|
5774
|
+
|
|
5775
|
+
const getSignalsByName=()=>{
|
|
5776
|
+
const signals=getSignals();
|
|
5777
|
+
return Object.fromEntries(signals.map(getSignalByName))
|
|
5778
|
+
};
|
|
5779
|
+
|
|
5780
|
+
const getSignalByName=({
|
|
5781
|
+
name,
|
|
5782
|
+
number,
|
|
5783
|
+
description,
|
|
5784
|
+
supported,
|
|
5785
|
+
action,
|
|
5786
|
+
forced,
|
|
5787
|
+
standard
|
|
5788
|
+
})=>[name,{name,number,description,supported,action,forced,standard}];
|
|
5789
|
+
|
|
5790
|
+
const signalsByName=getSignalsByName();
|
|
5791
|
+
|
|
5792
|
+
|
|
5793
|
+
|
|
5794
|
+
|
|
5795
|
+
const getSignalsByNumber=()=>{
|
|
5796
|
+
const signals=getSignals();
|
|
5797
|
+
const length=SIGRTMAX+1;
|
|
5798
|
+
const signalsA=Array.from({length},(value,number)=>
|
|
5799
|
+
getSignalByNumber(number,signals)
|
|
5800
|
+
);
|
|
5801
|
+
return Object.assign({},...signalsA)
|
|
5802
|
+
};
|
|
5803
|
+
|
|
5804
|
+
const getSignalByNumber=(number,signals)=>{
|
|
5805
|
+
const signal=findSignalByNumber(number,signals);
|
|
5806
|
+
|
|
5807
|
+
if(signal===undefined){
|
|
5808
|
+
return {}
|
|
5809
|
+
}
|
|
5810
|
+
|
|
5811
|
+
const{name,description,supported,action,forced,standard}=signal;
|
|
5812
|
+
return {
|
|
5813
|
+
[number]:{
|
|
5814
|
+
name,
|
|
5815
|
+
number,
|
|
5816
|
+
description,
|
|
5817
|
+
supported,
|
|
5818
|
+
action,
|
|
5819
|
+
forced,
|
|
5820
|
+
standard
|
|
5821
|
+
}
|
|
5822
|
+
}
|
|
5823
|
+
};
|
|
5824
|
+
|
|
5825
|
+
|
|
5826
|
+
|
|
5827
|
+
const findSignalByNumber=(number,signals)=>{
|
|
5828
|
+
const signal=signals.find(({name})=>constants$6.signals[name]===number);
|
|
5829
|
+
|
|
5830
|
+
if(signal!==undefined){
|
|
5831
|
+
return signal
|
|
5832
|
+
}
|
|
5833
|
+
|
|
5834
|
+
return signals.find((signalA)=>signalA.number===number)
|
|
5835
|
+
};
|
|
5836
|
+
|
|
5837
|
+
getSignalsByNumber();
|
|
5838
|
+
|
|
5443
5839
|
// Normalize signals for comparison purpose.
|
|
5444
5840
|
// Also validate the signal exists.
|
|
5445
5841
|
const normalizeKillSignal = killSignal => {
|
|
@@ -5448,14 +5844,14 @@ const normalizeKillSignal = killSignal => {
|
|
|
5448
5844
|
throw new TypeError(`Invalid ${optionName}: 0 cannot be used.`);
|
|
5449
5845
|
}
|
|
5450
5846
|
|
|
5451
|
-
return normalizeSignal
|
|
5847
|
+
return normalizeSignal(killSignal, optionName);
|
|
5452
5848
|
};
|
|
5453
5849
|
|
|
5454
5850
|
const normalizeSignalArgument = signal => signal === 0
|
|
5455
5851
|
? signal
|
|
5456
|
-
: normalizeSignal
|
|
5852
|
+
: normalizeSignal(signal, '`subprocess.kill()`\'s argument');
|
|
5457
5853
|
|
|
5458
|
-
const normalizeSignal
|
|
5854
|
+
const normalizeSignal = (signalNameOrInteger, optionName) => {
|
|
5459
5855
|
if (Number.isInteger(signalNameOrInteger)) {
|
|
5460
5856
|
return normalizeSignalInteger(signalNameOrInteger, optionName);
|
|
5461
5857
|
}
|
|
@@ -5505,6 +5901,9 @@ const getAvailableSignalIntegers = () => [...new Set(Object.values(constants$6.s
|
|
|
5505
5901
|
.sort((signalInteger, signalIntegerTwo) => signalInteger - signalIntegerTwo))]
|
|
5506
5902
|
.join(', ');
|
|
5507
5903
|
|
|
5904
|
+
// Human-friendly description of a signal
|
|
5905
|
+
const getSignalDescription = signal => signalsByName[signal].description;
|
|
5906
|
+
|
|
5508
5907
|
// Normalize the `forceKillAfterDelay` option
|
|
5509
5908
|
const normalizeForceKillAfterDelay = forceKillAfterDelay => {
|
|
5510
5909
|
if (forceKillAfterDelay === false) {
|
|
@@ -5526,7 +5925,7 @@ const DEFAULT_FORCE_KILL_TIMEOUT = 1000 * 5;
|
|
|
5526
5925
|
|
|
5527
5926
|
// Monkey-patches `subprocess.kill()` to add `forceKillAfterDelay` behavior and `.kill(error)`
|
|
5528
5927
|
const subprocessKill = (
|
|
5529
|
-
{kill, options: {forceKillAfterDelay, killSignal}, onInternalError, controller},
|
|
5928
|
+
{kill, options: {forceKillAfterDelay, killSignal}, onInternalError, context, controller},
|
|
5530
5929
|
signalOrError,
|
|
5531
5930
|
errorArgument,
|
|
5532
5931
|
) => {
|
|
@@ -5539,6 +5938,7 @@ const subprocessKill = (
|
|
|
5539
5938
|
forceKillAfterDelay,
|
|
5540
5939
|
killSignal,
|
|
5541
5940
|
killResult,
|
|
5941
|
+
context,
|
|
5542
5942
|
controller,
|
|
5543
5943
|
});
|
|
5544
5944
|
return killResult;
|
|
@@ -5569,20 +5969,834 @@ const emitKillError = (error, onInternalError) => {
|
|
|
5569
5969
|
}
|
|
5570
5970
|
};
|
|
5571
5971
|
|
|
5572
|
-
const setKillTimeout = async ({kill, signal, forceKillAfterDelay, killSignal, killResult, controller}) => {
|
|
5573
|
-
if (
|
|
5972
|
+
const setKillTimeout = async ({kill, signal, forceKillAfterDelay, killSignal, killResult, context, controller}) => {
|
|
5973
|
+
if (signal === killSignal && killResult) {
|
|
5974
|
+
killOnTimeout({
|
|
5975
|
+
kill,
|
|
5976
|
+
forceKillAfterDelay,
|
|
5977
|
+
context,
|
|
5978
|
+
controllerSignal: controller.signal,
|
|
5979
|
+
});
|
|
5980
|
+
}
|
|
5981
|
+
};
|
|
5982
|
+
|
|
5983
|
+
// Forcefully terminate a subprocess after a timeout
|
|
5984
|
+
const killOnTimeout = async ({kill, forceKillAfterDelay, context, controllerSignal}) => {
|
|
5985
|
+
if (forceKillAfterDelay === false) {
|
|
5574
5986
|
return;
|
|
5575
5987
|
}
|
|
5576
5988
|
|
|
5577
5989
|
try {
|
|
5578
|
-
await setTimeout$1(forceKillAfterDelay, undefined, {signal:
|
|
5579
|
-
kill('SIGKILL')
|
|
5990
|
+
await setTimeout$1(forceKillAfterDelay, undefined, {signal: controllerSignal});
|
|
5991
|
+
if (kill('SIGKILL')) {
|
|
5992
|
+
context.isForcefullyTerminated ??= true;
|
|
5993
|
+
}
|
|
5580
5994
|
} catch {}
|
|
5581
5995
|
};
|
|
5582
5996
|
|
|
5583
|
-
|
|
5584
|
-
|
|
5585
|
-
|
|
5997
|
+
// Combines `util.aborted()` and `events.addAbortListener()`: promise-based and cleaned up with a stop signal
|
|
5998
|
+
const onAbortedSignal = async (mainSignal, stopSignal) => {
|
|
5999
|
+
if (!mainSignal.aborted) {
|
|
6000
|
+
await once(mainSignal, 'abort', {signal: stopSignal});
|
|
6001
|
+
}
|
|
6002
|
+
};
|
|
6003
|
+
|
|
6004
|
+
// Validate the `cancelSignal` option
|
|
6005
|
+
const validateCancelSignal = ({cancelSignal}) => {
|
|
6006
|
+
if (cancelSignal !== undefined && Object.prototype.toString.call(cancelSignal) !== '[object AbortSignal]') {
|
|
6007
|
+
throw new Error(`The \`cancelSignal\` option must be an AbortSignal: ${String(cancelSignal)}`);
|
|
6008
|
+
}
|
|
6009
|
+
};
|
|
6010
|
+
|
|
6011
|
+
// Terminate the subprocess when aborting the `cancelSignal` option and `gracefulSignal` is `false`
|
|
6012
|
+
const throwOnCancel = ({subprocess, cancelSignal, gracefulCancel, context, controller}) => cancelSignal === undefined || gracefulCancel
|
|
6013
|
+
? []
|
|
6014
|
+
: [terminateOnCancel(subprocess, cancelSignal, context, controller)];
|
|
6015
|
+
|
|
6016
|
+
const terminateOnCancel = async (subprocess, cancelSignal, context, {signal}) => {
|
|
6017
|
+
await onAbortedSignal(cancelSignal, signal);
|
|
6018
|
+
context.terminationReason ??= 'cancel';
|
|
6019
|
+
subprocess.kill();
|
|
6020
|
+
throw cancelSignal.reason;
|
|
6021
|
+
};
|
|
6022
|
+
|
|
6023
|
+
// Validate the IPC channel is connected before receiving/sending messages
|
|
6024
|
+
const validateIpcMethod = ({methodName, isSubprocess, ipc, isConnected}) => {
|
|
6025
|
+
validateIpcOption(methodName, isSubprocess, ipc);
|
|
6026
|
+
validateConnection(methodName, isSubprocess, isConnected);
|
|
6027
|
+
};
|
|
6028
|
+
|
|
6029
|
+
// Better error message when forgetting to set `ipc: true` and using the IPC methods
|
|
6030
|
+
const validateIpcOption = (methodName, isSubprocess, ipc) => {
|
|
6031
|
+
if (!ipc) {
|
|
6032
|
+
throw new Error(`${getMethodName(methodName, isSubprocess)} can only be used if the \`ipc\` option is \`true\`.`);
|
|
6033
|
+
}
|
|
6034
|
+
};
|
|
6035
|
+
|
|
6036
|
+
// Better error message when one process does not send/receive messages once the other process has disconnected.
|
|
6037
|
+
// This also makes it clear that any buffered messages are lost once either process has disconnected.
|
|
6038
|
+
// Also when aborting `cancelSignal` after disconnecting the IPC.
|
|
6039
|
+
const validateConnection = (methodName, isSubprocess, isConnected) => {
|
|
6040
|
+
if (!isConnected) {
|
|
6041
|
+
throw new Error(`${getMethodName(methodName, isSubprocess)} cannot be used: the ${getOtherProcessName(isSubprocess)} has already exited or disconnected.`);
|
|
6042
|
+
}
|
|
6043
|
+
};
|
|
6044
|
+
|
|
6045
|
+
// When `getOneMessage()` could not complete due to an early disconnection
|
|
6046
|
+
const throwOnEarlyDisconnect = isSubprocess => {
|
|
6047
|
+
throw new Error(`${getMethodName('getOneMessage', isSubprocess)} could not complete: the ${getOtherProcessName(isSubprocess)} exited or disconnected.`);
|
|
6048
|
+
};
|
|
6049
|
+
|
|
6050
|
+
// When both processes use `sendMessage()` with `strict` at the same time
|
|
6051
|
+
const throwOnStrictDeadlockError = isSubprocess => {
|
|
6052
|
+
throw new Error(`${getMethodName('sendMessage', isSubprocess)} failed: the ${getOtherProcessName(isSubprocess)} is sending a message too, instead of listening to incoming messages.
|
|
6053
|
+
This can be fixed by both sending a message and listening to incoming messages at the same time:
|
|
6054
|
+
|
|
6055
|
+
const [receivedMessage] = await Promise.all([
|
|
6056
|
+
${getMethodName('getOneMessage', isSubprocess)},
|
|
6057
|
+
${getMethodName('sendMessage', isSubprocess, 'message, {strict: true}')},
|
|
6058
|
+
]);`);
|
|
6059
|
+
};
|
|
6060
|
+
|
|
6061
|
+
// When the other process used `strict` but the current process had I/O error calling `sendMessage()` for the response
|
|
6062
|
+
const getStrictResponseError = (error, isSubprocess) => new Error(`${getMethodName('sendMessage', isSubprocess)} failed when sending an acknowledgment response to the ${getOtherProcessName(isSubprocess)}.`, {cause: error});
|
|
6063
|
+
|
|
6064
|
+
// When using `strict` but the other process was not listening for messages
|
|
6065
|
+
const throwOnMissingStrict = isSubprocess => {
|
|
6066
|
+
throw new Error(`${getMethodName('sendMessage', isSubprocess)} failed: the ${getOtherProcessName(isSubprocess)} is not listening to incoming messages.`);
|
|
6067
|
+
};
|
|
6068
|
+
|
|
6069
|
+
// When using `strict` but the other process disconnected before receiving the message
|
|
6070
|
+
const throwOnStrictDisconnect = isSubprocess => {
|
|
6071
|
+
throw new Error(`${getMethodName('sendMessage', isSubprocess)} failed: the ${getOtherProcessName(isSubprocess)} exited without listening to incoming messages.`);
|
|
6072
|
+
};
|
|
6073
|
+
|
|
6074
|
+
// When the current process disconnects while the subprocess is listening to `cancelSignal`
|
|
6075
|
+
const getAbortDisconnectError = () => new Error(`\`cancelSignal\` aborted: the ${getOtherProcessName(true)} disconnected.`);
|
|
6076
|
+
|
|
6077
|
+
// When the subprocess uses `cancelSignal` but not the current process
|
|
6078
|
+
const throwOnMissingParent = () => {
|
|
6079
|
+
throw new Error('`getCancelSignal()` cannot be used without setting the `cancelSignal` subprocess option.');
|
|
6080
|
+
};
|
|
6081
|
+
|
|
6082
|
+
// EPIPE can happen when sending a message to a subprocess that is closing but has not disconnected yet
|
|
6083
|
+
const handleEpipeError = ({error, methodName, isSubprocess}) => {
|
|
6084
|
+
if (error.code === 'EPIPE') {
|
|
6085
|
+
throw new Error(`${getMethodName(methodName, isSubprocess)} cannot be used: the ${getOtherProcessName(isSubprocess)} is disconnecting.`, {cause: error});
|
|
6086
|
+
}
|
|
6087
|
+
};
|
|
6088
|
+
|
|
6089
|
+
// Better error message when sending messages which cannot be serialized.
|
|
6090
|
+
// Works with both `serialization: 'advanced'` and `serialization: 'json'`.
|
|
6091
|
+
const handleSerializationError = ({error, methodName, isSubprocess, message}) => {
|
|
6092
|
+
if (isSerializationError(error)) {
|
|
6093
|
+
throw new Error(`${getMethodName(methodName, isSubprocess)}'s argument type is invalid: the message cannot be serialized: ${String(message)}.`, {cause: error});
|
|
6094
|
+
}
|
|
6095
|
+
};
|
|
6096
|
+
|
|
6097
|
+
const isSerializationError = ({code, message}) => SERIALIZATION_ERROR_CODES.has(code)
|
|
6098
|
+
|| SERIALIZATION_ERROR_MESSAGES.some(serializationErrorMessage => message.includes(serializationErrorMessage));
|
|
6099
|
+
|
|
6100
|
+
// `error.code` set by Node.js when it failed to serialize the message
|
|
6101
|
+
const SERIALIZATION_ERROR_CODES = new Set([
|
|
6102
|
+
// Message is `undefined`
|
|
6103
|
+
'ERR_MISSING_ARGS',
|
|
6104
|
+
// Message is a function, a bigint, a symbol
|
|
6105
|
+
'ERR_INVALID_ARG_TYPE',
|
|
6106
|
+
]);
|
|
6107
|
+
|
|
6108
|
+
// `error.message` set by Node.js when it failed to serialize the message
|
|
6109
|
+
const SERIALIZATION_ERROR_MESSAGES = [
|
|
6110
|
+
// Message is a promise or a proxy, with `serialization: 'advanced'`
|
|
6111
|
+
'could not be cloned',
|
|
6112
|
+
// Message has cycles, with `serialization: 'json'`
|
|
6113
|
+
'circular structure',
|
|
6114
|
+
// Message has cycles inside toJSON(), with `serialization: 'json'`
|
|
6115
|
+
'call stack size exceeded',
|
|
6116
|
+
];
|
|
6117
|
+
|
|
6118
|
+
const getMethodName = (methodName, isSubprocess, parameters = '') => methodName === 'cancelSignal'
|
|
6119
|
+
? '`cancelSignal`\'s `controller.abort()`'
|
|
6120
|
+
: `${getNamespaceName(isSubprocess)}${methodName}(${parameters})`;
|
|
6121
|
+
|
|
6122
|
+
const getNamespaceName = isSubprocess => isSubprocess ? '' : 'subprocess.';
|
|
6123
|
+
|
|
6124
|
+
const getOtherProcessName = isSubprocess => isSubprocess ? 'parent process' : 'subprocess';
|
|
6125
|
+
|
|
6126
|
+
// When any error arises, we disconnect the IPC.
|
|
6127
|
+
// Otherwise, it is likely that one of the processes will stop sending/receiving messages.
|
|
6128
|
+
// This would leave the other process hanging.
|
|
6129
|
+
const disconnect = anyProcess => {
|
|
6130
|
+
if (anyProcess.connected) {
|
|
6131
|
+
anyProcess.disconnect();
|
|
6132
|
+
}
|
|
6133
|
+
};
|
|
6134
|
+
|
|
6135
|
+
const createDeferred = () => {
|
|
6136
|
+
const methods = {};
|
|
6137
|
+
const promise = new Promise((resolve, reject) => {
|
|
6138
|
+
Object.assign(methods, {resolve, reject});
|
|
6139
|
+
});
|
|
6140
|
+
return Object.assign(promise, methods);
|
|
6141
|
+
};
|
|
6142
|
+
|
|
6143
|
+
// Retrieve stream targeted by the `to` option
|
|
6144
|
+
const getToStream = (destination, to = 'stdin') => {
|
|
6145
|
+
const isWritable = true;
|
|
6146
|
+
const {options, fileDescriptors} = SUBPROCESS_OPTIONS.get(destination);
|
|
6147
|
+
const fdNumber = getFdNumber(fileDescriptors, to, isWritable);
|
|
6148
|
+
const destinationStream = destination.stdio[fdNumber];
|
|
6149
|
+
|
|
6150
|
+
if (destinationStream === null) {
|
|
6151
|
+
throw new TypeError(getInvalidStdioOptionMessage(fdNumber, to, options, isWritable));
|
|
6152
|
+
}
|
|
6153
|
+
|
|
6154
|
+
return destinationStream;
|
|
6155
|
+
};
|
|
6156
|
+
|
|
6157
|
+
// Retrieve stream targeted by the `from` option
|
|
6158
|
+
const getFromStream = (source, from = 'stdout') => {
|
|
6159
|
+
const isWritable = false;
|
|
6160
|
+
const {options, fileDescriptors} = SUBPROCESS_OPTIONS.get(source);
|
|
6161
|
+
const fdNumber = getFdNumber(fileDescriptors, from, isWritable);
|
|
6162
|
+
const sourceStream = fdNumber === 'all' ? source.all : source.stdio[fdNumber];
|
|
6163
|
+
|
|
6164
|
+
if (sourceStream === null || sourceStream === undefined) {
|
|
6165
|
+
throw new TypeError(getInvalidStdioOptionMessage(fdNumber, from, options, isWritable));
|
|
6166
|
+
}
|
|
6167
|
+
|
|
6168
|
+
return sourceStream;
|
|
6169
|
+
};
|
|
6170
|
+
|
|
6171
|
+
// Keeps track of the options passed to each Execa call
|
|
6172
|
+
const SUBPROCESS_OPTIONS = new WeakMap();
|
|
6173
|
+
|
|
6174
|
+
const getFdNumber = (fileDescriptors, fdName, isWritable) => {
|
|
6175
|
+
const fdNumber = parseFdNumber(fdName, isWritable);
|
|
6176
|
+
validateFdNumber(fdNumber, fdName, isWritable, fileDescriptors);
|
|
6177
|
+
return fdNumber;
|
|
6178
|
+
};
|
|
6179
|
+
|
|
6180
|
+
const parseFdNumber = (fdName, isWritable) => {
|
|
6181
|
+
const fdNumber = parseFd(fdName);
|
|
6182
|
+
if (fdNumber !== undefined) {
|
|
6183
|
+
return fdNumber;
|
|
6184
|
+
}
|
|
6185
|
+
|
|
6186
|
+
const {validOptions, defaultValue} = isWritable
|
|
6187
|
+
? {validOptions: '"stdin"', defaultValue: 'stdin'}
|
|
6188
|
+
: {validOptions: '"stdout", "stderr", "all"', defaultValue: 'stdout'};
|
|
6189
|
+
throw new TypeError(`"${getOptionName(isWritable)}" must not be "${fdName}".
|
|
6190
|
+
It must be ${validOptions} or "fd3", "fd4" (and so on).
|
|
6191
|
+
It is optional and defaults to "${defaultValue}".`);
|
|
6192
|
+
};
|
|
6193
|
+
|
|
6194
|
+
const validateFdNumber = (fdNumber, fdName, isWritable, fileDescriptors) => {
|
|
6195
|
+
const fileDescriptor = fileDescriptors[getUsedDescriptor(fdNumber)];
|
|
6196
|
+
if (fileDescriptor === undefined) {
|
|
6197
|
+
throw new TypeError(`"${getOptionName(isWritable)}" must not be ${fdName}. That file descriptor does not exist.
|
|
6198
|
+
Please set the "stdio" option to ensure that file descriptor exists.`);
|
|
6199
|
+
}
|
|
6200
|
+
|
|
6201
|
+
if (fileDescriptor.direction === 'input' && !isWritable) {
|
|
6202
|
+
throw new TypeError(`"${getOptionName(isWritable)}" must not be ${fdName}. It must be a readable stream, not writable.`);
|
|
6203
|
+
}
|
|
6204
|
+
|
|
6205
|
+
if (fileDescriptor.direction !== 'input' && isWritable) {
|
|
6206
|
+
throw new TypeError(`"${getOptionName(isWritable)}" must not be ${fdName}. It must be a writable stream, not readable.`);
|
|
6207
|
+
}
|
|
6208
|
+
};
|
|
6209
|
+
|
|
6210
|
+
const getInvalidStdioOptionMessage = (fdNumber, fdName, options, isWritable) => {
|
|
6211
|
+
if (fdNumber === 'all' && !options.all) {
|
|
6212
|
+
return 'The "all" option must be true to use "from: \'all\'".';
|
|
6213
|
+
}
|
|
6214
|
+
|
|
6215
|
+
const {optionName, optionValue} = getInvalidStdioOption(fdNumber, options);
|
|
6216
|
+
return `The "${optionName}: ${serializeOptionValue(optionValue)}" option is incompatible with using "${getOptionName(isWritable)}: ${serializeOptionValue(fdName)}".
|
|
6217
|
+
Please set this option with "pipe" instead.`;
|
|
6218
|
+
};
|
|
6219
|
+
|
|
6220
|
+
const getInvalidStdioOption = (fdNumber, {stdin, stdout, stderr, stdio}) => {
|
|
6221
|
+
const usedDescriptor = getUsedDescriptor(fdNumber);
|
|
6222
|
+
|
|
6223
|
+
if (usedDescriptor === 0 && stdin !== undefined) {
|
|
6224
|
+
return {optionName: 'stdin', optionValue: stdin};
|
|
6225
|
+
}
|
|
6226
|
+
|
|
6227
|
+
if (usedDescriptor === 1 && stdout !== undefined) {
|
|
6228
|
+
return {optionName: 'stdout', optionValue: stdout};
|
|
6229
|
+
}
|
|
6230
|
+
|
|
6231
|
+
if (usedDescriptor === 2 && stderr !== undefined) {
|
|
6232
|
+
return {optionName: 'stderr', optionValue: stderr};
|
|
6233
|
+
}
|
|
6234
|
+
|
|
6235
|
+
return {optionName: `stdio[${usedDescriptor}]`, optionValue: stdio[usedDescriptor]};
|
|
6236
|
+
};
|
|
6237
|
+
|
|
6238
|
+
const getUsedDescriptor = fdNumber => fdNumber === 'all' ? 1 : fdNumber;
|
|
6239
|
+
|
|
6240
|
+
const getOptionName = isWritable => isWritable ? 'to' : 'from';
|
|
6241
|
+
|
|
6242
|
+
const serializeOptionValue = value => {
|
|
6243
|
+
if (typeof value === 'string') {
|
|
6244
|
+
return `'${value}'`;
|
|
6245
|
+
}
|
|
6246
|
+
|
|
6247
|
+
return typeof value === 'number' ? `${value}` : 'Stream';
|
|
6248
|
+
};
|
|
6249
|
+
|
|
6250
|
+
// Temporarily increase the maximum number of listeners on an eventEmitter
|
|
6251
|
+
const incrementMaxListeners = (eventEmitter, maxListenersIncrement, signal) => {
|
|
6252
|
+
const maxListeners = eventEmitter.getMaxListeners();
|
|
6253
|
+
if (maxListeners === 0 || maxListeners === Number.POSITIVE_INFINITY) {
|
|
6254
|
+
return;
|
|
6255
|
+
}
|
|
6256
|
+
|
|
6257
|
+
eventEmitter.setMaxListeners(maxListeners + maxListenersIncrement);
|
|
6258
|
+
addAbortListener(signal, () => {
|
|
6259
|
+
eventEmitter.setMaxListeners(eventEmitter.getMaxListeners() - maxListenersIncrement);
|
|
6260
|
+
});
|
|
6261
|
+
};
|
|
6262
|
+
|
|
6263
|
+
// By default, Node.js keeps the subprocess alive while it has a `message` or `disconnect` listener.
|
|
6264
|
+
// We replicate the same logic for the events that we proxy.
|
|
6265
|
+
// This ensures the subprocess is kept alive while `getOneMessage()` and `getEachMessage()` are ongoing.
|
|
6266
|
+
// This is not a problem with `sendMessage()` since Node.js handles that method automatically.
|
|
6267
|
+
// We do not use `anyProcess.channel.ref()` since this would prevent the automatic `.channel.refCounted()` Node.js is doing.
|
|
6268
|
+
// We keep a reference to `anyProcess.channel` since it might be `null` while `getOneMessage()` or `getEachMessage()` is still processing debounced messages.
|
|
6269
|
+
// See https://github.com/nodejs/node/blob/2aaeaa863c35befa2ebaa98fb7737ec84df4d8e9/lib/internal/child_process.js#L547
|
|
6270
|
+
const addReference = (channel, reference) => {
|
|
6271
|
+
if (reference) {
|
|
6272
|
+
addReferenceCount(channel);
|
|
6273
|
+
}
|
|
6274
|
+
};
|
|
6275
|
+
|
|
6276
|
+
const addReferenceCount = channel => {
|
|
6277
|
+
channel.refCounted();
|
|
6278
|
+
};
|
|
6279
|
+
|
|
6280
|
+
const removeReference = (channel, reference) => {
|
|
6281
|
+
if (reference) {
|
|
6282
|
+
removeReferenceCount(channel);
|
|
6283
|
+
}
|
|
6284
|
+
};
|
|
6285
|
+
|
|
6286
|
+
const removeReferenceCount = channel => {
|
|
6287
|
+
channel.unrefCounted();
|
|
6288
|
+
};
|
|
6289
|
+
|
|
6290
|
+
// To proxy events, we setup some global listeners on the `message` and `disconnect` events.
|
|
6291
|
+
// Those should not keep the subprocess alive, so we remove the automatic counting that Node.js is doing.
|
|
6292
|
+
// See https://github.com/nodejs/node/blob/1b965270a9c273d4cf70e8808e9d28b9ada7844f/lib/child_process.js#L180
|
|
6293
|
+
const undoAddedReferences = (channel, isSubprocess) => {
|
|
6294
|
+
if (isSubprocess) {
|
|
6295
|
+
removeReferenceCount(channel);
|
|
6296
|
+
removeReferenceCount(channel);
|
|
6297
|
+
}
|
|
6298
|
+
};
|
|
6299
|
+
|
|
6300
|
+
// Reverse it during `disconnect`
|
|
6301
|
+
const redoAddedReferences = (channel, isSubprocess) => {
|
|
6302
|
+
if (isSubprocess) {
|
|
6303
|
+
addReferenceCount(channel);
|
|
6304
|
+
addReferenceCount(channel);
|
|
6305
|
+
}
|
|
6306
|
+
};
|
|
6307
|
+
|
|
6308
|
+
// By default, Node.js buffers `message` events.
|
|
6309
|
+
// - Buffering happens when there is a `message` event is emitted but there is no handler.
|
|
6310
|
+
// - As soon as a `message` event handler is set, all buffered `message` events are emitted, emptying the buffer.
|
|
6311
|
+
// - This happens both in the current process and the subprocess.
|
|
6312
|
+
// - See https://github.com/nodejs/node/blob/501546e8f37059cd577041e23941b640d0d4d406/lib/internal/child_process.js#L719
|
|
6313
|
+
// This is helpful. Notably, this allows sending messages to a subprocess that's still initializing.
|
|
6314
|
+
// However, it has several problems.
|
|
6315
|
+
// - This works with `events.on()` but not `events.once()` since all buffered messages are emitted at once.
|
|
6316
|
+
// For example, users cannot call `await getOneMessage()`/`getEachMessage()` multiple times in a row.
|
|
6317
|
+
// - When a user intentionally starts listening to `message` at a specific point in time, past `message` events are replayed, which might be unexpected.
|
|
6318
|
+
// - Buffering is unlimited, which might lead to an out-of-memory crash.
|
|
6319
|
+
// - This does not work well with multiple consumers.
|
|
6320
|
+
// For example, Execa consumes events with both `result.ipcOutput` and manual IPC calls like `getOneMessage()`.
|
|
6321
|
+
// Since `result.ipcOutput` reads all incoming messages, no buffering happens for manual IPC calls.
|
|
6322
|
+
// - Forgetting to setup a `message` listener, or setting it up too late, is a programming mistake.
|
|
6323
|
+
// The default behavior does not allow users to realize they made that mistake.
|
|
6324
|
+
// To solve those problems, instead of buffering messages, we debounce them.
|
|
6325
|
+
// The `message` event so it is emitted at most once per macrotask.
|
|
6326
|
+
const onMessage = async ({anyProcess, channel, isSubprocess, ipcEmitter}, wrappedMessage) => {
|
|
6327
|
+
if (handleStrictResponse(wrappedMessage) || handleAbort(wrappedMessage)) {
|
|
6328
|
+
return;
|
|
6329
|
+
}
|
|
6330
|
+
|
|
6331
|
+
if (!INCOMING_MESSAGES.has(anyProcess)) {
|
|
6332
|
+
INCOMING_MESSAGES.set(anyProcess, []);
|
|
6333
|
+
}
|
|
6334
|
+
|
|
6335
|
+
const incomingMessages = INCOMING_MESSAGES.get(anyProcess);
|
|
6336
|
+
incomingMessages.push(wrappedMessage);
|
|
6337
|
+
|
|
6338
|
+
if (incomingMessages.length > 1) {
|
|
6339
|
+
return;
|
|
6340
|
+
}
|
|
6341
|
+
|
|
6342
|
+
while (incomingMessages.length > 0) {
|
|
6343
|
+
// eslint-disable-next-line no-await-in-loop
|
|
6344
|
+
await waitForOutgoingMessages(anyProcess, ipcEmitter, wrappedMessage);
|
|
6345
|
+
// eslint-disable-next-line no-await-in-loop
|
|
6346
|
+
await scheduler.yield();
|
|
6347
|
+
|
|
6348
|
+
// eslint-disable-next-line no-await-in-loop
|
|
6349
|
+
const message = await handleStrictRequest({
|
|
6350
|
+
wrappedMessage: incomingMessages[0],
|
|
6351
|
+
anyProcess,
|
|
6352
|
+
channel,
|
|
6353
|
+
isSubprocess,
|
|
6354
|
+
ipcEmitter,
|
|
6355
|
+
});
|
|
6356
|
+
|
|
6357
|
+
incomingMessages.shift();
|
|
6358
|
+
ipcEmitter.emit('message', message);
|
|
6359
|
+
ipcEmitter.emit('message:done');
|
|
6360
|
+
}
|
|
6361
|
+
};
|
|
6362
|
+
|
|
6363
|
+
// If the `message` event is currently debounced, the `disconnect` event must wait for it
|
|
6364
|
+
const onDisconnect = async ({anyProcess, channel, isSubprocess, ipcEmitter, boundOnMessage}) => {
|
|
6365
|
+
abortOnDisconnect();
|
|
6366
|
+
|
|
6367
|
+
const incomingMessages = INCOMING_MESSAGES.get(anyProcess);
|
|
6368
|
+
while (incomingMessages?.length > 0) {
|
|
6369
|
+
// eslint-disable-next-line no-await-in-loop
|
|
6370
|
+
await once(ipcEmitter, 'message:done');
|
|
6371
|
+
}
|
|
6372
|
+
|
|
6373
|
+
anyProcess.removeListener('message', boundOnMessage);
|
|
6374
|
+
redoAddedReferences(channel, isSubprocess);
|
|
6375
|
+
ipcEmitter.connected = false;
|
|
6376
|
+
ipcEmitter.emit('disconnect');
|
|
6377
|
+
};
|
|
6378
|
+
|
|
6379
|
+
const INCOMING_MESSAGES = new WeakMap();
|
|
6380
|
+
|
|
6381
|
+
// Forward the `message` and `disconnect` events from the process and subprocess to a proxy emitter.
|
|
6382
|
+
// This prevents the `error` event from stopping IPC.
|
|
6383
|
+
// This also allows debouncing the `message` event.
|
|
6384
|
+
const getIpcEmitter = (anyProcess, channel, isSubprocess) => {
|
|
6385
|
+
if (IPC_EMITTERS.has(anyProcess)) {
|
|
6386
|
+
return IPC_EMITTERS.get(anyProcess);
|
|
6387
|
+
}
|
|
6388
|
+
|
|
6389
|
+
// Use an `EventEmitter`, like the `process` that is being proxied
|
|
6390
|
+
// eslint-disable-next-line unicorn/prefer-event-target
|
|
6391
|
+
const ipcEmitter = new EventEmitter$1();
|
|
6392
|
+
ipcEmitter.connected = true;
|
|
6393
|
+
IPC_EMITTERS.set(anyProcess, ipcEmitter);
|
|
6394
|
+
forwardEvents({
|
|
6395
|
+
ipcEmitter,
|
|
6396
|
+
anyProcess,
|
|
6397
|
+
channel,
|
|
6398
|
+
isSubprocess,
|
|
6399
|
+
});
|
|
6400
|
+
return ipcEmitter;
|
|
6401
|
+
};
|
|
6402
|
+
|
|
6403
|
+
const IPC_EMITTERS = new WeakMap();
|
|
6404
|
+
|
|
6405
|
+
// The `message` and `disconnect` events are buffered in the subprocess until the first listener is setup.
|
|
6406
|
+
// However, unbuffering happens after one tick, so this give enough time for the caller to setup the listener on the proxy emitter first.
|
|
6407
|
+
// See https://github.com/nodejs/node/blob/2aaeaa863c35befa2ebaa98fb7737ec84df4d8e9/lib/internal/child_process.js#L721
|
|
6408
|
+
const forwardEvents = ({ipcEmitter, anyProcess, channel, isSubprocess}) => {
|
|
6409
|
+
const boundOnMessage = onMessage.bind(undefined, {
|
|
6410
|
+
anyProcess,
|
|
6411
|
+
channel,
|
|
6412
|
+
isSubprocess,
|
|
6413
|
+
ipcEmitter,
|
|
6414
|
+
});
|
|
6415
|
+
anyProcess.on('message', boundOnMessage);
|
|
6416
|
+
anyProcess.once('disconnect', onDisconnect.bind(undefined, {
|
|
6417
|
+
anyProcess,
|
|
6418
|
+
channel,
|
|
6419
|
+
isSubprocess,
|
|
6420
|
+
ipcEmitter,
|
|
6421
|
+
boundOnMessage,
|
|
6422
|
+
}));
|
|
6423
|
+
undoAddedReferences(channel, isSubprocess);
|
|
6424
|
+
};
|
|
6425
|
+
|
|
6426
|
+
// Check whether there might still be some `message` events to receive
|
|
6427
|
+
const isConnected = anyProcess => {
|
|
6428
|
+
const ipcEmitter = IPC_EMITTERS.get(anyProcess);
|
|
6429
|
+
return ipcEmitter === undefined
|
|
6430
|
+
? anyProcess.channel !== null
|
|
6431
|
+
: ipcEmitter.connected;
|
|
6432
|
+
};
|
|
6433
|
+
|
|
6434
|
+
// When using the `strict` option, wrap the message with metadata during `sendMessage()`
|
|
6435
|
+
const handleSendStrict = ({anyProcess, channel, isSubprocess, message, strict}) => {
|
|
6436
|
+
if (!strict) {
|
|
6437
|
+
return message;
|
|
6438
|
+
}
|
|
6439
|
+
|
|
6440
|
+
const ipcEmitter = getIpcEmitter(anyProcess, channel, isSubprocess);
|
|
6441
|
+
const hasListeners = hasMessageListeners(anyProcess, ipcEmitter);
|
|
6442
|
+
return {
|
|
6443
|
+
id: count++,
|
|
6444
|
+
type: REQUEST_TYPE,
|
|
6445
|
+
message,
|
|
6446
|
+
hasListeners,
|
|
6447
|
+
};
|
|
6448
|
+
};
|
|
6449
|
+
|
|
6450
|
+
let count = 0n;
|
|
6451
|
+
|
|
6452
|
+
// Handles when both processes are calling `sendMessage()` with `strict` at the same time.
|
|
6453
|
+
// If neither process is listening, this would create a deadlock. We detect it and throw.
|
|
6454
|
+
const validateStrictDeadlock = (outgoingMessages, wrappedMessage) => {
|
|
6455
|
+
if (wrappedMessage?.type !== REQUEST_TYPE || wrappedMessage.hasListeners) {
|
|
6456
|
+
return;
|
|
6457
|
+
}
|
|
6458
|
+
|
|
6459
|
+
for (const {id} of outgoingMessages) {
|
|
6460
|
+
if (id !== undefined) {
|
|
6461
|
+
STRICT_RESPONSES[id].resolve({isDeadlock: true, hasListeners: false});
|
|
6462
|
+
}
|
|
6463
|
+
}
|
|
6464
|
+
};
|
|
6465
|
+
|
|
6466
|
+
// The other process then sends the acknowledgment back as a response
|
|
6467
|
+
const handleStrictRequest = async ({wrappedMessage, anyProcess, channel, isSubprocess, ipcEmitter}) => {
|
|
6468
|
+
if (wrappedMessage?.type !== REQUEST_TYPE || !anyProcess.connected) {
|
|
6469
|
+
return wrappedMessage;
|
|
6470
|
+
}
|
|
6471
|
+
|
|
6472
|
+
const {id, message} = wrappedMessage;
|
|
6473
|
+
const response = {id, type: RESPONSE_TYPE, message: hasMessageListeners(anyProcess, ipcEmitter)};
|
|
6474
|
+
|
|
6475
|
+
try {
|
|
6476
|
+
await sendMessage({
|
|
6477
|
+
anyProcess,
|
|
6478
|
+
channel,
|
|
6479
|
+
isSubprocess,
|
|
6480
|
+
ipc: true,
|
|
6481
|
+
}, response);
|
|
6482
|
+
} catch (error) {
|
|
6483
|
+
ipcEmitter.emit('strict:error', error);
|
|
6484
|
+
}
|
|
6485
|
+
|
|
6486
|
+
return message;
|
|
6487
|
+
};
|
|
6488
|
+
|
|
6489
|
+
// Reception of the acknowledgment response
|
|
6490
|
+
const handleStrictResponse = wrappedMessage => {
|
|
6491
|
+
if (wrappedMessage?.type !== RESPONSE_TYPE) {
|
|
6492
|
+
return false;
|
|
6493
|
+
}
|
|
6494
|
+
|
|
6495
|
+
const {id, message: hasListeners} = wrappedMessage;
|
|
6496
|
+
STRICT_RESPONSES[id]?.resolve({isDeadlock: false, hasListeners});
|
|
6497
|
+
return true;
|
|
6498
|
+
};
|
|
6499
|
+
|
|
6500
|
+
// Wait for the other process to receive the message from `sendMessage()`
|
|
6501
|
+
const waitForStrictResponse = async (wrappedMessage, anyProcess, isSubprocess) => {
|
|
6502
|
+
if (wrappedMessage?.type !== REQUEST_TYPE) {
|
|
6503
|
+
return;
|
|
6504
|
+
}
|
|
6505
|
+
|
|
6506
|
+
const deferred = createDeferred();
|
|
6507
|
+
STRICT_RESPONSES[wrappedMessage.id] = deferred;
|
|
6508
|
+
const controller = new AbortController();
|
|
6509
|
+
|
|
6510
|
+
try {
|
|
6511
|
+
const {isDeadlock, hasListeners} = await Promise.race([
|
|
6512
|
+
deferred,
|
|
6513
|
+
throwOnDisconnect$1(anyProcess, isSubprocess, controller),
|
|
6514
|
+
]);
|
|
6515
|
+
|
|
6516
|
+
if (isDeadlock) {
|
|
6517
|
+
throwOnStrictDeadlockError(isSubprocess);
|
|
6518
|
+
}
|
|
6519
|
+
|
|
6520
|
+
if (!hasListeners) {
|
|
6521
|
+
throwOnMissingStrict(isSubprocess);
|
|
6522
|
+
}
|
|
6523
|
+
} finally {
|
|
6524
|
+
controller.abort();
|
|
6525
|
+
delete STRICT_RESPONSES[wrappedMessage.id];
|
|
6526
|
+
}
|
|
6527
|
+
};
|
|
6528
|
+
|
|
6529
|
+
const STRICT_RESPONSES = {};
|
|
6530
|
+
|
|
6531
|
+
const throwOnDisconnect$1 = async (anyProcess, isSubprocess, {signal}) => {
|
|
6532
|
+
incrementMaxListeners(anyProcess, 1, signal);
|
|
6533
|
+
await once(anyProcess, 'disconnect', {signal});
|
|
6534
|
+
throwOnStrictDisconnect(isSubprocess);
|
|
6535
|
+
};
|
|
6536
|
+
|
|
6537
|
+
const REQUEST_TYPE = 'execa:ipc:request';
|
|
6538
|
+
const RESPONSE_TYPE = 'execa:ipc:response';
|
|
6539
|
+
|
|
6540
|
+
// When `sendMessage()` is ongoing, any `message` being received waits before being emitted.
|
|
6541
|
+
// This allows calling one or multiple `await sendMessage()` followed by `await getOneMessage()`/`await getEachMessage()`.
|
|
6542
|
+
// Without running into a race condition when the other process sends a response too fast, before the current process set up a listener.
|
|
6543
|
+
const startSendMessage = (anyProcess, wrappedMessage, strict) => {
|
|
6544
|
+
if (!OUTGOING_MESSAGES.has(anyProcess)) {
|
|
6545
|
+
OUTGOING_MESSAGES.set(anyProcess, new Set());
|
|
6546
|
+
}
|
|
6547
|
+
|
|
6548
|
+
const outgoingMessages = OUTGOING_MESSAGES.get(anyProcess);
|
|
6549
|
+
const onMessageSent = createDeferred();
|
|
6550
|
+
const id = strict ? wrappedMessage.id : undefined;
|
|
6551
|
+
const outgoingMessage = {onMessageSent, id};
|
|
6552
|
+
outgoingMessages.add(outgoingMessage);
|
|
6553
|
+
return {outgoingMessages, outgoingMessage};
|
|
6554
|
+
};
|
|
6555
|
+
|
|
6556
|
+
const endSendMessage = ({outgoingMessages, outgoingMessage}) => {
|
|
6557
|
+
outgoingMessages.delete(outgoingMessage);
|
|
6558
|
+
outgoingMessage.onMessageSent.resolve();
|
|
6559
|
+
};
|
|
6560
|
+
|
|
6561
|
+
// Await while `sendMessage()` is ongoing, unless there is already a `message` listener
|
|
6562
|
+
const waitForOutgoingMessages = async (anyProcess, ipcEmitter, wrappedMessage) => {
|
|
6563
|
+
while (!hasMessageListeners(anyProcess, ipcEmitter) && OUTGOING_MESSAGES.get(anyProcess)?.size > 0) {
|
|
6564
|
+
const outgoingMessages = [...OUTGOING_MESSAGES.get(anyProcess)];
|
|
6565
|
+
validateStrictDeadlock(outgoingMessages, wrappedMessage);
|
|
6566
|
+
// eslint-disable-next-line no-await-in-loop
|
|
6567
|
+
await Promise.all(outgoingMessages.map(({onMessageSent}) => onMessageSent));
|
|
6568
|
+
}
|
|
6569
|
+
};
|
|
6570
|
+
|
|
6571
|
+
const OUTGOING_MESSAGES = new WeakMap();
|
|
6572
|
+
|
|
6573
|
+
// Whether any `message` listener is setup
|
|
6574
|
+
const hasMessageListeners = (anyProcess, ipcEmitter) => ipcEmitter.listenerCount('message') > getMinListenerCount(anyProcess);
|
|
6575
|
+
|
|
6576
|
+
// When `buffer` is `false`, we set up a `message` listener that should be ignored.
|
|
6577
|
+
// That listener is only meant to intercept `strict` acknowledgement responses.
|
|
6578
|
+
const getMinListenerCount = anyProcess => SUBPROCESS_OPTIONS.has(anyProcess)
|
|
6579
|
+
&& !SUBPROCESS_OPTIONS.get(anyProcess).options.buffer.at(-1)
|
|
6580
|
+
? 1
|
|
6581
|
+
: 0;
|
|
6582
|
+
|
|
6583
|
+
// Like `[sub]process.send()` but promise-based.
|
|
6584
|
+
// We do not `await subprocess` during `.sendMessage()` nor `.getOneMessage()` since those methods are transient.
|
|
6585
|
+
// Users would still need to `await subprocess` after the method is done.
|
|
6586
|
+
// Also, this would prevent `unhandledRejection` event from being emitted, making it silent.
|
|
6587
|
+
const sendMessage = ({anyProcess, channel, isSubprocess, ipc}, message, {strict = false} = {}) => {
|
|
6588
|
+
const methodName = 'sendMessage';
|
|
6589
|
+
validateIpcMethod({
|
|
6590
|
+
methodName,
|
|
6591
|
+
isSubprocess,
|
|
6592
|
+
ipc,
|
|
6593
|
+
isConnected: anyProcess.connected,
|
|
6594
|
+
});
|
|
6595
|
+
|
|
6596
|
+
return sendMessageAsync({
|
|
6597
|
+
anyProcess,
|
|
6598
|
+
channel,
|
|
6599
|
+
methodName,
|
|
6600
|
+
isSubprocess,
|
|
6601
|
+
message,
|
|
6602
|
+
strict,
|
|
6603
|
+
});
|
|
6604
|
+
};
|
|
6605
|
+
|
|
6606
|
+
const sendMessageAsync = async ({anyProcess, channel, methodName, isSubprocess, message, strict}) => {
|
|
6607
|
+
const wrappedMessage = handleSendStrict({
|
|
6608
|
+
anyProcess,
|
|
6609
|
+
channel,
|
|
6610
|
+
isSubprocess,
|
|
6611
|
+
message,
|
|
6612
|
+
strict,
|
|
6613
|
+
});
|
|
6614
|
+
const outgoingMessagesState = startSendMessage(anyProcess, wrappedMessage, strict);
|
|
6615
|
+
try {
|
|
6616
|
+
await sendOneMessage({
|
|
6617
|
+
anyProcess,
|
|
6618
|
+
methodName,
|
|
6619
|
+
isSubprocess,
|
|
6620
|
+
wrappedMessage,
|
|
6621
|
+
message,
|
|
6622
|
+
});
|
|
6623
|
+
} catch (error) {
|
|
6624
|
+
disconnect(anyProcess);
|
|
6625
|
+
throw error;
|
|
6626
|
+
} finally {
|
|
6627
|
+
endSendMessage(outgoingMessagesState);
|
|
6628
|
+
}
|
|
6629
|
+
};
|
|
6630
|
+
|
|
6631
|
+
// Used internally by `cancelSignal`
|
|
6632
|
+
const sendOneMessage = async ({anyProcess, methodName, isSubprocess, wrappedMessage, message}) => {
|
|
6633
|
+
const sendMethod = getSendMethod(anyProcess);
|
|
6634
|
+
|
|
6635
|
+
try {
|
|
6636
|
+
await Promise.all([
|
|
6637
|
+
waitForStrictResponse(wrappedMessage, anyProcess, isSubprocess),
|
|
6638
|
+
sendMethod(wrappedMessage),
|
|
6639
|
+
]);
|
|
6640
|
+
} catch (error) {
|
|
6641
|
+
handleEpipeError({error, methodName, isSubprocess});
|
|
6642
|
+
handleSerializationError({
|
|
6643
|
+
error,
|
|
6644
|
+
methodName,
|
|
6645
|
+
isSubprocess,
|
|
6646
|
+
message,
|
|
6647
|
+
});
|
|
6648
|
+
throw error;
|
|
6649
|
+
}
|
|
6650
|
+
};
|
|
6651
|
+
|
|
6652
|
+
// [sub]process.send() promisified, memoized
|
|
6653
|
+
const getSendMethod = anyProcess => {
|
|
6654
|
+
if (PROCESS_SEND_METHODS.has(anyProcess)) {
|
|
6655
|
+
return PROCESS_SEND_METHODS.get(anyProcess);
|
|
6656
|
+
}
|
|
6657
|
+
|
|
6658
|
+
const sendMethod = promisify$1(anyProcess.send.bind(anyProcess));
|
|
6659
|
+
PROCESS_SEND_METHODS.set(anyProcess, sendMethod);
|
|
6660
|
+
return sendMethod;
|
|
6661
|
+
};
|
|
6662
|
+
|
|
6663
|
+
const PROCESS_SEND_METHODS = new WeakMap();
|
|
6664
|
+
|
|
6665
|
+
// Send an IPC message so the subprocess performs a graceful termination
|
|
6666
|
+
const sendAbort = (subprocess, message) => {
|
|
6667
|
+
const methodName = 'cancelSignal';
|
|
6668
|
+
validateConnection(methodName, false, subprocess.connected);
|
|
6669
|
+
return sendOneMessage({
|
|
6670
|
+
anyProcess: subprocess,
|
|
6671
|
+
methodName,
|
|
6672
|
+
isSubprocess: false,
|
|
6673
|
+
wrappedMessage: {type: GRACEFUL_CANCEL_TYPE, message},
|
|
6674
|
+
message,
|
|
6675
|
+
});
|
|
6676
|
+
};
|
|
6677
|
+
|
|
6678
|
+
// When the signal is being used, start listening for incoming messages.
|
|
6679
|
+
// Unbuffering messages takes one microtask to complete, so this must be async.
|
|
6680
|
+
const getCancelSignal = async ({anyProcess, channel, isSubprocess, ipc}) => {
|
|
6681
|
+
await startIpc({
|
|
6682
|
+
anyProcess,
|
|
6683
|
+
channel,
|
|
6684
|
+
isSubprocess,
|
|
6685
|
+
ipc,
|
|
6686
|
+
});
|
|
6687
|
+
return cancelController.signal;
|
|
6688
|
+
};
|
|
6689
|
+
|
|
6690
|
+
const startIpc = async ({anyProcess, channel, isSubprocess, ipc}) => {
|
|
6691
|
+
if (cancelListening) {
|
|
6692
|
+
return;
|
|
6693
|
+
}
|
|
6694
|
+
|
|
6695
|
+
cancelListening = true;
|
|
6696
|
+
|
|
6697
|
+
if (!ipc) {
|
|
6698
|
+
throwOnMissingParent();
|
|
6699
|
+
return;
|
|
6700
|
+
}
|
|
6701
|
+
|
|
6702
|
+
if (channel === null) {
|
|
6703
|
+
abortOnDisconnect();
|
|
6704
|
+
return;
|
|
6705
|
+
}
|
|
6706
|
+
|
|
6707
|
+
getIpcEmitter(anyProcess, channel, isSubprocess);
|
|
6708
|
+
await scheduler.yield();
|
|
6709
|
+
};
|
|
6710
|
+
|
|
6711
|
+
let cancelListening = false;
|
|
6712
|
+
|
|
6713
|
+
// Reception of IPC message to perform a graceful termination
|
|
6714
|
+
const handleAbort = wrappedMessage => {
|
|
6715
|
+
if (wrappedMessage?.type !== GRACEFUL_CANCEL_TYPE) {
|
|
6716
|
+
return false;
|
|
6717
|
+
}
|
|
6718
|
+
|
|
6719
|
+
cancelController.abort(wrappedMessage.message);
|
|
6720
|
+
return true;
|
|
6721
|
+
};
|
|
6722
|
+
|
|
6723
|
+
const GRACEFUL_CANCEL_TYPE = 'execa:ipc:cancel';
|
|
6724
|
+
|
|
6725
|
+
// When the current process disconnects early, the subprocess `cancelSignal` is aborted.
|
|
6726
|
+
// Otherwise, the signal would never be able to be aborted later on.
|
|
6727
|
+
const abortOnDisconnect = () => {
|
|
6728
|
+
cancelController.abort(getAbortDisconnectError());
|
|
6729
|
+
};
|
|
6730
|
+
|
|
6731
|
+
const cancelController = new AbortController();
|
|
6732
|
+
|
|
6733
|
+
// Validate the `gracefulCancel` option
|
|
6734
|
+
const validateGracefulCancel = ({gracefulCancel, cancelSignal, ipc, serialization}) => {
|
|
6735
|
+
if (!gracefulCancel) {
|
|
6736
|
+
return;
|
|
6737
|
+
}
|
|
6738
|
+
|
|
6739
|
+
if (cancelSignal === undefined) {
|
|
6740
|
+
throw new Error('The `cancelSignal` option must be defined when setting the `gracefulCancel` option.');
|
|
6741
|
+
}
|
|
6742
|
+
|
|
6743
|
+
if (!ipc) {
|
|
6744
|
+
throw new Error('The `ipc` option cannot be false when setting the `gracefulCancel` option.');
|
|
6745
|
+
}
|
|
6746
|
+
|
|
6747
|
+
if (serialization === 'json') {
|
|
6748
|
+
throw new Error('The `serialization` option cannot be \'json\' when setting the `gracefulCancel` option.');
|
|
6749
|
+
}
|
|
6750
|
+
};
|
|
6751
|
+
|
|
6752
|
+
// Send abort reason to the subprocess when aborting the `cancelSignal` option and `gracefulCancel` is `true`
|
|
6753
|
+
const throwOnGracefulCancel = ({
|
|
6754
|
+
subprocess,
|
|
6755
|
+
cancelSignal,
|
|
6756
|
+
gracefulCancel,
|
|
6757
|
+
forceKillAfterDelay,
|
|
6758
|
+
context,
|
|
6759
|
+
controller,
|
|
6760
|
+
}) => gracefulCancel
|
|
6761
|
+
? [sendOnAbort({
|
|
6762
|
+
subprocess,
|
|
6763
|
+
cancelSignal,
|
|
6764
|
+
forceKillAfterDelay,
|
|
6765
|
+
context,
|
|
6766
|
+
controller,
|
|
6767
|
+
})]
|
|
6768
|
+
: [];
|
|
6769
|
+
|
|
6770
|
+
const sendOnAbort = async ({subprocess, cancelSignal, forceKillAfterDelay, context, controller: {signal}}) => {
|
|
6771
|
+
await onAbortedSignal(cancelSignal, signal);
|
|
6772
|
+
const reason = getReason(cancelSignal);
|
|
6773
|
+
await sendAbort(subprocess, reason);
|
|
6774
|
+
killOnTimeout({
|
|
6775
|
+
kill: subprocess.kill,
|
|
6776
|
+
forceKillAfterDelay,
|
|
6777
|
+
context,
|
|
6778
|
+
controllerSignal: signal,
|
|
6779
|
+
});
|
|
6780
|
+
context.terminationReason ??= 'gracefulCancel';
|
|
6781
|
+
throw cancelSignal.reason;
|
|
6782
|
+
};
|
|
6783
|
+
|
|
6784
|
+
// The default `reason` is a DOMException, which is not serializable with V8
|
|
6785
|
+
// See https://github.com/nodejs/node/issues/53225
|
|
6786
|
+
const getReason = ({reason}) => {
|
|
6787
|
+
if (!(reason instanceof DOMException)) {
|
|
6788
|
+
return reason;
|
|
6789
|
+
}
|
|
6790
|
+
|
|
6791
|
+
const error = new Error(reason.message);
|
|
6792
|
+
Object.defineProperty(error, 'stack', {
|
|
6793
|
+
value: reason.stack,
|
|
6794
|
+
enumerable: false,
|
|
6795
|
+
configurable: true,
|
|
6796
|
+
writable: true,
|
|
6797
|
+
});
|
|
6798
|
+
return error;
|
|
6799
|
+
};
|
|
5586
6800
|
|
|
5587
6801
|
// Validate `timeout` option
|
|
5588
6802
|
const validateTimeout = ({timeout}) => {
|
|
@@ -5598,7 +6812,7 @@ const throwOnTimeout = (subprocess, timeout, context, controller) => timeout ===
|
|
|
5598
6812
|
|
|
5599
6813
|
const killAfterTimeout = async (subprocess, timeout, context, {signal}) => {
|
|
5600
6814
|
await setTimeout$1(timeout, undefined, {signal});
|
|
5601
|
-
context.
|
|
6815
|
+
context.terminationReason ??= 'timeout';
|
|
5602
6816
|
subprocess.kill();
|
|
5603
6817
|
throw new DiscardedError();
|
|
5604
6818
|
};
|
|
@@ -5651,6 +6865,49 @@ const handleNodeOption = (file, commandArguments, {
|
|
|
5651
6865
|
];
|
|
5652
6866
|
};
|
|
5653
6867
|
|
|
6868
|
+
// Validate the `ipcInput` option
|
|
6869
|
+
const validateIpcInputOption = ({ipcInput, ipc, serialization}) => {
|
|
6870
|
+
if (ipcInput === undefined) {
|
|
6871
|
+
return;
|
|
6872
|
+
}
|
|
6873
|
+
|
|
6874
|
+
if (!ipc) {
|
|
6875
|
+
throw new Error('The `ipcInput` option cannot be set unless the `ipc` option is `true`.');
|
|
6876
|
+
}
|
|
6877
|
+
|
|
6878
|
+
validateIpcInput[serialization](ipcInput);
|
|
6879
|
+
};
|
|
6880
|
+
|
|
6881
|
+
const validateAdvancedInput = ipcInput => {
|
|
6882
|
+
try {
|
|
6883
|
+
serialize(ipcInput);
|
|
6884
|
+
} catch (error) {
|
|
6885
|
+
throw new Error('The `ipcInput` option is not serializable with a structured clone.', {cause: error});
|
|
6886
|
+
}
|
|
6887
|
+
};
|
|
6888
|
+
|
|
6889
|
+
const validateJsonInput = ipcInput => {
|
|
6890
|
+
try {
|
|
6891
|
+
JSON.stringify(ipcInput);
|
|
6892
|
+
} catch (error) {
|
|
6893
|
+
throw new Error('The `ipcInput` option is not serializable with JSON.', {cause: error});
|
|
6894
|
+
}
|
|
6895
|
+
};
|
|
6896
|
+
|
|
6897
|
+
const validateIpcInput = {
|
|
6898
|
+
advanced: validateAdvancedInput,
|
|
6899
|
+
json: validateJsonInput,
|
|
6900
|
+
};
|
|
6901
|
+
|
|
6902
|
+
// When the `ipcInput` option is set, it is sent as an initial IPC message to the subprocess
|
|
6903
|
+
const sendIpcInput = async (subprocess, ipcInput) => {
|
|
6904
|
+
if (ipcInput === undefined) {
|
|
6905
|
+
return;
|
|
6906
|
+
}
|
|
6907
|
+
|
|
6908
|
+
await subprocess.sendMessage(ipcInput);
|
|
6909
|
+
};
|
|
6910
|
+
|
|
5654
6911
|
// Validate `encoding` option
|
|
5655
6912
|
const validateEncoding = ({encoding}) => {
|
|
5656
6913
|
if (ENCODINGS.has(encoding)) {
|
|
@@ -5749,6 +7006,9 @@ const normalizeOptions$2 = (filePath, rawArguments, rawOptions) => {
|
|
|
5749
7006
|
const options = addDefaultOptions(fdOptions);
|
|
5750
7007
|
validateTimeout(options);
|
|
5751
7008
|
validateEncoding(options);
|
|
7009
|
+
validateIpcInputOption(options);
|
|
7010
|
+
validateCancelSignal(options);
|
|
7011
|
+
validateGracefulCancel(options);
|
|
5752
7012
|
options.shell = normalizeFileUrl(options.shell);
|
|
5753
7013
|
options.env = getEnv(options);
|
|
5754
7014
|
options.killSignal = normalizeKillSignal(options.killSignal);
|
|
@@ -5775,7 +7035,9 @@ const addDefaultOptions = ({
|
|
|
5775
7035
|
windowsHide = true,
|
|
5776
7036
|
killSignal = 'SIGTERM',
|
|
5777
7037
|
forceKillAfterDelay = true,
|
|
5778
|
-
|
|
7038
|
+
gracefulCancel = false,
|
|
7039
|
+
ipcInput,
|
|
7040
|
+
ipc = ipcInput !== undefined || gracefulCancel,
|
|
5779
7041
|
serialization = 'advanced',
|
|
5780
7042
|
...options
|
|
5781
7043
|
}) => ({
|
|
@@ -5791,6 +7053,8 @@ const addDefaultOptions = ({
|
|
|
5791
7053
|
windowsHide,
|
|
5792
7054
|
killSignal,
|
|
5793
7055
|
forceKillAfterDelay,
|
|
7056
|
+
gracefulCancel,
|
|
7057
|
+
ipcInput,
|
|
5794
7058
|
ipc,
|
|
5795
7059
|
serialization,
|
|
5796
7060
|
});
|
|
@@ -5811,386 +7075,6 @@ const getEnv = ({env: envOption, extendEnv, preferLocal, node, localDirectory, n
|
|
|
5811
7075
|
return env;
|
|
5812
7076
|
};
|
|
5813
7077
|
|
|
5814
|
-
const getRealtimeSignals=()=>{
|
|
5815
|
-
const length=SIGRTMAX-SIGRTMIN+1;
|
|
5816
|
-
return Array.from({length},getRealtimeSignal)
|
|
5817
|
-
};
|
|
5818
|
-
|
|
5819
|
-
const getRealtimeSignal=(value,index)=>({
|
|
5820
|
-
name:`SIGRT${index+1}`,
|
|
5821
|
-
number:SIGRTMIN+index,
|
|
5822
|
-
action:"terminate",
|
|
5823
|
-
description:"Application-specific signal (realtime)",
|
|
5824
|
-
standard:"posix"
|
|
5825
|
-
});
|
|
5826
|
-
|
|
5827
|
-
const SIGRTMIN=34;
|
|
5828
|
-
const SIGRTMAX=64;
|
|
5829
|
-
|
|
5830
|
-
const SIGNALS=[
|
|
5831
|
-
{
|
|
5832
|
-
name:"SIGHUP",
|
|
5833
|
-
number:1,
|
|
5834
|
-
action:"terminate",
|
|
5835
|
-
description:"Terminal closed",
|
|
5836
|
-
standard:"posix"
|
|
5837
|
-
},
|
|
5838
|
-
{
|
|
5839
|
-
name:"SIGINT",
|
|
5840
|
-
number:2,
|
|
5841
|
-
action:"terminate",
|
|
5842
|
-
description:"User interruption with CTRL-C",
|
|
5843
|
-
standard:"ansi"
|
|
5844
|
-
},
|
|
5845
|
-
{
|
|
5846
|
-
name:"SIGQUIT",
|
|
5847
|
-
number:3,
|
|
5848
|
-
action:"core",
|
|
5849
|
-
description:"User interruption with CTRL-\\",
|
|
5850
|
-
standard:"posix"
|
|
5851
|
-
},
|
|
5852
|
-
{
|
|
5853
|
-
name:"SIGILL",
|
|
5854
|
-
number:4,
|
|
5855
|
-
action:"core",
|
|
5856
|
-
description:"Invalid machine instruction",
|
|
5857
|
-
standard:"ansi"
|
|
5858
|
-
},
|
|
5859
|
-
{
|
|
5860
|
-
name:"SIGTRAP",
|
|
5861
|
-
number:5,
|
|
5862
|
-
action:"core",
|
|
5863
|
-
description:"Debugger breakpoint",
|
|
5864
|
-
standard:"posix"
|
|
5865
|
-
},
|
|
5866
|
-
{
|
|
5867
|
-
name:"SIGABRT",
|
|
5868
|
-
number:6,
|
|
5869
|
-
action:"core",
|
|
5870
|
-
description:"Aborted",
|
|
5871
|
-
standard:"ansi"
|
|
5872
|
-
},
|
|
5873
|
-
{
|
|
5874
|
-
name:"SIGIOT",
|
|
5875
|
-
number:6,
|
|
5876
|
-
action:"core",
|
|
5877
|
-
description:"Aborted",
|
|
5878
|
-
standard:"bsd"
|
|
5879
|
-
},
|
|
5880
|
-
{
|
|
5881
|
-
name:"SIGBUS",
|
|
5882
|
-
number:7,
|
|
5883
|
-
action:"core",
|
|
5884
|
-
description:
|
|
5885
|
-
"Bus error due to misaligned, non-existing address or paging error",
|
|
5886
|
-
standard:"bsd"
|
|
5887
|
-
},
|
|
5888
|
-
{
|
|
5889
|
-
name:"SIGEMT",
|
|
5890
|
-
number:7,
|
|
5891
|
-
action:"terminate",
|
|
5892
|
-
description:"Command should be emulated but is not implemented",
|
|
5893
|
-
standard:"other"
|
|
5894
|
-
},
|
|
5895
|
-
{
|
|
5896
|
-
name:"SIGFPE",
|
|
5897
|
-
number:8,
|
|
5898
|
-
action:"core",
|
|
5899
|
-
description:"Floating point arithmetic error",
|
|
5900
|
-
standard:"ansi"
|
|
5901
|
-
},
|
|
5902
|
-
{
|
|
5903
|
-
name:"SIGKILL",
|
|
5904
|
-
number:9,
|
|
5905
|
-
action:"terminate",
|
|
5906
|
-
description:"Forced termination",
|
|
5907
|
-
standard:"posix",
|
|
5908
|
-
forced:true
|
|
5909
|
-
},
|
|
5910
|
-
{
|
|
5911
|
-
name:"SIGUSR1",
|
|
5912
|
-
number:10,
|
|
5913
|
-
action:"terminate",
|
|
5914
|
-
description:"Application-specific signal",
|
|
5915
|
-
standard:"posix"
|
|
5916
|
-
},
|
|
5917
|
-
{
|
|
5918
|
-
name:"SIGSEGV",
|
|
5919
|
-
number:11,
|
|
5920
|
-
action:"core",
|
|
5921
|
-
description:"Segmentation fault",
|
|
5922
|
-
standard:"ansi"
|
|
5923
|
-
},
|
|
5924
|
-
{
|
|
5925
|
-
name:"SIGUSR2",
|
|
5926
|
-
number:12,
|
|
5927
|
-
action:"terminate",
|
|
5928
|
-
description:"Application-specific signal",
|
|
5929
|
-
standard:"posix"
|
|
5930
|
-
},
|
|
5931
|
-
{
|
|
5932
|
-
name:"SIGPIPE",
|
|
5933
|
-
number:13,
|
|
5934
|
-
action:"terminate",
|
|
5935
|
-
description:"Broken pipe or socket",
|
|
5936
|
-
standard:"posix"
|
|
5937
|
-
},
|
|
5938
|
-
{
|
|
5939
|
-
name:"SIGALRM",
|
|
5940
|
-
number:14,
|
|
5941
|
-
action:"terminate",
|
|
5942
|
-
description:"Timeout or timer",
|
|
5943
|
-
standard:"posix"
|
|
5944
|
-
},
|
|
5945
|
-
{
|
|
5946
|
-
name:"SIGTERM",
|
|
5947
|
-
number:15,
|
|
5948
|
-
action:"terminate",
|
|
5949
|
-
description:"Termination",
|
|
5950
|
-
standard:"ansi"
|
|
5951
|
-
},
|
|
5952
|
-
{
|
|
5953
|
-
name:"SIGSTKFLT",
|
|
5954
|
-
number:16,
|
|
5955
|
-
action:"terminate",
|
|
5956
|
-
description:"Stack is empty or overflowed",
|
|
5957
|
-
standard:"other"
|
|
5958
|
-
},
|
|
5959
|
-
{
|
|
5960
|
-
name:"SIGCHLD",
|
|
5961
|
-
number:17,
|
|
5962
|
-
action:"ignore",
|
|
5963
|
-
description:"Child process terminated, paused or unpaused",
|
|
5964
|
-
standard:"posix"
|
|
5965
|
-
},
|
|
5966
|
-
{
|
|
5967
|
-
name:"SIGCLD",
|
|
5968
|
-
number:17,
|
|
5969
|
-
action:"ignore",
|
|
5970
|
-
description:"Child process terminated, paused or unpaused",
|
|
5971
|
-
standard:"other"
|
|
5972
|
-
},
|
|
5973
|
-
{
|
|
5974
|
-
name:"SIGCONT",
|
|
5975
|
-
number:18,
|
|
5976
|
-
action:"unpause",
|
|
5977
|
-
description:"Unpaused",
|
|
5978
|
-
standard:"posix",
|
|
5979
|
-
forced:true
|
|
5980
|
-
},
|
|
5981
|
-
{
|
|
5982
|
-
name:"SIGSTOP",
|
|
5983
|
-
number:19,
|
|
5984
|
-
action:"pause",
|
|
5985
|
-
description:"Paused",
|
|
5986
|
-
standard:"posix",
|
|
5987
|
-
forced:true
|
|
5988
|
-
},
|
|
5989
|
-
{
|
|
5990
|
-
name:"SIGTSTP",
|
|
5991
|
-
number:20,
|
|
5992
|
-
action:"pause",
|
|
5993
|
-
description:"Paused using CTRL-Z or \"suspend\"",
|
|
5994
|
-
standard:"posix"
|
|
5995
|
-
},
|
|
5996
|
-
{
|
|
5997
|
-
name:"SIGTTIN",
|
|
5998
|
-
number:21,
|
|
5999
|
-
action:"pause",
|
|
6000
|
-
description:"Background process cannot read terminal input",
|
|
6001
|
-
standard:"posix"
|
|
6002
|
-
},
|
|
6003
|
-
{
|
|
6004
|
-
name:"SIGBREAK",
|
|
6005
|
-
number:21,
|
|
6006
|
-
action:"terminate",
|
|
6007
|
-
description:"User interruption with CTRL-BREAK",
|
|
6008
|
-
standard:"other"
|
|
6009
|
-
},
|
|
6010
|
-
{
|
|
6011
|
-
name:"SIGTTOU",
|
|
6012
|
-
number:22,
|
|
6013
|
-
action:"pause",
|
|
6014
|
-
description:"Background process cannot write to terminal output",
|
|
6015
|
-
standard:"posix"
|
|
6016
|
-
},
|
|
6017
|
-
{
|
|
6018
|
-
name:"SIGURG",
|
|
6019
|
-
number:23,
|
|
6020
|
-
action:"ignore",
|
|
6021
|
-
description:"Socket received out-of-band data",
|
|
6022
|
-
standard:"bsd"
|
|
6023
|
-
},
|
|
6024
|
-
{
|
|
6025
|
-
name:"SIGXCPU",
|
|
6026
|
-
number:24,
|
|
6027
|
-
action:"core",
|
|
6028
|
-
description:"Process timed out",
|
|
6029
|
-
standard:"bsd"
|
|
6030
|
-
},
|
|
6031
|
-
{
|
|
6032
|
-
name:"SIGXFSZ",
|
|
6033
|
-
number:25,
|
|
6034
|
-
action:"core",
|
|
6035
|
-
description:"File too big",
|
|
6036
|
-
standard:"bsd"
|
|
6037
|
-
},
|
|
6038
|
-
{
|
|
6039
|
-
name:"SIGVTALRM",
|
|
6040
|
-
number:26,
|
|
6041
|
-
action:"terminate",
|
|
6042
|
-
description:"Timeout or timer",
|
|
6043
|
-
standard:"bsd"
|
|
6044
|
-
},
|
|
6045
|
-
{
|
|
6046
|
-
name:"SIGPROF",
|
|
6047
|
-
number:27,
|
|
6048
|
-
action:"terminate",
|
|
6049
|
-
description:"Timeout or timer",
|
|
6050
|
-
standard:"bsd"
|
|
6051
|
-
},
|
|
6052
|
-
{
|
|
6053
|
-
name:"SIGWINCH",
|
|
6054
|
-
number:28,
|
|
6055
|
-
action:"ignore",
|
|
6056
|
-
description:"Terminal window size changed",
|
|
6057
|
-
standard:"bsd"
|
|
6058
|
-
},
|
|
6059
|
-
{
|
|
6060
|
-
name:"SIGIO",
|
|
6061
|
-
number:29,
|
|
6062
|
-
action:"terminate",
|
|
6063
|
-
description:"I/O is available",
|
|
6064
|
-
standard:"other"
|
|
6065
|
-
},
|
|
6066
|
-
{
|
|
6067
|
-
name:"SIGPOLL",
|
|
6068
|
-
number:29,
|
|
6069
|
-
action:"terminate",
|
|
6070
|
-
description:"Watched event",
|
|
6071
|
-
standard:"other"
|
|
6072
|
-
},
|
|
6073
|
-
{
|
|
6074
|
-
name:"SIGINFO",
|
|
6075
|
-
number:29,
|
|
6076
|
-
action:"ignore",
|
|
6077
|
-
description:"Request for process information",
|
|
6078
|
-
standard:"other"
|
|
6079
|
-
},
|
|
6080
|
-
{
|
|
6081
|
-
name:"SIGPWR",
|
|
6082
|
-
number:30,
|
|
6083
|
-
action:"terminate",
|
|
6084
|
-
description:"Device running out of power",
|
|
6085
|
-
standard:"systemv"
|
|
6086
|
-
},
|
|
6087
|
-
{
|
|
6088
|
-
name:"SIGSYS",
|
|
6089
|
-
number:31,
|
|
6090
|
-
action:"core",
|
|
6091
|
-
description:"Invalid system call",
|
|
6092
|
-
standard:"other"
|
|
6093
|
-
},
|
|
6094
|
-
{
|
|
6095
|
-
name:"SIGUNUSED",
|
|
6096
|
-
number:31,
|
|
6097
|
-
action:"terminate",
|
|
6098
|
-
description:"Invalid system call",
|
|
6099
|
-
standard:"other"
|
|
6100
|
-
}];
|
|
6101
|
-
|
|
6102
|
-
const getSignals=()=>{
|
|
6103
|
-
const realtimeSignals=getRealtimeSignals();
|
|
6104
|
-
const signals=[...SIGNALS,...realtimeSignals].map(normalizeSignal);
|
|
6105
|
-
return signals
|
|
6106
|
-
};
|
|
6107
|
-
|
|
6108
|
-
|
|
6109
|
-
|
|
6110
|
-
|
|
6111
|
-
|
|
6112
|
-
|
|
6113
|
-
|
|
6114
|
-
const normalizeSignal=({
|
|
6115
|
-
name,
|
|
6116
|
-
number:defaultNumber,
|
|
6117
|
-
description,
|
|
6118
|
-
action,
|
|
6119
|
-
forced=false,
|
|
6120
|
-
standard
|
|
6121
|
-
})=>{
|
|
6122
|
-
const{
|
|
6123
|
-
signals:{[name]:constantSignal}
|
|
6124
|
-
}=constants$6;
|
|
6125
|
-
const supported=constantSignal!==undefined;
|
|
6126
|
-
const number=supported?constantSignal:defaultNumber;
|
|
6127
|
-
return {name,number,description,supported,action,forced,standard}
|
|
6128
|
-
};
|
|
6129
|
-
|
|
6130
|
-
const getSignalsByName=()=>{
|
|
6131
|
-
const signals=getSignals();
|
|
6132
|
-
return Object.fromEntries(signals.map(getSignalByName))
|
|
6133
|
-
};
|
|
6134
|
-
|
|
6135
|
-
const getSignalByName=({
|
|
6136
|
-
name,
|
|
6137
|
-
number,
|
|
6138
|
-
description,
|
|
6139
|
-
supported,
|
|
6140
|
-
action,
|
|
6141
|
-
forced,
|
|
6142
|
-
standard
|
|
6143
|
-
})=>[name,{name,number,description,supported,action,forced,standard}];
|
|
6144
|
-
|
|
6145
|
-
const signalsByName=getSignalsByName();
|
|
6146
|
-
|
|
6147
|
-
|
|
6148
|
-
|
|
6149
|
-
|
|
6150
|
-
const getSignalsByNumber=()=>{
|
|
6151
|
-
const signals=getSignals();
|
|
6152
|
-
const length=SIGRTMAX+1;
|
|
6153
|
-
const signalsA=Array.from({length},(value,number)=>
|
|
6154
|
-
getSignalByNumber(number,signals)
|
|
6155
|
-
);
|
|
6156
|
-
return Object.assign({},...signalsA)
|
|
6157
|
-
};
|
|
6158
|
-
|
|
6159
|
-
const getSignalByNumber=(number,signals)=>{
|
|
6160
|
-
const signal=findSignalByNumber(number,signals);
|
|
6161
|
-
|
|
6162
|
-
if(signal===undefined){
|
|
6163
|
-
return {}
|
|
6164
|
-
}
|
|
6165
|
-
|
|
6166
|
-
const{name,description,supported,action,forced,standard}=signal;
|
|
6167
|
-
return {
|
|
6168
|
-
[number]:{
|
|
6169
|
-
name,
|
|
6170
|
-
number,
|
|
6171
|
-
description,
|
|
6172
|
-
supported,
|
|
6173
|
-
action,
|
|
6174
|
-
forced,
|
|
6175
|
-
standard
|
|
6176
|
-
}
|
|
6177
|
-
}
|
|
6178
|
-
};
|
|
6179
|
-
|
|
6180
|
-
|
|
6181
|
-
|
|
6182
|
-
const findSignalByNumber=(number,signals)=>{
|
|
6183
|
-
const signal=signals.find(({name})=>constants$6.signals[name]===number);
|
|
6184
|
-
|
|
6185
|
-
if(signal!==undefined){
|
|
6186
|
-
return signal
|
|
6187
|
-
}
|
|
6188
|
-
|
|
6189
|
-
return signals.find((signalA)=>signalA.number===number)
|
|
6190
|
-
};
|
|
6191
|
-
|
|
6192
|
-
getSignalsByNumber();
|
|
6193
|
-
|
|
6194
7078
|
function stripFinalNewline(input) {
|
|
6195
7079
|
if (typeof input === 'string') {
|
|
6196
7080
|
return stripFinalNewlineString(input);
|
|
@@ -6712,6 +7596,17 @@ const getMaxBufferUnit = (readableObjectMode, lines, encoding) => {
|
|
|
6712
7596
|
return 'characters';
|
|
6713
7597
|
};
|
|
6714
7598
|
|
|
7599
|
+
// Check the `maxBuffer` option with `result.ipcOutput`
|
|
7600
|
+
const checkIpcMaxBuffer = (subprocess, ipcOutput, maxBuffer) => {
|
|
7601
|
+
if (ipcOutput.length !== maxBuffer) {
|
|
7602
|
+
return;
|
|
7603
|
+
}
|
|
7604
|
+
|
|
7605
|
+
const error = new MaxBufferError();
|
|
7606
|
+
error.maxBufferInfo = {fdNumber: 'ipc'};
|
|
7607
|
+
throw error;
|
|
7608
|
+
};
|
|
7609
|
+
|
|
6715
7610
|
// Error message when `maxBuffer` is hit
|
|
6716
7611
|
const getMaxBufferMessage = (error, maxBuffer) => {
|
|
6717
7612
|
const {streamName, threshold, unit} = getMaxBufferInfo(error, maxBuffer);
|
|
@@ -6725,6 +7620,11 @@ const getMaxBufferInfo = (error, maxBuffer) => {
|
|
|
6725
7620
|
|
|
6726
7621
|
const {maxBufferInfo: {fdNumber, unit}} = error;
|
|
6727
7622
|
delete error.maxBufferInfo;
|
|
7623
|
+
|
|
7624
|
+
if (fdNumber === 'ipc') {
|
|
7625
|
+
return {streamName: 'IPC output', threshold: maxBuffer.at(-1), unit: 'messages'};
|
|
7626
|
+
}
|
|
7627
|
+
|
|
6728
7628
|
return {streamName: getStreamName(fdNumber), threshold: maxBuffer[fdNumber], unit};
|
|
6729
7629
|
};
|
|
6730
7630
|
|
|
@@ -6752,6 +7652,7 @@ const getMaxBufferSync = ([, stdoutMaxBuffer]) => stdoutMaxBuffer;
|
|
|
6752
7652
|
const createMessages = ({
|
|
6753
7653
|
stdio,
|
|
6754
7654
|
all,
|
|
7655
|
+
ipcOutput,
|
|
6755
7656
|
originalError,
|
|
6756
7657
|
signal,
|
|
6757
7658
|
signalDescription,
|
|
@@ -6759,7 +7660,11 @@ const createMessages = ({
|
|
|
6759
7660
|
escapedCommand,
|
|
6760
7661
|
timedOut,
|
|
6761
7662
|
isCanceled,
|
|
7663
|
+
isGracefullyCanceled,
|
|
6762
7664
|
isMaxBuffer,
|
|
7665
|
+
isForcefullyTerminated,
|
|
7666
|
+
forceKillAfterDelay,
|
|
7667
|
+
killSignal,
|
|
6763
7668
|
maxBuffer,
|
|
6764
7669
|
timeout,
|
|
6765
7670
|
cwd,
|
|
@@ -6776,33 +7681,73 @@ const createMessages = ({
|
|
|
6776
7681
|
signalDescription,
|
|
6777
7682
|
exitCode,
|
|
6778
7683
|
isCanceled,
|
|
7684
|
+
isGracefullyCanceled,
|
|
7685
|
+
isForcefullyTerminated,
|
|
7686
|
+
forceKillAfterDelay,
|
|
7687
|
+
killSignal,
|
|
6779
7688
|
});
|
|
6780
7689
|
const originalMessage = getOriginalMessage(originalError, cwd);
|
|
6781
7690
|
const suffix = originalMessage === undefined ? '' : `\n${originalMessage}`;
|
|
6782
7691
|
const shortMessage = `${prefix}: ${escapedCommand}${suffix}`;
|
|
6783
7692
|
const messageStdio = all === undefined ? [stdio[2], stdio[1]] : [all];
|
|
6784
|
-
const message = [
|
|
7693
|
+
const message = [
|
|
7694
|
+
shortMessage,
|
|
7695
|
+
...messageStdio,
|
|
7696
|
+
...stdio.slice(3),
|
|
7697
|
+
ipcOutput.map(ipcMessage => serializeIpcMessage(ipcMessage)).join('\n'),
|
|
7698
|
+
]
|
|
6785
7699
|
.map(messagePart => escapeLines(stripFinalNewline(serializeMessagePart(messagePart))))
|
|
6786
7700
|
.filter(Boolean)
|
|
6787
7701
|
.join('\n\n');
|
|
6788
7702
|
return {originalMessage, shortMessage, message};
|
|
6789
7703
|
};
|
|
6790
7704
|
|
|
6791
|
-
const getErrorPrefix = ({
|
|
7705
|
+
const getErrorPrefix = ({
|
|
7706
|
+
originalError,
|
|
7707
|
+
timedOut,
|
|
7708
|
+
timeout,
|
|
7709
|
+
isMaxBuffer,
|
|
7710
|
+
maxBuffer,
|
|
7711
|
+
errorCode,
|
|
7712
|
+
signal,
|
|
7713
|
+
signalDescription,
|
|
7714
|
+
exitCode,
|
|
7715
|
+
isCanceled,
|
|
7716
|
+
isGracefullyCanceled,
|
|
7717
|
+
isForcefullyTerminated,
|
|
7718
|
+
forceKillAfterDelay,
|
|
7719
|
+
killSignal,
|
|
7720
|
+
}) => {
|
|
7721
|
+
const forcefulSuffix = getForcefulSuffix(isForcefullyTerminated, forceKillAfterDelay);
|
|
7722
|
+
|
|
6792
7723
|
if (timedOut) {
|
|
6793
|
-
return `Command timed out after ${timeout} milliseconds`;
|
|
7724
|
+
return `Command timed out after ${timeout} milliseconds${forcefulSuffix}`;
|
|
7725
|
+
}
|
|
7726
|
+
|
|
7727
|
+
if (isGracefullyCanceled) {
|
|
7728
|
+
if (signal === undefined) {
|
|
7729
|
+
return `Command was gracefully canceled with exit code ${exitCode}`;
|
|
7730
|
+
}
|
|
7731
|
+
|
|
7732
|
+
return isForcefullyTerminated
|
|
7733
|
+
? `Command was gracefully canceled${forcefulSuffix}`
|
|
7734
|
+
: `Command was gracefully canceled with ${signal} (${signalDescription})`;
|
|
6794
7735
|
}
|
|
6795
7736
|
|
|
6796
7737
|
if (isCanceled) {
|
|
6797
|
-
return
|
|
7738
|
+
return `Command was canceled${forcefulSuffix}`;
|
|
6798
7739
|
}
|
|
6799
7740
|
|
|
6800
7741
|
if (isMaxBuffer) {
|
|
6801
|
-
return getMaxBufferMessage(originalError, maxBuffer)
|
|
7742
|
+
return `${getMaxBufferMessage(originalError, maxBuffer)}${forcefulSuffix}`;
|
|
6802
7743
|
}
|
|
6803
7744
|
|
|
6804
7745
|
if (errorCode !== undefined) {
|
|
6805
|
-
return `Command failed with ${errorCode}`;
|
|
7746
|
+
return `Command failed with ${errorCode}${forcefulSuffix}`;
|
|
7747
|
+
}
|
|
7748
|
+
|
|
7749
|
+
if (isForcefullyTerminated) {
|
|
7750
|
+
return `Command was killed with ${killSignal} (${getSignalDescription(killSignal)})${forcefulSuffix}`;
|
|
6806
7751
|
}
|
|
6807
7752
|
|
|
6808
7753
|
if (signal !== undefined) {
|
|
@@ -6816,6 +7761,10 @@ const getErrorPrefix = ({originalError, timedOut, timeout, isMaxBuffer, maxBuffe
|
|
|
6816
7761
|
return 'Command failed';
|
|
6817
7762
|
};
|
|
6818
7763
|
|
|
7764
|
+
const getForcefulSuffix = (isForcefullyTerminated, forceKillAfterDelay) => isForcefullyTerminated
|
|
7765
|
+
? ` and was forcefully terminated after ${forceKillAfterDelay} milliseconds`
|
|
7766
|
+
: '';
|
|
7767
|
+
|
|
6819
7768
|
const getOriginalMessage = (originalError, cwd) => {
|
|
6820
7769
|
if (originalError instanceof DiscardedError) {
|
|
6821
7770
|
return;
|
|
@@ -6828,6 +7777,10 @@ const getOriginalMessage = (originalError, cwd) => {
|
|
|
6828
7777
|
return escapedOriginalMessage === '' ? undefined : escapedOriginalMessage;
|
|
6829
7778
|
};
|
|
6830
7779
|
|
|
7780
|
+
const serializeIpcMessage = ipcMessage => typeof ipcMessage === 'string'
|
|
7781
|
+
? ipcMessage
|
|
7782
|
+
: inspect(ipcMessage);
|
|
7783
|
+
|
|
6831
7784
|
const serializeMessagePart = messagePart => Array.isArray(messagePart)
|
|
6832
7785
|
? messagePart.map(messageItem => stripFinalNewline(serializeMessageItem(messageItem))).filter(Boolean).join('\n')
|
|
6833
7786
|
: serializeMessageItem(messagePart);
|
|
@@ -6850,6 +7803,7 @@ const makeSuccessResult = ({
|
|
|
6850
7803
|
escapedCommand,
|
|
6851
7804
|
stdio,
|
|
6852
7805
|
all,
|
|
7806
|
+
ipcOutput,
|
|
6853
7807
|
options: {cwd},
|
|
6854
7808
|
startTime,
|
|
6855
7809
|
}) => omitUndefinedProperties({
|
|
@@ -6860,13 +7814,16 @@ const makeSuccessResult = ({
|
|
|
6860
7814
|
failed: false,
|
|
6861
7815
|
timedOut: false,
|
|
6862
7816
|
isCanceled: false,
|
|
7817
|
+
isGracefullyCanceled: false,
|
|
6863
7818
|
isTerminated: false,
|
|
6864
7819
|
isMaxBuffer: false,
|
|
7820
|
+
isForcefullyTerminated: false,
|
|
6865
7821
|
exitCode: 0,
|
|
6866
7822
|
stdout: stdio[1],
|
|
6867
7823
|
stderr: stdio[2],
|
|
6868
7824
|
all,
|
|
6869
7825
|
stdio,
|
|
7826
|
+
ipcOutput,
|
|
6870
7827
|
pipedFrom: [],
|
|
6871
7828
|
});
|
|
6872
7829
|
|
|
@@ -6886,8 +7843,11 @@ const makeEarlyError = ({
|
|
|
6886
7843
|
startTime,
|
|
6887
7844
|
timedOut: false,
|
|
6888
7845
|
isCanceled: false,
|
|
7846
|
+
isGracefullyCanceled: false,
|
|
6889
7847
|
isMaxBuffer: false,
|
|
7848
|
+
isForcefullyTerminated: false,
|
|
6890
7849
|
stdio: Array.from({length: fileDescriptors.length}),
|
|
7850
|
+
ipcOutput: [],
|
|
6891
7851
|
options,
|
|
6892
7852
|
isSync,
|
|
6893
7853
|
});
|
|
@@ -6900,18 +7860,29 @@ const makeError = ({
|
|
|
6900
7860
|
startTime,
|
|
6901
7861
|
timedOut,
|
|
6902
7862
|
isCanceled,
|
|
7863
|
+
isGracefullyCanceled,
|
|
6903
7864
|
isMaxBuffer,
|
|
7865
|
+
isForcefullyTerminated,
|
|
6904
7866
|
exitCode: rawExitCode,
|
|
6905
7867
|
signal: rawSignal,
|
|
6906
7868
|
stdio,
|
|
6907
7869
|
all,
|
|
6908
|
-
|
|
7870
|
+
ipcOutput,
|
|
7871
|
+
options: {
|
|
7872
|
+
timeoutDuration,
|
|
7873
|
+
timeout = timeoutDuration,
|
|
7874
|
+
forceKillAfterDelay,
|
|
7875
|
+
killSignal,
|
|
7876
|
+
cwd,
|
|
7877
|
+
maxBuffer,
|
|
7878
|
+
},
|
|
6909
7879
|
isSync,
|
|
6910
7880
|
}) => {
|
|
6911
7881
|
const {exitCode, signal, signalDescription} = normalizeExitPayload(rawExitCode, rawSignal);
|
|
6912
7882
|
const {originalMessage, shortMessage, message} = createMessages({
|
|
6913
7883
|
stdio,
|
|
6914
7884
|
all,
|
|
7885
|
+
ipcOutput,
|
|
6915
7886
|
originalError,
|
|
6916
7887
|
signal,
|
|
6917
7888
|
signalDescription,
|
|
@@ -6919,7 +7890,11 @@ const makeError = ({
|
|
|
6919
7890
|
escapedCommand,
|
|
6920
7891
|
timedOut,
|
|
6921
7892
|
isCanceled,
|
|
7893
|
+
isGracefullyCanceled,
|
|
6922
7894
|
isMaxBuffer,
|
|
7895
|
+
isForcefullyTerminated,
|
|
7896
|
+
forceKillAfterDelay,
|
|
7897
|
+
killSignal,
|
|
6923
7898
|
maxBuffer,
|
|
6924
7899
|
timeout,
|
|
6925
7900
|
cwd,
|
|
@@ -6932,12 +7907,15 @@ const makeError = ({
|
|
|
6932
7907
|
startTime,
|
|
6933
7908
|
timedOut,
|
|
6934
7909
|
isCanceled,
|
|
7910
|
+
isGracefullyCanceled,
|
|
6935
7911
|
isMaxBuffer,
|
|
7912
|
+
isForcefullyTerminated,
|
|
6936
7913
|
exitCode,
|
|
6937
7914
|
signal,
|
|
6938
7915
|
signalDescription,
|
|
6939
7916
|
stdio,
|
|
6940
7917
|
all,
|
|
7918
|
+
ipcOutput,
|
|
6941
7919
|
cwd,
|
|
6942
7920
|
originalMessage,
|
|
6943
7921
|
shortMessage,
|
|
@@ -6952,12 +7930,15 @@ const getErrorProperties = ({
|
|
|
6952
7930
|
startTime,
|
|
6953
7931
|
timedOut,
|
|
6954
7932
|
isCanceled,
|
|
7933
|
+
isGracefullyCanceled,
|
|
6955
7934
|
isMaxBuffer,
|
|
7935
|
+
isForcefullyTerminated,
|
|
6956
7936
|
exitCode,
|
|
6957
7937
|
signal,
|
|
6958
7938
|
signalDescription,
|
|
6959
7939
|
stdio,
|
|
6960
7940
|
all,
|
|
7941
|
+
ipcOutput,
|
|
6961
7942
|
cwd,
|
|
6962
7943
|
originalMessage,
|
|
6963
7944
|
shortMessage,
|
|
@@ -6971,8 +7952,10 @@ const getErrorProperties = ({
|
|
|
6971
7952
|
failed: true,
|
|
6972
7953
|
timedOut,
|
|
6973
7954
|
isCanceled,
|
|
7955
|
+
isGracefullyCanceled,
|
|
6974
7956
|
isTerminated: signal !== undefined,
|
|
6975
7957
|
isMaxBuffer,
|
|
7958
|
+
isForcefullyTerminated,
|
|
6976
7959
|
exitCode,
|
|
6977
7960
|
signal,
|
|
6978
7961
|
signalDescription,
|
|
@@ -6981,6 +7964,7 @@ const getErrorProperties = ({
|
|
|
6981
7964
|
stderr: stdio[2],
|
|
6982
7965
|
all,
|
|
6983
7966
|
stdio,
|
|
7967
|
+
ipcOutput,
|
|
6984
7968
|
pipedFrom: [],
|
|
6985
7969
|
});
|
|
6986
7970
|
|
|
@@ -6991,7 +7975,7 @@ const omitUndefinedProperties = result => Object.fromEntries(Object.entries(resu
|
|
|
6991
7975
|
const normalizeExitPayload = (rawExitCode, rawSignal) => {
|
|
6992
7976
|
const exitCode = rawExitCode === null ? undefined : rawExitCode;
|
|
6993
7977
|
const signal = rawSignal === null ? undefined : rawSignal;
|
|
6994
|
-
const signalDescription = signal === undefined ? undefined :
|
|
7978
|
+
const signalDescription = signal === undefined ? undefined : getSignalDescription(rawSignal);
|
|
6995
7979
|
return {exitCode, signal, signalDescription};
|
|
6996
7980
|
};
|
|
6997
7981
|
|
|
@@ -7631,11 +8615,18 @@ const getStandardStreamDirection = value => {
|
|
|
7631
8615
|
// When the ambiguity remains, we default to `output` since it is the most common use case for additional file descriptors.
|
|
7632
8616
|
const DEFAULT_DIRECTION = 'output';
|
|
7633
8617
|
|
|
8618
|
+
// The `ipc` option adds an `ipc` item to the `stdio` option
|
|
8619
|
+
const normalizeIpcStdioArray = (stdioArray, ipc) => ipc && !stdioArray.includes('ipc')
|
|
8620
|
+
? [...stdioArray, 'ipc']
|
|
8621
|
+
: stdioArray;
|
|
8622
|
+
|
|
7634
8623
|
// Add support for `stdin`/`stdout`/`stderr` as an alias for `stdio`.
|
|
7635
8624
|
// Also normalize the `stdio` option.
|
|
7636
8625
|
const normalizeStdioOption = ({stdio, ipc, buffer, verbose, ...options}, isSync) => {
|
|
7637
8626
|
const stdioArray = getStdioArray(stdio, options).map((stdioOption, fdNumber) => addDefaultValue(stdioOption, fdNumber));
|
|
7638
|
-
return isSync
|
|
8627
|
+
return isSync
|
|
8628
|
+
? normalizeStdioSync(stdioArray, buffer, verbose)
|
|
8629
|
+
: normalizeIpcStdioArray(stdioArray, ipc);
|
|
7639
8630
|
};
|
|
7640
8631
|
|
|
7641
8632
|
const getStdioArray = (stdio, options) => {
|
|
@@ -7686,118 +8677,6 @@ const normalizeStdioSync = (stdioArray, buffer, verbose) => stdioArray.map((stdi
|
|
|
7686
8677
|
const isOutputPipeOnly = stdioOption => stdioOption === 'pipe'
|
|
7687
8678
|
|| (Array.isArray(stdioOption) && stdioOption.every(item => item === 'pipe'));
|
|
7688
8679
|
|
|
7689
|
-
// The `ipc` option adds an `ipc` item to the `stdio` option
|
|
7690
|
-
const normalizeStdioAsync = (stdioArray, ipc) => ipc && !stdioArray.includes('ipc')
|
|
7691
|
-
? [...stdioArray, 'ipc']
|
|
7692
|
-
: stdioArray;
|
|
7693
|
-
|
|
7694
|
-
// Retrieve stream targeted by the `to` option
|
|
7695
|
-
const getToStream = (destination, to = 'stdin') => {
|
|
7696
|
-
const isWritable = true;
|
|
7697
|
-
const {options, fileDescriptors} = SUBPROCESS_OPTIONS.get(destination);
|
|
7698
|
-
const fdNumber = getFdNumber(fileDescriptors, to, isWritable);
|
|
7699
|
-
const destinationStream = destination.stdio[fdNumber];
|
|
7700
|
-
|
|
7701
|
-
if (destinationStream === null) {
|
|
7702
|
-
throw new TypeError(getInvalidStdioOptionMessage(fdNumber, to, options, isWritable));
|
|
7703
|
-
}
|
|
7704
|
-
|
|
7705
|
-
return destinationStream;
|
|
7706
|
-
};
|
|
7707
|
-
|
|
7708
|
-
// Retrieve stream targeted by the `from` option
|
|
7709
|
-
const getFromStream = (source, from = 'stdout') => {
|
|
7710
|
-
const isWritable = false;
|
|
7711
|
-
const {options, fileDescriptors} = SUBPROCESS_OPTIONS.get(source);
|
|
7712
|
-
const fdNumber = getFdNumber(fileDescriptors, from, isWritable);
|
|
7713
|
-
const sourceStream = fdNumber === 'all' ? source.all : source.stdio[fdNumber];
|
|
7714
|
-
|
|
7715
|
-
if (sourceStream === null || sourceStream === undefined) {
|
|
7716
|
-
throw new TypeError(getInvalidStdioOptionMessage(fdNumber, from, options, isWritable));
|
|
7717
|
-
}
|
|
7718
|
-
|
|
7719
|
-
return sourceStream;
|
|
7720
|
-
};
|
|
7721
|
-
|
|
7722
|
-
// Keeps track of the options passed to each Execa call
|
|
7723
|
-
const SUBPROCESS_OPTIONS = new WeakMap();
|
|
7724
|
-
|
|
7725
|
-
const getFdNumber = (fileDescriptors, fdName, isWritable) => {
|
|
7726
|
-
const fdNumber = parseFdNumber(fdName, isWritable);
|
|
7727
|
-
validateFdNumber(fdNumber, fdName, isWritable, fileDescriptors);
|
|
7728
|
-
return fdNumber;
|
|
7729
|
-
};
|
|
7730
|
-
|
|
7731
|
-
const parseFdNumber = (fdName, isWritable) => {
|
|
7732
|
-
const fdNumber = parseFd(fdName);
|
|
7733
|
-
if (fdNumber !== undefined) {
|
|
7734
|
-
return fdNumber;
|
|
7735
|
-
}
|
|
7736
|
-
|
|
7737
|
-
const {validOptions, defaultValue} = isWritable
|
|
7738
|
-
? {validOptions: '"stdin"', defaultValue: 'stdin'}
|
|
7739
|
-
: {validOptions: '"stdout", "stderr", "all"', defaultValue: 'stdout'};
|
|
7740
|
-
throw new TypeError(`"${getOptionName(isWritable)}" must not be "${fdName}".
|
|
7741
|
-
It must be ${validOptions} or "fd3", "fd4" (and so on).
|
|
7742
|
-
It is optional and defaults to "${defaultValue}".`);
|
|
7743
|
-
};
|
|
7744
|
-
|
|
7745
|
-
const validateFdNumber = (fdNumber, fdName, isWritable, fileDescriptors) => {
|
|
7746
|
-
const fileDescriptor = fileDescriptors[getUsedDescriptor(fdNumber)];
|
|
7747
|
-
if (fileDescriptor === undefined) {
|
|
7748
|
-
throw new TypeError(`"${getOptionName(isWritable)}" must not be ${fdName}. That file descriptor does not exist.
|
|
7749
|
-
Please set the "stdio" option to ensure that file descriptor exists.`);
|
|
7750
|
-
}
|
|
7751
|
-
|
|
7752
|
-
if (fileDescriptor.direction === 'input' && !isWritable) {
|
|
7753
|
-
throw new TypeError(`"${getOptionName(isWritable)}" must not be ${fdName}. It must be a readable stream, not writable.`);
|
|
7754
|
-
}
|
|
7755
|
-
|
|
7756
|
-
if (fileDescriptor.direction !== 'input' && isWritable) {
|
|
7757
|
-
throw new TypeError(`"${getOptionName(isWritable)}" must not be ${fdName}. It must be a writable stream, not readable.`);
|
|
7758
|
-
}
|
|
7759
|
-
};
|
|
7760
|
-
|
|
7761
|
-
const getInvalidStdioOptionMessage = (fdNumber, fdName, options, isWritable) => {
|
|
7762
|
-
if (fdNumber === 'all' && !options.all) {
|
|
7763
|
-
return 'The "all" option must be true to use "from: \'all\'".';
|
|
7764
|
-
}
|
|
7765
|
-
|
|
7766
|
-
const {optionName, optionValue} = getInvalidStdioOption(fdNumber, options);
|
|
7767
|
-
return `The "${optionName}: ${serializeOptionValue(optionValue)}" option is incompatible with using "${getOptionName(isWritable)}: ${serializeOptionValue(fdName)}".
|
|
7768
|
-
Please set this option with "pipe" instead.`;
|
|
7769
|
-
};
|
|
7770
|
-
|
|
7771
|
-
const getInvalidStdioOption = (fdNumber, {stdin, stdout, stderr, stdio}) => {
|
|
7772
|
-
const usedDescriptor = getUsedDescriptor(fdNumber);
|
|
7773
|
-
|
|
7774
|
-
if (usedDescriptor === 0 && stdin !== undefined) {
|
|
7775
|
-
return {optionName: 'stdin', optionValue: stdin};
|
|
7776
|
-
}
|
|
7777
|
-
|
|
7778
|
-
if (usedDescriptor === 1 && stdout !== undefined) {
|
|
7779
|
-
return {optionName: 'stdout', optionValue: stdout};
|
|
7780
|
-
}
|
|
7781
|
-
|
|
7782
|
-
if (usedDescriptor === 2 && stderr !== undefined) {
|
|
7783
|
-
return {optionName: 'stderr', optionValue: stderr};
|
|
7784
|
-
}
|
|
7785
|
-
|
|
7786
|
-
return {optionName: `stdio[${usedDescriptor}]`, optionValue: stdio[usedDescriptor]};
|
|
7787
|
-
};
|
|
7788
|
-
|
|
7789
|
-
const getUsedDescriptor = fdNumber => fdNumber === 'all' ? 1 : fdNumber;
|
|
7790
|
-
|
|
7791
|
-
const getOptionName = isWritable => isWritable ? 'to' : 'from';
|
|
7792
|
-
|
|
7793
|
-
const serializeOptionValue = value => {
|
|
7794
|
-
if (typeof value === 'string') {
|
|
7795
|
-
return `'${value}'`;
|
|
7796
|
-
}
|
|
7797
|
-
|
|
7798
|
-
return typeof value === 'number' ? `${value}` : 'Stream';
|
|
7799
|
-
};
|
|
7800
|
-
|
|
7801
8680
|
// When we use multiple `stdio` values for the same streams, we pass 'pipe' to `child_process.spawn()`.
|
|
7802
8681
|
// We then emulate the piping done by core Node.js.
|
|
7803
8682
|
// To do so, we transform the following values:
|
|
@@ -8030,7 +8909,7 @@ const getDuplicateStreamInstance = ({otherStdioItems, type, value, optionName, d
|
|
|
8030
8909
|
|
|
8031
8910
|
const hasSameValue = ({type, value}, secondValue) => {
|
|
8032
8911
|
if (type === 'filePath') {
|
|
8033
|
-
return value.
|
|
8912
|
+
return value.file === secondValue.file;
|
|
8034
8913
|
}
|
|
8035
8914
|
|
|
8036
8915
|
if (type === 'fileUrl') {
|
|
@@ -8780,7 +9659,7 @@ const fdUsesVerbose = fdNumber => fdNumber === 1 || fdNumber === 2;
|
|
|
8780
9659
|
|
|
8781
9660
|
const PIPED_STDIO_VALUES = new Set(['pipe', 'overlapped']);
|
|
8782
9661
|
|
|
8783
|
-
// `verbose` printing logic with async methods
|
|
9662
|
+
// `verbose: 'full'` printing logic with async methods
|
|
8784
9663
|
const logLines = async (linesIterable, stream, verboseInfo) => {
|
|
8785
9664
|
for await (const line of linesIterable) {
|
|
8786
9665
|
if (!isPipingStream(stream)) {
|
|
@@ -8789,7 +9668,7 @@ const logLines = async (linesIterable, stream, verboseInfo) => {
|
|
|
8789
9668
|
}
|
|
8790
9669
|
};
|
|
8791
9670
|
|
|
8792
|
-
// `verbose` printing logic with sync methods
|
|
9671
|
+
// `verbose: 'full'` printing logic with sync methods
|
|
8793
9672
|
const logLinesSync = (linesArray, verboseInfo) => {
|
|
8794
9673
|
for (const line of linesArray) {
|
|
8795
9674
|
logLine(line, verboseInfo);
|
|
@@ -8807,15 +9686,9 @@ const isPipingStream = stream => stream._readableState.pipes.length > 0;
|
|
|
8807
9686
|
|
|
8808
9687
|
// When `verbose` is `full`, print stdout|stderr
|
|
8809
9688
|
const logLine = (line, {verboseId}) => {
|
|
8810
|
-
|
|
8811
|
-
const escapedLines = escapeLines(lines);
|
|
8812
|
-
const spacedLines = escapedLines.replaceAll('\t', ' '.repeat(TAB_SIZE));
|
|
8813
|
-
verboseLog(spacedLines, verboseId, 'output');
|
|
9689
|
+
verboseLog(serializeLogMessage(line), verboseId, 'output');
|
|
8814
9690
|
};
|
|
8815
9691
|
|
|
8816
|
-
// Same as `util.inspect()`
|
|
8817
|
-
const TAB_SIZE = 2;
|
|
8818
|
-
|
|
8819
9692
|
// Apply `stdout`/`stderr` options, after spawning, in sync mode
|
|
8820
9693
|
const transformOutputSync = ({fileDescriptors, syncResult: {output}, options, isMaxBuffer, verboseInfo}) => {
|
|
8821
9694
|
if (output === null) {
|
|
@@ -8957,13 +9830,19 @@ const getAllSync = ([, stdout, stderr], options) => {
|
|
|
8957
9830
|
};
|
|
8958
9831
|
|
|
8959
9832
|
// If `error` is emitted before `spawn`, `exit` will never be emitted.
|
|
8960
|
-
// However, `error` might be emitted after `spawn
|
|
9833
|
+
// However, `error` might be emitted after `spawn`.
|
|
8961
9834
|
// In that case, `exit` will still be emitted.
|
|
8962
9835
|
// Since the `exit` event contains the signal name, we want to make sure we are listening for it.
|
|
8963
9836
|
// This function also takes into account the following unlikely cases:
|
|
8964
9837
|
// - `exit` being emitted in the same microtask as `spawn`
|
|
8965
9838
|
// - `error` being emitted multiple times
|
|
8966
|
-
const waitForExit = async subprocess => {
|
|
9839
|
+
const waitForExit = async (subprocess, context) => {
|
|
9840
|
+
const [exitCode, signal] = await waitForExitOrError(subprocess);
|
|
9841
|
+
context.isForcefullyTerminated ??= false;
|
|
9842
|
+
return [exitCode, signal];
|
|
9843
|
+
};
|
|
9844
|
+
|
|
9845
|
+
const waitForExitOrError = async subprocess => {
|
|
8967
9846
|
const [spawnPayload, exitPayload] = await Promise.allSettled([
|
|
8968
9847
|
once(subprocess, 'spawn'),
|
|
8969
9848
|
once(subprocess, 'exit'),
|
|
@@ -9024,7 +9903,7 @@ const getResultError = (error, exitCode, signal) => {
|
|
|
9024
9903
|
return isFailedExit(exitCode, signal) ? new DiscardedError() : undefined;
|
|
9025
9904
|
};
|
|
9026
9905
|
|
|
9027
|
-
// Main shared logic for all sync methods: `execaSync()`,
|
|
9906
|
+
// Main shared logic for all sync methods: `execaSync()`, `$.sync()`
|
|
9028
9907
|
const execaCoreSync = (rawFile, rawArguments, rawOptions) => {
|
|
9029
9908
|
const {file, commandArguments, command, escapedCommand, startTime, verboseInfo, options, fileDescriptors} = handleSyncArguments(rawFile, rawArguments, rawOptions);
|
|
9030
9909
|
const result = spawnSubprocessSync({
|
|
@@ -9069,7 +9948,11 @@ const handleSyncArguments = (rawFile, rawArguments, rawOptions) => {
|
|
|
9069
9948
|
const normalizeSyncOptions = options => options.node && !options.ipc ? {...options, ipc: false} : options;
|
|
9070
9949
|
|
|
9071
9950
|
// Options validation logic specific to sync methods
|
|
9072
|
-
const validateSyncOptions = ({ipc, detached, cancelSignal}) => {
|
|
9951
|
+
const validateSyncOptions = ({ipc, ipcInput, detached, cancelSignal}) => {
|
|
9952
|
+
if (ipcInput) {
|
|
9953
|
+
throwInvalidSyncOption('ipcInput');
|
|
9954
|
+
}
|
|
9955
|
+
|
|
9073
9956
|
if (ipc) {
|
|
9074
9957
|
throwInvalidSyncOption('ipc: true');
|
|
9075
9958
|
}
|
|
@@ -9153,6 +10036,7 @@ const getSyncResult = ({error, exitCode, signal, timedOut, isMaxBuffer, stdio, a
|
|
|
9153
10036
|
escapedCommand,
|
|
9154
10037
|
stdio,
|
|
9155
10038
|
all,
|
|
10039
|
+
ipcOutput: [],
|
|
9156
10040
|
options,
|
|
9157
10041
|
startTime,
|
|
9158
10042
|
})
|
|
@@ -9162,16 +10046,208 @@ const getSyncResult = ({error, exitCode, signal, timedOut, isMaxBuffer, stdio, a
|
|
|
9162
10046
|
escapedCommand,
|
|
9163
10047
|
timedOut,
|
|
9164
10048
|
isCanceled: false,
|
|
10049
|
+
isGracefullyCanceled: false,
|
|
9165
10050
|
isMaxBuffer,
|
|
10051
|
+
isForcefullyTerminated: false,
|
|
9166
10052
|
exitCode,
|
|
9167
10053
|
signal,
|
|
9168
10054
|
stdio,
|
|
9169
10055
|
all,
|
|
10056
|
+
ipcOutput: [],
|
|
9170
10057
|
options,
|
|
9171
10058
|
startTime,
|
|
9172
10059
|
isSync: true,
|
|
9173
10060
|
});
|
|
9174
10061
|
|
|
10062
|
+
// Like `[sub]process.once('message')` but promise-based
|
|
10063
|
+
const getOneMessage = ({anyProcess, channel, isSubprocess, ipc}, {reference = true, filter} = {}) => {
|
|
10064
|
+
validateIpcMethod({
|
|
10065
|
+
methodName: 'getOneMessage',
|
|
10066
|
+
isSubprocess,
|
|
10067
|
+
ipc,
|
|
10068
|
+
isConnected: isConnected(anyProcess),
|
|
10069
|
+
});
|
|
10070
|
+
|
|
10071
|
+
return getOneMessageAsync({
|
|
10072
|
+
anyProcess,
|
|
10073
|
+
channel,
|
|
10074
|
+
isSubprocess,
|
|
10075
|
+
filter,
|
|
10076
|
+
reference,
|
|
10077
|
+
});
|
|
10078
|
+
};
|
|
10079
|
+
|
|
10080
|
+
const getOneMessageAsync = async ({anyProcess, channel, isSubprocess, filter, reference}) => {
|
|
10081
|
+
addReference(channel, reference);
|
|
10082
|
+
const ipcEmitter = getIpcEmitter(anyProcess, channel, isSubprocess);
|
|
10083
|
+
const controller = new AbortController();
|
|
10084
|
+
try {
|
|
10085
|
+
return await Promise.race([
|
|
10086
|
+
getMessage(ipcEmitter, filter, controller),
|
|
10087
|
+
throwOnDisconnect(ipcEmitter, isSubprocess, controller),
|
|
10088
|
+
throwOnStrictError(ipcEmitter, isSubprocess, controller),
|
|
10089
|
+
]);
|
|
10090
|
+
} catch (error) {
|
|
10091
|
+
disconnect(anyProcess);
|
|
10092
|
+
throw error;
|
|
10093
|
+
} finally {
|
|
10094
|
+
controller.abort();
|
|
10095
|
+
removeReference(channel, reference);
|
|
10096
|
+
}
|
|
10097
|
+
};
|
|
10098
|
+
|
|
10099
|
+
const getMessage = async (ipcEmitter, filter, {signal}) => {
|
|
10100
|
+
if (filter === undefined) {
|
|
10101
|
+
const [message] = await once(ipcEmitter, 'message', {signal});
|
|
10102
|
+
return message;
|
|
10103
|
+
}
|
|
10104
|
+
|
|
10105
|
+
for await (const [message] of on(ipcEmitter, 'message', {signal})) {
|
|
10106
|
+
if (filter(message)) {
|
|
10107
|
+
return message;
|
|
10108
|
+
}
|
|
10109
|
+
}
|
|
10110
|
+
};
|
|
10111
|
+
|
|
10112
|
+
const throwOnDisconnect = async (ipcEmitter, isSubprocess, {signal}) => {
|
|
10113
|
+
await once(ipcEmitter, 'disconnect', {signal});
|
|
10114
|
+
throwOnEarlyDisconnect(isSubprocess);
|
|
10115
|
+
};
|
|
10116
|
+
|
|
10117
|
+
const throwOnStrictError = async (ipcEmitter, isSubprocess, {signal}) => {
|
|
10118
|
+
const [error] = await once(ipcEmitter, 'strict:error', {signal});
|
|
10119
|
+
throw getStrictResponseError(error, isSubprocess);
|
|
10120
|
+
};
|
|
10121
|
+
|
|
10122
|
+
// Like `[sub]process.on('message')` but promise-based
|
|
10123
|
+
const getEachMessage = ({anyProcess, channel, isSubprocess, ipc}, {reference = true} = {}) => loopOnMessages({
|
|
10124
|
+
anyProcess,
|
|
10125
|
+
channel,
|
|
10126
|
+
isSubprocess,
|
|
10127
|
+
ipc,
|
|
10128
|
+
shouldAwait: !isSubprocess,
|
|
10129
|
+
reference,
|
|
10130
|
+
});
|
|
10131
|
+
|
|
10132
|
+
// Same but used internally
|
|
10133
|
+
const loopOnMessages = ({anyProcess, channel, isSubprocess, ipc, shouldAwait, reference}) => {
|
|
10134
|
+
validateIpcMethod({
|
|
10135
|
+
methodName: 'getEachMessage',
|
|
10136
|
+
isSubprocess,
|
|
10137
|
+
ipc,
|
|
10138
|
+
isConnected: isConnected(anyProcess),
|
|
10139
|
+
});
|
|
10140
|
+
|
|
10141
|
+
addReference(channel, reference);
|
|
10142
|
+
const ipcEmitter = getIpcEmitter(anyProcess, channel, isSubprocess);
|
|
10143
|
+
const controller = new AbortController();
|
|
10144
|
+
const state = {};
|
|
10145
|
+
stopOnDisconnect(anyProcess, ipcEmitter, controller);
|
|
10146
|
+
abortOnStrictError({
|
|
10147
|
+
ipcEmitter,
|
|
10148
|
+
isSubprocess,
|
|
10149
|
+
controller,
|
|
10150
|
+
state,
|
|
10151
|
+
});
|
|
10152
|
+
return iterateOnMessages({
|
|
10153
|
+
anyProcess,
|
|
10154
|
+
channel,
|
|
10155
|
+
ipcEmitter,
|
|
10156
|
+
isSubprocess,
|
|
10157
|
+
shouldAwait,
|
|
10158
|
+
controller,
|
|
10159
|
+
state,
|
|
10160
|
+
reference,
|
|
10161
|
+
});
|
|
10162
|
+
};
|
|
10163
|
+
|
|
10164
|
+
const stopOnDisconnect = async (anyProcess, ipcEmitter, controller) => {
|
|
10165
|
+
try {
|
|
10166
|
+
await once(ipcEmitter, 'disconnect', {signal: controller.signal});
|
|
10167
|
+
controller.abort();
|
|
10168
|
+
} catch {}
|
|
10169
|
+
};
|
|
10170
|
+
|
|
10171
|
+
const abortOnStrictError = async ({ipcEmitter, isSubprocess, controller, state}) => {
|
|
10172
|
+
try {
|
|
10173
|
+
const [error] = await once(ipcEmitter, 'strict:error', {signal: controller.signal});
|
|
10174
|
+
state.error = getStrictResponseError(error, isSubprocess);
|
|
10175
|
+
controller.abort();
|
|
10176
|
+
} catch {}
|
|
10177
|
+
};
|
|
10178
|
+
|
|
10179
|
+
const iterateOnMessages = async function * ({anyProcess, channel, ipcEmitter, isSubprocess, shouldAwait, controller, state, reference}) {
|
|
10180
|
+
try {
|
|
10181
|
+
for await (const [message] of on(ipcEmitter, 'message', {signal: controller.signal})) {
|
|
10182
|
+
throwIfStrictError(state);
|
|
10183
|
+
yield message;
|
|
10184
|
+
}
|
|
10185
|
+
} catch {
|
|
10186
|
+
throwIfStrictError(state);
|
|
10187
|
+
} finally {
|
|
10188
|
+
controller.abort();
|
|
10189
|
+
removeReference(channel, reference);
|
|
10190
|
+
|
|
10191
|
+
if (!isSubprocess) {
|
|
10192
|
+
disconnect(anyProcess);
|
|
10193
|
+
}
|
|
10194
|
+
|
|
10195
|
+
if (shouldAwait) {
|
|
10196
|
+
await anyProcess;
|
|
10197
|
+
}
|
|
10198
|
+
}
|
|
10199
|
+
};
|
|
10200
|
+
|
|
10201
|
+
const throwIfStrictError = ({error}) => {
|
|
10202
|
+
if (error) {
|
|
10203
|
+
throw error;
|
|
10204
|
+
}
|
|
10205
|
+
};
|
|
10206
|
+
|
|
10207
|
+
// Add promise-based IPC methods in current process
|
|
10208
|
+
const addIpcMethods = (subprocess, {ipc}) => {
|
|
10209
|
+
Object.assign(subprocess, getIpcMethods(subprocess, false, ipc));
|
|
10210
|
+
};
|
|
10211
|
+
|
|
10212
|
+
// Get promise-based IPC in the subprocess
|
|
10213
|
+
const getIpcExport = () => {
|
|
10214
|
+
const anyProcess = process$3;
|
|
10215
|
+
const isSubprocess = true;
|
|
10216
|
+
const ipc = process$3.channel !== undefined;
|
|
10217
|
+
|
|
10218
|
+
return {
|
|
10219
|
+
...getIpcMethods(anyProcess, isSubprocess, ipc),
|
|
10220
|
+
getCancelSignal: getCancelSignal.bind(undefined, {
|
|
10221
|
+
anyProcess,
|
|
10222
|
+
channel: anyProcess.channel,
|
|
10223
|
+
isSubprocess,
|
|
10224
|
+
ipc,
|
|
10225
|
+
}),
|
|
10226
|
+
};
|
|
10227
|
+
};
|
|
10228
|
+
|
|
10229
|
+
// Retrieve the `ipc` shared by both the current process and the subprocess
|
|
10230
|
+
const getIpcMethods = (anyProcess, isSubprocess, ipc) => ({
|
|
10231
|
+
sendMessage: sendMessage.bind(undefined, {
|
|
10232
|
+
anyProcess,
|
|
10233
|
+
channel: anyProcess.channel,
|
|
10234
|
+
isSubprocess,
|
|
10235
|
+
ipc,
|
|
10236
|
+
}),
|
|
10237
|
+
getOneMessage: getOneMessage.bind(undefined, {
|
|
10238
|
+
anyProcess,
|
|
10239
|
+
channel: anyProcess.channel,
|
|
10240
|
+
isSubprocess,
|
|
10241
|
+
ipc,
|
|
10242
|
+
}),
|
|
10243
|
+
getEachMessage: getEachMessage.bind(undefined, {
|
|
10244
|
+
anyProcess,
|
|
10245
|
+
channel: anyProcess.channel,
|
|
10246
|
+
isSubprocess,
|
|
10247
|
+
ipc,
|
|
10248
|
+
}),
|
|
10249
|
+
});
|
|
10250
|
+
|
|
9175
10251
|
// When the subprocess fails to spawn.
|
|
9176
10252
|
// We ensure the returned error is always both a promise and a subprocess.
|
|
9177
10253
|
const handleEarlyError = ({error, command, escapedCommand, fileDescriptors, options, startTime, verboseInfo}) => {
|
|
@@ -9530,19 +10606,6 @@ const PASSTHROUGH_LISTENERS_COUNT = 2;
|
|
|
9530
10606
|
// - once due to `stream.pipe(passThroughStream)`
|
|
9531
10607
|
const PASSTHROUGH_LISTENERS_PER_STREAM = 1;
|
|
9532
10608
|
|
|
9533
|
-
// Temporarily increase the maximum number of listeners on an eventEmitter
|
|
9534
|
-
const incrementMaxListeners = (eventEmitter, maxListenersIncrement, signal) => {
|
|
9535
|
-
const maxListeners = eventEmitter.getMaxListeners();
|
|
9536
|
-
if (maxListeners === 0 || maxListeners === Number.POSITIVE_INFINITY) {
|
|
9537
|
-
return;
|
|
9538
|
-
}
|
|
9539
|
-
|
|
9540
|
-
eventEmitter.setMaxListeners(maxListeners + maxListenersIncrement);
|
|
9541
|
-
addAbortListener(signal, () => {
|
|
9542
|
-
eventEmitter.setMaxListeners(eventEmitter.getMaxListeners() - maxListenersIncrement);
|
|
9543
|
-
});
|
|
9544
|
-
};
|
|
9545
|
-
|
|
9546
10609
|
// Similar to `Stream.pipeline(source, destination)`, but does not destroy standard streams
|
|
9547
10610
|
const pipeStreams = (source, destination) => {
|
|
9548
10611
|
source.pipe(destination);
|
|
@@ -10669,10 +11732,72 @@ const getAllMixed = ({all, stdout, stderr}) => all
|
|
|
10669
11732
|
&& stderr
|
|
10670
11733
|
&& stdout.readableObjectMode !== stderr.readableObjectMode;
|
|
10671
11734
|
|
|
11735
|
+
// When `verbose` is `'full'`, print IPC messages from the subprocess
|
|
11736
|
+
const shouldLogIpc = ({verbose}) => verbose.at(-1) === 'full';
|
|
11737
|
+
|
|
11738
|
+
const logIpcOutput = (message, {verboseId}) => {
|
|
11739
|
+
verboseLog(serializeLogMessage(message), verboseId, 'ipc');
|
|
11740
|
+
};
|
|
11741
|
+
|
|
11742
|
+
// Iterate through IPC messages sent by the subprocess
|
|
11743
|
+
const waitForIpcOutput = async ({
|
|
11744
|
+
subprocess,
|
|
11745
|
+
buffer: bufferArray,
|
|
11746
|
+
maxBuffer: maxBufferArray,
|
|
11747
|
+
ipc,
|
|
11748
|
+
ipcOutput,
|
|
11749
|
+
verboseInfo,
|
|
11750
|
+
}) => {
|
|
11751
|
+
if (!ipc) {
|
|
11752
|
+
return ipcOutput;
|
|
11753
|
+
}
|
|
11754
|
+
|
|
11755
|
+
const isVerbose = shouldLogIpc(verboseInfo);
|
|
11756
|
+
const buffer = bufferArray.at(-1);
|
|
11757
|
+
const maxBuffer = maxBufferArray.at(-1);
|
|
11758
|
+
|
|
11759
|
+
for await (const message of loopOnMessages({
|
|
11760
|
+
anyProcess: subprocess,
|
|
11761
|
+
channel: subprocess.channel,
|
|
11762
|
+
isSubprocess: false,
|
|
11763
|
+
ipc,
|
|
11764
|
+
shouldAwait: false,
|
|
11765
|
+
reference: true,
|
|
11766
|
+
})) {
|
|
11767
|
+
if (buffer) {
|
|
11768
|
+
checkIpcMaxBuffer(subprocess, ipcOutput, maxBuffer);
|
|
11769
|
+
ipcOutput.push(message);
|
|
11770
|
+
}
|
|
11771
|
+
|
|
11772
|
+
if (isVerbose) {
|
|
11773
|
+
logIpcOutput(message, verboseInfo);
|
|
11774
|
+
}
|
|
11775
|
+
}
|
|
11776
|
+
|
|
11777
|
+
return ipcOutput;
|
|
11778
|
+
};
|
|
11779
|
+
|
|
11780
|
+
const getBufferedIpcOutput = async (ipcOutputPromise, ipcOutput) => {
|
|
11781
|
+
await Promise.allSettled([ipcOutputPromise]);
|
|
11782
|
+
return ipcOutput;
|
|
11783
|
+
};
|
|
11784
|
+
|
|
10672
11785
|
// Retrieve result of subprocess: exit code, signal, error, streams (stdout/stderr/all)
|
|
10673
11786
|
const waitForSubprocessResult = async ({
|
|
10674
11787
|
subprocess,
|
|
10675
|
-
options: {
|
|
11788
|
+
options: {
|
|
11789
|
+
encoding,
|
|
11790
|
+
buffer,
|
|
11791
|
+
maxBuffer,
|
|
11792
|
+
lines,
|
|
11793
|
+
timeoutDuration: timeout,
|
|
11794
|
+
cancelSignal,
|
|
11795
|
+
gracefulCancel,
|
|
11796
|
+
forceKillAfterDelay,
|
|
11797
|
+
stripFinalNewline,
|
|
11798
|
+
ipc,
|
|
11799
|
+
ipcInput,
|
|
11800
|
+
},
|
|
10676
11801
|
context,
|
|
10677
11802
|
verboseInfo,
|
|
10678
11803
|
fileDescriptors,
|
|
@@ -10680,7 +11805,7 @@ const waitForSubprocessResult = async ({
|
|
|
10680
11805
|
onInternalError,
|
|
10681
11806
|
controller,
|
|
10682
11807
|
}) => {
|
|
10683
|
-
const exitPromise = waitForExit(subprocess);
|
|
11808
|
+
const exitPromise = waitForExit(subprocess, context);
|
|
10684
11809
|
const streamInfo = {
|
|
10685
11810
|
originalStreams,
|
|
10686
11811
|
fileDescriptors,
|
|
@@ -10709,6 +11834,15 @@ const waitForSubprocessResult = async ({
|
|
|
10709
11834
|
verboseInfo,
|
|
10710
11835
|
streamInfo,
|
|
10711
11836
|
});
|
|
11837
|
+
const ipcOutput = [];
|
|
11838
|
+
const ipcOutputPromise = waitForIpcOutput({
|
|
11839
|
+
subprocess,
|
|
11840
|
+
buffer,
|
|
11841
|
+
maxBuffer,
|
|
11842
|
+
ipc,
|
|
11843
|
+
ipcOutput,
|
|
11844
|
+
verboseInfo,
|
|
11845
|
+
});
|
|
10712
11846
|
const originalPromises = waitForOriginalStreams(originalStreams, subprocess, streamInfo);
|
|
10713
11847
|
const customStreamsEndPromises = waitForCustomStreamsEnd(fileDescriptors, streamInfo);
|
|
10714
11848
|
|
|
@@ -10719,19 +11853,38 @@ const waitForSubprocessResult = async ({
|
|
|
10719
11853
|
waitForSuccessfulExit(exitPromise),
|
|
10720
11854
|
Promise.all(stdioPromises),
|
|
10721
11855
|
allPromise,
|
|
11856
|
+
ipcOutputPromise,
|
|
11857
|
+
sendIpcInput(subprocess, ipcInput),
|
|
10722
11858
|
...originalPromises,
|
|
10723
11859
|
...customStreamsEndPromises,
|
|
10724
11860
|
]),
|
|
10725
11861
|
onInternalError,
|
|
10726
11862
|
throwOnSubprocessError(subprocess, controller),
|
|
10727
11863
|
...throwOnTimeout(subprocess, timeout, context, controller),
|
|
11864
|
+
...throwOnCancel({
|
|
11865
|
+
subprocess,
|
|
11866
|
+
cancelSignal,
|
|
11867
|
+
gracefulCancel,
|
|
11868
|
+
context,
|
|
11869
|
+
controller,
|
|
11870
|
+
}),
|
|
11871
|
+
...throwOnGracefulCancel({
|
|
11872
|
+
subprocess,
|
|
11873
|
+
cancelSignal,
|
|
11874
|
+
gracefulCancel,
|
|
11875
|
+
forceKillAfterDelay,
|
|
11876
|
+
context,
|
|
11877
|
+
controller,
|
|
11878
|
+
}),
|
|
10728
11879
|
]);
|
|
10729
11880
|
} catch (error) {
|
|
11881
|
+
context.terminationReason ??= 'other';
|
|
10730
11882
|
return Promise.all([
|
|
10731
11883
|
{error},
|
|
10732
11884
|
exitPromise,
|
|
10733
11885
|
Promise.all(stdioPromises.map(stdioPromise => getBufferedData(stdioPromise))),
|
|
10734
11886
|
getBufferedData(allPromise),
|
|
11887
|
+
getBufferedIpcOutput(ipcOutputPromise, ipcOutput),
|
|
10735
11888
|
Promise.allSettled(originalPromises),
|
|
10736
11889
|
Promise.allSettled(customStreamsEndPromises),
|
|
10737
11890
|
]);
|
|
@@ -10761,14 +11914,6 @@ const throwOnSubprocessError = async (subprocess, {signal}) => {
|
|
|
10761
11914
|
throw error;
|
|
10762
11915
|
};
|
|
10763
11916
|
|
|
10764
|
-
const createDeferred = () => {
|
|
10765
|
-
const methods = {};
|
|
10766
|
-
const promise = new Promise((resolve, reject) => {
|
|
10767
|
-
Object.assign(methods, {resolve, reject});
|
|
10768
|
-
});
|
|
10769
|
-
return Object.assign(promise, methods);
|
|
10770
|
-
};
|
|
10771
|
-
|
|
10772
11917
|
// When using multiple `.readable()`/`.writable()`/`.duplex()`, `final` and `destroy` should wait for other streams
|
|
10773
11918
|
const initializeConcurrentStreams = () => ({
|
|
10774
11919
|
readableDestroy: new WeakMap(),
|
|
@@ -11135,7 +12280,7 @@ const descriptors = ['then', 'catch', 'finally'].map(property => [
|
|
|
11135
12280
|
Reflect.getOwnPropertyDescriptor(nativePromisePrototype, property),
|
|
11136
12281
|
]);
|
|
11137
12282
|
|
|
11138
|
-
// Main shared logic for all async methods: `execa()`,
|
|
12283
|
+
// Main shared logic for all async methods: `execa()`, `$`, `execaNode()`
|
|
11139
12284
|
const execaCoreAsync = (rawFile, rawArguments, rawOptions, createNested) => {
|
|
11140
12285
|
const {file, commandArguments, command, escapedCommand, startTime, verboseInfo, options, fileDescriptors} = handleAsyncArguments(rawFile, rawArguments, rawOptions);
|
|
11141
12286
|
const {subprocess, promise} = spawnSubprocessAsync({
|
|
@@ -11185,12 +12330,12 @@ const handleAsyncArguments = (rawFile, rawArguments, rawOptions) => {
|
|
|
11185
12330
|
|
|
11186
12331
|
// Options normalization logic specific to async methods.
|
|
11187
12332
|
// Prevent passing the `timeout` option directly to `child_process.spawn()`.
|
|
11188
|
-
const handleAsyncOptions = ({timeout, signal,
|
|
12333
|
+
const handleAsyncOptions = ({timeout, signal, ...options}) => {
|
|
11189
12334
|
if (signal !== undefined) {
|
|
11190
12335
|
throw new TypeError('The "signal" option has been renamed to "cancelSignal" instead.');
|
|
11191
12336
|
}
|
|
11192
12337
|
|
|
11193
|
-
return {...options, timeoutDuration: timeout
|
|
12338
|
+
return {...options, timeoutDuration: timeout};
|
|
11194
12339
|
};
|
|
11195
12340
|
|
|
11196
12341
|
const spawnSubprocessAsync = ({file, commandArguments, options, startTime, verboseInfo, command, escapedCommand, fileDescriptors}) => {
|
|
@@ -11216,15 +12361,18 @@ const spawnSubprocessAsync = ({file, commandArguments, options, startTime, verbo
|
|
|
11216
12361
|
pipeOutputAsync(subprocess, fileDescriptors, controller);
|
|
11217
12362
|
cleanupOnExit(subprocess, options, controller);
|
|
11218
12363
|
|
|
12364
|
+
const context = {};
|
|
11219
12365
|
const onInternalError = createDeferred();
|
|
11220
12366
|
subprocess.kill = subprocessKill.bind(undefined, {
|
|
11221
12367
|
kill: subprocess.kill.bind(subprocess),
|
|
11222
12368
|
options,
|
|
11223
12369
|
onInternalError,
|
|
12370
|
+
context,
|
|
11224
12371
|
controller,
|
|
11225
12372
|
});
|
|
11226
12373
|
subprocess.all = makeAllStream(subprocess, options);
|
|
11227
12374
|
addConvertedStreams(subprocess, options);
|
|
12375
|
+
addIpcMethods(subprocess, options);
|
|
11228
12376
|
|
|
11229
12377
|
const promise = handlePromise({
|
|
11230
12378
|
subprocess,
|
|
@@ -11235,6 +12383,7 @@ const spawnSubprocessAsync = ({file, commandArguments, options, startTime, verbo
|
|
|
11235
12383
|
originalStreams,
|
|
11236
12384
|
command,
|
|
11237
12385
|
escapedCommand,
|
|
12386
|
+
context,
|
|
11238
12387
|
onInternalError,
|
|
11239
12388
|
controller,
|
|
11240
12389
|
});
|
|
@@ -11242,10 +12391,14 @@ const spawnSubprocessAsync = ({file, commandArguments, options, startTime, verbo
|
|
|
11242
12391
|
};
|
|
11243
12392
|
|
|
11244
12393
|
// Asynchronous logic, as opposed to the previous logic which can be run synchronously, i.e. can be returned to user right away
|
|
11245
|
-
const handlePromise = async ({subprocess, options, startTime, verboseInfo, fileDescriptors, originalStreams, command, escapedCommand, onInternalError, controller}) => {
|
|
11246
|
-
const
|
|
11247
|
-
|
|
11248
|
-
|
|
12394
|
+
const handlePromise = async ({subprocess, options, startTime, verboseInfo, fileDescriptors, originalStreams, command, escapedCommand, context, onInternalError, controller}) => {
|
|
12395
|
+
const [
|
|
12396
|
+
errorInfo,
|
|
12397
|
+
[exitCode, signal],
|
|
12398
|
+
stdioResults,
|
|
12399
|
+
allResult,
|
|
12400
|
+
ipcOutput,
|
|
12401
|
+
] = await waitForSubprocessResult({
|
|
11249
12402
|
subprocess,
|
|
11250
12403
|
options,
|
|
11251
12404
|
context,
|
|
@@ -11266,6 +12419,7 @@ const handlePromise = async ({subprocess, options, startTime, verboseInfo, fileD
|
|
|
11266
12419
|
signal,
|
|
11267
12420
|
stdio,
|
|
11268
12421
|
all,
|
|
12422
|
+
ipcOutput,
|
|
11269
12423
|
context,
|
|
11270
12424
|
options,
|
|
11271
12425
|
command,
|
|
@@ -11275,18 +12429,21 @@ const handlePromise = async ({subprocess, options, startTime, verboseInfo, fileD
|
|
|
11275
12429
|
return handleResult(result, verboseInfo, options);
|
|
11276
12430
|
};
|
|
11277
12431
|
|
|
11278
|
-
const getAsyncResult = ({errorInfo, exitCode, signal, stdio, all, context, options, command, escapedCommand, startTime}) => 'error' in errorInfo
|
|
12432
|
+
const getAsyncResult = ({errorInfo, exitCode, signal, stdio, all, ipcOutput, context, options, command, escapedCommand, startTime}) => 'error' in errorInfo
|
|
11279
12433
|
? makeError({
|
|
11280
12434
|
error: errorInfo.error,
|
|
11281
12435
|
command,
|
|
11282
12436
|
escapedCommand,
|
|
11283
|
-
timedOut: context.
|
|
11284
|
-
isCanceled:
|
|
12437
|
+
timedOut: context.terminationReason === 'timeout',
|
|
12438
|
+
isCanceled: context.terminationReason === 'cancel' || context.terminationReason === 'gracefulCancel',
|
|
12439
|
+
isGracefullyCanceled: context.terminationReason === 'gracefulCancel',
|
|
11285
12440
|
isMaxBuffer: errorInfo.error instanceof MaxBufferError,
|
|
12441
|
+
isForcefullyTerminated: context.isForcefullyTerminated,
|
|
11286
12442
|
exitCode,
|
|
11287
12443
|
signal,
|
|
11288
12444
|
stdio,
|
|
11289
12445
|
all,
|
|
12446
|
+
ipcOutput,
|
|
11290
12447
|
options,
|
|
11291
12448
|
startTime,
|
|
11292
12449
|
isSync: false,
|
|
@@ -11296,6 +12453,7 @@ const getAsyncResult = ({errorInfo, exitCode, signal, stdio, all, context, optio
|
|
|
11296
12453
|
escapedCommand,
|
|
11297
12454
|
stdio,
|
|
11298
12455
|
all,
|
|
12456
|
+
ipcOutput,
|
|
11299
12457
|
options,
|
|
11300
12458
|
startTime,
|
|
11301
12459
|
});
|
|
@@ -11392,8 +12550,23 @@ const parseCommand = (command, unusedArguments) => {
|
|
|
11392
12550
|
throw new TypeError(`The command and its arguments must be passed as a single string: ${command} ${unusedArguments}.`);
|
|
11393
12551
|
}
|
|
11394
12552
|
|
|
12553
|
+
const [file, ...commandArguments] = parseCommandString(command);
|
|
12554
|
+
return {file, commandArguments};
|
|
12555
|
+
};
|
|
12556
|
+
|
|
12557
|
+
// Convert `command` string into an array of file or arguments to pass to $`${...fileOrCommandArguments}`
|
|
12558
|
+
const parseCommandString = command => {
|
|
12559
|
+
if (typeof command !== 'string') {
|
|
12560
|
+
throw new TypeError(`The command must be a string: ${String(command)}.`);
|
|
12561
|
+
}
|
|
12562
|
+
|
|
12563
|
+
const trimmedCommand = command.trim();
|
|
12564
|
+
if (trimmedCommand === '') {
|
|
12565
|
+
return [];
|
|
12566
|
+
}
|
|
12567
|
+
|
|
11395
12568
|
const tokens = [];
|
|
11396
|
-
for (const token of
|
|
12569
|
+
for (const token of trimmedCommand.split(SPACES_REGEXP)) {
|
|
11397
12570
|
// Allow spaces to be escaped by a backslash if not meant as a delimiter
|
|
11398
12571
|
const previousToken = tokens.at(-1);
|
|
11399
12572
|
if (previousToken && previousToken.endsWith('\\')) {
|
|
@@ -11404,8 +12577,7 @@ const parseCommand = (command, unusedArguments) => {
|
|
|
11404
12577
|
}
|
|
11405
12578
|
}
|
|
11406
12579
|
|
|
11407
|
-
|
|
11408
|
-
return {file, commandArguments};
|
|
12580
|
+
return tokens;
|
|
11409
12581
|
};
|
|
11410
12582
|
|
|
11411
12583
|
const SPACES_REGEXP = / +/g;
|
|
@@ -11440,6 +12612,8 @@ createExeca(mapCommandSync);
|
|
|
11440
12612
|
createExeca(mapNode);
|
|
11441
12613
|
createExeca(mapScriptAsync, {}, deepScriptOptions, setScriptSync);
|
|
11442
12614
|
|
|
12615
|
+
getIpcExport();
|
|
12616
|
+
|
|
11443
12617
|
var lodash = {exports: {}};
|
|
11444
12618
|
|
|
11445
12619
|
/**
|
|
@@ -32270,7 +33444,7 @@ var utils$j = {};
|
|
|
32270
33444
|
*/
|
|
32271
33445
|
|
|
32272
33446
|
exports.escapeNode = (block, n = 0, type) => {
|
|
32273
|
-
|
|
33447
|
+
const node = block.nodes[n];
|
|
32274
33448
|
if (!node) return;
|
|
32275
33449
|
|
|
32276
33450
|
if ((type && node.type === type) || node.type === 'open' || node.type === 'close') {
|
|
@@ -32339,13 +33513,23 @@ var utils$j = {};
|
|
|
32339
33513
|
|
|
32340
33514
|
exports.flatten = (...args) => {
|
|
32341
33515
|
const result = [];
|
|
33516
|
+
|
|
32342
33517
|
const flat = arr => {
|
|
32343
33518
|
for (let i = 0; i < arr.length; i++) {
|
|
32344
|
-
|
|
32345
|
-
|
|
33519
|
+
const ele = arr[i];
|
|
33520
|
+
|
|
33521
|
+
if (Array.isArray(ele)) {
|
|
33522
|
+
flat(ele);
|
|
33523
|
+
continue;
|
|
33524
|
+
}
|
|
33525
|
+
|
|
33526
|
+
if (ele !== undefined) {
|
|
33527
|
+
result.push(ele);
|
|
33528
|
+
}
|
|
32346
33529
|
}
|
|
32347
33530
|
return result;
|
|
32348
33531
|
};
|
|
33532
|
+
|
|
32349
33533
|
flat(args);
|
|
32350
33534
|
return result;
|
|
32351
33535
|
};
|
|
@@ -32354,9 +33538,9 @@ var utils$j = {};
|
|
|
32354
33538
|
const utils$i = utils$j;
|
|
32355
33539
|
|
|
32356
33540
|
var stringify$4 = (ast, options = {}) => {
|
|
32357
|
-
|
|
32358
|
-
|
|
32359
|
-
|
|
33541
|
+
const stringify = (node, parent = {}) => {
|
|
33542
|
+
const invalidBlock = options.escapeInvalid && utils$i.isInvalidBrace(parent);
|
|
33543
|
+
const invalidNode = node.invalid === true && options.escapeInvalid === true;
|
|
32360
33544
|
let output = '';
|
|
32361
33545
|
|
|
32362
33546
|
if (node.value) {
|
|
@@ -32371,7 +33555,7 @@ var stringify$4 = (ast, options = {}) => {
|
|
|
32371
33555
|
}
|
|
32372
33556
|
|
|
32373
33557
|
if (node.nodes) {
|
|
32374
|
-
for (
|
|
33558
|
+
for (const child of node.nodes) {
|
|
32375
33559
|
output += stringify(child);
|
|
32376
33560
|
}
|
|
32377
33561
|
}
|
|
@@ -32745,7 +33929,7 @@ const toMaxLen = (input, maxLength) => {
|
|
|
32745
33929
|
return negative ? ('-' + input) : input;
|
|
32746
33930
|
};
|
|
32747
33931
|
|
|
32748
|
-
const toSequence = (parts, options) => {
|
|
33932
|
+
const toSequence = (parts, options, maxLen) => {
|
|
32749
33933
|
parts.negatives.sort((a, b) => a < b ? -1 : a > b ? 1 : 0);
|
|
32750
33934
|
parts.positives.sort((a, b) => a < b ? -1 : a > b ? 1 : 0);
|
|
32751
33935
|
|
|
@@ -32755,11 +33939,11 @@ const toSequence = (parts, options) => {
|
|
|
32755
33939
|
let result;
|
|
32756
33940
|
|
|
32757
33941
|
if (parts.positives.length) {
|
|
32758
|
-
positives = parts.positives.join('|');
|
|
33942
|
+
positives = parts.positives.map(v => toMaxLen(String(v), maxLen)).join('|');
|
|
32759
33943
|
}
|
|
32760
33944
|
|
|
32761
33945
|
if (parts.negatives.length) {
|
|
32762
|
-
negatives = `-(${prefix}${parts.negatives.join('|')})`;
|
|
33946
|
+
negatives = `-(${prefix}${parts.negatives.map(v => toMaxLen(String(v), maxLen)).join('|')})`;
|
|
32763
33947
|
}
|
|
32764
33948
|
|
|
32765
33949
|
if (positives && negatives) {
|
|
@@ -32857,7 +34041,7 @@ const fillNumbers = (start, end, step = 1, options = {}) => {
|
|
|
32857
34041
|
|
|
32858
34042
|
if (options.toRegex === true) {
|
|
32859
34043
|
return step > 1
|
|
32860
|
-
? toSequence(parts, options)
|
|
34044
|
+
? toSequence(parts, options, maxLen)
|
|
32861
34045
|
: toRegex(range, null, { wrap: false, ...options });
|
|
32862
34046
|
}
|
|
32863
34047
|
|
|
@@ -32869,7 +34053,6 @@ const fillLetters = (start, end, step = 1, options = {}) => {
|
|
|
32869
34053
|
return invalidRange(start, end, options);
|
|
32870
34054
|
}
|
|
32871
34055
|
|
|
32872
|
-
|
|
32873
34056
|
let format = options.transform || (val => String.fromCharCode(val));
|
|
32874
34057
|
let a = `${start}`.charCodeAt(0);
|
|
32875
34058
|
let b = `${end}`.charCodeAt(0);
|
|
@@ -32937,30 +34120,32 @@ const fill$1 = fillRange;
|
|
|
32937
34120
|
const utils$h = utils$j;
|
|
32938
34121
|
|
|
32939
34122
|
const compile$1 = (ast, options = {}) => {
|
|
32940
|
-
|
|
32941
|
-
|
|
32942
|
-
|
|
32943
|
-
|
|
32944
|
-
|
|
34123
|
+
const walk = (node, parent = {}) => {
|
|
34124
|
+
const invalidBlock = utils$h.isInvalidBrace(parent);
|
|
34125
|
+
const invalidNode = node.invalid === true && options.escapeInvalid === true;
|
|
34126
|
+
const invalid = invalidBlock === true || invalidNode === true;
|
|
34127
|
+
const prefix = options.escapeInvalid === true ? '\\' : '';
|
|
32945
34128
|
let output = '';
|
|
32946
34129
|
|
|
32947
34130
|
if (node.isOpen === true) {
|
|
32948
34131
|
return prefix + node.value;
|
|
32949
34132
|
}
|
|
34133
|
+
|
|
32950
34134
|
if (node.isClose === true) {
|
|
34135
|
+
console.log('node.isClose', prefix, node.value);
|
|
32951
34136
|
return prefix + node.value;
|
|
32952
34137
|
}
|
|
32953
34138
|
|
|
32954
34139
|
if (node.type === 'open') {
|
|
32955
|
-
return invalid ?
|
|
34140
|
+
return invalid ? prefix + node.value : '(';
|
|
32956
34141
|
}
|
|
32957
34142
|
|
|
32958
34143
|
if (node.type === 'close') {
|
|
32959
|
-
return invalid ?
|
|
34144
|
+
return invalid ? prefix + node.value : ')';
|
|
32960
34145
|
}
|
|
32961
34146
|
|
|
32962
34147
|
if (node.type === 'comma') {
|
|
32963
|
-
return node.prev.type === 'comma' ? '' :
|
|
34148
|
+
return node.prev.type === 'comma' ? '' : invalid ? node.value : '|';
|
|
32964
34149
|
}
|
|
32965
34150
|
|
|
32966
34151
|
if (node.value) {
|
|
@@ -32968,8 +34153,8 @@ const compile$1 = (ast, options = {}) => {
|
|
|
32968
34153
|
}
|
|
32969
34154
|
|
|
32970
34155
|
if (node.nodes && node.ranges > 0) {
|
|
32971
|
-
|
|
32972
|
-
|
|
34156
|
+
const args = utils$h.reduce(node.nodes);
|
|
34157
|
+
const range = fill$1(...args, { ...options, wrap: false, toRegex: true, strictZeros: true });
|
|
32973
34158
|
|
|
32974
34159
|
if (range.length !== 0) {
|
|
32975
34160
|
return args.length > 1 && range.length > 1 ? `(${range})` : range;
|
|
@@ -32977,10 +34162,11 @@ const compile$1 = (ast, options = {}) => {
|
|
|
32977
34162
|
}
|
|
32978
34163
|
|
|
32979
34164
|
if (node.nodes) {
|
|
32980
|
-
for (
|
|
34165
|
+
for (const child of node.nodes) {
|
|
32981
34166
|
output += walk(child, node);
|
|
32982
34167
|
}
|
|
32983
34168
|
}
|
|
34169
|
+
|
|
32984
34170
|
return output;
|
|
32985
34171
|
};
|
|
32986
34172
|
|
|
@@ -32994,7 +34180,7 @@ const stringify$2 = stringify$4;
|
|
|
32994
34180
|
const utils$g = utils$j;
|
|
32995
34181
|
|
|
32996
34182
|
const append = (queue = '', stash = '', enclose = false) => {
|
|
32997
|
-
|
|
34183
|
+
const result = [];
|
|
32998
34184
|
|
|
32999
34185
|
queue = [].concat(queue);
|
|
33000
34186
|
stash = [].concat(stash);
|
|
@@ -33004,15 +34190,15 @@ const append = (queue = '', stash = '', enclose = false) => {
|
|
|
33004
34190
|
return enclose ? utils$g.flatten(stash).map(ele => `{${ele}}`) : stash;
|
|
33005
34191
|
}
|
|
33006
34192
|
|
|
33007
|
-
for (
|
|
34193
|
+
for (const item of queue) {
|
|
33008
34194
|
if (Array.isArray(item)) {
|
|
33009
|
-
for (
|
|
34195
|
+
for (const value of item) {
|
|
33010
34196
|
result.push(append(value, stash, enclose));
|
|
33011
34197
|
}
|
|
33012
34198
|
} else {
|
|
33013
34199
|
for (let ele of stash) {
|
|
33014
34200
|
if (enclose === true && typeof ele === 'string') ele = `{${ele}}`;
|
|
33015
|
-
result.push(Array.isArray(ele) ? append(item, ele, enclose) :
|
|
34201
|
+
result.push(Array.isArray(ele) ? append(item, ele, enclose) : item + ele);
|
|
33016
34202
|
}
|
|
33017
34203
|
}
|
|
33018
34204
|
}
|
|
@@ -33020,9 +34206,9 @@ const append = (queue = '', stash = '', enclose = false) => {
|
|
|
33020
34206
|
};
|
|
33021
34207
|
|
|
33022
34208
|
const expand$1 = (ast, options = {}) => {
|
|
33023
|
-
|
|
34209
|
+
const rangeLimit = options.rangeLimit === undefined ? 1000 : options.rangeLimit;
|
|
33024
34210
|
|
|
33025
|
-
|
|
34211
|
+
const walk = (node, parent = {}) => {
|
|
33026
34212
|
node.queue = [];
|
|
33027
34213
|
|
|
33028
34214
|
let p = parent;
|
|
@@ -33044,7 +34230,7 @@ const expand$1 = (ast, options = {}) => {
|
|
|
33044
34230
|
}
|
|
33045
34231
|
|
|
33046
34232
|
if (node.nodes && node.ranges > 0) {
|
|
33047
|
-
|
|
34233
|
+
const args = utils$g.reduce(node.nodes);
|
|
33048
34234
|
|
|
33049
34235
|
if (utils$g.exceedsLimit(...args, options.step, rangeLimit)) {
|
|
33050
34236
|
throw new RangeError('expanded array length exceeds range limit. Use options.rangeLimit to increase or disable the limit.');
|
|
@@ -33060,7 +34246,7 @@ const expand$1 = (ast, options = {}) => {
|
|
|
33060
34246
|
return;
|
|
33061
34247
|
}
|
|
33062
34248
|
|
|
33063
|
-
|
|
34249
|
+
const enclose = utils$g.encloseBrace(node);
|
|
33064
34250
|
let queue = node.queue;
|
|
33065
34251
|
let block = node;
|
|
33066
34252
|
|
|
@@ -33070,7 +34256,7 @@ const expand$1 = (ast, options = {}) => {
|
|
|
33070
34256
|
}
|
|
33071
34257
|
|
|
33072
34258
|
for (let i = 0; i < node.nodes.length; i++) {
|
|
33073
|
-
|
|
34259
|
+
const child = node.nodes[i];
|
|
33074
34260
|
|
|
33075
34261
|
if (child.type === 'comma' && node.type === 'brace') {
|
|
33076
34262
|
if (i === 1) queue.push('');
|
|
@@ -33102,7 +34288,7 @@ const expand$1 = (ast, options = {}) => {
|
|
|
33102
34288
|
var expand_1 = expand$1;
|
|
33103
34289
|
|
|
33104
34290
|
var constants$4 = {
|
|
33105
|
-
MAX_LENGTH:
|
|
34291
|
+
MAX_LENGTH: 10000,
|
|
33106
34292
|
|
|
33107
34293
|
// Digits
|
|
33108
34294
|
CHAR_0: '0', /* 0 */
|
|
@@ -33190,18 +34376,18 @@ const parse$3 = (input, options = {}) => {
|
|
|
33190
34376
|
throw new TypeError('Expected a string');
|
|
33191
34377
|
}
|
|
33192
34378
|
|
|
33193
|
-
|
|
33194
|
-
|
|
34379
|
+
const opts = options || {};
|
|
34380
|
+
const max = typeof opts.maxLength === 'number' ? Math.min(MAX_LENGTH$1, opts.maxLength) : MAX_LENGTH$1;
|
|
33195
34381
|
if (input.length > max) {
|
|
33196
34382
|
throw new SyntaxError(`Input length (${input.length}), exceeds max characters (${max})`);
|
|
33197
34383
|
}
|
|
33198
34384
|
|
|
33199
|
-
|
|
33200
|
-
|
|
34385
|
+
const ast = { type: 'root', input, nodes: [] };
|
|
34386
|
+
const stack = [ast];
|
|
33201
34387
|
let block = ast;
|
|
33202
34388
|
let prev = ast;
|
|
33203
34389
|
let brackets = 0;
|
|
33204
|
-
|
|
34390
|
+
const length = input.length;
|
|
33205
34391
|
let index = 0;
|
|
33206
34392
|
let depth = 0;
|
|
33207
34393
|
let value;
|
|
@@ -33266,6 +34452,7 @@ const parse$3 = (input, options = {}) => {
|
|
|
33266
34452
|
|
|
33267
34453
|
if (value === CHAR_LEFT_SQUARE_BRACKET$1) {
|
|
33268
34454
|
brackets++;
|
|
34455
|
+
|
|
33269
34456
|
let next;
|
|
33270
34457
|
|
|
33271
34458
|
while (index < length && (next = advance())) {
|
|
@@ -33321,7 +34508,7 @@ const parse$3 = (input, options = {}) => {
|
|
|
33321
34508
|
*/
|
|
33322
34509
|
|
|
33323
34510
|
if (value === CHAR_DOUBLE_QUOTE || value === CHAR_SINGLE_QUOTE || value === CHAR_BACKTICK) {
|
|
33324
|
-
|
|
34511
|
+
const open = value;
|
|
33325
34512
|
let next;
|
|
33326
34513
|
|
|
33327
34514
|
if (options.keepQuotes !== true) {
|
|
@@ -33353,8 +34540,8 @@ const parse$3 = (input, options = {}) => {
|
|
|
33353
34540
|
if (value === CHAR_LEFT_CURLY_BRACE$1) {
|
|
33354
34541
|
depth++;
|
|
33355
34542
|
|
|
33356
|
-
|
|
33357
|
-
|
|
34543
|
+
const dollar = prev.value && prev.value.slice(-1) === '$' || block.dollar === true;
|
|
34544
|
+
const brace = {
|
|
33358
34545
|
type: 'brace',
|
|
33359
34546
|
open: true,
|
|
33360
34547
|
close: false,
|
|
@@ -33381,7 +34568,7 @@ const parse$3 = (input, options = {}) => {
|
|
|
33381
34568
|
continue;
|
|
33382
34569
|
}
|
|
33383
34570
|
|
|
33384
|
-
|
|
34571
|
+
const type = 'close';
|
|
33385
34572
|
block = stack.pop();
|
|
33386
34573
|
block.close = true;
|
|
33387
34574
|
|
|
@@ -33399,7 +34586,7 @@ const parse$3 = (input, options = {}) => {
|
|
|
33399
34586
|
if (value === CHAR_COMMA$1 && depth > 0) {
|
|
33400
34587
|
if (block.ranges > 0) {
|
|
33401
34588
|
block.ranges = 0;
|
|
33402
|
-
|
|
34589
|
+
const open = block.nodes.shift();
|
|
33403
34590
|
block.nodes = [open, { type: 'text', value: stringify$1(block) }];
|
|
33404
34591
|
}
|
|
33405
34592
|
|
|
@@ -33413,7 +34600,7 @@ const parse$3 = (input, options = {}) => {
|
|
|
33413
34600
|
*/
|
|
33414
34601
|
|
|
33415
34602
|
if (value === CHAR_DOT$1 && depth > 0 && block.commas === 0) {
|
|
33416
|
-
|
|
34603
|
+
const siblings = block.nodes;
|
|
33417
34604
|
|
|
33418
34605
|
if (depth === 0 || siblings.length === 0) {
|
|
33419
34606
|
push({ type: 'text', value });
|
|
@@ -33440,7 +34627,7 @@ const parse$3 = (input, options = {}) => {
|
|
|
33440
34627
|
if (prev.type === 'range') {
|
|
33441
34628
|
siblings.pop();
|
|
33442
34629
|
|
|
33443
|
-
|
|
34630
|
+
const before = siblings[siblings.length - 1];
|
|
33444
34631
|
before.value += prev.value + value;
|
|
33445
34632
|
prev = before;
|
|
33446
34633
|
block.ranges--;
|
|
@@ -33473,8 +34660,8 @@ const parse$3 = (input, options = {}) => {
|
|
|
33473
34660
|
});
|
|
33474
34661
|
|
|
33475
34662
|
// get the location of the block on parent.nodes (block's siblings)
|
|
33476
|
-
|
|
33477
|
-
|
|
34663
|
+
const parent = stack[stack.length - 1];
|
|
34664
|
+
const index = parent.nodes.indexOf(block);
|
|
33478
34665
|
// replace the (invalid) block with it's nodes
|
|
33479
34666
|
parent.nodes.splice(index, 1, ...block.nodes);
|
|
33480
34667
|
}
|
|
@@ -33509,8 +34696,8 @@ const braces$1 = (input, options = {}) => {
|
|
|
33509
34696
|
let output = [];
|
|
33510
34697
|
|
|
33511
34698
|
if (Array.isArray(input)) {
|
|
33512
|
-
for (
|
|
33513
|
-
|
|
34699
|
+
for (const pattern of input) {
|
|
34700
|
+
const result = braces$1.create(pattern, options);
|
|
33514
34701
|
if (Array.isArray(result)) {
|
|
33515
34702
|
output.push(...result);
|
|
33516
34703
|
} else {
|
|
@@ -33644,7 +34831,7 @@ braces$1.create = (input, options = {}) => {
|
|
|
33644
34831
|
return [input];
|
|
33645
34832
|
}
|
|
33646
34833
|
|
|
33647
|
-
|
|
34834
|
+
return options.expand !== true
|
|
33648
34835
|
? braces$1.compile(input, options)
|
|
33649
34836
|
: braces$1.expand(input, options);
|
|
33650
34837
|
};
|