"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.isPnpmLoglevelFlag = void 0;
exports.execNpm = execNpm;
exports.execPnpm = execPnpm;
exports.execYarn = execYarn;
exports.isNpmAuditFlag = isNpmAuditFlag;
exports.isNpmFundFlag = isNpmFundFlag;
exports.isNpmLoglevelFlag = isNpmLoglevelFlag;
exports.isNpmNodeOptionsFlag = isNpmNodeOptionsFlag;
exports.isNpmProgressFlag = isNpmProgressFlag;
exports.isPnpmIgnoreScriptsFlag = isPnpmIgnoreScriptsFlag;
exports.isPnpmFrozenLockfileFlag = isPnpmFrozenLockfileFlag;
exports.isPnpmInstallCommand = isPnpmInstallCommand;
exports.execScript = execScript;
/**
 * @fileoverview Package manager agent for executing npm, pnpm, and yarn commands.
 * Provides cross-platform utilities with optimized flags and security defaults.
 */
const bin_1 = require("./bin");
const ENV_1 = require("./constants/ENV");
const NODE_HARDEN_FLAGS_1 = require("./constants/NODE_HARDEN_FLAGS");
const NODE_NO_WARNINGS_FLAGS_1 = require("./constants/NODE_NO_WARNINGS_FLAGS");
const NPM_REAL_EXEC_PATH_1 = require("./constants/NPM_REAL_EXEC_PATH");
const PACKAGE_LOCK_JSON_1 = require("./constants/PACKAGE_LOCK_JSON");
const PNPM_LOCK_YAML_1 = require("./constants/PNPM_LOCK_YAML");
const SUPPORTS_NODE_RUN_1 = require("./constants/SUPPORTS_NODE_RUN");
const YARN_LOCK_1 = require("./constants/YARN_LOCK");
const exec_path_1 = require("./constants/exec-path");
const debug_1 = require("./debug");
const fs_1 = require("./fs");
const objects_1 = require("./objects");
const spawn_1 = require("./spawn");
// Note: npm flag checking is done with regex patterns in the is*Flag functions below.
const pnpmIgnoreScriptsFlags = new Set([
    '--ignore-scripts',
    '--no-ignore-scripts',
]);
const pnpmFrozenLockfileFlags = new Set([
    '--frozen-lockfile',
    '--no-frozen-lockfile',
]);
const pnpmInstallCommands = new Set(['install', 'i']);
// Commands that support --ignore-scripts flag in pnpm:
// Installation-related: install, add, update, remove, link, unlink, import, rebuild.
const pnpmInstallLikeCommands = new Set([
    'install',
    'i',
    'add',
    'update',
    'up',
    'remove',
    'rm',
    'link',
    'ln',
    'unlink',
    'import',
    'rebuild',
    'rb',
]);
// Commands that support --ignore-scripts flag in yarn:
// Similar to npm/pnpm: installation-related commands.
const yarnInstallLikeCommands = new Set([
    'install',
    'add',
    'upgrade',
    'remove',
    'link',
    'unlink',
    'import',
]);
/**
 * Execute npm commands with optimized flags and settings.
 */
/*@__NO_SIDE_EFFECTS__*/
function execNpm(args, options) {
    const useDebug = (0, debug_1.isDebug)();
    const terminatorPos = args.indexOf('--');
    const npmArgs = (terminatorPos === -1 ? args : args.slice(0, terminatorPos)).filter((a) => !isNpmAuditFlag(a) && !isNpmFundFlag(a) && !isNpmProgressFlag(a));
    const otherArgs = terminatorPos === -1 ? [] : args.slice(terminatorPos);
    const logLevelArgs = 
    // The default value of loglevel is "notice". We default to "warn" which is
    // one level quieter.
    useDebug || npmArgs.some(isNpmLoglevelFlag) ? [] : ['--loglevel', 'warn'];
    return (0, spawn_1.spawn)(exec_path_1, [
        ...NODE_HARDEN_FLAGS_1,
        ...NODE_NO_WARNINGS_FLAGS_1,
        NPM_REAL_EXEC_PATH_1,
        // Even though '--loglevel=error' is passed npm will still run through
        // code paths for 'audit' and 'fund' unless '--no-audit' and '--no-fund'
        // flags are passed.
        '--no-audit',
        '--no-fund',
        // Add `--no-progress` and `--silent` flags to fix input being swallowed
        // by the spinner when running the command with recent versions of npm.
        '--no-progress',
        // Add '--loglevel=error' if a loglevel flag is not provided and the
        // SOCKET_CLI_DEBUG environment variable is not truthy.
        ...logLevelArgs,
        ...npmArgs,
        ...otherArgs,
    ], {
        __proto__: null,
        ...options,
    });
}
/**
 * Execute pnpm commands with optimized flags and settings.
 */
