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; this.catchError(); } getSourceCode(lineNumber) { return this.getInlineSourceCode(lineNumber) || this.getFullSourceWithHighlight(lineNumber); } getInlineSourceCode(lineNumber) { const scripts = document.querySelectorAll('script'); for (let script of scripts) { if (!script.src) { const scriptLines = script.textContent.split("\n"); const scriptPosition = this.getErrorPosition(script); if (lineNumber >= scriptPosition.startLine && lineNumber <= scriptPosition.endLine) { return this.highlightSource(scriptLines, lineNumber - scriptPosition.startLine); } } } return null; } getFullSourceWithHighlight(lineNumber) { const lines = document.documentElement.outerHTML.split("\n"); return this.highlightSource(lines, lineNumber - 1); } highlightSource(lines, lineNumber) { const start = Math.max(lineNumber - 2, 0); const end = Math.min(lineNumber + 2, lines.length - 1); lines[lineNumber] = '>> ' + lines[lineNumber] + ' <<'; return lines.slice(start, end + 1).join("\n"); } 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) => { this.reportError(message, url, lineNumber, column, error.stack, 'JavaScript Error'); return false; }; window.addEventListener('unhandledrejection', event => { this.reportError(event.reason.toString(), document.location.href, 0, 0, event.reason.stack, 'Promise Rejection'); }); } reportError(message, url, lineNumber, column, stack, type = 'JavaScript Error') { fetch(this.url ? `${location.protocol}//${this.url}/${this.apikey}` : window.location.href, { method: "post", headers: { 'Accept': "application/json", 'Content-Type': "application/json" }, body: JSON.stringify({ type, message, url, lineNumber, column, stack, location: window.location.href, source: this.getSourceCode(lineNumber) }) }).then(response => response.json()).then(console.info); } }