diff --git a/src/run.ts b/src/run.ts index 7725f02..378d08f 100644 --- a/src/run.ts +++ b/src/run.ts @@ -228,13 +228,12 @@ export async function runFile( proc.stdin.end() const totalTimeout = options.timeout * file.commands.length - const stdout = await readWithTimeout(proc.stdout, totalTimeout) + const lastSentinelSuffix = `_${file.commands.length - 1}__` + const stdout = await readUntilSentinel(proc.stdout, sentinel, lastSentinelSuffix, totalTimeout) if (!verbose) { await readWithTimeout(proc.stderr, 1000).catch(() => "") } - await proc.exited - const { outputs, exitCodes } = parseSentinelOutput( stdout, sentinel, @@ -262,6 +261,40 @@ export async function runFile( } } +async function readUntilSentinel( + stream: ReadableStream, + sentinelPrefix: string, + sentinelSuffix: string, + timeoutMs: number, +): Promise { + const reader = stream.getReader() + const decoder = new TextDecoder() + let accumulated = "" + + let timerId: ReturnType + const timeout = new Promise((_, reject) => + timerId = setTimeout(() => reject(new Error("Timeout reading output")), timeoutMs), + ) + + try { + while (true) { + const { done, value } = await Promise.race([reader.read(), timeout]) as ReadableStreamReadResult + if (done) break + if (value) { + accumulated += decoder.decode(value, { stream: true }) + // Check if the last sentinel has appeared (prefix + exitcode + suffix) + const prefixIdx = accumulated.lastIndexOf(sentinelPrefix) + if (prefixIdx !== -1 && accumulated.indexOf(sentinelSuffix, prefixIdx) !== -1) break + } + } + } finally { + clearTimeout(timerId!) + reader.releaseLock() + } + + return accumulated + decoder.decode() +} + async function readWithTimeout( stream: ReadableStream, timeoutMs: number,