UNPKG

ai-debug-local-mcp

Version:

🎯 ENHANCED AI GUIDANCE v4.1.2: Dramatically improved tool descriptions help AI users choose the right tools instead of 'close enough' options. Ultra-fast keyboard automation (10x speed), universal recording, multi-ecosystem debugging support, and compreh

410 lines • 18.8 kB
export class JavaScriptErrorDetectionEngine { page = null; errors = { scriptErrors: [], moduleConflicts: [], htmlErrors: [], consoleErrors: [], networkErrors: [], suggestions: [] }; isMonitoring = false; attach(page) { this.page = page; } async startMonitoring() { if (!this.page) { throw new Error('No page attached'); } this.isMonitoring = true; this.errors = { scriptErrors: [], moduleConflicts: [], htmlErrors: [], consoleErrors: [], networkErrors: [], suggestions: [] }; // Monitor console errors this.page.on('console', (msg) => { if (msg.type() === 'error') { const error = { type: msg.type(), text: msg.text(), stack: msg.location()?.url ? `at ${msg.location()?.url}:${msg.location()?.lineNumber}:${msg.location()?.columnNumber}` : undefined, timestamp: Date.now() }; this.errors.consoleErrors.push(error); this.analyzeConsoleError(error); } }); // Monitor page errors (uncaught exceptions) this.page.on('pageerror', (error) => { const scriptError = { type: 'runtime', severity: 'error', message: error.message, stackTrace: error.stack, timestamp: Date.now() }; // Extract file and line info from stack const stackMatch = error.stack?.match(/at\s+.*?\s+\(?(.*?):(\d+):(\d+)\)?/); if (stackMatch) { scriptError.file = stackMatch[1]; scriptError.line = parseInt(stackMatch[2]); scriptError.column = parseInt(stackMatch[3]); } this.errors.scriptErrors.push(scriptError); this.analyzeScriptError(scriptError); }); // Monitor network failures this.page.on('requestfailed', (request) => { const error = { url: request.url(), status: 0, method: request.method(), timestamp: Date.now() }; this.errors.networkErrors.push(error); // Check if it's a script loading error if (request.url().endsWith('.js') || request.url().endsWith('.mjs')) { this.errors.scriptErrors.push({ type: 'network', severity: 'error', message: `Failed to load script: ${request.url()}`, timestamp: Date.now(), suggestion: 'Check if the script URL is correct and the server is responding' }); } }); // Inject error detection code await this.injectErrorDetection(); } async stopMonitoring() { this.isMonitoring = false; // Remove listeners if (this.page) { this.page.removeAllListeners('console'); this.page.removeAllListeners('pageerror'); this.page.removeAllListeners('requestfailed'); } } async injectErrorDetection() { if (!this.page) return; await this.page.addInitScript(() => { // Create error detection namespace window.__errorDetection = { scriptErrors: [], moduleConflicts: [], htmlErrors: [], // Override error handling captureError: function (error, context) { const errorInfo = { type: 'runtime', message: error.message, stack: error.stack, timestamp: Date.now(), context }; window.__errorDetection.scriptErrors.push(errorInfo); // Send to parent window.postMessage({ type: '__errorDetection', error: errorInfo }, '*'); }, // Check for module conflicts checkModuleSystem: function () { // Detect if ES modules are being used in a CommonJS context const scripts = document.querySelectorAll('script'); scripts.forEach(script => { if (script.type === 'module' && typeof module !== 'undefined') { const conflict = { file: script.src || 'inline script', expectedType: 'module', actualType: 'commonjs', suggestion: 'Parent package.json may have "type": "module" causing conflicts with CommonJS libraries' }; window.__errorDetection.moduleConflicts.push(conflict); } }); }, // Validate HTML for common errors validateHTML: function () { // Check for self-closing script tags const scriptTags = document.getElementsByTagName('script'); Array.from(scriptTags).forEach(script => { // Check if script tag has self-closing syntax in HTML if (script.outerHTML.includes('/>')) { window.__errorDetection.htmlErrors.push({ type: 'malformed_tag', element: 'script', suggestion: 'Script tags should not be self-closing. Use <script></script> instead of <script />', code: script.outerHTML }); } }); // Check for other malformed tags const allElements = document.querySelectorAll('*'); allElements.forEach(element => { // Check for invalid nesting if (element.tagName === 'P' && element.querySelector('div, p, h1, h2, h3, h4, h5, h6')) { window.__errorDetection.htmlErrors.push({ type: 'invalid_nesting', element: element.tagName, suggestion: `<${element.tagName.toLowerCase()}> cannot contain block-level elements`, code: element.outerHTML.substring(0, 100) + '...' }); } }); } }; // Override dynamic import to catch module errors const originalImport = window.import; if (originalImport) { window.import = function (specifier) { return originalImport.call(this, specifier).catch((error) => { window.__errorDetection.captureError(error, { type: 'module_import', specifier }); throw error; }); }; } // Monitor script loading errors window.addEventListener('error', (event) => { if (event.target && event.target.tagName === 'SCRIPT') { const script = event.target; window.__errorDetection.scriptErrors.push({ type: 'network', message: `Failed to load script: ${script.src}`, file: script.src, timestamp: Date.now() }); } }, true); // Run initial checks setTimeout(() => { window.__errorDetection.checkModuleSystem(); window.__errorDetection.validateHTML(); }, 100); }); // Listen for error messages await this.page.evaluate(() => { window.addEventListener('message', (event) => { if (event.data && event.data.type === '__errorDetection') { // Store for later retrieval window.__errorDetectionEvents = window.__errorDetectionEvents || []; window.__errorDetectionEvents.push(event.data.error); } }); }); } analyzeConsoleError(error) { const errorText = error.text.toLowerCase(); // Check for module-related errors if (errorText.includes('cannot use import statement') || errorText.includes('unexpected token import') || errorText.includes('syntaxerror: import')) { this.errors.moduleConflicts.push({ file: error.stack?.split(':')[0] || 'unknown', expectedType: 'commonjs', actualType: 'module', suggestion: 'This file is using ES6 imports but is being loaded as CommonJS. Check your package.json "type" field.' }); this.errors.suggestions.push('Consider adding "type": "commonjs" to package.json or converting imports to require()'); } // Check for export errors if (errorText.includes('exports is not defined') || errorText.includes('module is not defined')) { this.errors.moduleConflicts.push({ file: error.stack?.split(':')[0] || 'unknown', expectedType: 'module', actualType: 'commonjs', suggestion: 'CommonJS code is running in an ES module context. Check parent package.json for "type": "module"' }); this.errors.suggestions.push('Remove "type": "module" from package.json or use .cjs extension for CommonJS files'); } // Check for malformed HTML errors if (errorText.includes('unexpected token <') || errorText.includes('invalid or unexpected token')) { this.errors.suggestions.push('Check for malformed HTML tags, especially self-closing script tags'); } } analyzeScriptError(error) { // Add specific suggestions based on error patterns if (error.message.includes('Cannot read property') || error.message.includes('Cannot read properties')) { error.suggestion = 'Add null/undefined checks before accessing properties'; } if (error.message.includes('is not a function')) { error.suggestion = 'Verify the variable is a function before calling it'; } if (error.message.includes('Maximum call stack')) { error.suggestion = 'Check for infinite recursion or circular dependencies'; } } async detectCommonIssues() { if (!this.page) return; // Check for common HTML issues const htmlIssues = await this.page.evaluate(() => { const issues = []; // Check for self-closing script tags const scripts = Array.from(document.scripts); scripts.forEach(script => { if (script.outerHTML.includes('/>')) { issues.push({ type: 'malformed_tag', element: 'script', file: script.src || 'inline', suggestion: 'Script tags cannot be self-closing in HTML. Use <script></script>', code: script.outerHTML }); } }); // Check for scripts with wrong type scripts.forEach(script => { if (script.type && script.type !== 'module' && script.type !== 'text/javascript' && script.type !== 'application/javascript') { issues.push({ type: 'invalid_attribute', element: 'script', file: script.src || 'inline', suggestion: `Invalid script type "${script.type}". Use "module" for ES modules or omit for regular scripts.`, code: script.outerHTML }); } }); return issues; }); htmlIssues.forEach(issue => { this.errors.htmlErrors.push(issue); }); // Check package.json module type conflicts await this.checkModuleTypeConflicts(); } async checkModuleTypeConflicts() { if (!this.page) return; const conflicts = await this.page.evaluate(() => { const conflicts = []; // Check if we're in a module context but using CommonJS if (typeof module !== 'undefined' && typeof exports !== 'undefined') { // We're in CommonJS context const scripts = document.querySelectorAll('script[type="module"]'); if (scripts.length > 0) { conflicts.push({ file: 'package.json', expectedType: 'commonjs', actualType: 'module', suggestion: 'You have ES modules in your HTML but the environment is CommonJS. This can cause conflicts.' }); } } // Check for mixed module systems const hasESModules = document.querySelector('script[type="module"]') !== null; const hasCommonJS = Array.from(document.scripts).some(script => script.textContent?.includes('require(') || script.textContent?.includes('module.exports')); if (hasESModules && hasCommonJS) { conflicts.push({ file: 'mixed', expectedType: 'module', actualType: 'commonjs', suggestion: 'Mixing ES modules and CommonJS can cause issues. Consider using one module system consistently.' }); } return conflicts; }); conflicts.forEach(conflict => { this.errors.moduleConflicts.push(conflict); }); } async getErrorReport() { // Get any errors captured in the page if (this.page) { const pageErrors = await this.page.evaluate(() => { return { scriptErrors: window.__errorDetection?.scriptErrors || [], moduleConflicts: window.__errorDetection?.moduleConflicts || [], htmlErrors: window.__errorDetection?.htmlErrors || [], events: window.__errorDetectionEvents || [] }; }); // Merge page errors with our collected errors if (pageErrors.scriptErrors.length > 0) { this.errors.scriptErrors.push(...pageErrors.scriptErrors); } if (pageErrors.moduleConflicts.length > 0) { this.errors.moduleConflicts.push(...pageErrors.moduleConflicts); } if (pageErrors.htmlErrors.length > 0) { this.errors.htmlErrors.push(...pageErrors.htmlErrors); } } // Generate final suggestions this.generateSuggestions(); return this.errors; } generateSuggestions() { // Clear existing suggestions this.errors.suggestions = []; // Module conflict suggestions if (this.errors.moduleConflicts.length > 0) { const hasParentModuleType = this.errors.moduleConflicts.some(c => c.suggestion.includes('parent package.json')); if (hasParentModuleType) { this.errors.suggestions.push('1. Check parent package.json for "type": "module" - this affects all .js files', '2. Use .mjs extension for ES modules or .cjs for CommonJS files', '3. Or remove "type": "module" from package.json to use CommonJS by default'); } } // HTML error suggestions if (this.errors.htmlErrors.some(e => e.type === 'malformed_tag' && e.element === 'script')) { this.errors.suggestions.push('Fix malformed script tags: Change <script /> to <script></script>'); } // Script loading error suggestions if (this.errors.scriptErrors.some(e => e.type === 'network')) { this.errors.suggestions.push('Check that all script URLs are correct and accessible', 'Verify CORS settings if loading scripts from different domains'); } // Runtime error suggestions const runtimeErrors = this.errors.scriptErrors.filter(e => e.type === 'runtime'); if (runtimeErrors.length > 5) { this.errors.suggestions.push('Multiple runtime errors detected - check for missing dependencies or initialization order issues'); } } async validateScriptTags() { if (!this.page) return []; return await this.page.evaluate(() => { const errors = []; const scripts = document.getElementsByTagName('script'); Array.from(scripts).forEach((script, index) => { // Get the original HTML to check for self-closing syntax const htmlString = script.outerHTML; // Check for self-closing script tags if (htmlString.endsWith('/>')) { errors.push({ type: 'malformed_tag', element: 'script', line: index + 1, // Approximate line number suggestion: 'Script tags must not be self-closing. Change <script .../> to <script ...></script>', code: htmlString }); } // Check for empty src with self-closing if (!script.src && !script.textContent?.trim() && htmlString.includes('/>')) { errors.push({ type: 'malformed_tag', element: 'script', line: index + 1, suggestion: 'Empty script tags should use <script></script> syntax, not <script />', code: htmlString }); } }); return errors; }); } } //# sourceMappingURL=javascript-error-detection-engine.js.map