/** * Running a binary out of the project's `node_modules/.bin` — on Windows as well as POSIX. * * ── Why this is not just `spawn(bin, args)` ────────────────────────────────────────────────── * * `node_modules/.bin/tsc` is a POSIX shell script. Windows cannot execute it: every package * manager writes a `tsc.cmd` next to it for that platform, and handing the extensionless path to * `spawnSync` there fails with ENOENT before tsc is ever reached. `projectBin` picks the right * file; this module runs it. * * Picking the `.cmd` is only half of it. Since the CVE-2024-27980 fix (Node 18.20.2 / 20.12.2), * spawning a `.cmd` or `.bat` WITHOUT `shell: true` throws EINVAL — the shell is now mandatory for * those, not an alternative to naming them. So on Windows the command and every argument are * quoted for `cmd.exe` and the spawn is a shell spawn. `"` needs no escape because Windows forbids * it in a path, and our own arguments are flags and paths; a literal `%` in a path can still be * eaten by cmd's variable expansion, which has no command-line escape and which no Bitmagic path * has ever contained. * * The shell brings one more consequence, which `stopChild` exists for: the child is `cmd.exe`, and * killing it leaves the vite or tsc it launched running — holding `--strictPort` against the next * `bitmagic dev`. Windows kills the tree instead. * * `spawnPathCommandSync` is the same defect one step out: a package manager looked up on PATH is * `npm.cmd`/`pnpm.cmd` on Windows, and `spawnSync('pnpm', …)` there fails the same ENOENT way. */ import { spawnSync, type ChildProcess, type SpawnOptions, type SpawnSyncOptions } from 'child_process'; /** How to hand a `.bin` entry to `child_process` on one platform. */ export interface BinInvocation { command: string; args: string[]; shell: boolean; } /** * Pure, so the Windows shape is testable from any machine — the same split `openerFor` uses. * * Only `.cmd`/`.bat` take the shell. A `.exe` (some packages ship a real one) and the POSIX * extensionless script are executables the OS can launch directly, and routing those through * cmd.exe would add the orphaned-child problem for nothing. */ export declare function binInvocation(bin: string, args: readonly string[], platform?: string): BinInvocation; /** `spawnSync` for a project binary. Same return value, so callers read `error`/`status` as before. */ export declare function spawnBinSync(bin: string, args: readonly string[], options: SpawnSyncOptions): ReturnType; /** `spawn` for a project binary. Same `ChildProcess`, but stop it with `stopChild`, not `kill`. */ export declare function spawnBin(bin: string, args: readonly string[], options: SpawnOptions): ChildProcess; /** * Stops a child started by `spawnBin`, including anything it started. * * On Windows that child is the `cmd.exe` wrapper, so `kill` alone would orphan the real server; * `taskkill /T` takes the tree. * * Throws whatever `kill` throws, exactly as the bare `child.kill('SIGTERM')` this replaced did — * `killQuietly` in forge/voxelize-session.ts reports that rather than losing the command to it, * and swallowing it here would silently take that report away. */ export declare function stopChild(child: ChildProcess, platform?: string): void; /** * Runs a command looked up on PATH — a package manager, not a project binary. * * On Windows `npm`, `pnpm` and `yarn` are all `.cmd` shims, and `CreateProcess` (which is what a * non-shell spawn ends at) cannot execute a batch file however it was named. cmd.exe resolves the * PATHEXT extension itself, so the shell does double duty here: it finds the shim AND it is the * `shell: true` the batch file needs anyway. * * An absolute path is spawned directly. `self-install.ts` runs npm as `process.execPath * npm-cli.js` — a real executable with nothing for a shell to add. */ export declare function pathCommandInvocation(command: string, args: readonly string[], platform?: string): BinInvocation; /** `spawnSync` for a PATH command. Same return value, so callers read `error`/`status` as before. */ export declare function spawnPathCommandSync(command: string, args: readonly string[], options: SpawnSyncOptions): ReturnType;