"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.isSpawnError = isSpawnError;
exports.isStdioType = isStdioType;
exports.spawn = spawn;
exports.spawnSync = spawnSync;
/**
 * @fileoverview Child process spawning utilities with cross-platform support.
 * Provides enhanced spawn functionality with stdio handling and error management.
 */
const abort_signal_1 = require("./constants/abort-signal");
const spinner_1 = require("./constants/spinner");
const objects_1 = require("./objects");
const strings_1 = require("./strings");
// IMPORTANT: Do not use destructuring here - use direct assignment instead.
// tsgo has a bug that incorrectly transpiles destructured exports, resulting in
// `exports.SomeName = void 0;` which causes runtime errors.
// See: https://github.com/SocketDev/socket-packageurl-js/issues/3
const ArrayIsArray = Array.isArray;
// IMPORTANT: Do not use destructuring here - use direct assignment instead.
// tsgo has a bug that incorrectly transpiles destructured exports, resulting in
// `exports.SomeName = void 0;` which causes runtime errors.
// See: https://github.com/SocketDev/socket-packageurl-js/issues/3
const ObjectHasOwn = Object.hasOwn;
const windowsScriptExtRegExp = /\.(?:cmd|bat|ps1)$/i;
let _child_process;
/**
 * Lazily load the child_process module.
 */
/*@__NO_SIDE_EFFECTS__*/
function getChildProcess() {
    if (_child_process === undefined) {
        // Use non-'node:' prefixed require to avoid Webpack errors.
        // eslint-disable-next-line n/prefer-node-protocol
        _child_process = /*@__PURE__*/ require('child_process');
    }
    return _child_process;
}
let _npmCliPromiseSpawn;
/**
 * Lazily load the promise-spawn module for async process spawning.
 */
/*@__NO_SIDE_EFFECTS__*/
function getNpmcliPromiseSpawn() {
    if (_npmCliPromiseSpawn === undefined) {
        _npmCliPromiseSpawn =
            /*@__PURE__*/ require('../external/@npmcli/promise-spawn');
    }
    return _npmCliPromiseSpawn;
}
let _path;
/**
 * Lazily load the path module to avoid Webpack errors.
 */
/*@__NO_SIDE_EFFECTS__*/
function getPath() {
    if (_path === undefined) {
        // Use non-'node:' prefixed require to avoid Webpack errors.
        // eslint-disable-next-line n/prefer-node-protocol
        _path = /*@__PURE__*/ require('path');
    }
    return _path;
}
/*@__NO_SIDE_EFFECTS__*/
function isSpawnError(value) {
    if (value === null || typeof value !== 'object') {
        return false;
    }
    // Check for spawn-specific error properties.
    const err = value;
    return ((ObjectHasOwn(err, 'code') && typeof err.code !== 'undefined') ||
        (ObjectHasOwn(err, 'errno') && typeof err.errno !== 'undefined') ||
        (ObjectHasOwn(err, 'syscall') && typeof err.syscall === 'string'));
}
/**
 * Check if stdio configuration matches a specific type.
 */
/*@__NO_SIDE_EFFECTS__*/
function isStdioType(stdio, type) {
    // If called with one argument, check if it's a valid stdio type.
    if (arguments.length === 1) {
        const validTypes = ['pipe', 'ignore', 'inherit', 'overlapped'];
        return typeof stdio === 'string' && validTypes.includes(stdio);
    }
    // Original two-argument behavior.
    return (stdio === type ||
        ((stdio === null || stdio === undefined) && type === 'pipe') ||
        (ArrayIsArray(stdio) &&
            stdio.length > 2 &&
            stdio[0] === type &&
            stdio[1] === type &&
            stdio[2] === type));
}
/**
 * Strip ANSI escape codes from spawn result stdout and stderr.
 */
