diff --git a/public/_kleap/kleap-nextjs-error-detector.js b/public/_kleap/kleap-nextjs-error-detector.js new file mode 100644 index 0000000..40af9d6 --- /dev/null +++ b/public/_kleap/kleap-nextjs-error-detector.js @@ -0,0 +1,263 @@ +/** + * Kleap Next.js Error Detector + * + * Detects and reports Next.js build/runtime errors to parent window via postMessage + * Works with Next.js 15 error overlay system + * + * Version: 1.0.0 + */ +(function () { + console.debug('[Kleap] Next.js error detector loaded v1.0.0'); + + // Only run inside iframe + const isInsideIframe = window.parent !== window; + if (!isInsideIframe) { + console.debug('[Kleap] Not inside iframe, skipping error detection'); + return; + } + + const PARENT_TARGET_ORIGIN = '*'; + + // Track reported errors to avoid duplicates + const reportedErrors = new Set(); + const ERROR_COOLDOWN_MS = 5000; + + /** + * Send error report to parent window + */ + function reportError(errorData) { + const errorKey = `${errorData.type}-${errorData.message}`; + + // Check if already reported recently + if (reportedErrors.has(errorKey)) { + console.debug('[Kleap] Error already reported, skipping:', errorKey); + return; + } + + // Mark as reported + reportedErrors.add(errorKey); + setTimeout(() => reportedErrors.delete(errorKey), ERROR_COOLDOWN_MS); + + console.log('[Kleap] Reporting error to parent:', errorData); + + window.parent.postMessage( + { + type: 'nextjs-build-error', + payload: errorData, + }, + PARENT_TARGET_ORIGIN + ); + } + + /** + * Extract error details from Next.js error overlay + */ + function extractNextJsError(container) { + try { + // Next.js 15 error overlay structure + // Look for common error patterns + + // Method 1: Check for Next.js error container + const errorTitle = container.querySelector('[data-nextjs-dialog-header]'); + const errorBody = container.querySelector('[data-nextjs-dialog-body]'); + + if (errorTitle || errorBody) { + const title = errorTitle?.textContent?.trim() || ''; + const body = errorBody?.textContent?.trim() || ''; + + return { + type: 'build-error', + message: title || 'Build error detected', + details: body, + fullText: `${title}\n\n${body}`.trim(), + }; + } + + // Method 2: Look for error message in generic containers + const h4 = container.querySelector('h4'); + const pre = container.querySelector('pre'); + + if (h4 || pre) { + return { + type: 'build-error', + message: h4?.textContent?.trim() || 'Build error', + details: pre?.textContent?.trim() || '', + fullText: container.textContent?.trim() || 'Unknown error', + }; + } + + // Method 3: Generic text extraction + const text = container.textContent?.trim(); + if (text && text.length > 10) { + // Check for common Next.js error patterns + if (text.includes('Module not found') || + text.includes("Can't resolve") || + text.includes('Build Error') || + text.includes('Compile Error') || + text.includes('Runtime Error')) { + + // Extract first meaningful line as message + const lines = text.split('\n').map(l => l.trim()).filter(Boolean); + const message = lines[0] || 'Build error detected'; + + return { + type: 'build-error', + message: message.substring(0, 200), // Limit length + details: text.substring(0, 1000), // Limit details + fullText: text.substring(0, 2000), // Full context + }; + } + } + + return null; + } catch (error) { + console.error('[Kleap] Error extracting Next.js error:', error); + return null; + } + } + + /** + * Watch for Next.js error overlay in DOM + */ + function watchForNextJsErrors() { + console.debug('[Kleap] Setting up Next.js error detection'); + + // Configuration for MutationObserver + const config = { + childList: true, + subtree: true, // Watch all descendants (Next.js uses portals) + attributes: false, + }; + + // Callback when DOM changes + const observerCallback = function (mutationsList) { + for (const mutation of mutationsList) { + if (mutation.type === 'childList' && mutation.addedNodes.length > 0) { + for (const node of mutation.addedNodes) { + if (node.nodeType !== Node.ELEMENT_NODE) continue; + + // Check for Next.js error overlay elements + // Next.js 15 uses various IDs and attributes + const isErrorOverlay = + node.id?.includes('nextjs') || + node.id?.includes('error') || + node.getAttribute?.('data-nextjs-dialog') !== null || + node.getAttribute?.('data-nextjs-toast') !== null || + node.className?.includes?.('nextjs') || + node.className?.includes?.('error-overlay'); + + if (isErrorOverlay) { + console.log('[Kleap] Detected Next.js error overlay:', node); + + // Wait a bit for content to populate + setTimeout(() => { + const errorData = extractNextJsError(node); + if (errorData) { + reportError(errorData); + } + }, 100); + } + + // Also check children recursively + if (node.querySelector) { + const errorChildren = node.querySelectorAll('[data-nextjs-dialog], [data-nextjs-toast], [id*="nextjs"], [id*="error"]'); + errorChildren.forEach(child => { + setTimeout(() => { + const errorData = extractNextJsError(child); + if (errorData) { + reportError(errorData); + } + }, 100); + }); + } + } + } + } + }; + + // Start observing when DOM is ready + if (document.readyState === 'loading') { + document.addEventListener('DOMContentLoaded', () => { + console.debug('[Kleap] DOM loaded, starting error detection'); + const observer = new MutationObserver(observerCallback); + observer.observe(document.body || document.documentElement, config); + + // Check for existing errors + checkExistingErrors(); + }); + } else { + console.debug('[Kleap] DOM already loaded, starting error detection'); + const observer = new MutationObserver(observerCallback); + observer.observe(document.body || document.documentElement, config); + + // Check for existing errors + checkExistingErrors(); + } + } + + /** + * Check for errors that already exist in the DOM + */ + function checkExistingErrors() { + console.debug('[Kleap] Checking for existing errors'); + + try { + // Look for Next.js error elements + const selectors = [ + '[data-nextjs-dialog]', + '[data-nextjs-toast]', + '[id*="nextjs"][id*="error"]', + 'div[class*="error-overlay"]', + ]; + + selectors.forEach(selector => { + const elements = document.querySelectorAll(selector); + elements.forEach(element => { + console.log('[Kleap] Found existing error element:', element); + const errorData = extractNextJsError(element); + if (errorData) { + reportError(errorData); + } + }); + }); + } catch (error) { + console.error('[Kleap] Error checking existing errors:', error); + } + } + + /** + * Also intercept console.error for build errors + * Next.js sometimes logs errors to console before showing overlay + */ + const originalConsoleError = console.error; + console.error = function(...args) { + // Call original + originalConsoleError.apply(console, args); + + // Check if it looks like a Next.js error + const message = args.map(arg => + typeof arg === 'string' ? arg : String(arg) + ).join(' '); + + if (message.includes('Module not found') || + message.includes("Can't resolve 'tailwindcss'") || + message.includes('Build Error') || + message.includes('Compile Error')) { + + console.log('[Kleap] Detected build error in console.error'); + + reportError({ + type: 'build-error', + message: message.substring(0, 200), + details: message.substring(0, 1000), + fullText: message.substring(0, 2000), + source: 'console.error', + }); + } + }; + + // Start watching + watchForNextJsErrors(); + + console.debug('[Kleap] Next.js error detector initialized'); +})();