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
JavaScript
/**
* 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