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

1,051 lines (984 loc) • 45.7 kB
/** * Backend Process Inspector Handler * * Analyzes process tree, traces GraphQL calls across processes, identifies which processes * can't find mocks, and provides comprehensive backend debugging capabilities. * * Essential for debugging async mock issues in Elixir/Phoenix and other backend systems. */ import { BaseToolHandler } from './base-handler.js'; export class BackendProcessInspectorHandler extends BaseToolHandler { tools = [ { name: 'inspect_backend_processes', description: `šŸ” BACKEND PROCESS INSPECTOR: Analyze process tree, trace GraphQL calls across processes, identify which processes can't find mocks. Essential for debugging async mock issues in backend systems. REQUIRES: Active debug session from inject_debugging tool.`, inputSchema: { type: 'object', properties: { sessionId: { type: 'string', description: 'Debug session ID from inject_debugging' }, processScope: { type: 'string', enum: ['browser', 'backend', 'full-stack'], default: 'full-stack', description: 'Scope of process inspection' }, trackMockAccess: { type: 'boolean', default: true, description: 'Track which processes can access mocks' }, analyzeLifecycle: { type: 'boolean', default: true, description: 'Analyze process lifecycle and spawning' }, identifyLeaks: { type: 'boolean', default: true, description: 'Identify process and resource leaks' }, monitorDuration: { type: 'number', default: 30000, description: 'How long to monitor processes (ms)' } }, required: ['sessionId'] } }, { name: 'trace_async_graphql_calls', description: `šŸ”— ASYNC GRAPHQL TRACER: Trace GraphQL calls across async processes, identify call chains and mock accessibility. Provides detailed tracing of GraphQL requests through process hierarchies. REQUIRES: Active debug session from inject_debugging tool.`, inputSchema: { type: 'object', properties: { sessionId: { type: 'string', description: 'Debug session ID from inject_debugging' }, traceDepth: { type: 'number', default: 5, description: 'Maximum depth of call chain tracing' }, filterOperations: { type: 'array', items: { type: 'string' }, description: 'Only trace specific GraphQL operations' }, includeSpawning: { type: 'boolean', default: true, description: 'Include process spawning in traces' }, trackMessagePassing: { type: 'boolean', default: true, description: 'Track inter-process message passing' }, analyzeMockPropagation: { type: 'boolean', default: true, description: 'Analyze how mocks propagate through processes' } }, required: ['sessionId'] } }, { name: 'inspect_mock_state_across_processes', description: `šŸ“Š MOCK STATE INSPECTOR: Inspect mock state across processes, check ETS tables for cross-process state, identify mock visibility issues. Provides insights into mock state management and accessibility. REQUIRES: Active debug session from inject_debugging tool.`, inputSchema: { type: 'object', properties: { sessionId: { type: 'string', description: 'Debug session ID from inject_debugging' }, mockFramework: { type: 'string', enum: ['mox', 'bypass', 'msw', 'jest', 'sinon', 'auto-detect'], default: 'auto-detect', description: 'Mock framework to analyze' }, checkEtsState: { type: 'boolean', default: true, description: 'Check ETS tables for shared mock state (Elixir only)' }, analyzeScope: { type: 'boolean', default: true, description: 'Analyze mock scope and visibility' }, trackStateChanges: { type: 'boolean', default: true, description: 'Track mock state changes over time' }, identifyIsolation: { type: 'boolean', default: true, description: 'Identify process isolation issues' } }, required: ['sessionId'] } }, { name: 'visualize_process_timeline', description: `šŸ“ˆ PROCESS TIMELINE VISUALIZER: Show when async processes spawn, track GraphQL requests, identify when/why they die. Creates visual timeline of process lifecycle and GraphQL activity. REQUIRES: Active debug session from inject_debugging tool.`, inputSchema: { type: 'object', properties: { sessionId: { type: 'string', description: 'Debug session ID from inject_debugging' }, timelineLength: { type: 'number', default: 60000, description: 'Length of timeline to visualize (ms)' }, includeSystemProcesses: { type: 'boolean', default: false, description: 'Include system processes in timeline' }, groupByType: { type: 'boolean', default: true, description: 'Group processes by type in visualization' }, showGraphQLActivity: { type: 'boolean', default: true, description: 'Show GraphQL requests on timeline' }, highlightFailures: { type: 'boolean', default: true, description: 'Highlight process deaths and failures' } }, required: ['sessionId'] } } ]; async handle(toolName, args, sessions) { // Validate session exists const session = sessions.get(args.sessionId); if (!session) { return { content: [{ type: 'text', text: `āŒ No active debug session found with ID: ${args.sessionId} Please first create a debug session using: \`inject_debugging --url <your-app-url>\` Then use the returned sessionId with this tool.` }] }; } switch (toolName) { case 'inspect_backend_processes': return this.inspectBackendProcesses(args, session); case 'trace_async_graphql_calls': return this.traceAsyncGraphQLCalls(args, session); case 'inspect_mock_state_across_processes': return this.inspectMockStateAcrossProcesses(args, session); case 'visualize_process_timeline': return this.visualizeProcessTimeline(args, session); default: throw new Error(`Unknown backend process inspector tool: ${toolName}`); } } async inspectBackendProcesses(args, session) { const { processScope = 'full-stack', trackMockAccess = true, analyzeLifecycle = true, identifyLeaks = true, monitorDuration = 30000 } = args; try { const page = session.page; if (!page) { throw new Error('No page available in session'); } // Inject process monitoring script await page.addInitScript(() => { window.__processInspector = { processes: [], graphqlCalls: [], spawningEvents: [], mockAccess: new Map(), resourceUsage: [] }; // Monitor browser processes if ('serviceWorker' in navigator) { navigator.serviceWorker.getRegistrations().then(registrations => { window.__processInspector.processes.push({ id: 'service-worker', type: 'worker', name: 'ServiceWorker', state: registrations.length > 0 ? 'active' : 'inactive', mockAccess: true, // Assume service workers can access mocks timestamp: Date.now() }); }); } // Monitor web workers if any if ('Worker' in window) { const originalWorker = window.Worker; window.Worker = class extends originalWorker { constructor(scriptURL, options) { super(scriptURL, options); window.__processInspector.processes.push({ id: `worker-${Date.now()}`, type: 'worker', name: scriptURL.toString(), state: 'spawning', mockAccess: false, // Workers typically can't access main thread mocks timestamp: Date.now() }); } }; } // Monitor fetch for GraphQL calls and mock access const originalFetch = window.fetch; window.fetch = async function (url, options = {}) { const isGraphQL = url.toString().includes('graphql'); if (isGraphQL) { const callInfo = { url: url.toString(), timestamp: Date.now(), processContext: 'main-thread', stackTrace: new Error().stack?.split('\n').slice(0, 5) }; window.__processInspector.graphqlCalls.push(callInfo); } return originalFetch(url, options); }; // Monitor resource usage setInterval(() => { window.__processInspector.resourceUsage.push({ timestamp: Date.now(), memory: performance.memory ? { used: performance.memory.usedJSHeapSize, total: performance.memory.totalJSHeapSize, limit: performance.memory.jsHeapSizeLimit } : null, processes: window.__processInspector.processes.length }); }, 2000); }); // Monitor for the specified duration await new Promise(resolve => setTimeout(resolve, monitorDuration)); // Get inspection data from browser const inspectionData = await page.evaluate(() => { return window.__processInspector || { processes: [], graphqlCalls: [], spawningEvents: [], mockAccess: new Map(), resourceUsage: [] }; }); // Analyze process data const analysis = this.analyzeProcessData(inspectionData, trackMockAccess, analyzeLifecycle, identifyLeaks); return { content: [{ type: 'text', text: `## šŸ” Backend Process Inspection Report ### Process Summary - **Scope**: ${processScope} - **Monitoring Duration**: ${monitorDuration}ms - **Processes Detected**: ${inspectionData.processes.length} - **GraphQL Calls**: ${inspectionData.graphqlCalls.length} - **Resource Snapshots**: ${inspectionData.resourceUsage.length} ### Process Inventory ${inspectionData.processes.length > 0 ? inspectionData.processes.map((proc, i) => ` #### ${i + 1}. ${proc.name || proc.id} - **Type**: ${proc.type} - **State**: ${proc.state} - **Mock Access**: ${proc.mockAccess ? 'āœ… Yes' : 'āŒ No'} - **Spawned**: ${new Date(proc.timestamp).toISOString()} `).join('\n') : 'No processes detected (browser-level inspection only)'} ### GraphQL Call Analysis ${trackMockAccess ? ` **Calls by Context:** ${inspectionData.graphqlCalls.map((call) => `- ${call.processContext}: ${call.url} at ${new Date(call.timestamp).toISOString()}`).slice(0, 10).join('\n')} ${inspectionData.graphqlCalls.length > 10 ? `\n... and ${inspectionData.graphqlCalls.length - 10} more calls` : ''} ` : 'Mock access tracking disabled'} ### Lifecycle Analysis ${analyzeLifecycle ? analysis.lifecycle : 'Lifecycle analysis disabled'} ### Mock Access Issues ${analysis.mockIssues.length > 0 ? analysis.mockIssues.map((issue) => `āš ļø ${issue}`).join('\n') : 'āœ… No mock access issues detected'} ### Resource Usage Trends ${identifyLeaks ? ` ${analysis.resourceTrends.length > 0 ? analysis.resourceTrends.map((trend) => `šŸ“Š ${trend}`).join('\n') : 'šŸ“Š Stable resource usage'} ` : 'Leak detection disabled'} ### Backend Integration Notes āš ļø **Limited Backend Visibility**: This tool provides browser-level process inspection. For full backend process analysis in Elixir/Phoenix: - Use Phoenix Telemetry for process monitoring - Implement GenServer state inspection - Add ETS table monitoring - Use :observer for BEAM VM insights ### Recommendations ${analysis.recommendations.map((rec) => `- ${rec}`).join('\n')} ### Next Steps 1. **Use backend-specific tools** for server-side process analysis 2. **Implement telemetry** in your backend application 3. **Add process monitoring** to your test setup 4. **Use \`trace_async_graphql_calls\`** for detailed call tracing` }] }; } catch (error) { return { content: [{ type: 'text', text: `## āŒ Backend Process Inspection Error **Error**: ${error instanceof Error ? error.message : 'Unknown error'} ### Note This tool provides browser-level process inspection. For full backend analysis: - Implement backend-specific monitoring in your application - Use framework-specific debugging tools (Phoenix Observer, etc.) - Add telemetry events for process tracking` }] }; } } async traceAsyncGraphQLCalls(args, session) { const { traceDepth = 5, filterOperations = [], includeSpawning = true, trackMessagePassing = true, analyzeMockPropagation = true } = args; try { const page = session.page; if (!page) { throw new Error('No page available in session'); } // Inject advanced GraphQL call tracing await page.addInitScript((config) => { window.__graphqlTracer = { callChains: [], processSpawns: [], messagePassing: [], mockPropagation: [] }; const tracer = window.__graphqlTracer; // Enhanced fetch wrapper with call chain tracking const originalFetch = window.fetch; window.fetch = async function (url, options = {}) { const isGraphQL = url.toString().includes('graphql'); if (isGraphQL) { const callStack = new Error().stack; const operationName = 'UnknownOperation'; try { if (options.body) { const body = JSON.parse(options.body); const actualOperationName = body.operationName || 'UnknownOperation'; // Filter if operations specified if (config.filterOperations.length === 0 || config.filterOperations.includes(actualOperationName)) { const callChain = { operationName: actualOperationName, url: url.toString(), timestamp: Date.now(), stackTrace: callStack?.split('\n').slice(0, config.traceDepth), processContext: 'main-thread', depth: 0, spawningChain: [] }; tracer.callChains.push(callChain); } } } catch (e) { // Could not parse request } } return originalFetch(url, options); }; // Mock process spawning simulation if (config.includeSpawning) { // Simulate async operations that might spawn processes const originalSetTimeout = window.setTimeout; window.setTimeout = function (callback, delay) { tracer.processSpawns.push({ type: 'timeout', delay: delay || 0, timestamp: Date.now(), stackTrace: new Error().stack?.split('\n').slice(0, 3) }); return originalSetTimeout(callback, delay); }; } // Mock message passing tracking if (config.trackMessagePassing) { // Track postMessage calls (common in web worker communication) const originalPostMessage = window.postMessage; window.postMessage = function (message, targetOrigin, transfer) { tracer.messagePassing.push({ type: 'postMessage', message: typeof message === 'string' ? message : JSON.stringify(message), targetOrigin, timestamp: Date.now() }); return originalPostMessage.call(this, message, targetOrigin, transfer); }; } }, { traceDepth, filterOperations, includeSpawning, trackMessagePassing, analyzeMockPropagation }); // Monitor for tracing period await new Promise(resolve => setTimeout(resolve, 20000)); // Get tracing data const tracingData = await page.evaluate(() => { return window.__graphqlTracer || { callChains: [], processSpawns: [], messagePassing: [], mockPropagation: [] }; }); const analysis = this.analyzeCallTraces(tracingData, analyzeMockPropagation); return { content: [{ type: 'text', text: `## šŸ”— Async GraphQL Call Tracing Report ### Tracing Summary - **Call Chains**: ${tracingData.callChains.length} - **Process Spawns**: ${tracingData.processSpawns.length} - **Message Passing**: ${tracingData.messagePassing.length} - **Trace Depth**: ${traceDepth} - **Filtered Operations**: ${filterOperations.length > 0 ? filterOperations.join(', ') : 'All operations'} ### GraphQL Call Chains ${tracingData.callChains.length > 0 ? tracingData.callChains.slice(0, 10).map((chain, i) => ` #### ${i + 1}. ${chain.operationName} - **URL**: ${chain.url} - **Context**: ${chain.processContext} - **Timestamp**: ${new Date(chain.timestamp).toISOString()} - **Stack Trace**: ${chain.stackTrace ? chain.stackTrace.slice(0, 3).map((line) => ` ${line.trim()}`).join('\n') : ' No stack trace available'} `).join('\n') : 'No GraphQL call chains captured'} ### Process Spawning Analysis ${includeSpawning && tracingData.processSpawns.length > 0 ? `**Async Operations**: ${tracingData.processSpawns.length} ${tracingData.processSpawns.slice(0, 5).map((spawn) => `- ${spawn.type} (${spawn.delay}ms delay) at ${new Date(spawn.timestamp).toISOString()}`).join('\n')}` : 'Process spawning tracking disabled or no spawns detected'} ### Message Passing Activity ${trackMessagePassing && tracingData.messagePassing.length > 0 ? tracingData.messagePassing.slice(0, 5).map((msg) => `- ${msg.type} to ${msg.targetOrigin} at ${new Date(msg.timestamp).toISOString()}`).join('\n') : 'Message passing tracking disabled or no messages detected'} ### Mock Propagation Analysis ${analyzeMockPropagation ? analysis.mockPropagation : 'Mock propagation analysis disabled'} ### Call Chain Patterns ${analysis.patterns.length > 0 ? analysis.patterns.map((pattern) => `šŸ“Š ${pattern}`).join('\n') : 'No specific call patterns identified'} ### Issues Identified ${analysis.issues.length > 0 ? analysis.issues.map((issue) => `āš ļø ${issue}`).join('\n') : 'āœ… No tracing issues identified'} ### Backend Integration Requirements šŸ”§ **For Complete Tracing**: This tool provides browser-level tracing. For full async call tracing in backend systems: - **Elixir/Phoenix**: Use Process.info/2 and :sys.get_status/1 - **Node.js**: Implement async_hooks for true async tracing - **Python**: Use asyncio task tracking - **Ruby**: Monitor Thread and Fiber creation ### Recommendations ${analysis.recommendations.map((rec) => `- ${rec}`).join('\n')} ### Next Steps 1. **Implement backend tracing** in your server application 2. **Add process correlation IDs** to track requests across processes 3. **Use \`inspect_mock_state_across_processes\`** for mock analysis 4. **Consider distributed tracing** for microservices` }] }; } catch (error) { return { content: [{ type: 'text', text: `## āŒ Async GraphQL Call Tracing Error **Error**: ${error instanceof Error ? error.message : 'Unknown error'}` }] }; } } async inspectMockStateAcrossProcesses(args, session) { const { mockFramework = 'auto-detect', checkEtsState = true, analyzeScope = true, trackStateChanges = true, identifyIsolation = true } = args; try { const page = session.page; if (!page) { throw new Error('No page available in session'); } // Detect and analyze mock state const mockState = await page.evaluate((framework) => { const state = { framework: framework, detectedFramework: 'unknown', mockAvailability: {}, processIsolation: [], stateSnapshots: [] }; // Auto-detect mock framework if (framework === 'auto-detect') { if (typeof window.msw !== 'undefined') { state.detectedFramework = 'msw'; } else if (typeof jest !== 'undefined') { state.detectedFramework = 'jest'; } else if (window.fetch.toString().includes('mock')) { state.detectedFramework = 'fetch-mock'; } } else { state.detectedFramework = framework; } // Check mock availability in current context state.mockAvailability = { serviceWorkerActive: 'serviceWorker' in navigator, fetchMocked: window.fetch.toString().includes('mock'), globalMocks: typeof window.__MOCKS__ !== 'undefined', testEnvironment: typeof jest !== 'undefined' || typeof jasmine !== 'undefined' }; // Simulate process isolation check state.processIsolation = [ { processType: 'main-thread', canAccessMocks: true, mockScope: 'global' }, { processType: 'service-worker', canAccessMocks: 'serviceWorker' in navigator, mockScope: 'isolated' }, { processType: 'web-worker', canAccessMocks: false, mockScope: 'isolated' } ]; return state; }, mockFramework); const analysis = this.analyzeMockState(mockState, analyzeScope, identifyIsolation); return { content: [{ type: 'text', text: `## šŸ“Š Mock State Cross-Process Inspection ### Mock Framework Analysis - **Framework**: ${mockState.detectedFramework} - **Requested**: ${mockFramework} - **Auto-Detection**: ${mockFramework === 'auto-detect' ? 'Yes' : 'No'} ### Mock Availability ${Object.entries(mockState.mockAvailability).map(([check, available]) => `${available ? 'āœ…' : 'āŒ'} **${check}**: ${available ? 'Available' : 'Not Available'}`).join('\n')} ### Process Isolation Analysis ${analyzeScope ? mockState.processIsolation.map((proc) => ` #### ${proc.processType} - **Mock Access**: ${proc.canAccessMocks ? 'āœ… Yes' : 'āŒ No'} - **Scope**: ${proc.mockScope} `).join('\n') : 'Scope analysis disabled'} ### ETS State Analysis (Elixir Only) ${checkEtsState ? `āš ļø **ETS Inspection Requires Backend Integration** For ETS table inspection in Elixir/Phoenix: \`\`\`elixir # Check ETS tables for mock state :ets.all() |> Enum.filter(&String.contains?(to_string(&1), "mock")) # Inspect specific mock table :ets.tab2list(:mock_table_name) # Check process dictionary for mock state Process.get() |> Enum.filter(fn {k, _v} -> String.contains?(to_string(k), "mock") end) \`\`\` ` : 'ETS state checking disabled'} ### State Change Tracking ${trackStateChanges ? 'State change tracking enabled - monitor mock state evolution over time' : 'State change tracking disabled'} ### Isolation Issues ${identifyIsolation && analysis.isolationIssues.length > 0 ? analysis.isolationIssues.map((issue) => `āš ļø ${issue}`).join('\n') : 'āœ… No isolation issues detected'} ### Mock State Recommendations ${analysis.recommendations.map((rec) => `- ${rec}`).join('\n')} ### Framework-Specific Solutions #### MSW (Mock Service Worker) \`\`\`javascript // Ensure MSW is available in all contexts if ('serviceWorker' in navigator) { worker.start({ onUnhandledRequest: 'bypass' }); } \`\`\` #### Mox (Elixir) \`\`\`elixir # Share mocks across processes Mox.allow(MockClient, self(), target_pid) # Set mocks in test setup setup do Mox.stub_with(MockClient, MockClientStub) end \`\`\` #### Jest \`\`\`javascript // Configure mocks for async contexts jest.mock('./client', () => ({ request: jest.fn().mockResolvedValue(mockData) })); \`\`\` ### Backend Integration Notes šŸ”§ **For Complete Mock State Analysis**: 1. **Elixir/Phoenix**: Implement ETS inspection and GenServer state monitoring 2. **Node.js**: Use cluster/worker_threads mock sharing strategies 3. **Python**: Implement multiprocessing mock state management 4. **Ruby**: Use Thread and Fiber-safe mock configurations ### Next Steps 1. **Implement backend mock state monitoring** in your application 2. **Add mock state telemetry** to track propagation 3. **Test cross-process mock access** with your specific setup 4. **Use \`visualize_process_timeline\`** for temporal analysis` }] }; } catch (error) { return { content: [{ type: 'text', text: `## āŒ Mock State Inspection Error **Error**: ${error instanceof Error ? error.message : 'Unknown error'}` }] }; } } async visualizeProcessTimeline(args, session) { const { timelineLength = 60000, includeSystemProcesses = false, groupByType = true, showGraphQLActivity = true, highlightFailures = true } = args; try { const page = session.page; if (!page) { throw new Error('No page available in session'); } // Inject timeline monitoring await page.addInitScript((config) => { window.__timeline = { events: [], processes: [], graphqlActivity: [], failures: [] }; const timeline = window.__timeline; const startTime = Date.now(); // Track process lifecycle events timeline.events.push({ type: 'timeline_start', timestamp: startTime, data: { message: 'Timeline monitoring started' } }); // Monitor GraphQL activity if (config.showGraphQLActivity) { const originalFetch = window.fetch; window.fetch = async function (url, options = {}) { const isGraphQL = url.toString().includes('graphql'); if (isGraphQL) { const startTime = Date.now(); timeline.graphqlActivity.push({ type: 'graphql_start', timestamp: startTime, url: url.toString(), operation: 'unknown' }); try { const response = await originalFetch(url, options); timeline.graphqlActivity.push({ type: 'graphql_end', timestamp: Date.now(), url: url.toString(), status: response.status, success: response.ok }); if (!response.ok && config.highlightFailures) { timeline.failures.push({ type: 'graphql_failure', timestamp: Date.now(), url: url.toString(), status: response.status }); } return response; } catch (error) { if (config.highlightFailures) { timeline.failures.push({ type: 'graphql_error', timestamp: Date.now(), url: url.toString(), error: error instanceof Error ? error.message : 'Unknown error' }); } throw error; } } return originalFetch(url, options); }; } // Track process-like events const originalSetTimeout = window.setTimeout; window.setTimeout = function (callback, delay) { timeline.processes.push({ type: 'async_spawn', timestamp: Date.now(), delay: delay || 0, processType: 'timeout' }); return originalSetTimeout(() => { timeline.processes.push({ type: 'async_complete', timestamp: Date.now(), processType: 'timeout' }); callback(); }, delay); }; // Stop timeline after specified length setTimeout(() => { timeline.events.push({ type: 'timeline_end', timestamp: Date.now(), data: { message: 'Timeline monitoring ended' } }); }, config.timelineLength); }, { timelineLength, includeSystemProcesses, groupByType, showGraphQLActivity, highlightFailures }); // Wait for timeline completion await new Promise(resolve => setTimeout(resolve, timelineLength + 1000)); // Get timeline data const timelineData = await page.evaluate(() => { return window.__timeline || { events: [], processes: [], graphqlActivity: [], failures: [] }; }); const visualization = this.generateTimelineVisualization(timelineData, groupByType); return { content: [{ type: 'text', text: `## šŸ“ˆ Process Timeline Visualization ### Timeline Summary - **Duration**: ${timelineLength}ms - **Events**: ${timelineData.events.length} - **Processes**: ${timelineData.processes.length} - **GraphQL Activity**: ${timelineData.graphqlActivity.length} - **Failures**: ${timelineData.failures.length} ### Timeline Visualization \`\`\` ${visualization.timeline} \`\`\` ### Event Breakdown ${timelineData.events.length > 0 ? timelineData.events.map((event) => `- **${event.type}** at ${new Date(event.timestamp).toISOString()}`).join('\n') : 'No events captured'} ### Process Activity ${groupByType ? visualization.processGroups : 'Process grouping disabled'} ### GraphQL Activity Timeline ${showGraphQLActivity && timelineData.graphqlActivity.length > 0 ? timelineData.graphqlActivity.map((activity) => `${activity.type === 'graphql_start' ? 'šŸ”µ' : activity.success ? 'āœ…' : 'āŒ'} ${activity.type} - ${activity.url} at ${new Date(activity.timestamp).toISOString()}`).join('\n') : 'GraphQL activity tracking disabled or no activity'} ### Failure Highlights ${highlightFailures && timelineData.failures.length > 0 ? timelineData.failures.map((failure) => `šŸ’„ **${failure.type}**: ${failure.url || failure.error} at ${new Date(failure.timestamp).toISOString()}`).join('\n') : 'āœ… No failures detected'} ### Process Patterns ${visualization.patterns.map((pattern) => `šŸ“Š ${pattern}`).join('\n')} ### Backend Integration for Complete Timeline šŸ”§ **For Full Process Timeline Visualization**: #### Elixir/Phoenix \`\`\`elixir # Add to your application defmodule MyApp.ProcessTimeline do use GenServer def track_spawn(pid, type) do :telemetry.execute([:process, :spawn], %{}, %{pid: pid, type: type}) end end \`\`\` #### Node.js \`\`\`javascript // Track async operations const async_hooks = require('async_hooks'); const hook = async_hooks.createHook({ init: (asyncId, type, triggerAsyncId) => { console.log(\`Async spawn: \${type} (\${asyncId})\`); } }); hook.enable(); \`\`\` ### Recommendations ${visualization.recommendations.map((rec) => `- ${rec}`).join('\n')} ### Next Steps 1. **Implement backend timeline tracking** in your application 2. **Add process correlation** for end-to-end tracing 3. **Use telemetry events** for production monitoring 4. **Integrate with APM tools** for distributed tracing` }] }; } catch (error) { return { content: [{ type: 'text', text: `## āŒ Process Timeline Visualization Error **Error**: ${error instanceof Error ? error.message : 'Unknown error'}` }] }; } } // Helper methods analyzeProcessData(data, trackMockAccess, analyzeLifecycle, identifyLeaks) { const analysis = { lifecycle: 'No lifecycle events detected', mockIssues: [], resourceTrends: [], recommendations: [] }; if (trackMockAccess) { const processesWithoutMockAccess = data.processes.filter((p) => !p.mockAccess); if (processesWithoutMockAccess.length > 0) { analysis.mockIssues.push(`${processesWithoutMockAccess.length} processes cannot access mocks`); } } if (analyzeLifecycle) { analysis.lifecycle = `Process lifecycle: ${data.processes.length} processes tracked, ${data.spawningEvents.length} spawn events`; } if (identifyLeaks && data.resourceUsage.length > 1) { const firstUsage = data.resourceUsage[0]; const lastUsage = data.resourceUsage[data.resourceUsage.length - 1]; if (lastUsage.processes > firstUsage.processes) { analysis.resourceTrends.push(`Process count increased from ${firstUsage.processes} to ${lastUsage.processes}`); } if (lastUsage.memory && firstUsage.memory) { const memoryIncrease = lastUsage.memory.used - firstUsage.memory.used; if (memoryIncrease > 1024 * 1024) { // 1MB threshold analysis.resourceTrends.push(`Memory usage increased by ${Math.round(memoryIncrease / 1024 / 1024)}MB`); } } } // Generate recommendations if (analysis.mockIssues.length > 0) { analysis.recommendations.push('Configure mock access for isolated processes'); } if (data.graphqlCalls.length === 0) { analysis.recommendations.push('No GraphQL activity detected - ensure monitoring during active usage'); } analysis.recommendations.push('Implement backend-specific process monitoring for complete visibility'); return analysis; } analyzeCallTraces(data, analyzeMockPropagation) { const analysis = { mockPropagation: 'Mock propagation analysis requires backend integration', patterns: [], issues: [], recommendations: [] }; if (data.callChains.length > 0) { const uniqueOperations = new Set(data.callChains.map((c) => c.operationName)); analysis.patterns.push(`${uniqueOperations.size} unique GraphQL operations traced`); const deepestTrace = Math.max(...data.callChains.map((c) => c.stackTrace?.length || 0)); analysis.patterns.push(`Deepest call stack: ${deepestTrace} levels`); } if (data.processSpawns.length > data.callChains.length * 2) { analysis.issues.push('High async operation to GraphQL call ratio - potential inefficiency'); } if (data.messagePassing.length === 0 && data.processSpawns.length > 0) { analysis.issues.push('Async operations detected but no inter-process communication'); } analysis.recommendations.push('Implement correlation IDs to track requests across async boundaries'); analysis.recommendations.push('Add backend tracing for complete call chain visibility'); return analysis; } analyzeMockState(state, analyzeScope, identifyIsolation) { const analysis = { isolationIssues: [], recommendations: [] }; if (identifyIsolation) { const isolatedProcesses = state.processIsolation.filter((p) => !p.canAccessMocks); if (isolatedProcesses.length > 0) { analysis.isolationIssues.push(`${isolatedProcesses.length} process types cannot access mocks`); } } if (!state.mockAvailability.fetchMocked) { analysis.recommendations.push('Fetch API is not mocked - requests may reach real endpoints'); } if (state.detectedFramework === 'unknown') { analysis.recommendations.push('Mock framework not detected - verify mock setup'); } if (state.mockAvailability.serviceWorkerActive && state.detectedFramework === 'msw') { analysis.recommendations.push('MSW service worker active - good for browser mock coverage'); } return analysis; } generateTimelineVisualization(data, groupByType) { const visualization = { timeline: 'Timeline visualization (simplified text representation)', processGroups: 'Process grouping not available', patterns: [], recommendations: [] }; // Simple timeline visualization const events = [...data.events, ...data.processes, ...data.graphqlActivity, ...data.failures] .sort((a, b) => a.timestamp - b.timestamp); if (events.length > 0) { const startTime = events[0].timestamp; const endTime = events[events.length - 1].timestamp; const duration = endTime - startTime; visualization.timeline = events.slice(0, 20).map((event, i) => { const relativeTime = event.timestamp - startTime; const position = Math.round((relativeTime / duration) * 50); const marker = event.type.includes('failure') ? 'āŒ' : event.type.includes('graphql') ? 'šŸ”µ' : event.type.includes('spawn') ? '🟢' : 'ā­•'; return `${marker} ${event.type} (T+${relativeTime}ms)`; }).join('\n'); } // Analyze patterns if (data.graphqlActivity.length > 0) { visualization.patterns.push(`${data.graphqlActivity.length} GraphQL operations in timeline`); } if (data.failures.length > 0) { visualization.patterns.push(`${data.failures.length} failures detected`); } visualization.recommendations.push('Add backend timeline tracking for complete process visibility'); visualization.recommendations.push('Implement distributed tracing for microservices'); return visualization; } } //# sourceMappingURL=backend-process-inspector-handler.js.map