import axios from 'axios'; import {configs, isDesktop} from '@/lib/configs'; interface CrashReportPayload { appName: string; appVersion: string; platform: string; osVersion?: string; errorType: string; errorMessage: string; stackTrace?: string; screenName?: string; userId?: string; breadcrumbs?: Breadcrumb[]; extraData?: Record; } interface Breadcrumb { timestamp: number; action: string; detail?: string; } const MAX_BREADCRUMBS = 30; const breadcrumbs: Breadcrumb[] = []; export function addBreadcrumb(action: string, detail?: string): void { breadcrumbs.push({timestamp: Date.now(), action, detail}); if (breadcrumbs.length > MAX_BREADCRUMBS) breadcrumbs.shift(); } function getPlatform(): string { if (!isDesktop) return 'web'; const ua: string = navigator.userAgent.toLowerCase(); if (ua.includes('mac')) return 'desktop-macos'; if (ua.includes('win')) return 'desktop-windows'; if (ua.includes('linux')) return 'desktop-linux'; return 'desktop'; } function getUserId(): string | undefined { try { const raw: string | null = localStorage.getItem('userId'); return raw ?? undefined; } catch { return undefined; } } async function sendCrashReport(payload: CrashReportPayload): Promise { try { await axios.post(configs.apiUrl + 'crash-report', payload, { headers: {'Content-Type': 'application/json'}, timeout: 5000, }); } catch { // Silently fail — we can't crash while reporting a crash } } function buildReport(errorType: string, errorMessage: string, stackTrace?: string): CrashReportPayload { return { appName: configs.appName, appVersion: configs.appVersion, platform: getPlatform(), osVersion: navigator.userAgent, errorType, errorMessage, stackTrace, screenName: window.location.pathname, userId: getUserId(), breadcrumbs: [...breadcrumbs], }; } export function reportError(error: Error, extraData?: Record): void { const report: CrashReportPayload = buildReport( error.name || 'Error', error.message, error.stack, ); if (extraData) report.extraData = extraData; sendCrashReport(report); } export function initCrashReporter(): void { window.onerror = (_message, _source, _lineno, _colno, error: Error | undefined): void => { if (error) { sendCrashReport(buildReport('UncaughtError', error.message, error.stack)); } }; window.onunhandledrejection = (event: PromiseRejectionEvent): void => { const reason: unknown = event.reason; if (reason instanceof Error) { sendCrashReport(buildReport('UnhandledRejection', reason.message, reason.stack)); } else { sendCrashReport(buildReport('UnhandledRejection', String(reason))); } }; }