@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.
@@ -1,18 +1,19 @@
1
1
  #!/usr/bin/env node
2
- import require$$0, { once, on, addAbortListener, setMaxListeners } from 'node:events';
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, { hrtime, platform as platform$1, execPath, execArgv } from 'node:process';
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, callbackify, inspect, aborted } from 'node:util';
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$1(killSignal, optionName);
5848
+ return normalizeSignal(killSignal, optionName);
5453
5849
  };
5454
5850
 
5455
5851
  const normalizeSignalArgument = signal => signal === 0
5456
5852
  ? signal
5457
- : normalizeSignal$1(signal, '`subprocess.kill()`\'s argument');
5853
+ : normalizeSignal(signal, '`subprocess.kill()`\'s argument');
5458
5854
 
5459
- const normalizeSignal$1 = (signalNameOrInteger, optionName) => {
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 (!shouldForceKill(signal, forceKillAfterDelay, killSignal, killResult)) {
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: controller.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
- const shouldForceKill = (signal, forceKillAfterDelay, killSignal, killResult) => signal === killSignal
5585
- && forceKillAfterDelay !== false
5586
- && killResult;
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.timedOut = true;
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
- ipc = false,
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 = [shortMessage, ...messageStdio, ...stdio.slice(3)]
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 = ({originalError, timedOut, timeout, isMaxBuffer, maxBuffer, errorCode, signal, signalDescription, exitCode, isCanceled}) => {
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 'Command was canceled';
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
- options: {timeoutDuration, timeout = timeoutDuration, cwd, maxBuffer},
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 : signalsByName[rawSignal].description;
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 ? normalizeStdioSync(stdioArray, buffer, verbose) : normalizeStdioAsync(stdioArray, ipc);
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.path === secondValue.path;
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
- const lines = typeof line === 'string' ? line : inspect(line);
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`, e.g. with the `cancelSignal` option.
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()`, `execaCommandSync()`, `$.sync()`
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: {encoding, buffer, maxBuffer, lines, timeoutDuration: timeout, stripFinalNewline},
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()`, `execaCommand()`, `$`, `execaNode()`
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, cancelSignal, ...options}) => {
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, signal: cancelSignal};
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 context = {timedOut: false};
11248
-
11249
- const [errorInfo, [exitCode, signal], stdioResults, allResult] = await waitForSubprocessResult({
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.timedOut,
11285
- isCanceled: options.signal?.aborted === true,
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 command.trim().split(SPACES_REGEXP)) {
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
- const [file, ...commandArguments] = tokens;
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
- let node = block.nodes[n];
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
- let ele = arr[i];
32346
- Array.isArray(ele) ? flat(ele) : ele !== void 0 && result.push(ele);
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
- let stringify = (node, parent = {}) => {
32359
- let invalidBlock = options.escapeInvalid && utils$i.isInvalidBrace(parent);
32360
- let invalidNode = node.invalid === true && options.escapeInvalid === true;
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 (let child of node.nodes) {
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
- let walk = (node, parent = {}) => {
32942
- let invalidBlock = utils$h.isInvalidBrace(parent);
32943
- let invalidNode = node.invalid === true && options.escapeInvalid === true;
32944
- let invalid = invalidBlock === true || invalidNode === true;
32945
- let prefix = options.escapeInvalid === true ? '\\' : '';
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 ? (prefix + node.value) : '(';
34141
+ return invalid ? prefix + node.value : '(';
32957
34142
  }
32958
34143
 
32959
34144
  if (node.type === 'close') {
32960
- return invalid ? (prefix + node.value) : ')';
34145
+ return invalid ? prefix + node.value : ')';
32961
34146
  }
32962
34147
 
32963
34148
  if (node.type === 'comma') {
32964
- return node.prev.type === 'comma' ? '' : (invalid ? node.value : '|');
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
- let args = utils$h.reduce(node.nodes);
32973
- let range = fill$1(...args, { ...options, wrap: false, toRegex: true });
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 (let child of node.nodes) {
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
- let result = [];
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 (let item of queue) {
34194
+ for (const item of queue) {
33009
34195
  if (Array.isArray(item)) {
33010
- for (let value of item) {
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) : (item + ele));
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
- let rangeLimit = options.rangeLimit === void 0 ? 1000 : options.rangeLimit;
34210
+ const rangeLimit = options.rangeLimit === undefined ? 1000 : options.rangeLimit;
33025
34211
 
33026
- let walk = (node, parent = {}) => {
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
- let args = utils$g.reduce(node.nodes);
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
- let enclose = utils$g.encloseBrace(node);
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
- let child = node.nodes[i];
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: 1024 * 64,
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
- let opts = options || {};
33195
- let max = typeof opts.maxLength === 'number' ? Math.min(MAX_LENGTH$1, opts.maxLength) : MAX_LENGTH$1;
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
- let ast = { type: 'root', input, nodes: [] };
33201
- let stack = [ast];
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
- let length = input.length;
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
- let open = value;
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
- let dollar = prev.value && prev.value.slice(-1) === '$' || block.dollar === true;
33358
- let brace = {
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
- let type = 'close';
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
- let open = block.nodes.shift();
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
- let siblings = block.nodes;
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
- let before = siblings[siblings.length - 1];
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
- let parent = stack[stack.length - 1];
33478
- let index = parent.nodes.indexOf(block);
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 (let pattern of input) {
33514
- let result = braces$1.create(pattern, options);
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
- return options.expand !== true
34835
+ return options.expand !== true
33649
34836
  ? braces$1.compile(input, options)
33650
34837
  : braces$1.expand(input, options);
33651
34838
  };