import { PATHS } from "./constants.js"; const RECENT_EVENT_TTL_MS = 15_000; const recentEventMap = new Map(); function normalizeErrorDetail(detail) { if (!detail) return ""; if (detail instanceof Error) { return detail.stack || detail.message || String(detail); } if (typeof detail === "string") { return detail; } try { return JSON.stringify(detail); } catch { return String(detail); } } function dedupeKey(level, message, detail, category) { return `${level}::${category || ""}::${message}::${detail}`; } function shouldSkip(level, message, detail, category) { const key = dedupeKey(level, message, detail, category); const now = Date.now(); const lastSeenAt = recentEventMap.get(key); recentEventMap.set(key, now); for (const [entryKey, entryTime] of recentEventMap.entries()) { if (now - entryTime > RECENT_EVENT_TTL_MS) { recentEventMap.delete(entryKey); } } return lastSeenAt && now - lastSeenAt < RECENT_EVENT_TTL_MS; } export async function reportEarthClientLog({ level = "error", message, category = "runtime", module = "earth", detail = "", }) { if (!message) return; const normalizedDetail = normalizeErrorDetail(detail); if (shouldSkip(level, message, normalizedDetail, category)) { return; } try { await fetch(PATHS.earthClientLogsApi, { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify({ level, message, category, module, url: window.location.href, detail: normalizedDetail.slice(0, 4000), }), keepalive: true, }); } catch { // Swallow reporting failures to avoid recursive log noise. } } export function registerEarthClientErrorHandlers() { window.addEventListener("error", (event) => { console.error("全局错误:", event.error); void reportEarthClientLog({ level: "error", category: "window-error", module: "main", message: event.message || "Earth 页面发生未捕获错误", detail: event.error || `${event.filename || ""}:${event.lineno || 0}:${event.colno || 0}`, }); }); window.addEventListener("unhandledrejection", (event) => { console.error("未处理的 Promise 错误:", event.reason); void reportEarthClientLog({ level: "error", category: "unhandledrejection", module: "main", message: "Earth 页面发生未处理 Promise 错误", detail: event.reason, }); }); }