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