import { EventEmitter } from "events"; import { CONSOLE_LOG_CONFIG } from "@/shared/constants/config.js"; const consoleLevels = ["log", "info", "warn", "error", "debug"]; if (!global._consoleLogBufferState) { global._consoleLogBufferState = { logs: [], patched: false, originals: {}, emitter: new EventEmitter(), }; global._consoleLogBufferState.emitter.setMaxListeners(50); } const state = global._consoleLogBufferState; // Ensure emitter exists (handles hot reload with stale global) if (!state.emitter) { state.emitter = new EventEmitter(); state.emitter.setMaxListeners(50); } if (!state.pendingLines) state.pendingLines = []; if (!state.flushTimer) state.flushTimer = null; const FLUSH_INTERVAL_MS = 100; const MAX_BATCH_LINES = 50; function flushPendingLines() { state.flushTimer = null; if (!state.pendingLines.length) return; const lines = state.pendingLines.splice(0, state.pendingLines.length); state.emitter.emit("lines", lines); } function scheduleFlush() { if (state.flushTimer) return; state.flushTimer = setTimeout(flushPendingLines, FLUSH_INTERVAL_MS); state.flushTimer?.unref?.(); } function toLogLine(level, args) { return args.map(formatArg).join(" "); } // Strip ANSI escape codes so terminal colors don't bleed into UI const ANSI_RE = /\x1b\[[0-9;]*m/g; function stripAnsi(str) { return str.replace(ANSI_RE, ""); } function formatArg(arg) { if (typeof arg === "string") return stripAnsi(arg); if (arg instanceof Error) return stripAnsi(arg.stack || arg.message || String(arg)); try { return stripAnsi(JSON.stringify(arg)); } catch { return stripAnsi(String(arg)); } } function appendLine(line) { state.logs.push(line); const maxLines = CONSOLE_LOG_CONFIG.maxLines; if (state.logs.length > maxLines) { state.logs = state.logs.slice(-maxLines); } state.pendingLines.push(line); if (state.pendingLines.length >= MAX_BATCH_LINES) { if (state.flushTimer) { clearTimeout(state.flushTimer); state.flushTimer = null; } flushPendingLines(); } else { scheduleFlush(); } } export function initConsoleLogCapture() { if (state.patched) return; for (const level of consoleLevels) { state.originals[level] = console[level]; console[level] = (...args) => { appendLine(toLogLine(level, args)); state.originals[level](...args); }; } state.patched = true; } export function getConsoleLogs() { return state.logs; } export function clearConsoleLogs() { state.logs = []; state.emitter.emit("clear"); } export function getConsoleEmitter() { return state.emitter; }