/** * A newline line-splitter for streamed child-process output (audit CC3/CC4/SIMP5). * * `download.ts` and `terminal.ts` each had the same hand-rolled loop: accumulate * chunk strings, emit every complete `\n`-terminated line (stripping a trailing * `\r`), and keep the partial remainder for the next chunk. This is that logic in * one place, and — being pure and synchronous — it's unit-tested directly. * * Usage: feed decoded chunks with `push()`, and call `flush()` on stream close to * emit any trailing partial line that never saw a newline. */ export interface LineBuffer { /** Accept a decoded chunk; invokes the sink for each newly-completed line. */ push: (chunk: string) => void /** Emit any buffered remainder (a final line with no trailing newline). */ flush: () => void } export function createLineBuffer(onLine: (line: string) => void): LineBuffer { let buf = '' return { push(chunk: string): void { buf += chunk let nl: number while ((nl = buf.indexOf('\n')) >= 0) { onLine(buf.slice(0, nl).replace(/\r$/, '')) buf = buf.slice(nl + 1) } }, flush(): void { if (buf) { onLine(buf) buf = '' } } } }