@karmaniverous/get-dotenv 4.4.3 → 4.4.4

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