class ErrorHandler { constructor(options) { if (!options) throw new Error("Options not provided"); if (!options.url) throw new Error("URL is required"); if (!options.apikey) throw new Error("API key is required"); this.url = options.url; this.apikey = options.apikey; getErrorPosition(script) { let totalLines = 0; let element = script.previousElementSibling; while (element) { totalLines += (element.outerHTML || element.textContent).split("\n").length; element = element.previousElementSibling; } return { startLine: totalLines + 1, endLine: totalLines + script.textContent.split("\n").length }; } catchError() { window.onerror = (message, url, lineNumber, column, error) => { let type = 'JavaScript Error'; if (message.indexOf(':')) { type = message.substring(0, message.indexOf(':')); message = message.substring(message.indexOf(':') + 1).trim(); } this.reportError(message, url, lineNumber, column, error.stack, type); return false; }; window.addEventListener('unhandledrejection', event => { this.reportError(event.reason.toString(), document.location.href, 0, 0, event.reason.stack, 'Promise Rejection'); }); this.createErrorAgain(); } async reportError(message, url, line, column, stack, type = 'JavaScript Error') { await new Promise(r => setTimeout(r, 2000)); fetch(this.url ? `${this.url}/${this.apikey}` : window.location.href, { method: "POST", headers: { 'Accept': "application/json", 'Content-Type': "application/json" }, body: JSON.stringify({ type, message, url, line, code: column, backtrace: stack ? stack.split("\n").filter((e) => !!e).map((e) => this.parseErrorLine(e)) : [], file: window.location.href, source: await this.getSourceCode(url, line), }) }).then(response => response.json()).then(console.info); } async getSourceCode(url, errorLineNumber) { try { const response = await fetch(url); const originalHtml = await response.text(); const lines = originalHtml.split('\n'); const start = Math.max(0, errorLineNumber - 5); const end = Math.min(lines.length, errorLineNumber + 5); return lines.slice(start, end).join('\n'); } catch (error) { console.error('Failed to fetch original HTML:', error); } } parseErrorLine(errorLine) { const obj = {}; // Pattern for: functionName@url:line:column const pattern = /^(.*)@(.+?):(\d+):(\d+)$/; const match = errorLine.match(pattern); if (match) { obj.function = match[1]; obj.file = match[2]; obj.line = match[3]; // column would be match[4] if needed console.log(match); } // Remove undefined/empty values Object.keys(obj).forEach(key => { if (!obj[key]) delete obj[key]; }); return obj; } }