const ERROR_REPORT_ENDPOINT = "/api/client-errors"; interface ErrorReport { message: string; stack?: string; source: "boundary" | "unhandled" | "rejection" | "manual"; url: string; timestamp: number; userAgent: string; sessionId?: string; } const reportQueue: ErrorReport[] = []; let flushTimer: ReturnType | null = null; function getSessionId(): string | undefined { try { const raw = localStorage.getItem("omniai:session") || sessionStorage.getItem("omniai:session"); if (!raw) return undefined; const parsed = JSON.parse(raw); return parsed?.user?.sessionId ?? undefined; } catch { return undefined; } } function flush() { if (reportQueue.length === 0) return; const batch = reportQueue.splice(0, 10); const payload = new Blob([JSON.stringify({ errors: batch })], { type: "application/json" }); if (navigator.sendBeacon?.(ERROR_REPORT_ENDPOINT, payload)) return; void fetch(ERROR_REPORT_ENDPOINT, { method: "POST", body: JSON.stringify({ errors: batch }), headers: { "Content-Type": "application/json" }, credentials: "include", keepalive: true, }).catch(() => {}); } function scheduleFlush() { if (flushTimer) return; flushTimer = setTimeout(() => { flushTimer = null; flush(); }, 2000); } export function reportError(error: unknown, source: ErrorReport["source"] = "manual") { const err = error instanceof Error ? error : new Error(String(error)); const report: ErrorReport = { message: err.message, stack: err.stack?.slice(0, 2000), source, url: window.location.href, timestamp: Date.now(), userAgent: navigator.userAgent, sessionId: getSessionId(), }; reportQueue.push(report); scheduleFlush(); }