import { type Options as ExecaOptions, type ExecaSyncMethod, execaSync, } from 'execa' import { getCwd } from '../utils/cwd.js' import { slowLogging } from './slowOperations.js' const MS_IN_SECOND = 1000 const SECONDS_IN_MINUTE = 60 type ExecSyncOptions = { abortSignal?: AbortSignal timeout?: number input?: string stdio?: ExecaOptions['stdio'] } type ShellCommandExecaSync = ( command: string, options: ExecaOptions, ) => ReturnType /** * @deprecated Use `execa` directly with `{ shell: true, reject: false }` for non-blocking execution. * Sync exec calls block the event loop and cause performance issues. */ export function execSyncWithDefaults_DEPRECATED(command: string): string | null /** * @deprecated Use `execa` directly with `{ shell: true, reject: false }` for non-blocking execution. * Sync exec calls block the event loop and cause performance issues. */ export function execSyncWithDefaults_DEPRECATED( command: string, options: ExecSyncOptions, ): string | null /** * @deprecated Use `execa` directly with `{ shell: true, reject: false }` for non-blocking execution. * Sync exec calls block the event loop and cause performance issues. */ export function execSyncWithDefaults_DEPRECATED( command: string, abortSignal: AbortSignal, timeout?: number, ): string | null /** * @deprecated Use `execa` directly with `{ shell: true, reject: false }` for non-blocking execution. * Sync exec calls block the event loop and cause performance issues. */ export function execSyncWithDefaults_DEPRECATED( command: string, optionsOrAbortSignal?: ExecSyncOptions | AbortSignal, timeout = 10 * SECONDS_IN_MINUTE * MS_IN_SECOND, ): string | null { let options: ExecSyncOptions if (optionsOrAbortSignal === undefined) { // No second argument - use defaults options = {} } else if (optionsOrAbortSignal instanceof AbortSignal) { // Old signature - second argument is AbortSignal options = { abortSignal: optionsOrAbortSignal, timeout, } } else { // New signature - second argument is options object options = optionsOrAbortSignal } const { abortSignal, timeout: finalTimeout = 10 * SECONDS_IN_MINUTE * MS_IN_SECOND, input, stdio = ['ignore', 'pipe', 'pipe'], } = options abortSignal?.throwIfAborted() using _ = slowLogging`exec: ${command.slice(0, 200)}` try { // DEPRECATED: caller is responsible for sanitizing shell metacharacters // in the command argument. This function uses shell: true which can lead // to command injection if command contains untrusted input. const runShellCommand = execaSync as unknown as ShellCommandExecaSync const result = runShellCommand(command, { env: process.env, maxBuffer: 1_000_000, timeout: finalTimeout, cwd: getCwd(), stdio, shell: true, // execSync typically runs shell commands reject: false, // Don't throw on non-zero exit codes input, }) if (!result.stdout) { return null } const stdout = typeof result.stdout === 'string' ? result.stdout : result.stdout instanceof Uint8Array ? new TextDecoder().decode(result.stdout) : Array.isArray(result.stdout) ? result.stdout.join('') : String(result.stdout) return stdout.trim() || null } catch { return null } }