'use strict';

var require$$0$1 = require('child_process');
var require$$1$2 = require('os');
var require$$0 = require('fs');
var require$$1 = require('fs/promises');
var require$$1$1 = require('path');

var cjs = {};

var posix = {};

var hasRequiredPosix;
function requirePosix() {
  if (hasRequiredPosix) return posix;
  hasRequiredPosix = 1;
  /**
   * This is the Posix implementation of isexe, which uses the file
   * mode and uid/gid values.
   *
   * @module
   */
  Object.defineProperty(posix, "__esModule", {
    value: true
  });
  posix.sync = posix.isexe = void 0;
  const fs_1 = require$$0;
  const promises_1 = require$$1;
  /**
   * Determine whether a path is executable according to the mode and
   * current (or specified) user and group IDs.
   */
  const isexe = async (path, options = {}) => {
    const {
      ignoreErrors = false
    } = options;
    try {
      return checkStat(await (0, promises_1.stat)(path), options);
    } catch (e) {
      const er = e;
      if (ignoreErrors || er.code === 'EACCES') return false;
      throw er;
    }
  };
  posix.isexe = isexe;
  /**
   * Synchronously determine whether a path is executable according to
   * the mode and current (or specified) user and group IDs.
   */
  const sync = (path, options = {}) => {
    const {
      ignoreErrors = false
    } = options;
    try {
      return checkStat((0, fs_1.statSync)(path), options);
    } catch (e) {
      const er = e;
      if (ignoreErrors || er.code === 'EACCES') return false;
      throw er;
    }
  };
  posix.sync = sync;
  const checkStat = (stat, options) => stat.isFile() && checkMode(stat, options);
  const checkMode = (stat, options) => {
    const myUid = options.uid ?? process.getuid?.();
    const myGroups = options.groups ?? process.getgroups?.() ?? [];
    const myGid = options.gid ?? process.getgid?.() ?? myGroups[0];
    if (myUid === undefined || myGid === undefined) {
      throw new Error('cannot get uid or gid');
    }
    const groups = new Set([myGid, ...myGroups]);
    const mod = stat.mode;
    const uid = stat.uid;
    const gid = stat.gid;
    const u = parseInt('100', 8);
    const g = parseInt('010', 8);
    const o = parseInt('001', 8);
    const ug = u | g;
    return !!(mod & o || mod & g && groups.has(gid) || mod & u && uid === myUid || mod & ug && myUid === 0);
  };
  return posix;
}

var win32 = {};

var hasRequiredWin32;
function requireWin32() {
  if (hasRequiredWin32) return win32;
  hasRequiredWin32 = 1;
  /**
   * This is the Windows implementation of isexe, which uses the file
   * extension and PATHEXT setting.
   *
   * @module
   */
  Object.defineProperty(win32, "__esModule", {
    value: true
  });
  win32.sync = win32.isexe = void 0;
  const fs_1 = require$$0;
  const promises_1 = require$$1;
  /**
   * Determine whether a path is executable based on the file extension
   * and PATHEXT environment variable (or specified pathExt option)
   */
  const isexe = async (path, options = {}) => {
    const {
      ignoreErrors = false
    } = options;
    try {
      return checkStat(await (0, promises_1.stat)(path), path, options);
    } catch (e) {
      const er = e;
      if (ignoreErrors || er.code === 'EACCES') return false;
      throw er;
    }
  };
  win32.isexe = isexe;
  /**
   * Synchronously determine whether a path is executable based on the file
   * extension and PATHEXT environment variable (or specified pathExt option)
   */
  const sync = (path, options = {}) => {
    const {
      ignoreErrors = false
    } = options;
    try {
      return checkStat((0, fs_1.statSync)(path), path, options);
    } catch (e) {
      const er = e;
      if (ignoreErrors || er.code === 'EACCES') return false;
      throw er;
    }
  };
  win32.sync = sync;
  const checkPathExt = (path, options) => {
    const {
      pathExt = process.env.PATHEXT || ''
    } = options;
    const peSplit = pathExt.split(';');
    if (peSplit.indexOf('') !== -1) {
      return true;
    }
    for (let i = 0; i < peSplit.length; i++) {
      const p = peSplit[i].toLowerCase();
      const ext = path.substring(path.length - p.length).toLowerCase();
      if (p && ext === p) {
        return true;
      }
    }
    return false;
  };
  const checkStat = (stat, path, options) => stat.isFile() && checkPathExt(path, options);
  return win32;
}

