@@ -6,6 +6,42 @@ interface StdoutTakeoverState {
|
||||
|
||||
let stdoutTakeoverState: StdoutTakeoverState | undefined;
|
||||
|
||||
const RAW_STDOUT_RETRY_DELAY_MS = 10;
|
||||
|
||||
let rawStdoutWriteTail: Promise<void> = Promise.resolve();
|
||||
|
||||
function getRawStdoutWrite(): StdoutTakeoverState["rawStdoutWrite"] {
|
||||
if (stdoutTakeoverState) {
|
||||
return stdoutTakeoverState.rawStdoutWrite;
|
||||
}
|
||||
return process.stdout.write.bind(process.stdout) as StdoutTakeoverState["rawStdoutWrite"];
|
||||
}
|
||||
|
||||
async function writeRawStdoutChunk(text: string): Promise<void> {
|
||||
while (true) {
|
||||
try {
|
||||
await new Promise<void>((resolve, reject) => {
|
||||
try {
|
||||
getRawStdoutWrite()(text, (error) => {
|
||||
if (error) reject(error);
|
||||
else resolve();
|
||||
});
|
||||
} catch (error) {
|
||||
reject(error instanceof Error ? error : new Error(String(error)));
|
||||
}
|
||||
});
|
||||
return;
|
||||
} catch (error) {
|
||||
const writeError = error instanceof Error ? error : new Error(String(error));
|
||||
const code = (writeError as Error & { code?: unknown }).code;
|
||||
if (code !== "ENOBUFS" && code !== "EAGAIN" && code !== "EWOULDBLOCK") {
|
||||
throw writeError;
|
||||
}
|
||||
await new Promise<void>((resolve) => setTimeout(resolve, RAW_STDOUT_RETRY_DELAY_MS));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function takeOverStdout(): void {
|
||||
if (stdoutTakeoverState) {
|
||||
return;
|
||||
@@ -47,28 +83,26 @@ export function isStdoutTakenOver(): boolean {
|
||||
}
|
||||
|
||||
export function writeRawStdout(text: string): void {
|
||||
if (stdoutTakeoverState) {
|
||||
stdoutTakeoverState.rawStdoutWrite(text);
|
||||
if (text.length === 0) {
|
||||
return;
|
||||
}
|
||||
process.stdout.write(text);
|
||||
rawStdoutWriteTail = rawStdoutWriteTail.then(() => writeRawStdoutChunk(text));
|
||||
void rawStdoutWriteTail.catch(() => {
|
||||
process.exit(1);
|
||||
});
|
||||
}
|
||||
|
||||
export async function waitForRawStdoutBackpressure(): Promise<void> {
|
||||
while (true) {
|
||||
const tail = rawStdoutWriteTail;
|
||||
await tail;
|
||||
if (tail === rawStdoutWriteTail) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export async function flushRawStdout(): Promise<void> {
|
||||
if (stdoutTakeoverState) {
|
||||
await new Promise<void>((resolve, reject) => {
|
||||
stdoutTakeoverState?.rawStdoutWrite("", (err) => {
|
||||
if (err) reject(err);
|
||||
else resolve();
|
||||
});
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
await new Promise<void>((resolve, reject) => {
|
||||
process.stdout.write("", (err) => {
|
||||
if (err) reject(err);
|
||||
else resolve();
|
||||
});
|
||||
});
|
||||
await waitForRawStdoutBackpressure();
|
||||
await writeRawStdoutChunk("");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user