Files
AeroFetch/src/main/lib/exec.ts
T
debont80 63687aaec3 docs(exec): correct execFileAsync comment — schedule.ts is not migrated
schedule.ts keeps its own schtasks wrapper because it needs the numeric exit
code (getScheduledSync checks r.code === 0), which execFileAsync deliberately
doesn't surface. The 5 migrated sites are probe/ytdlp(x2)/ffmpeg/indexer/
download.probeMeta. Comment-only.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-01 10:25:36 -04:00

54 lines
2.1 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { execFile, type ExecFileOptions } from 'child_process'
/**
* Normalized result of a one-shot child-process run. `ok` is true on a clean
* exit-0; `timedOut` distinguishes a kill-by-timeout (Node sets `err.killed` when
* it terminates the process on the `timeout` option) from an ordinary non-zero
* exit, so callers can show "Timed out …" versus the captured stderr.
*/
export interface ExecResult {
ok: boolean
stdout: string
stderr: string
timedOut: boolean
/** The raw error on a non-zero exit / spawn failure (undefined when ok). */
error?: Error
}
/**
* `execFile` as a promise that never rejects (audit SIMP2/CC4/CC5). Replaces the
* six hand-rolled `new Promise((resolve) => execFile(…, cb))` wrappers across
* probe / ytdlp (×2) / ffmpeg / indexer / download.probeMeta — each caller now maps
* this normalized result to its own shape instead of repeating the `err.killed`
* timeout check and the resolve/reject plumbing. (schedule.ts keeps its own wrapper:
* it needs the numeric exit code, which this helper deliberately doesn't surface.)
*
* `windowsHide` defaults on (every caller wants the console window suppressed);
* pass `timeout` / `maxBuffer` per call exactly as before.
*/
export function execFileAsync(
file: string,
args: readonly string[],
options: ExecFileOptions = {}
): Promise<ExecResult> {
return new Promise((resolve) => {
execFile(file, [...args], { windowsHide: true, ...options }, (err, stdout, stderr) => {
// With the default (utf8) encoding stdout/stderr are strings, but ExecFileOptions
// permits a Buffer encoding, so normalize defensively.
const out = typeof stdout === 'string' ? stdout : stdout.toString()
const errOut = typeof stderr === 'string' ? stderr : stderr.toString()
if (err) {
resolve({
ok: false,
stdout: out,
stderr: errOut,
timedOut: (err as { killed?: boolean }).killed === true,
error: err
})
} else {
resolve({ ok: true, stdout: out, stderr: errOut, timedOut: false })
}
})
})
}