import { rename, rm, writeFile } from "node:fs/promises"; import { join } from "node:path"; const attempt = async (failures, phase, action) => { try { await action(); return true; } catch (cause) { failures.push(`${phase}: ${String(cause)}`); return false; } }; const writeReceipt = (stream, text) => new Promise((resolve, reject) => { const onError = (cause) => reject(cause); stream.once("error", onError); try { stream.write(text, (cause) => { if (cause !== null && cause !== undefined) { // A Writable emits its error after notifying the write callback. reject(cause); } else { stream.off("error", onError); resolve(); } }); } catch (cause) { stream.off("error", onError); reject(cause); } }); /** Preserve task costs through independent cleanup and receipt-output failures. */ export const finalizeRetainedQueryReceipt = async ({ report, client, transport, account, serverLog, serverLogFinished, output, stdout = process.stdout, }) => { report.cleanup_failures = []; await Promise.all([ attempt(report.cleanup_failures, "client", () => client.close()), attempt(report.cleanup_failures, "transport", () => transport.close()), ]); await attempt(report.cleanup_failures, "server-log", async () => { if (!serverLog.writableEnded) serverLog.end(); await serverLogFinished; }); if (report.cleanup_failures.length === 0) await attempt(report.cleanup_failures, "workspace", () => rm(account, { recursive: true }), ); if (report.cleanup_failures.length > 0) report.retained_workspace = account; report.receipt_failures = []; let publication = 0; const saveReport = async () => { // Keep failed publications in the owned output directory for inspection. // A partial retry must never truncate an already published cost receipt. const pending = join(output, `.report-${++publication}.json.pending`); await writeFile(pending, JSON.stringify(report, null, 2) + "\n", { flag: "wx", }); await rename(pending, join(output, "report.json")); }; await attempt(report.receipt_failures, "report-file", saveReport); const stdoutWritten = await attempt(report.receipt_failures, "stdout", () => writeReceipt(stdout, JSON.stringify(report) + "\n"), ); // Preserve a broken stdout diagnostic in the file that may already exist. if (!stdoutWritten) await attempt(report.receipt_failures, "report-file-retry", saveReport); return ( report.cleanup_failures.length === 0 && report.receipt_failures.length === 0 ); };