fix: TypeScript readonly property errors in EBADF workaround

This commit is contained in:
Sean McLellan 2026-01-28 15:48:11 -05:00
parent 0cc9ed8b65
commit 52ffd744f7

View File

@ -54,21 +54,31 @@ function shouldRetry(err: unknown, codes: string[]): boolean {
return code.length > 0 && codes.includes(code); return code.length > 0 && codes.includes(code);
} }
// Fake child process interface for EBADF workaround
interface FakeChildProcess extends EventEmitter {
stdout: PassThrough;
stderr: PassThrough;
stdin: PassThrough;
pid: number;
killed: boolean;
kill: () => boolean;
}
// EBADF workaround: capture output via temp files, use stdio: ignore for spawn // EBADF workaround: capture output via temp files, use stdio: ignore for spawn
function createFakeChildFromSync(argv: string[], options: SpawnOptions): ChildProcess { function createFakeChildFromSync(argv: string[], options: SpawnOptions): ChildProcess {
const child = new EventEmitter() as unknown as ChildProcess; const fakeChild: FakeChildProcess = Object.assign(new EventEmitter(), {
const stdout = new PassThrough(); stdout: new PassThrough(),
const stderr = new PassThrough(); stderr: new PassThrough(),
stdin: new PassThrough(),
pid: process.pid,
killed: false,
kill: () => {
fakeChild.killed = true;
return true;
},
});
child.stdout = stdout; const child = fakeChild as unknown as ChildProcess;
child.stderr = stderr;
child.stdin = new PassThrough();
child.pid = process.pid;
child.killed = false;
child.kill = () => {
child.killed = true;
return true;
};
// Extract command from argv (typically [shell, "-c", command]) // Extract command from argv (typically [shell, "-c", command])
const command = argv.length >= 3 ? argv[2] : argv.join(" "); const command = argv.length >= 3 ? argv[2] : argv.join(" ");
@ -99,21 +109,21 @@ function createFakeChildFromSync(argv: string[], options: SpawnOptions): ChildPr
try { try {
fs.unlinkSync(stderrFile); fs.unlinkSync(stderrFile);
} catch {} } catch {}
child.emit("error", result.error); fakeChild.emit("error", result.error);
return; return;
} }
child.pid = result.pid || process.pid; fakeChild.pid = result.pid || process.pid;
// Read output from temp files // Read output from temp files
try { try {
const stdoutData = fs.readFileSync(stdoutFile, "utf8"); const stdoutData = fs.readFileSync(stdoutFile, "utf8");
if (stdoutData) stdout.write(stdoutData); if (stdoutData) fakeChild.stdout.write(stdoutData);
} catch {} } catch {}
try { try {
const stderrData = fs.readFileSync(stderrFile, "utf8"); const stderrData = fs.readFileSync(stderrFile, "utf8");
if (stderrData) stderr.write(stderrData); if (stderrData) fakeChild.stderr.write(stderrData);
} catch {} } catch {}
// Clean up temp files // Clean up temp files
@ -124,9 +134,9 @@ function createFakeChildFromSync(argv: string[], options: SpawnOptions): ChildPr
fs.unlinkSync(stderrFile); fs.unlinkSync(stderrFile);
} catch {} } catch {}
stdout.end(); fakeChild.stdout.end();
stderr.end(); fakeChild.stderr.end();
child.emit("close", result.status, result.signal); fakeChild.emit("close", result.status, result.signal);
} catch (err) { } catch (err) {
// Clean up temp files // Clean up temp files
try { try {
@ -135,13 +145,13 @@ function createFakeChildFromSync(argv: string[], options: SpawnOptions): ChildPr
try { try {
fs.unlinkSync(stderrFile); fs.unlinkSync(stderrFile);
} catch {} } catch {}
child.emit("error", err); fakeChild.emit("error", err);
} }
}); });
// Emit spawn immediately // Emit spawn immediately
process.nextTick(() => { process.nextTick(() => {
child.emit("spawn"); fakeChild.emit("spawn");
}); });
return child; return child;