import { apiBase, getToken } from './api'; const CLIENT_APP = 'PARTNER_H5'; export type ClientErrorLevel = 'fatal' | 'error' | 'warn'; export type ClientErrorCategory = | 'js_error' | 'unhandled_rejection' | 'api_error' | 'network' | 'render' | 'bridge' | 'other'; export type ClientErrorPayload = { level: ClientErrorLevel; category: ClientErrorCategory; message: string; stack?: string; pagePath?: string; extra?: Record; }; function currentPagePath(): string | undefined { try { return typeof window !== 'undefined' ? window.location.pathname : undefined; } catch { return undefined; } } /** 上报客户端错误(失败静默,避免递归) */ export function reportClientError(payload: ClientErrorPayload): void { const body = { level: payload.level, category: payload.category, message: String(payload.message || 'unknown').slice(0, 1000), stack: payload.stack ? String(payload.stack).slice(0, 4000) : undefined, pagePath: (payload.pagePath || currentPagePath() || '').slice(0, 128) || undefined, clientApp: CLIENT_APP, extra: payload.extra, }; const headers: Record = { 'Content-Type': 'application/json', 'X-Client-App': CLIENT_APP, }; const token = getToken(); if (token) headers.Authorization = `Bearer ${token}`; void fetch(`${apiBase}/common/client-errors`, { method: 'POST', headers, body: JSON.stringify(body), keepalive: true, }).catch(() => {}); } let installed = false; /** 安装 H5 全局未捕获错误钩子(幂等) */ export function installClientErrorReporting(): void { if (installed || typeof window === 'undefined') return; installed = true; window.addEventListener('error', (ev) => { reportClientError({ level: 'fatal', category: 'js_error', message: ev.message || 'window.error', stack: ev.error instanceof Error ? ev.error.stack : undefined, extra: { filename: ev.filename, lineno: ev.lineno, colno: ev.colno }, }); }); window.addEventListener('unhandledrejection', (ev) => { const reason = ev.reason; reportClientError({ level: 'error', category: 'unhandled_rejection', message: reason instanceof Error ? reason.message : typeof reason === 'string' ? reason : 'unhandledrejection', stack: reason instanceof Error ? reason.stack : undefined, }); }); }