var options = {};

var hasRequiredOptions;
function requireOptions() {
  if (hasRequiredOptions) return options;
  hasRequiredOptions = 1;
  Object.defineProperty(options, "__esModule", {
    value: true
  });
  return options;
}

var hasRequiredCjs;
function requireCjs() {
  if (hasRequiredCjs) return cjs;
  hasRequiredCjs = 1;
  (function (exports) {

    var __createBinding = this && this.__createBinding || (Object.create ? function (o, m, k, k2) {
      if (k2 === undefined) k2 = k;
      var desc = Object.getOwnPropertyDescriptor(m, k);
      if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
        desc = {
          enumerable: true,
          get: function () {
            return m[k];
          }
        };
      }
      Object.defineProperty(o, k2, desc);
    } : function (o, m, k, k2) {
      if (k2 === undefined) k2 = k;
      o[k2] = m[k];
    });
    var __setModuleDefault = this && this.__setModuleDefault || (Object.create ? function (o, v) {
      Object.defineProperty(o, "default", {
        enumerable: true,
        value: v
      });
    } : function (o, v) {
      o["default"] = v;
    });
    var __importStar = this && this.__importStar || function (mod) {
      if (mod && mod.__esModule) return mod;
      var result = {};
      if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
      __setModuleDefault(result, mod);
      return result;
    };
    var __exportStar = this && this.__exportStar || function (m, exports) {
      for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
    };
    Object.defineProperty(exports, "__esModule", {
      value: true
    });
    exports.sync = exports.isexe = exports.posix = exports.win32 = void 0;
    const posix = __importStar(requirePosix());
    exports.posix = posix;
    const win32 = __importStar(requireWin32());
    exports.win32 = win32;
    __exportStar(requireOptions(), exports);
    const platform = process.env._ISEXE_TEST_PLATFORM_ || process.platform;
    const impl = platform === 'win32' ? win32 : posix;
    /**
     * Determine whether a path is executable on the current platform.
     */
    exports.isexe = impl.isexe;
    /**
     * Synchronously determine whether a path is executable on the
     * current platform.
     */
    exports.sync = impl.sync;
  })(cjs);
  return cjs;
}

