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

517 lines 25.1 kB
/** * Unified Framework Detector * * Consolidates all framework detection logic from: * - V1 FrameworkDetector (local-debug-engine/framework-detector.ts) * - V2 FrameworkDetector (v2/utils/framework-detector.ts) * - Enhanced FrameworkDetector (v2/utils/enhanced-framework-detector.ts) * * This unified detector ensures consistent framework detection across all tools. */ export class UnifiedFrameworkDetector { /** * Main detection method that combines all detection strategies */ async detectFramework(page) { try { // Run comprehensive detection in the browser const detectionResult = await page.evaluate(() => { const features = []; let framework = 'unknown'; let confidence = 0; let version; let primaryFramework; let secondaryFramework; let isHybrid = false; // ==== PHOENIX LIVEVIEW DETECTION (Enhanced) ==== const liveViewIndicators = { liveSocket: !!window.liveSocket, phoenixLiveView: !!window.Phoenix?.LiveView, phxHooks: !!document.querySelector('[phx-hook]'), phxEvents: !!(document.querySelector('[phx-click]') || document.querySelector('[phx-submit]') || document.querySelector('[phx-change]') || document.querySelector('[phx-keyup]') || document.querySelector('[phx-keydown]') || document.querySelector('[phx-blur]') || document.querySelector('[phx-focus]') || document.querySelector('[phx-window-keyup]') || document.querySelector('[phx-window-keydown]')), phxMain: !!document.querySelector('div[data-phx-main]'), phxSession: !!document.querySelector('[data-phx-session]'), phxStatic: !!document.querySelector('[data-phx-static]'), phxLink: !!document.querySelector('[data-phx-link]'), phxId: !!document.querySelector('[data-phx-id]'), liveViewScript: !!document.querySelector('script[src*="phoenix_live_view"]'), csrfToken: !!document.querySelector('meta[name="csrf-token"]'), phoenixChannel: !!window.Phoenix?.Channel, phoenixSocket: !!window.Phoenix?.Socket, // Check HTML source for LiveView patterns htmlMarkers: document.documentElement.innerHTML.includes('phx-') || document.documentElement.innerHTML.includes('Phoenix.LiveView') || document.documentElement.innerHTML.includes('liveSocket') }; const liveViewCount = Object.values(liveViewIndicators).filter(Boolean).length; if (liveViewCount >= 2) { features.push('phoenix', 'liveview', 'websockets', 'server-rendering'); framework = 'phoenix-liveview'; primaryFramework = 'phoenix-liveview'; confidence = Math.min(0.85 + (liveViewCount * 0.015), 0.99); // Log for debugging console.log('[Unified Framework Detector] LiveView indicators:', liveViewIndicators); console.log('[Unified Framework Detector] LiveView confidence:', confidence); } // ==== LIVE_SVELTE DETECTION (Enhanced) ==== const liveSvelteIndicators = { // Direct live_svelte markers liveSvelteGlobal: !!window.LiveSvelte || !!window.live_svelte, liveSvelteData: !!document.querySelector('[data-live-svelte]'), liveSvelteHook: !!document.querySelector('[phx-hook="LiveSvelte"]') || !!document.querySelector('[phx-hook*="Svelte"]'), // Container patterns liveSvelteContainer: !!document.querySelector('.live-svelte-component') || !!document.querySelector('[data-svelte-component]') || !!document.querySelector('[data-live-svelte-component]'), // Script analysis liveSvelteScript: Array.from(document.querySelectorAll('script')).some(script => script.innerHTML.includes('LiveSvelte') || script.innerHTML.includes('live_svelte') || script.src?.includes('live_svelte')), // Svelte components in Phoenix hooks phoenixHookWithSvelte: Array.from(document.querySelectorAll('[phx-hook]')).some(el => { const hookName = el.getAttribute('phx-hook') || ''; return hookName.toLowerCase().includes('svelte'); }) }; const liveSvelteCount = Object.values(liveSvelteIndicators).filter(Boolean).length; const hasLiveSvelte = liveSvelteCount >= 1; // ==== SVELTE DETECTION (Comprehensive) ==== const svelteIndicators = { // Svelte 3/4 internals svelteGlobal: !!window.__svelte, svelteInternals: !!window.$$, // DOM markers svelteData: !!document.querySelector('[data-svelte]'), svelteClasses: !!document.querySelector('[class*="svelte-"]'), // Svelte store patterns svelteStores: document.documentElement.innerHTML.includes('$$invalidate'), // Svelte event syntax svelteEvents: document.documentElement.innerHTML.includes('on:') || document.documentElement.innerHTML.includes('bind:'), // Svelte template syntax svelteTemplates: document.documentElement.innerHTML.includes('{#if') || document.documentElement.innerHTML.includes('{#each') || document.documentElement.innerHTML.includes('{@html'), // Svelte imports in scripts svelteImports: Array.from(document.querySelectorAll('script')).some(script => script.innerHTML.includes('svelte') || script.src?.includes('svelte')) }; const svelteCount = Object.values(svelteIndicators).filter(Boolean).length; const hasSvelte = svelteCount >= 1 || hasLiveSvelte; // Handle LiveView + Svelte hybrid detection if (framework === 'phoenix-liveview' && (hasSvelte || hasLiveSvelte)) { if (hasLiveSvelte) { // Specific live_svelte integration framework = 'phoenix-liveview-svelte'; features.push('live_svelte', 'svelte-components', 'reactive-ui', 'liveview-integration', 'hybrid-framework'); secondaryFramework = 'live_svelte'; isHybrid = true; confidence = 0.95; console.log('[Unified Framework Detector] live_svelte integration detected!'); console.log('[Unified Framework Detector] live_svelte indicators:', liveSvelteIndicators); } else { // Generic Svelte with LiveView framework = 'phoenix-liveview-svelte'; features.push('svelte-components', 'reactive-ui', 'hybrid-framework'); secondaryFramework = 'svelte'; isHybrid = true; confidence = 0.9; } } else if (!framework.includes('phoenix') && hasSvelte) { // Standalone Svelte framework = 'svelte'; features.push('svelte', 'reactive-ui', 'compiled-components'); primaryFramework = 'svelte'; confidence = 0.8; } // ==== FLUTTER WEB DETECTION ==== if (window.flutterCanvasKit || document.querySelector('flutter-view') || document.querySelector('[flt-renderer]') || document.querySelector('flt-scene') || document.querySelector('flt-glass-pane')) { features.push('flutter', 'dart', 'canvas-rendering', 'flutter-web'); framework = 'flutter-web'; primaryFramework = 'flutter-web'; confidence = 0.95; } // ==== REACT DETECTION (Including Next.js) ==== if (framework === 'unknown' || framework === 'svelte') { const reactIndicators = { react: !!window.React, reactDevTools: !!window.__REACT_DEVTOOLS_GLOBAL_HOOK__, reactRoot: !!document.querySelector('[data-reactroot]'), reactHelmet: !!document.querySelector('[data-react-helmet]'), reactFiber: !!window.__REACT_FIBER_DEVTOOLS_HOOK__ }; const hasReact = Object.values(reactIndicators).filter(Boolean).length >= 1; if (hasReact) { features.push('react', 'virtual-dom', 'component-based'); framework = 'react'; primaryFramework = 'react'; confidence = 0.8; // Check for Next.js if (window.__NEXT_DATA__ || document.querySelector('#__next')) { features.push('nextjs', 'ssr', 'file-based-routing'); framework = 'nextjs'; primaryFramework = 'nextjs'; confidence = 0.9; version = window.__NEXT_DATA__?.buildId || 'unknown'; } } } // ==== VUE DETECTION ==== if (framework === 'unknown') { const vueIndicators = { vue: !!window.Vue, vue3: !!window.__VUE__, vueDevtools: !!window.__VUE_DEVTOOLS_GLOBAL_HOOK__, vueData: !!document.querySelector('[data-v-]'), vueCloak: !!document.querySelector('[v-cloak]') }; if (Object.values(vueIndicators).filter(Boolean).length >= 1) { features.push('vue', 'reactive-data', 'template-based'); framework = 'vue'; primaryFramework = 'vue'; confidence = 0.8; // Check for Nuxt if (window.__NUXT__ || document.querySelector('#__nuxt')) { features.push('nuxt', 'ssr', 'vue-meta-framework'); framework = 'nuxt'; confidence = 0.85; } } } // ==== ANGULAR DETECTION ==== if (framework === 'unknown') { if (window.ng || document.querySelector('[ng-app]') || document.querySelector('[ng-version]') || document.querySelector('app-root')) { features.push('angular', 'typescript', 'dependency-injection'); framework = 'angular'; primaryFramework = 'angular'; confidence = 0.8; } } // ==== META-FRAMEWORK DETECTION ==== if (document.querySelector('[data-astro-cid]') || window.__astro) { features.push('astro', 'islands-architecture', 'partial-hydration'); if (framework === 'unknown') { framework = 'astro'; confidence = 0.85; } else { isHybrid = true; secondaryFramework = 'astro'; } } if (window.__remixContext || window.__remixManifest) { features.push('remix', 'nested-routing', 'progressive-enhancement'); if (framework === 'unknown') { framework = 'remix'; confidence = 0.85; } else { isHybrid = true; secondaryFramework = 'remix'; } } if (window.__qwik__ || document.querySelector('[q\\:container]')) { features.push('qwik', 'resumability', 'lazy-loading'); if (framework === 'unknown') { framework = 'qwik'; confidence = 0.85; } } if (window.__sveltekit || document.querySelector('[data-sveltekit]')) { features.push('sveltekit', 'file-based-routing', 'ssr'); framework = 'sveltekit'; confidence = 0.85; } // ==== RAILS/DJANGO DETECTION ==== // Check for Rails (Turbo/Stimulus) if (window.Turbo || document.querySelector('[data-turbo]')) { features.push('rails', 'turbo', 'server-driven'); if (framework === 'unknown') { framework = 'rails'; confidence = 0.7; } } if (window.Stimulus || document.querySelector('[data-controller]')) { features.push('stimulus', 'html-over-the-wire'); if (!features.includes('rails')) { features.push('rails'); } } // Check for Django const csrfToken = document.querySelector('input[name="csrfmiddlewaretoken"]'); if (csrfToken && framework === 'unknown') { features.push('django', 'python-backend'); framework = 'django'; confidence = 0.6; } // ==== ADDITIONAL FEATURE DETECTION ==== // WebSocket connections if (window.WebSocket && performance.getEntriesByType('resource').some(r => r.name.includes('ws://'))) { features.push('websockets'); } // Server-sent events if (window.EventSource) { features.push('server-sent-events'); } // Progressive Web App if ('serviceWorker' in navigator) { features.push('pwa-capable'); } // GraphQL if (document.documentElement.innerHTML.includes('__typename') || performance.getEntriesByType('resource').some(r => r.name.includes('graphql'))) { features.push('graphql'); } // Default to vanilla if nothing detected if (framework === 'unknown') { framework = 'vanilla'; confidence = 0.5; features.push('vanilla-js'); } return { framework, confidence, features: [...new Set(features)], // Remove duplicates version, isHybrid, primaryFramework: primaryFramework || framework, secondaryFramework }; }); console.error(`🔍 [Unified Framework Detector] Detected: ${detectionResult.framework} (${Math.round(detectionResult.confidence * 100)}% confidence)`); console.error(`📋 [Unified Framework Detector] Features: ${detectionResult.features.join(', ')}`); if (detectionResult.isHybrid) { console.error(`🎯 [Unified Framework Detector] Hybrid framework: ${detectionResult.primaryFramework} + ${detectionResult.secondaryFramework}`); } return detectionResult; } catch (error) { console.warn('⚠️ [Unified Framework Detector] Detection failed:', error); return { framework: 'unknown', confidence: 0, features: ['detection-failed'], version: undefined, isHybrid: false }; } } /** * Quick framework detection from URL patterns (for early optimization) */ detectFromUrl(url) { const hostname = new URL(url).hostname.toLowerCase(); const pathname = new URL(url).pathname.toLowerCase(); // Hosting patterns if (hostname.includes('vercel') || hostname.includes('netlify')) { return { framework: 'nextjs', confidence: 0.6, features: ['hosting-pattern', 'jamstack'], version: undefined }; } if (hostname.includes('herokuapp') || hostname.includes('gigalixir')) { return { framework: 'phoenix', confidence: 0.6, features: ['hosting-pattern', 'elixir-hosting'], version: undefined }; } // Domain patterns if (hostname.includes('flutter') || hostname.includes('dart')) { return { framework: 'flutter-web', confidence: 0.7, features: ['domain-pattern', 'flutter-domain'], version: undefined }; } if (hostname.includes('phoenix') || hostname.includes('elixir') || hostname.includes('liveview')) { return { framework: 'phoenix-liveview', confidence: 0.7, features: ['domain-pattern', 'phoenix-domain'], version: undefined }; } // Path patterns if (pathname.includes('/app') || pathname.includes('/_next')) { return { framework: 'nextjs', confidence: 0.5, features: ['path-pattern', 'nextjs-paths'], version: undefined }; } return { framework: 'unknown', confidence: 0, features: ['no-url-patterns'], version: undefined }; } /** * Detect framework from HTTP headers */ detectFromHeaders(headers) { const serverHeader = headers['server']?.toLowerCase() || ''; const poweredBy = headers['x-powered-by']?.toLowerCase() || ''; // Phoenix/Elixir if (serverHeader.includes('phoenix') || poweredBy.includes('phoenix') || serverHeader.includes('cowboy')) { return { framework: 'phoenix', confidence: 0.8, features: ['server-header', 'elixir-backend'], version: undefined }; } // Next.js if (serverHeader.includes('next') || poweredBy.includes('next') || headers['x-nextjs-cache']) { return { framework: 'nextjs', confidence: 0.7, features: ['server-header', 'nextjs-backend'], version: undefined }; } // Express/Node.js if (poweredBy.includes('express')) { return { framework: 'express', confidence: 0.6, features: ['server-header', 'nodejs-backend'], version: undefined }; } return { framework: 'unknown', confidence: 0, features: ['no-header-patterns'], version: undefined }; } /** * Merge multiple detection results for best accuracy */ mergeDetectionResults(urlResult, pageResult, headerResult) { // Page-based detection is most reliable if (pageResult.confidence > 0.5) { // Enhance with URL/header features if available const mergedFeatures = [...pageResult.features]; if (urlResult.confidence > 0.5) { mergedFeatures.push(...urlResult.features); } if (headerResult && headerResult.confidence > 0.5) { mergedFeatures.push(...headerResult.features); } return { ...pageResult, features: [...new Set(mergedFeatures)], confidence: Math.min(pageResult.confidence + 0.05, 0.99) // Slight boost for multiple confirmations }; } // Fall back to header-based if available if (headerResult && headerResult.confidence > 0.5) { return headerResult; } // Fall back to URL-based if page detection failed if (urlResult.confidence > 0.5) { return urlResult; } // Return the one with highest confidence const results = [urlResult, pageResult]; if (headerResult) results.push(headerResult); return results.reduce((best, current) => current.confidence > best.confidence ? current : best); } /** * Get framework-specific debugging recommendations */ getFrameworkRecommendations(framework) { const recommendations = { 'phoenix-liveview-svelte': [ 'Use LiveSvelteDebugger for integrated LiveView + Svelte debugging', 'Monitor WebSocket connections for LiveView state changes', 'Track Svelte component lifecycle within LiveView hooks', 'Use debug_liveview_svelte for comprehensive analysis', 'Check live_svelte hook initialization and data flow' ], 'phoenix-liveview': [ 'Use debug_liveview for LiveView-specific debugging', 'Monitor WebSocket connections and Phoenix channels', 'Track server-side state changes and client updates', 'Use analyze_liveview_lifecycle for event flow', 'Check CSRF tokens and session management' ], 'flutter-web': [ 'Use Flutter-specific debugging tools', 'Monitor canvas rendering performance', 'Track widget tree changes', 'Use getFlutterWidgetTree for component inspection', 'Check for Flutter DevTools connection' ], 'nextjs': [ 'Use Next.js-specific debugging tools', 'Monitor server-side rendering and hydration', 'Track API routes and data fetching', 'Use analyzeNextJSServerClientFlow for SSR debugging', 'Check build optimization and bundle size' ], 'svelte': [ 'Monitor Svelte store updates and reactivity', 'Track component lifecycle and state changes', 'Use browser DevTools with Svelte extension', 'Check compiled output for optimization', 'Monitor reactive statements and bindings' ], 'react': [ 'Use React DevTools for component inspection', 'Monitor virtual DOM updates and reconciliation', 'Track state and props changes', 'Use getReactComponentTree for analysis', 'Check for unnecessary re-renders' ], 'vue': [ 'Use Vue DevTools for component inspection', 'Monitor reactive data changes', 'Track computed properties and watchers', 'Check template compilation', 'Monitor Vuex state if applicable' ] }; return recommendations[framework] || [ 'Use general debugging tools', 'Monitor console errors and warnings', 'Track network requests and responses', 'Check performance metrics', 'Use browser DevTools for inspection' ]; } } // Export singleton instance for consistent usage export const unifiedFrameworkDetector = new UnifiedFrameworkDetector(); //# sourceMappingURL=unified-framework-detector.js.map