/*@__NO_SIDE_EFFECTS__*/
function execPnpm(args, options) {
    const { allowLockfileUpdate, ...extBinOpts } = {
        __proto__: null,
        ...options,
    };
    const useDebug = (0, debug_1.isDebug)();
    const terminatorPos = args.indexOf('--');
    const pnpmArgs = (terminatorPos === -1 ? args : args.slice(0, terminatorPos)).filter((a) => !isNpmProgressFlag(a));
    const otherArgs = terminatorPos === -1 ? [] : args.slice(terminatorPos);
    const firstArg = pnpmArgs[0];
    const supportsIgnoreScripts = firstArg
        ? pnpmInstallLikeCommands.has(firstArg)
        : false;
    // pnpm uses --loglevel for all commands.
    const logLevelArgs = useDebug || pnpmArgs.some(exports.isPnpmLoglevelFlag) ? [] : ['--loglevel', 'warn'];
    // Only add --ignore-scripts for commands that support it.
    const hasIgnoreScriptsFlag = pnpmArgs.some(isPnpmIgnoreScriptsFlag);
    const ignoreScriptsArgs = !supportsIgnoreScripts || hasIgnoreScriptsFlag ? [] : ['--ignore-scripts'];
    // In CI environments, pnpm uses --frozen-lockfile by default which prevents lockfile updates.
    // For commands that need to update the lockfile (like install with new packages/overrides),
    // we need to explicitly add --no-frozen-lockfile in CI mode if not already present.
    const frozenLockfileArgs = [];
    if (ENV_1.CI &&
        allowLockfileUpdate &&
        firstArg &&
        isPnpmInstallCommand(firstArg) &&
        !pnpmArgs.some(isPnpmFrozenLockfileFlag)) {
        frozenLockfileArgs.push('--no-frozen-lockfile');
    }
    // Note: pnpm doesn't have a --no-progress flag. It uses --reporter instead.
    // We removed --no-progress as it causes "Unknown option" errors with pnpm.
    return (0, bin_1.execBin)('pnpm', [
        // Add '--loglevel=warn' if a loglevel flag is not provided and debug is off.
        ...logLevelArgs,
        // Add '--ignore-scripts' by default for security (only for installation commands).
        ...ignoreScriptsArgs,
        // Add '--no-frozen-lockfile' in CI when lockfile updates are needed.
        ...frozenLockfileArgs,
        ...pnpmArgs,
        ...otherArgs,
    ], extBinOpts);
}
/**
 * Execute yarn commands with optimized flags and settings.
 */
/*@__NO_SIDE_EFFECTS__*/
function execYarn(args, options) {
    const useDebug = (0, debug_1.isDebug)();
    const terminatorPos = args.indexOf('--');
    const yarnArgs = (terminatorPos === -1 ? args : args.slice(0, terminatorPos)).filter((a) => !isNpmProgressFlag(a));
    const otherArgs = terminatorPos === -1 ? [] : args.slice(terminatorPos);
    const firstArg = yarnArgs[0];
    const supportsIgnoreScripts = firstArg
        ? yarnInstallLikeCommands.has(firstArg)
        : false;
    // Yarn uses --silent flag for quieter output.
    const logLevelArgs = useDebug || yarnArgs.some(isNpmLoglevelFlag) ? [] : ['--silent'];
    // Only add --ignore-scripts for commands that support it.
    const hasIgnoreScriptsFlag = yarnArgs.some(isPnpmIgnoreScriptsFlag);
    const ignoreScriptsArgs = !supportsIgnoreScripts || hasIgnoreScriptsFlag ? [] : ['--ignore-scripts'];
    return (0, bin_1.execBin)('yarn', [
        // Add '--silent' if a loglevel flag is not provided and debug is off.
        ...logLevelArgs,
        // Add '--ignore-scripts' by default for security (only for installation commands).
        ...ignoreScriptsArgs,
        ...yarnArgs,
        ...otherArgs,
    ], {
        __proto__: null,
        ...options,
    });
}
/**
 * Check if a command argument is an npm audit flag.
 */
/*@__NO_SIDE_EFFECTS__*/
function isNpmAuditFlag(cmdArg) {
    return /^--(no-)?audit(=.*)?$/.test(cmdArg);
}
/**
 * Check if a command argument is an npm fund flag.
 */