var lib$1;
var hasRequiredLib$1;
function requireLib$1() {
  if (hasRequiredLib$1) return lib$1;
  hasRequiredLib$1 = 1;
  const {
    isexe,
    sync: isexeSync
  } = requireCjs();
  const {
    join,
    delimiter,
    sep,
    posix
  } = require$$1$1;
  const isWindows = process.platform === 'win32';

  // used to check for slashed in commands passed in. always checks for the posix
  // seperator on all platforms, and checks for the current separator when not on
  // a posix platform. don't use the isWindows check for this since that is mocked
  // in tests but we still need the code to actually work when called. that is also
  // why it is ignored from coverage.
  /* istanbul ignore next */
  const rSlash = new RegExp(`[${posix.sep}${sep === posix.sep ? '' : sep}]`.replace(/(\\)/g, '\\$1'));
  const rRel = new RegExp(`^\\.${rSlash.source}`);
  const getNotFoundError = cmd => Object.assign(new Error(`not found: ${cmd}`), {
    code: 'ENOENT'
  });
  const getPathInfo = (cmd, {
    path: optPath = process.env.PATH,
    pathExt: optPathExt = process.env.PATHEXT,
    delimiter: optDelimiter = delimiter
  }) => {
    // If it has a slash, then we don't bother searching the pathenv.
    // just check the file itself, and that's it.
    const pathEnv = cmd.match(rSlash) ? [''] : [
    // windows always checks the cwd first
    ...(isWindows ? [process.cwd()] : []), ...(optPath || /* istanbul ignore next: very unusual */'').split(optDelimiter)];
    if (isWindows) {
      const pathExtExe = optPathExt || ['.EXE', '.CMD', '.BAT', '.COM'].join(optDelimiter);
      const pathExt = pathExtExe.split(optDelimiter).flatMap(item => [item, item.toLowerCase()]);
      if (cmd.includes('.') && pathExt[0] !== '') {
        pathExt.unshift('');
      }
      return {
        pathEnv,
        pathExt,
        pathExtExe
      };
    }
    return {
      pathEnv,
      pathExt: ['']
    };
  };
  const getPathPart = (raw, cmd) => {
    const pathPart = /^".*"$/.test(raw) ? raw.slice(1, -1) : raw;
    const prefix = !pathPart && rRel.test(cmd) ? cmd.slice(0, 2) : '';
    return prefix + join(pathPart, cmd);
  };
  const which = async (cmd, opt = {}) => {
    const {
      pathEnv,
      pathExt,
      pathExtExe
    } = getPathInfo(cmd, opt);
    const found = [];
    for (const envPart of pathEnv) {
      const p = getPathPart(envPart, cmd);
      for (const ext of pathExt) {
        const withExt = p + ext;
        const is = await isexe(withExt, {
          pathExt: pathExtExe,
          ignoreErrors: true
        });
        if (is) {
          if (!opt.all) {
            return withExt;
          }
          found.push(withExt);
        }
      }
    }
    if (opt.all && found.length) {
      return found;
    }
    if (opt.nothrow) {
      return null;
    }
    throw getNotFoundError(cmd);
  };
  const whichSync = (cmd, opt = {}) => {
    const {
      pathEnv,
      pathExt,
      pathExtExe
    } = getPathInfo(cmd, opt);
    const found = [];
    for (const pathEnvPart of pathEnv) {
      const p = getPathPart(pathEnvPart, cmd);
      for (const ext of pathExt) {
        const withExt = p + ext;
        const is = isexeSync(withExt, {
          pathExt: pathExtExe,
          ignoreErrors: true
        });
        if (is) {
          if (!opt.all) {
            return withExt;
          }
          found.push(withExt);
        }
      }
    }
    if (opt.all && found.length) {
      return found;
    }
    if (opt.nothrow) {
      return null;
    }
    throw getNotFoundError(cmd);
  };
  lib$1 = which;
  which.sync = whichSync;
  return lib$1;
}

