import { appendFile, mkdir } from "node:fs/promises"; import { dirname } from "node:path"; export type TraceWriteErrorHandler = (error: unknown) => void; export class TraceWriter { private readonly tracePath: string; private readonly onError: TraceWriteErrorHandler; private pending: Promise = Promise.resolve(); private failed = false; constructor(tracePath: string, onError: TraceWriteErrorHandler) { this.tracePath = tracePath; this.onError = onError; } enqueue(line: string): boolean { if (this.failed) return false; this.pending = this.pending.then(async () => { if (this.failed) return; try { await mkdir(dirname(this.tracePath), { recursive: true }); await appendFile(this.tracePath, line, "utf8"); } catch (error) { this.failed = true; try { this.onError(error); } catch { // Error reporting is also observational and must not reject the writer queue. } } }); return true; } async flush(timeoutMs: number): Promise { const pending = this.pending; return await new Promise((resolve) => { let settled = false; const finish = (result: boolean): void => { if (settled) return; settled = true; clearTimeout(timer); resolve(result); }; const timer = setTimeout(() => finish(false), Math.max(0, timeoutMs)); void pending.then( () => finish(true), () => finish(true), ); }); } }