/*@__NO_SIDE_EFFECTS__*/
function isNpmFundFlag(cmdArg) {
    return /^--(no-)?fund(=.*)?$/.test(cmdArg);
}
/**
 * Check if a command argument is an npm loglevel flag.
 */
/*@__NO_SIDE_EFFECTS__*/
function isNpmLoglevelFlag(cmdArg) {
    // https://docs.npmjs.com/cli/v11/using-npm/logging#setting-log-levels
    if (/^--loglevel(=.*)?$/.test(cmdArg)) {
        return true;
    }
    // Check for long form flags
    if (/^--(silent|verbose|info|warn|error|quiet)$/.test(cmdArg)) {
        return true;
    }
    // Check for shorthand flags
    return /^-(s|q|d|dd|ddd|v)$/.test(cmdArg);
}
/**
 * Check if a command argument is an npm node-options flag.
 */
/*@__NO_SIDE_EFFECTS__*/
function isNpmNodeOptionsFlag(cmdArg) {
    // https://docs.npmjs.com/cli/v9/using-npm/config#node-options
    return /^--node-options(=.*)?$/.test(cmdArg);
}
/**
 * Check if a command argument is an npm progress flag.
 */
/*@__NO_SIDE_EFFECTS__*/
function isNpmProgressFlag(cmdArg) {
    return /^--(no-)?progress(=.*)?$/.test(cmdArg);
}
/**
 * Check if a command argument is a pnpm ignore-scripts flag.
 */
/*@__NO_SIDE_EFFECTS__*/
function isPnpmIgnoreScriptsFlag(cmdArg) {
    return pnpmIgnoreScriptsFlags.has(cmdArg);
}
/**
 * Check if a command argument is a pnpm frozen-lockfile flag.
 */
/*@__NO_SIDE_EFFECTS__*/
function isPnpmFrozenLockfileFlag(cmdArg) {
    return pnpmFrozenLockfileFlags.has(cmdArg);
}
/**
 * Check if a command argument is a pnpm install command.
 */
/*@__NO_SIDE_EFFECTS__*/
function isPnpmInstallCommand(cmdArg) {
    return pnpmInstallCommands.has(cmdArg);
}
/**
 * Alias for isNpmLoglevelFlag for pnpm usage.
 */
exports.isPnpmLoglevelFlag = isNpmLoglevelFlag;
/*@__NO_SIDE_EFFECTS__*/
function execScript(scriptName, args, options) {
    // Handle overloaded signatures: execScript(name, options) or execScript(name, args, options).
    if (!Array.isArray(args) && args !== null && typeof args === 'object') {
        options = args;
        args = [];
    }
    args = (args || []);
    const { prepost, ...spawnOptions } = {
        __proto__: null,
        ...options,
    };
    // If shell: true is passed, run the command directly as a shell command.
    if (spawnOptions.shell === true) {
        return (0, spawn_1.spawn)(scriptName, args, spawnOptions);
    }
    const useNodeRun = !prepost && SUPPORTS_NODE_RUN_1;
    // Detect package manager based on lockfile by traversing up from current directory.
    const cwd = (0, objects_1.getOwn)(spawnOptions, 'cwd') ?? process.cwd();
    // Check for pnpm-lock.yaml.
    const pnpmLockPath = (0, fs_1.findUpSync)(PNPM_LOCK_YAML_1, { cwd });
    if (pnpmLockPath) {
        return execPnpm(['run', scriptName, ...args], spawnOptions);
    }
    // Check for package-lock.json.
    // When in an npm workspace, use npm run to ensure workspace binaries are available.
    const packageLockPath = (0, fs_1.findUpSync)(PACKAGE_LOCK_JSON_1, { cwd });
    if (packageLockPath) {
        return execNpm(['run', scriptName, ...args], spawnOptions);
    }
    // Check for yarn.lock.
    const yarnLockPath = (0, fs_1.findUpSync)(YARN_LOCK_1, { cwd });
    if (yarnLockPath) {
        return execYarn(['run', scriptName, ...args], spawnOptions);
    }
    return (0, spawn_1.spawn)(exec_path_1, [
        ...NODE_NO_WARNINGS_FLAGS_1,
        ...(useNodeRun ? ['--run'] : [NPM_REAL_EXEC_PATH_1, 'run']),
        scriptName,
        ...args,
    ], {
        ...spawnOptions,
    });
}