var _escape;
var hasRequired_escape;
function require_escape() {
  if (hasRequired_escape) return _escape;
  hasRequired_escape = 1;

  // eslint-disable-next-line max-len
  // this code adapted from: https://blogs.msdn.microsoft.com/twistylittlepassagesallalike/2011/04/23/everyone-quotes-command-line-arguments-the-wrong-way/
  const cmd = (input, doubleEscape) => {
    if (!input.length) {
      return '""';
    }
    let result;
    if (!/[ \t\n\v"]/.test(input)) {
      result = input;
    } else {
      result = '"';
      for (let i = 0; i <= input.length; ++i) {
        let slashCount = 0;
        while (input[i] === '\\') {
          ++i;
          ++slashCount;
        }
        if (i === input.length) {
          result += '\\'.repeat(slashCount * 2);
          break;
        }
        if (input[i] === '"') {
          result += '\\'.repeat(slashCount * 2 + 1);
          result += input[i];
        } else {
          result += '\\'.repeat(slashCount);
          result += input[i];
        }
      }
      result += '"';
    }

    // and finally, prefix shell meta chars with a ^
    result = result.replace(/[ !%^&()<>|"]/g, '^$&');
    if (doubleEscape) {
      result = result.replace(/[ !%^&()<>|"]/g, '^$&');
    }
    return result;
  };
  const sh = input => {
    if (!input.length) {
      return `''`;
    }
    if (!/[\t\n\r "#$&'()*;<>?\\`|~]/.test(input)) {
      return input;
    }

    // replace single quotes with '\'' and wrap the whole result in a fresh set of quotes
    const result = `'${input.replace(/'/g, `'\\''`)}'`
    // if the input string already had single quotes around it, clean those up
    .replace(/^(?:'')+(?!$)/, '').replace(/\\'''/g, `\\'`);
    return result;
  };
  _escape = {
    cmd,
    sh
  };
  return _escape;
}

var lib;
var hasRequiredLib;
function requireLib() {
  if (hasRequiredLib) return lib;
  hasRequiredLib = 1;
  const {
    spawn
  } = require$$0$1;
  const os = require$$1$2;
  const which = requireLib$1();
  const escape = require_escape();

  // 'extra' object is for decorating the error a bit more
  const promiseSpawn = (cmd, args, opts = {}, extra = {}) => {
    if (opts.shell) {
      return spawnWithShell(cmd, args, opts, extra);
    }
    let resolve, reject;
    const promise = new Promise((_resolve, _reject) => {
      resolve = _resolve;
      reject = _reject;
    });

    // Create error here so we have a more useful stack trace when rejecting
    const closeError = new Error('command failed');
    const stdout = [];
    const stderr = [];
    const getResult = result => ({
      cmd,
      args,
      ...result,
      ...stdioResult(stdout, stderr, opts),
      ...extra
    });
    const rejectWithOpts = (er, erOpts) => {
      const resultError = getResult(erOpts);
      reject(Object.assign(er, resultError));
    };
    const proc = spawn(cmd, args, opts);
    promise.stdin = proc.stdin;
    promise.process = proc;
    proc.on('error', rejectWithOpts);
    if (proc.stdout) {
      proc.stdout.on('data', c => stdout.push(c));
      proc.stdout.on('error', rejectWithOpts);
    }
    if (proc.stderr) {
      proc.stderr.on('data', c => stderr.push(c));
      proc.stderr.on('error', rejectWithOpts);
    }
    proc.on('close', (code, signal) => {
      if (code || signal) {
        rejectWithOpts(closeError, {
          code,
          signal
        });
      } else {
        resolve(getResult({
          code,
          signal
        }));
      }
    });
    return promise;
  };
  const spawnWithShell = (cmd, args, opts, extra) => {
    let command = opts.shell;
    // if shell is set to true, we use a platform default. we can't let the core
    // spawn method decide this for us because we need to know what shell is in use
    // ahead of time so that we can escape arguments properly. we don't need coverage here.
    if (command === true) {
      // istanbul ignore next
      command = process.platform === 'win32' ? process.env.ComSpec || 'cmd.exe' : 'sh';
    }
    const options = {
      ...opts,
      shell: false
    };
    const realArgs = [];
    let script = cmd;

    // first, determine if we're in windows because if we are we need to know if we're
    // running an .exe or a .cmd/.bat since the latter requires extra escaping
    const isCmd = /(?:^|\\)cmd(?:\.exe)?$/i.test(command);
    if (isCmd) {
      let doubleEscape = false;

      // find the actual command we're running
      let initialCmd = '';
      let insideQuotes = false;
      for (let i = 0; i < cmd.length; ++i) {
        const char = cmd.charAt(i);
        if (char === ' ' && !insideQuotes) {
          break;
        }
        initialCmd += char;
        if (char === '"' || char === "'") {
          insideQuotes = !insideQuotes;
        }
      }
      let pathToInitial;
      try {
        pathToInitial = which.sync(initialCmd, {
          path: options.env && findInObject(options.env, 'PATH') || process.env.PATH,
          pathext: options.env && findInObject(options.env, 'PATHEXT') || process.env.PATHEXT
        }).toLowerCase();
      } catch (err) {
        pathToInitial = initialCmd.toLowerCase();
      }
      doubleEscape = pathToInitial.endsWith('.cmd') || pathToInitial.endsWith('.bat');
      for (const arg of args) {
        script += ` ${escape.cmd(arg, doubleEscape)}`;
      }
      realArgs.push('/d', '/s', '/c', script);
      options.windowsVerbatimArguments = true;
    } else {
      for (const arg of args) {
        script += ` ${escape.sh(arg)}`;
      }
      realArgs.push('-c', script);
    }
    return promiseSpawn(command, realArgs, options, extra);
  };

  // open a file with the default application as defined by the user's OS
  const open = (_args, opts = {}, extra = {}) => {
    const options = {
      ...opts,
      shell: true
    };
    const args = [].concat(_args);
    let platform = process.platform;
    // process.platform === 'linux' may actually indicate WSL, if that's the case
    // open the argument with sensible-browser which is pre-installed
    // In WSL, set the default browser using, for example,
    // export BROWSER="/mnt/c/Program Files (x86)/Google/Chrome/Application/chrome.exe"
    // or
    // export BROWSER="/mnt/c/Program Files (x86)/Microsoft/Edge/Application/msedge.exe"
    // To permanently set the default browser, add the appropriate entry to your shell's
    // RC file, e.g. .bashrc or .zshrc.
    if (platform === 'linux' && os.release().toLowerCase().includes('microsoft')) {
      platform = 'wsl';
      if (!process.env.BROWSER) {
        return Promise.reject(new Error('Set the BROWSER environment variable to your desired browser.'));
      }
    }
    let command = options.command;
    if (!command) {
      if (platform === 'win32') {
        // spawnWithShell does not do the additional os.release() check, so we
        // have to force the shell here to make sure we treat WSL as windows.
        options.shell = process.env.ComSpec;
        // also, the start command accepts a title so to make sure that we don't
        // accidentally interpret the first arg as the title, we stick an empty
        // string immediately after the start command
        command = 'start ""';
      } else if (platform === 'wsl') {
        command = 'sensible-browser';
      } else if (platform === 'darwin') {
        command = 'open';
      } else {
        command = 'xdg-open';
      }
    }
    return spawnWithShell(command, args, options, extra);
  };
  promiseSpawn.open = open;
  const isPipe = (stdio = 'pipe', fd) => {
    if (stdio === 'pipe' || stdio === null) {
      return true;
    }
    if (Array.isArray(stdio)) {
      return isPipe(stdio[fd], fd);
    }
    return false;
  };
  const stdioResult = (stdout, stderr, {
    stdioString = true,
    stdio
  }) => {
    const result = {
      stdout: null,
      stderr: null
    };

    // stdio is [stdin, stdout, stderr]
    if (isPipe(stdio, 1)) {
      result.stdout = Buffer.concat(stdout);
      if (stdioString) {
        result.stdout = result.stdout.toString().trim();
      }
    }
    if (isPipe(stdio, 2)) {
      result.stderr = Buffer.concat(stderr);
      if (stdioString) {
        result.stderr = result.stderr.toString().trim();
      }
    }
    return result;
  };

  // case insensitive lookup in an object
  const findInObject = (obj, key) => {
    key = key.toLowerCase();
    for (const objKey of Object.keys(obj).sort()) {
      if (objKey.toLowerCase() === key) {
        return obj[objKey];
      }
    }
  };
  lib = promiseSpawn;
  return lib;
}

var promiseSpawn;
var hasRequiredPromiseSpawn;
function requirePromiseSpawn() {
  if (hasRequiredPromiseSpawn) return promiseSpawn;
  hasRequiredPromiseSpawn = 1;
  promiseSpawn = requireLib();
  return promiseSpawn;
}

var promiseSpawnExports = requirePromiseSpawn();

module.exports = promiseSpawnExports;
