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
254 lines • 10.8 kB
JavaScript
/**
* Live Svelte Debugger - Specialized tool for Phoenix LiveView + Svelte debugging
*
* Handles the unique challenges of debugging live_svelte components
*/
export class LiveSvelteDebugger {
page = null;
async attachToPage(page) {
this.page = page;
// Inject debugging helpers
await page.evaluate(() => {
// Create global debug helper
window.__liveSvelteDebug = {
components: new Map(),
hooks: new Map(),
events: [],
// Track Svelte component lifecycle
trackComponent: function (name, component) {
this.components.set(name, {
component,
props: component.$$.props,
state: component.$$.ctx,
timestamp: Date.now()
});
},
// Track LiveView hooks
trackHook: function (el, hook) {
const hookName = el.getAttribute('phx-hook');
this.hooks.set(hookName, {
element: el,
hook,
mounted: hook.mounted ? true : false,
updated: hook.updated ? true : false
});
},
// Track events between LiveView and Svelte
trackEvent: function (type, data) {
this.events.push({
type,
data,
timestamp: Date.now(),
stack: new Error().stack
});
}
};
// Override console methods to capture errors
const originalError = console.error;
const originalWarn = console.warn;
const debugErrors = [];
console.error = function (...args) {
debugErrors.push({
type: 'error',
message: args.join(' '),
timestamp: Date.now(),
stack: new Error().stack
});
originalError.apply(console, args);
};
console.warn = function (...args) {
debugErrors.push({
type: 'warning',
message: args.join(' '),
timestamp: Date.now(),
stack: new Error().stack
});
originalWarn.apply(console, args);
};
window.__liveSvelteDebug.errors = debugErrors;
});
}
async getDebugInfo() {
if (!this.page) {
throw new Error('No page attached');
}
const debugData = await this.page.evaluate(() => {
const debug = window.__liveSvelteDebug || {};
// Get LiveView state
const liveViewState = (() => {
try {
const liveSocket = window.liveSocket;
if (!liveSocket)
return null;
// Get all LiveView components
const views = [];
const viewEls = document.querySelectorAll('[data-phx-view]');
viewEls.forEach((el) => {
const viewId = el.getAttribute('data-phx-view');
const session = el.getAttribute('data-phx-session');
const main = el.getAttribute('data-phx-main') === 'true';
views.push({
id: viewId,
session,
main,
element: el.tagName,
hooks: Array.from(el.querySelectorAll('[phx-hook]')).map((hookEl) => ({
hook: hookEl.getAttribute('phx-hook'),
element: hookEl.tagName
}))
});
});
return {
connected: liveSocket.isConnected(),
views,
transport: liveSocket.transport?.name || 'unknown'
};
}
catch (e) {
return { error: e instanceof Error ? e.message : String(e) };
}
})();
// Get Svelte components
const svelteComponents = (() => {
const components = [];
// From our debug tracking
if (debug.components) {
debug.components.forEach((data, name) => {
components.push({
name,
props: data.props,
state: data.state,
timestamp: data.timestamp
});
});
}
// Also check for live_svelte containers
document.querySelectorAll('[phx-hook="LiveSvelte"]').forEach((el) => {
const componentName = el.getAttribute('data-name') || 'Unknown';
const props = el.getAttribute('data-props');
components.push({
name: componentName,
element: el.tagName,
props: props ? JSON.parse(props) : {},
container: true
});
});
return components;
})();
// Get hooks info
const hooks = (() => {
const hooksList = [];
if (debug.hooks) {
debug.hooks.forEach((data, name) => {
hooksList.push({
name,
mounted: data.mounted,
updated: data.updated,
element: data.element?.tagName
});
});
}
return hooksList;
})();
// Get props passed from LiveView to Svelte
const props = (() => {
const propsData = {};
document.querySelectorAll('[data-props]').forEach((el) => {
const name = el.getAttribute('data-name') || el.id || 'unknown';
const props = el.getAttribute('data-props');
if (props) {
try {
propsData[name] = JSON.parse(props);
}
catch (e) {
propsData[name] = { error: 'Failed to parse props', raw: props };
}
}
});
return propsData;
})();
// Get tracked events
const events = debug.events || [];
// Get console errors/warnings
const consoleErrors = debug.errors || [];
return {
liveViewState,
svelteComponents,
hooks,
props,
events,
consoleErrors
};
});
return debugData;
}
async debugLiveSvelteIssue() {
const info = await this.getDebugInfo();
const issues = [];
const suggestions = [];
let mainIssue = 'No specific issue detected';
const details = {};
// Check for LiveView connection issues
if (!info.liveViewState?.connected) {
mainIssue = 'LiveView is not connected';
issues.push('LiveView WebSocket connection is down');
suggestions.push('Check if Phoenix server is running');
suggestions.push('Verify WebSocket endpoint configuration');
suggestions.push('Check browser console for connection errors');
}
// Check for Svelte component issues
if (info.svelteComponents.length === 0) {
issues.push('No Svelte components detected');
suggestions.push('Ensure live_svelte is properly installed');
suggestions.push('Check if LiveView is rendering the live_component correctly');
suggestions.push('Verify Svelte component files are being compiled');
}
// Check for prop passing issues
const emptyProps = Object.entries(info.props).filter(([_, props]) => Object.keys(props).length === 0);
if (emptyProps.length > 0) {
issues.push(`${emptyProps.length} components have no props`);
suggestions.push('Verify props are being passed from LiveView: assigns |> Map.take([:your_prop])');
suggestions.push('Check live_svelte component call syntax');
}
// Check for console errors
if (info.consoleErrors.length > 0) {
mainIssue = 'JavaScript errors detected';
issues.push(`${info.consoleErrors.length} console errors found`);
details.errors = info.consoleErrors;
// Analyze common error patterns
info.consoleErrors.forEach(error => {
if (error.message.includes('live is not defined')) {
suggestions.push('The "live" prop might not be passed to Svelte component');
suggestions.push('Ensure LiveView is passing the socket binding');
}
if (error.message.includes('Cannot read properties of undefined')) {
suggestions.push('Check if all required props are being passed');
suggestions.push('Add prop validation in Svelte component');
}
});
}
// Check for hook issues
const unmountedHooks = info.hooks.filter(h => !h.mounted);
if (unmountedHooks.length > 0) {
issues.push(`${unmountedHooks.length} hooks failed to mount`);
suggestions.push('Check if hooks are registered in app.js');
suggestions.push('Verify hook implementation has mounted() method');
}
if (issues.length === 0) {
mainIssue = 'Components detected but may have internal issues';
suggestions.push('Check Svelte component internal logic');
suggestions.push('Add console.log statements to trace prop flow');
suggestions.push('Verify event handlers are properly bound');
}
return {
issue: mainIssue,
details: {
...details,
allIssues: issues,
debugInfo: info
},
suggestions
};
}
}
//# sourceMappingURL=live-svelte-debugger.js.map