fix(process): wait for windows close state settlement

This commit is contained in:
Ayaan Zaidi
2026-03-28 16:55:15 +05:30
parent 0ebd7df9dc
commit cfba0ab68f
2 changed files with 37 additions and 11 deletions

View File

@@ -173,6 +173,9 @@ export type CommandOptions = {
noOutputTimeoutMs?: number;
};
const WINDOWS_CLOSE_STATE_SETTLE_TIMEOUT_MS = 250;
const WINDOWS_CLOSE_STATE_POLL_MS = 10;
export function resolveCommandEnv(params: {
argv: string[];
env?: NodeJS.ProcessEnv;
@@ -366,16 +369,33 @@ export async function runCommandWithTimeout(
};
child.on("close", (code, signal) => {
if (
childExitState == null &&
code == null &&
signal == null &&
child.exitCode == null &&
child.signalCode == null
process.platform !== "win32" ||
childExitState != null ||
code != null ||
signal != null ||
child.exitCode != null ||
child.signalCode != null
) {
setImmediate(() => resolveFromClose(code, signal));
resolveFromClose(code, signal);
return;
}
resolveFromClose(code, signal);
const startedAt = Date.now();
const waitForExitState = () => {
if (settled) {
return;
}
if (childExitState != null || child.exitCode != null || child.signalCode != null) {
resolveFromClose(code, signal);
return;
}
if (Date.now() - startedAt >= WINDOWS_CLOSE_STATE_SETTLE_TIMEOUT_MS) {
resolveFromClose(code, signal);
return;
}
setTimeout(waitForExitState, WINDOWS_CLOSE_STATE_POLL_MS);
};
waitForExitState();
});
});
}

View File

@@ -32,6 +32,7 @@ function createMockChild(params?: {
closeSignal?: NodeJS.Signals | null;
exitCode?: number | null;
exitCodeAfterClose?: number | null;
exitCodeAfterCloseDelayMs?: number;
signal?: NodeJS.Signals | null;
}): MockChild {
const child = new EventEmitter() as MockChild;
@@ -49,9 +50,9 @@ function createMockChild(params?: {
queueMicrotask(() => {
child.emit("close", params?.closeCode ?? 0, params?.closeSignal ?? params?.signal ?? null);
if (params?.exitCodeAfterClose !== undefined) {
setImmediate(() => {
setTimeout(() => {
child.exitCode = params.exitCodeAfterClose ?? null;
});
}, params.exitCodeAfterCloseDelayMs ?? 0);
}
});
return child;
@@ -123,9 +124,14 @@ describe("windows command wrapper behavior", () => {
}
});
it("waits one tick when Windows close reports null before exitCode settles", async () => {
it("waits for Windows exitCode settlement after close reports null", async () => {
const platformSpy = vi.spyOn(process, "platform", "get").mockReturnValue("win32");
const child = createMockChild({ closeCode: null, exitCode: null, exitCodeAfterClose: 0 });
const child = createMockChild({
closeCode: null,
exitCode: null,
exitCodeAfterClose: 0,
exitCodeAfterCloseDelayMs: 50,
});
spawnMock.mockImplementation(() => child);