/** Bounded desktop diagnostics. Error paths must never write to a failed pipe. */ const fs = require("node:fs"); const path = require("node:path"); const MAX_LOG_BYTES = 5 * 1024 * 1024; function createDesktopLogger({ directory, stdout, stderr, filesystem = fs, maxFileBytes = MAX_LOG_BYTES }) { let initialized = false; let stdoutEnabled = Boolean(stdout); let writing = false; const location = () => path.join(directory(), "main.log"); // Read only the retained tail, even when an older release left a huge file. function trim(file) { let handle; try { handle = filesystem.openSync(file, "r+"); const size = filesystem.fstatSync(handle).size; if (size <= maxFileBytes) return; const tail = Buffer.alloc(maxFileBytes); let read = 0; while (read < tail.length) { const count = filesystem.readSync(handle, tail, read, tail.length - read, size - tail.length + read); if (!count) break; read += count; } filesystem.writeFileSync(handle, tail.subarray(0, read)); filesystem.ftruncateSync(handle, read); } catch (error) { if (error.code !== "ENOENT") throw error; } finally { if (handle !== undefined) filesystem.closeSync(handle); } } function initialize() { try { filesystem.mkdirSync(directory(), { recursive: true }); trim(location()); trim(`${location()}.1`); initialized = true; } catch { // Diagnostics are best effort; never report a logger failure via itself. } } function record(line) { const prefix = Buffer.from(`[${new Date().toISOString()}] `); const body = Buffer.from(String(line)); if (prefix.length + body.length + 1 <= maxFileBytes) return Buffer.concat([prefix, body, Buffer.from("\n")]); const marker = Buffer.from("[truncated] "); const tail = body.subarray(body.length - (maxFileBytes - prefix.length - marker.length - 1)); // A retained suffix can start inside a UTF-8 character. let start = 0; while (start < tail.length && (tail[start] & 0xc0) === 0x80) start++; return Buffer.concat([prefix, marker, tail.subarray(start), Buffer.from("\n")]); } function append(entry) { try { if (!initialized) initialize(); if (!initialized) return; const file = location(); let size = 0; try { size = filesystem.statSync(file).size; } catch (error) { if (error.code !== "ENOENT") throw error; } if (size + entry.length > maxFileBytes) { filesystem.rmSync(`${file}.1`, { force: true }); filesystem.renameSync(file, `${file}.1`); } filesystem.appendFileSync(file, entry); } catch { // Includes ENOSPC and rotation failures. Do not fall back to a pipe. } } function log(line, { fileOnly = false } = {}) { if (writing) return; writing = true; try { const entry = record(line); append(entry); if (!fileOnly && stdoutEnabled && !stdout.destroyed && !stdout.writableEnded) { try { // A false return means backpressure: skip mirroring until drained. if (!stdout.write(entry, (error) => { if (error) disableStdout(error); })) stdoutEnabled = false; } catch (error) { disableStdout(error); } } } catch { // Formatting must not escape into the uncaught-exception handler either. } finally { writing = false; } } let stdoutFailed = false; function disableStdout(error) { stdoutEnabled = false; if (stdoutFailed) return; stdoutFailed = true; append(record(`stdout disabled: ${error?.code ?? error?.message ?? "stream error"}`)); } stdout?.on("error", disableStdout); stdout?.on("drain", () => { if (!stdoutFailed) stdoutEnabled = true; }); // Console / dependency writes to stderr can fail asynchronously as well. let stderrFailed = false; stderr?.on("error", (error) => { if (stderrFailed) return; stderrFailed = true; log(`stderr disabled: ${error?.code ?? error?.message ?? "stream error"}`, { fileOnly: true }); }); return { log, initialize }; } module.exports = { createDesktopLogger, MAX_LOG_BYTES };