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
274 lines • 11.9 kB
JavaScript
import { FlutterDebugEngine } from '../flutter-debug-engine.js';
import { FlutterDebugEngineEnhanced } from '../flutter-debug-engine-enhanced-modular.js';
import { NextJSDebugEngine } from '../nextjs-debug-engine.js';
import { NextJSDebugEngineEnhanced } from '../nextjs-debug-engine-enhanced-modular.js';
import { ServerFrameworkDebugEngine } from '../server-framework-debug-engine.js';
import { MetaFrameworkDebugEngine } from '../meta-framework-debug-engine-modular.js';
import { StateCapture } from './state-capture.js';
export class FrameworkDetector {
reactStateEngine;
jsExecutionEngine;
stateCapture;
page;
metaFrameworkEngine;
nextjsEngine;
nextjsEnhancedEngine;
serverFrameworkEngine;
flutterEngine;
flutterEnhancedEngine;
constructor(reactStateEngine, jsExecutionEngine, stateCapture) {
this.reactStateEngine = reactStateEngine;
this.jsExecutionEngine = jsExecutionEngine;
this.stateCapture = stateCapture;
}
/**
* Detect the framework used by the current page and initialize appropriate engines
*/
async detectFramework(page) {
this.page = page;
// Check for meta-frameworks first (they might also be React/Vue/etc)
this.metaFrameworkEngine = new MetaFrameworkDebugEngine();
await this.metaFrameworkEngine.attachToPage(page);
const metaFrameworkInfo = await this.metaFrameworkEngine.getMetaFrameworkInfo();
if (metaFrameworkInfo?.framework) {
return {
framework: metaFrameworkInfo.framework,
engines: { metaFrameworkEngine: this.metaFrameworkEngine }
};
}
// Check for Next.js (if not detected by meta-framework engine)
this.nextjsEngine = new NextJSDebugEngine();
const isNextJS = await this.nextjsEngine.detectNextJS(page);
if (isNextJS) {
await this.nextjsEngine.attachToPage(page);
// Also initialize enhanced engine
this.nextjsEnhancedEngine = new NextJSDebugEngineEnhanced();
await this.nextjsEnhancedEngine.attachToPage(page);
return {
framework: 'nextjs',
engines: {
nextjsEngine: this.nextjsEngine,
nextjsEnhancedEngine: this.nextjsEnhancedEngine
}
};
}
// Check for server frameworks
this.serverFrameworkEngine = new ServerFrameworkDebugEngine();
const serverFramework = await this.serverFrameworkEngine.detectServerFramework(page);
if (serverFramework) {
await this.serverFrameworkEngine.attachToPage(page);
return {
framework: serverFramework,
engines: { serverFrameworkEngine: this.serverFrameworkEngine }
};
}
// Check for Flutter Web
this.flutterEngine = new FlutterDebugEngine();
const isFlutter = await this.flutterEngine.detectFlutterWeb(page);
if (isFlutter) {
// Try to connect to Flutter DevTools
const port = await this.flutterEngine.findDebugPort(page);
if (port) {
await this.flutterEngine.connect(port);
}
// Also initialize enhanced engine for comprehensive Flutter debugging
this.flutterEnhancedEngine = new FlutterDebugEngineEnhanced();
await this.flutterEnhancedEngine.attachToPage(page);
// Update StateCapture with Flutter engine
const updatedStateCapture = new StateCapture(this.reactStateEngine, this.jsExecutionEngine, this.flutterEnhancedEngine);
await updatedStateCapture.attachToPage(page);
return {
framework: 'flutter-web',
engines: {
flutterEngine: this.flutterEngine,
flutterEnhancedEngine: this.flutterEnhancedEngine
}
};
}
// Check for Elixir/Phoenix applications (comprehensive detection)
const elixirDetection = await this.detectElixirFramework(page);
if (elixirDetection.framework) {
return {
framework: elixirDetection.framework,
engines: elixirDetection.engines || {}
};
}
// Check for React
const reactComponents = await this.findReactComponents();
if (reactComponents.length > 0) {
return {
framework: 'react',
engines: {}
};
}
// Check for Vue
const hasVue = await page.evaluate(() => {
return !!window.Vue || document.querySelector('[data-v-]');
});
if (hasVue) {
return {
framework: 'vue',
engines: {}
};
}
// Check for Angular
const hasAngular = await page.evaluate(() => {
return !!window.ng || document.querySelector('[ng-version]');
});
if (hasAngular) {
return {
framework: 'angular',
engines: {}
};
}
// Check for Svelte
const hasSvelte = await page.evaluate(() => {
return document.querySelector('[data-svelte]') !== null;
});
if (hasSvelte) {
return {
framework: 'svelte',
engines: {}
};
}
return {
framework: 'unknown',
engines: {}
};
}
/**
* Get initialized engines for access by LocalDebugEngine
*/
getEngines() {
return {
metaFrameworkEngine: this.metaFrameworkEngine,
nextjsEngine: this.nextjsEngine,
nextjsEnhancedEngine: this.nextjsEnhancedEngine,
serverFrameworkEngine: this.serverFrameworkEngine,
flutterEngine: this.flutterEngine,
flutterEnhancedEngine: this.flutterEnhancedEngine
};
}
/**
* Helper method to find Phoenix LiveView state
*/
async findPhoenixLiveViewState() {
if (!this.page) {
throw new Error('No page attached');
}
return await this.stateCapture.findPhoenixLiveViewState();
}
/**
* Helper method to find React components
*/
async findReactComponents() {
if (!this.page) {
throw new Error('No page attached');
}
return await this.stateCapture.findReactComponents();
}
/**
* Comprehensive Elixir framework detection
* Detects Phoenix LiveView, traditional Phoenix, pure Elixir, and Nerves
*/
async detectElixirFramework(page) {
try {
// Check for Phoenix LiveView (highest priority)
const hasLiveView = await page.evaluate(() => {
// Look for LiveView JavaScript
return !!(window.liveSocket ||
window.Phoenix?.LiveView ||
document.querySelector('[phx-hook]') ||
document.querySelector('[data-phx-link]') ||
document.querySelector('[phx-click]') ||
document.querySelector('[phx-submit]') ||
document.querySelector('div[data-phx-main]') ||
document.querySelector('script[src*="phoenix_live_view"]'));
});
if (hasLiveView) {
return { framework: 'phoenix' }; // Phoenix LiveView
}
// Check for traditional Phoenix (without LiveView)
const hasPhoenix = await page.evaluate(() => {
return !!(window.Phoenix ||
document.querySelector('script[src*="phoenix"]') ||
document.querySelector('meta[name="csrf-token"]') ||
document.querySelector('input[name="_csrf_token"]') ||
// Check for Phoenix channel syntax in script tags
document.documentElement.innerHTML.includes('channel.join()') ||
document.documentElement.innerHTML.includes('socket.connect()'));
});
if (hasPhoenix) {
return { framework: 'phoenix-traditional' };
}
// Check for pure Elixir by examining the URL and response headers
const response = await page.goto(page.url(), { waitUntil: 'domcontentloaded' });
const headers = response?.headers() || {};
// Look for Elixir/Erlang server signatures
const serverHeader = headers['server'] || '';
const hasElixirSignature = serverHeader.includes('Cowboy') ||
serverHeader.includes('Plug') ||
serverHeader.includes('Phoenix') ||
headers['x-powered-by']?.includes('Elixir') ||
headers['x-frame-options'] === 'SAMEORIGIN'; // Common Phoenix default
// Check for Nerves/embedded Elixir indicators
const userAgent = headers['user-agent'] || '';
const hasNervesIndicators = await page.evaluate(() => {
return !!(
// Nerves-specific meta tags or content
document.querySelector('meta[name="nerves"]') ||
document.querySelector('meta[name="embedded-elixir"]') ||
// GPIO or hardware control interfaces
document.documentElement.innerHTML.includes('GPIO') ||
document.documentElement.innerHTML.includes('hardware') ||
document.documentElement.innerHTML.includes('sensor') ||
// Embedded system patterns
window.location.hostname.match(/^\d+\.\d+\.\d+\.\d+$/) || // IP address
window.location.hostname.includes('.local') // mDNS
);
});
if (hasNervesIndicators || userAgent.includes('Nerves')) {
return { framework: 'nerves' };
}
// Check for pure Elixir API endpoints or Elixir-specific patterns
const hasPureElixir = await page.evaluate(() => {
// Look for Elixir/OTP patterns in JSON responses or error messages
const bodyText = document.body?.textContent || '';
return !!(bodyText.includes('GenServer') ||
bodyText.includes('Supervisor') ||
bodyText.includes('Process') ||
bodyText.includes('OTP') ||
// Check for Elixir-style error messages
bodyText.includes('** (') ||
bodyText.includes('no function clause matching') ||
// Phoenix API patterns (but not full Phoenix)
document.querySelector('script[type="application/json"]'));
});
if (hasElixirSignature || hasPureElixir) {
// Determine if it's API-only Elixir or has some web interface
const hasWebInterface = await page.evaluate(() => {
return (document.querySelector('html')?.innerHTML.length || 0) > 100;
});
if (hasWebInterface) {
return { framework: 'elixir' }; // Pure Elixir with web interface
}
else {
return { framework: 'elixir' }; // Pure Elixir API
}
}
}
catch (error) {
console.warn('Failed to detect Elixir framework:', error);
}
return { framework: null };
}
/**
* Legacy method for compatibility - engines are attached during detection
*/
async attachFrameworkEngines(page) {
// This is called after framework detection in detectFramework
// Engines are already attached there
this.page = page;
}
}
//# sourceMappingURL=framework-detector.js.map