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