/*@__NO_SIDE_EFFECTS__*/
function stripAnsiFromSpawnResult(result) {
    const { stderr, stdout } = result;
    if (typeof stdout === 'string') {
        result.stdout = (0, strings_1.stripAnsi)(stdout);
    }
    if (typeof stderr === 'string') {
        result.stderr = (0, strings_1.stripAnsi)(stderr);
    }
    return result;
}
function spawn(cmd, args, options, extra) {
    // On Windows with shell: true, use just the command name, not the full path.
    // When shell: true is used, Windows cmd.exe has issues executing full paths to
    // .cmd/.bat files (e.g., 'C:\...\pnpm.cmd'), often resulting in ENOENT errors.
    // Using just the command name (e.g., 'pnpm') allows cmd.exe to find it via PATH.
    // See: https://github.com/nodejs/node/issues/3675
    // Check for .cmd, .bat, .ps1 extensions that indicate a Windows script.
    const shell = (0, objects_1.getOwn)(options, 'shell');
    const WIN32 = /*@__PURE__*/ require('./constants/WIN32.js');
    if (WIN32 && shell && windowsScriptExtRegExp.test(cmd)) {
        const path = getPath();
        // Extract just the command name without path and extension.
        cmd = path.basename(cmd, path.extname(cmd));
    }
    const { spinner: optionsSpinner = spinner_1, stripAnsi: shouldStripAnsi = true, ...spawnOptions } = { __proto__: null, ...options };
    const spinnerInstance = optionsSpinner;
    const { env, stdio, stdioString = true } = spawnOptions;
    // The stdio option can be a string or an array.
    // https://nodejs.org/api/child_process.html#optionsstdio
    const wasSpinning = !!spinnerInstance?.isSpinning;
    const shouldStopSpinner = wasSpinning && !isStdioType(stdio, 'ignore') && !isStdioType(stdio, 'pipe');
    const shouldRestartSpinner = shouldStopSpinner;
    if (shouldStopSpinner) {
        spinnerInstance.stop();
    }
    const npmCliPromiseSpawn = getNpmcliPromiseSpawn();
    const promiseSpawnOptions = {
        signal: abort_signal_1,
        cwd: typeof spawnOptions.cwd === 'string' ? spawnOptions.cwd : undefined,
        stdio: spawnOptions.stdio,
        stdioString,
        shell: spawnOptions.shell,
        timeout: spawnOptions.timeout,
        uid: spawnOptions.uid,
        gid: spawnOptions.gid,
        // Node includes inherited properties of options.env when it normalizes
        // it due to backwards compatibility. However, this is a prototype sink and
        // undesired behavior so to prevent it we spread options.env onto a fresh
        // object with a null [[Prototype]].
        // https://github.com/nodejs/node/blob/v24.0.1/lib/child_process.js#L674-L678
        env: Object.assign(Object.create(null), process.env, env),
    };
    const spawnPromise = npmCliPromiseSpawn(cmd, args ? [...args] : [], promiseSpawnOptions, extra);
    const oldSpawnPromise = spawnPromise;
    let newSpawnPromise;
    if (shouldStripAnsi && stdioString) {
        newSpawnPromise = spawnPromise
            .then(result => {
            result = stripAnsiFromSpawnResult(result);
            // Add exitCode as an alias for code.
            if ('code' in result) {
                ;
                result['exitCode'] = result.code;
            }
            return result;
        })
            .catch(error => {
            throw stripAnsiFromSpawnResult(error);
        });
    }
    else {
        newSpawnPromise = spawnPromise.then(result => {
            // Add exitCode as an alias for code.
            if ('code' in result) {
                ;
                result['exitCode'] = result.code;
            }
            return result;
        });
    }
    if (shouldRestartSpinner) {
        newSpawnPromise = newSpawnPromise.finally(() => {
            spinnerInstance.start();
        });
    }
    // Copy process and stdin properties from original promise
    newSpawnPromise.process = oldSpawnPromise.process;
    newSpawnPromise.stdin = oldSpawnPromise.stdin;
    return newSpawnPromise;
}
function spawnSync(cmd, args, options) {
    // On Windows with shell: true, use just the command name, not the full path.
    // When shell: true is used, Windows cmd.exe has issues executing full paths to
    // .cmd/.bat files (e.g., 'C:\...\pnpm.cmd'), often resulting in ENOENT errors.
    // Using just the command name (e.g., 'pnpm') allows cmd.exe to find it via PATH.
    // See: https://github.com/nodejs/node/issues/3675
    // Check for .cmd, .bat, .ps1 extensions that indicate a Windows script.
    const shell = (0, objects_1.getOwn)(options, 'shell');
    const WIN32 = /*@__PURE__*/ require('./constants/WIN32.js');
    if (WIN32 && shell && windowsScriptExtRegExp.test(cmd)) {
        const path = getPath();
        // Extract just the command name without path and extension.
        cmd = path.basename(cmd, path.extname(cmd));
    }
    const { stripAnsi: shouldStripAnsi = true, ...rawSpawnOptions } = {
        __proto__: null,
        ...options,
    };
    const { stdioString: rawStdioString = true } = rawSpawnOptions;
    const rawEncoding = rawStdioString ? 'utf8' : 'buffer';
    const spawnOptions = {
        encoding: rawEncoding,
        ...rawSpawnOptions,
    };
    const stdioString = spawnOptions.encoding !== 'buffer';
    const result = getChildProcess().spawnSync(cmd, args, spawnOptions);
    if (stdioString) {
        const { stderr, stdout } = result;
        if (stdout) {
            result.stdout = stdout.toString().trim();
        }
        if (stderr) {
            result.stderr = stderr.toString().trim();
        }
    }
    return shouldStripAnsi && stdioString
        ? stripAnsiFromSpawnResult(result)
        : result;
}
//# sourceMappingURL=spawn.js.map