// file: error_observer_v16.js // V16: Comprehensive Error Observer // Captures JS errors, Promise rejections, Resource failures, Console errors (() => { if (window.__ERROR_V16_INSTALLED__) return; window.__ERROR_V16_INSTALLED__ = true; // --- CONFIG --- const MAX_STACK_LENGTH = 1000; const MAX_MESSAGE_LENGTH = 500; const CAPTURE_CONSOLE_ERRORS = true; // Set false to disable console.error capture // --- UTILS --- function emit(data) { try { const payload = JSON.stringify({ ...data, ts: Date.now(), cat: "error" }); if (window.__py_event) window.__py_event("ERROR_EVT:" + payload); } catch (_) { } } function sanitize(str, maxLen = MAX_MESSAGE_LENGTH) { if (!str) return null; return String(str).slice(0, maxLen); } function parseStackTrace(stack) { if (!stack) return null; try { // Extract file:line:col from stack const lines = stack.split('\n') .slice(0, 5) // First 5 frames .map(line => { const match = line.match(/\((.+):(\d+):(\d+)\)/); if (match) { return { file: match[1], line: parseInt(match[2]), column: parseInt(match[3]) }; } return null; }) .filter(Boolean); return lines.length > 0 ? lines : sanitize(stack, MAX_STACK_LENGTH); } catch (_) { return sanitize(stack, MAX_STACK_LENGTH); } } // --- 1. JAVASCRIPT ERRORS --- window.addEventListener('error', (event) => { // Skip if this is a resource error (will be handled separately) if (event.target && event.target.tagName) { return; } emit({ type: 'JS_ERROR', message: sanitize(event.message), source: sanitize(event.filename), line: event.lineno, column: event.colno, stack: parseStackTrace(event.error?.stack), url: window.location.href }); }, true); // --- 2. UNHANDLED PROMISE REJECTIONS --- window.addEventListener('unhandledrejection', (event) => { const reason = event.reason; emit({ type: 'PROMISE_REJECTION', reason: sanitize(String(reason)), stack: parseStackTrace(reason?.stack), url: window.location.href }); }); // --- 3. RESOURCE LOAD ERRORS --- window.addEventListener('error', (event) => { if (event.target && event.target.tagName) { const target = event.target; emit({ type: 'RESOURCE_ERROR', tag: target.tagName.toLowerCase(), src: sanitize(target.src || target.href), url: window.location.href, // Additional context id: target.id || null, className: target.className || null }); } }, true); // --- 4. CONSOLE ERRORS (Optional) --- if (CAPTURE_CONSOLE_ERRORS) { const origConsoleError = console.error; console.error = function (...args) { // Only capture if not already an Error object being logged // (to avoid duplicates with JS_ERROR) const isErrorObject = args.length === 1 && args[0] instanceof Error; if (!isErrorObject) { emit({ type: 'CONSOLE_ERROR', args: args.map(a => sanitize(String(a))), url: window.location.href }); } // Call original origConsoleError.apply(console, args); }; // Also capture console.warn for completeness const origConsoleWarn = console.warn; console.warn = function (...args) { emit({ type: 'CONSOLE_WARN', args: args.map(a => sanitize(String(a))), url: window.location.href }); origConsoleWarn.apply(console, args); }; } // --- 5. GLOBAL ERROR COUNT (for Network Observer V5 health) --- // Share error count with network observer for health monitoring if (!window.__WCS_ERROR_COUNT__) { window.__WCS_ERROR_COUNT__ = 0; } const incrementErrorCount = () => { window.__WCS_ERROR_COUNT__++; }; ['error', 'unhandledrejection'].forEach(evt => { window.addEventListener(evt, incrementErrorCount, true); }); console.log("[WCS] Error Observer V16 Active"); })();