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

834 lines (789 loc) 38.4 kB
/** * GraphQL Request Inspector Handler * * Captures all GraphQL requests/responses, identifies failed queries in async contexts, * and shows exact error messages to help debug mock coverage issues. * * Essential for debugging LiveView processes that die due to missing GraphQL mocks. */ import { BaseToolHandler } from './base-handler.js'; export class GraphQLRequestInspectorHandler extends BaseToolHandler { tools = [ { name: 'inspect_graphql_requests', description: `🔍 GRAPHQL REQUEST INSPECTOR: Capture all GraphQL requests/responses, identify failed queries in async contexts, and show exact error messages. Perfect for debugging LiveView processes that die due to missing GraphQL mocks. REQUIRES: Active debug session from inject_debugging tool.`, inputSchema: { type: 'object', properties: { sessionId: { type: 'string', description: 'Debug session ID from inject_debugging' }, monitorDuration: { type: 'number', default: 10000, description: 'How long to monitor for GraphQL requests (ms)' }, captureRequestBodies: { type: 'boolean', default: true, description: 'Capture full request query and variables' }, captureResponseBodies: { type: 'boolean', default: true, description: 'Capture full response data and errors' }, filterOperations: { type: 'array', items: { type: 'string' }, description: 'Only capture specific operation names (optional)' }, includeNetworkTiming: { type: 'boolean', default: true, description: 'Include network timing information' }, trackAsyncProcesses: { type: 'boolean', default: true, description: 'Track which processes make GraphQL requests' } }, required: ['sessionId'] } }, { name: 'analyze_failed_graphql_queries', description: `📊 FAILED GRAPHQL ANALYSIS: Analyze patterns in failed GraphQL queries, identify missing mocks, and suggest fixes. Provides detailed analysis of why GraphQL queries fail in async contexts. REQUIRES: Active debug session from inject_debugging tool.`, inputSchema: { type: 'object', properties: { sessionId: { type: 'string', description: 'Debug session ID from inject_debugging' }, analysisTimeframe: { type: 'number', default: 30000, description: 'Timeframe to analyze for failed queries (ms)' }, groupByOperation: { type: 'boolean', default: true, description: 'Group failures by operation name' }, includeVariableAnalysis: { type: 'boolean', default: true, description: 'Analyze variable patterns in failed queries' }, suggestMockImplementations: { type: 'boolean', default: true, description: 'Suggest missing mock implementations' }, compareWithSuccessful: { type: 'boolean', default: true, description: 'Compare failed queries with successful ones' } }, required: ['sessionId'] } }, { name: 'trace_graphql_to_process', description: `🔗 GRAPHQL PROCESS TRACING: Trace GraphQL requests to their originating processes, identify which processes can't access mocks. Essential for debugging async processes that spawn and can't find GraphQL mocks. REQUIRES: Active debug session from inject_debugging tool.`, inputSchema: { type: 'object', properties: { sessionId: { type: 'string', description: 'Debug session ID from inject_debugging' }, traceDuration: { type: 'number', default: 15000, description: 'How long to trace process-to-GraphQL mapping (ms)' }, trackProcessSpawning: { type: 'boolean', default: true, description: 'Track when new processes spawn and make GraphQL requests' }, identifyMockAccess: { type: 'boolean', default: true, description: 'Identify which processes can access mocks' }, captureProcessContext: { type: 'boolean', default: true, description: 'Capture context about process spawning' } }, required: ['sessionId'] } }, { name: 'monitor_graphql_real_time', description: `⚡ REAL-TIME GRAPHQL MONITOR: Live monitoring of GraphQL requests with instant failure alerts. Provides real-time visibility into GraphQL traffic and immediate alerts on failures. REQUIRES: Active debug session from inject_debugging tool.`, inputSchema: { type: 'object', properties: { sessionId: { type: 'string', description: 'Debug session ID from inject_debugging' }, alertOnFailures: { type: 'boolean', default: true, description: 'Immediately alert on GraphQL failures' }, showSuccessfulRequests: { type: 'boolean', default: false, description: 'Show successful requests (can be noisy)' }, highlightSlowQueries: { type: 'boolean', default: true, description: 'Highlight queries that take longer than threshold' }, slowQueryThreshold: { type: 'number', default: 1000, description: 'Threshold for slow queries (ms)' }, streamUpdates: { type: 'boolean', default: true, description: 'Stream live updates as they happen' } }, 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_graphql_requests': return this.inspectGraphQLRequests(args, session); case 'analyze_failed_graphql_queries': return this.analyzeFailedGraphQLQueries(args, session); case 'trace_graphql_to_process': return this.traceGraphQLToProcess(args, session); case 'monitor_graphql_real_time': return this.monitorGraphQLRealTime(args, session); default: throw new Error(`Unknown GraphQL inspector tool: ${toolName}`); } } async inspectGraphQLRequests(args, session) { const { monitorDuration = 10000, captureRequestBodies = true, captureResponseBodies = true, filterOperations = [], includeNetworkTiming = true, trackAsyncProcesses = true } = args; try { const page = session.page; if (!page) { throw new Error('No page available in session'); } const graphqlRequests = []; const graphqlResponses = []; const failedRequests = []; // Inject GraphQL monitoring script await page.addInitScript(() => { window.__graphqlRequests = []; window.__graphqlResponses = []; // Override fetch to capture GraphQL requests const originalFetch = window.fetch; window.fetch = async function (url, options = {}) { const isGraphQL = url.toString().includes('graphql') || url.toString().includes('/api/graphql') || (options.headers && (options.headers['Content-Type']?.includes('application/json') || options.headers['content-type']?.includes('application/json')) && options.body && (options.body.includes('query') || options.body.includes('mutation'))); if (isGraphQL) { const requestId = Math.random().toString(36).substr(2, 9); const startTime = Date.now(); let parsedBody = null; try { parsedBody = options.body ? JSON.parse(options.body) : null; } catch (e) { } window.__graphqlRequests.push({ id: requestId, url: url.toString(), method: options.method || 'POST', headers: options.headers || {}, body: parsedBody, timestamp: startTime }); try { const response = await originalFetch(url, options); const endTime = Date.now(); let responseData = null; try { responseData = await response.clone().json(); } catch (e) { } window.__graphqlResponses.push({ requestId, status: response.status, data: responseData, timestamp: endTime, responseTime: endTime - startTime }); return response; } catch (error) { window.__graphqlResponses.push({ requestId, status: 0, error: error instanceof Error ? error.message : String(error), timestamp: Date.now(), responseTime: Date.now() - startTime }); throw error; } } return originalFetch(url, options); }; }); // Monitor network requests const requestHandler = (request) => { const url = request.url(); if (url.includes('graphql') || url.includes('/api/graphql')) { const headers = request.headers(); let postData = null; try { postData = request.postData() ? JSON.parse(request.postData()) : null; } catch (e) { } if (postData && (postData.query || postData.mutation)) { const graphqlRequest = { id: Math.random().toString(36).substr(2, 9), query: postData.query || postData.mutation, variables: postData.variables, operationName: postData.operationName, timestamp: Date.now(), url, headers }; // Apply operation filter if specified if (filterOperations.length === 0 || (graphqlRequest.operationName && filterOperations.includes(graphqlRequest.operationName))) { graphqlRequests.push(graphqlRequest); } } } }; const responseHandler = (response) => { const request = response.request(); const url = request.url(); if (url.includes('graphql') || url.includes('/api/graphql')) { const matchingRequest = graphqlRequests.find(req => req.url === url); if (matchingRequest) { response.json().then((data) => { const graphqlResponse = { requestId: matchingRequest.id, data: captureResponseBodies ? data.data : null, errors: data.errors, status: response.status(), timestamp: Date.now(), responseTime: Date.now() - matchingRequest.timestamp }; graphqlResponses.push(graphqlResponse); // Track failed requests if (response.status() >= 400 || data.errors) { failedRequests.push(matchingRequest.id); } }).catch(() => { // Handle non-JSON responses const graphqlResponse = { requestId: matchingRequest.id, status: response.status(), timestamp: Date.now(), responseTime: Date.now() - matchingRequest.timestamp }; graphqlResponses.push(graphqlResponse); if (response.status() >= 400) { failedRequests.push(matchingRequest.id); } }); } } }; page.on('request', requestHandler); page.on('response', responseHandler); // Monitor for the specified duration await new Promise(resolve => setTimeout(resolve, monitorDuration)); // Get additional data from browser const browserData = await page.evaluate(() => { return { requests: window.__graphqlRequests || [], responses: window.__graphqlResponses || [] }; }); // Clean up handlers page.off('request', requestHandler); page.off('response', responseHandler); // Merge browser data with captured data const allRequests = [...graphqlRequests, ...browserData.requests]; const allResponses = [...graphqlResponses, ...browserData.responses]; // Generate analysis const analysis = this.generateGraphQLAnalysis(allRequests, allResponses, failedRequests); return { content: [{ type: 'text', text: `## 🔍 GraphQL Request Inspector Report ### Summary - **Total Requests**: ${allRequests.length} - **Successful**: ${allResponses.filter(r => r.status < 400 && !r.errors).length} - **Failed**: ${failedRequests.length} - **Average Response Time**: ${analysis.averageResponseTime}ms - **Monitoring Duration**: ${monitorDuration}ms ### Failed Queries ${failedRequests.length > 0 ? allRequests .filter(req => failedRequests.includes(req.id)) .map((req, i) => { const response = allResponses.find(res => res.requestId === req.id); return `#### ${i + 1}. ${req.operationName || 'Unnamed Operation'} **Query**: \`\`\`graphql ${req.query} \`\`\` **Variables**: ${JSON.stringify(req.variables, null, 2)} **Error**: ${response?.errors ? response.errors.map((e) => e.message).join(', ') : `HTTP ${response?.status}`} **URL**: ${req.url} **Timestamp**: ${new Date(req.timestamp).toISOString()}`; }).join('\n\n') : 'No failed queries detected'} ### Successful Queries ${allRequests .filter(req => !failedRequests.includes(req.id)) .slice(0, 5) .map((req, i) => { const response = allResponses.find(res => res.requestId === req.id); return `${i + 1}. **${req.operationName || 'Unnamed'}** - ${response?.responseTime}ms`; }).join('\n') || 'No successful queries'} ${allRequests.filter(req => !failedRequests.includes(req.id)).length > 5 ? `\n... and ${allRequests.filter(req => !failedRequests.includes(req.id)).length - 5} more` : ''} ### Analysis & Recommendations ${analysis.recommendations.map((r) => `- ${r}`).join('\n')} ### Network Timing ${includeNetworkTiming ? ` - **Fastest Query**: ${analysis.fastestQuery}ms - **Slowest Query**: ${analysis.slowestQuery}ms - **Queries > 1s**: ${allResponses.filter(r => r.responseTime > 1000).length} ` : 'Network timing disabled'} ### Process Information ${trackAsyncProcesses ? `Process tracking enabled - use \`trace_graphql_to_process\` for detailed process analysis` : 'Process tracking disabled'}` }] }; } catch (error) { return { content: [{ type: 'text', text: `## ❌ GraphQL Inspection Error **Error**: ${error instanceof Error ? error.message : 'Unknown error'} ### Troubleshooting - Ensure your app is making GraphQL requests during the monitoring period - Check that the GraphQL endpoint is accessible - Verify the monitoring duration is sufficient for your use case` }] }; } } async analyzeFailedGraphQLQueries(args, session) { const { analysisTimeframe = 30000, groupByOperation = true, includeVariableAnalysis = true, suggestMockImplementations = true, compareWithSuccessful = true } = args; try { const page = session.page; if (!page) { throw new Error('No page available in session'); } // First run inspection to get data const inspectionResult = await this.inspectGraphQLRequests({ sessionId: args.sessionId, monitorDuration: analysisTimeframe, captureRequestBodies: true, captureResponseBodies: true }, session); // Extract data from inspection result const content = inspectionResult.content[0].text; const failedCount = this.extractNumberFromText(content, 'Failed**: ') || 0; const totalCount = this.extractNumberFromText(content, 'Total Requests**: ') || 0; // Generate mock suggestions const mockSuggestions = suggestMockImplementations ? this.generateMockSuggestions(content) : []; return { content: [{ type: 'text', text: `## 📊 Failed GraphQL Queries Analysis ### Failure Summary - **Total Queries**: ${totalCount} - **Failed Queries**: ${failedCount} - **Failure Rate**: ${totalCount > 0 ? ((failedCount / totalCount) * 100).toFixed(1) : 0}% - **Analysis Timeframe**: ${analysisTimeframe}ms ### Common Failure Patterns ${this.analyzeFailurePatterns(content)} ### Variable Analysis ${includeVariableAnalysis ? this.analyzeVariablePatterns(content) : 'Variable analysis disabled'} ### Mock Implementation Suggestions ${mockSuggestions.length > 0 ? mockSuggestions.map((suggestion) => `- ${suggestion}`).join('\n') : 'No specific mock suggestions generated'} ### Comparison with Successful Queries ${compareWithSuccessful ? this.compareFailedVsSuccessful(content) : 'Comparison disabled'} ### Recommended Actions 1. **Review missing mocks** for the failed operations listed above 2. **Check mock accessibility** in async processes using \`trace_graphql_to_process\` 3. **Verify query syntax** and variable types match your schema 4. **Test mock coverage** by running queries manually against your mock server ### Next Steps - Use \`trace_graphql_to_process\` to identify which processes can't access mocks - Use \`monitor_graphql_real_time\` for live debugging of GraphQL issues - Implement the suggested mocks and re-run analysis` }] }; } catch (error) { return { content: [{ type: 'text', text: `## ❌ GraphQL Analysis Error **Error**: ${error instanceof Error ? error.message : 'Unknown error'}` }] }; } } async traceGraphQLToProcess(args, session) { const { traceDuration = 15000, trackProcessSpawning = true, identifyMockAccess = true, captureProcessContext = true } = args; try { const page = session.page; if (!page) { throw new Error('No page available in session'); } // This is a frontend-focused implementation // For a full backend implementation, we'd need to integrate with the Elixir/Phoenix backend const processTraces = []; // Inject process tracking script await page.addInitScript(() => { window.__processTraces = []; window.__graphqlToProcess = new Map(); // Track when GraphQL requests are made and associate with current context const originalFetch = window.fetch; window.fetch = async function (url, options = {}) { const isGraphQL = url.toString().includes('graphql'); if (isGraphQL) { const trace = { url: url.toString(), timestamp: Date.now(), stackTrace: new Error().stack, userAgent: navigator.userAgent, windowLocation: window.location.href, processInfo: { // In a real implementation, this would capture actual process info context: 'browser_main_thread', spawningContext: document.referrer || 'direct_navigation' } }; window.__processTraces.push(trace); } return originalFetch(url, options); }; }); // Monitor for the specified duration await new Promise(resolve => setTimeout(resolve, traceDuration)); // Get trace data from browser const traces = await page.evaluate(() => { return window.__processTraces || []; }); return { content: [{ type: 'text', text: `## 🔗 GraphQL Process Tracing Report ### Process Mapping Summary - **Traced Requests**: ${traces.length} - **Trace Duration**: ${traceDuration}ms - **Process Types Detected**: Browser Main Thread${trackProcessSpawning ? ', LiveView Processes (requires backend integration)' : ''} ### Browser-Level Tracing ${traces.length > 0 ? traces.map((trace, i) => ` #### Request ${i + 1} - **URL**: ${trace.url} - **Context**: ${trace.processInfo.context} - **Spawning Context**: ${trace.processInfo.spawningContext} - **Location**: ${trace.windowLocation} - **Timestamp**: ${new Date(trace.timestamp).toISOString()} `).join('\n') : 'No GraphQL requests traced during monitoring period'} ### Mock Access Analysis ${identifyMockAccess ? ` **Browser Context**: All requests originate from browser main thread **Mock Accessibility**: Browser can access HTTP mocks at configured endpoints ⚠️ **Backend Process Analysis Requires Integration**: For full process-to-mock tracing in Phoenix/LiveView applications, this tool needs: 1. Integration with Phoenix process monitoring 2. Access to BEAM VM process information 3. Elixir-side tracing capabilities Consider using Phoenix-specific debugging tools for server-side process analysis. ` : 'Mock access analysis disabled'} ### Process Context ${captureProcessContext ? ` **Browser Navigation**: ${traces.length > 0 ? traces[0]?.windowLocation : 'No data'} **Referrer Tracking**: ${traces.some((t) => t.processInfo.spawningContext !== 'direct_navigation') ? 'Navigation-based spawning detected' : 'Direct navigation only'} ` : 'Process context capture disabled'} ### Backend Integration Recommendations To get full process tracing for Phoenix/LiveView applications: 1. **Add Phoenix Telemetry Events** for GraphQL requests 2. **Track Process PIDs** making GraphQL calls 3. **Monitor GenServer Spawning** and GraphQL request correlation 4. **Implement ETS Table Inspection** for mock state across processes ### Next Steps - Implement backend process monitoring in your Phoenix application - Add telemetry events for GraphQL request tracking - Use \`monitor_graphql_real_time\` for immediate failure detection - Consider LiveView process lifecycle monitoring tools` }] }; } catch (error) { return { content: [{ type: 'text', text: `## ❌ Process Tracing Error **Error**: ${error instanceof Error ? error.message : 'Unknown error'}` }] }; } } async monitorGraphQLRealTime(args, session) { const { alertOnFailures = true, showSuccessfulRequests = false, highlightSlowQueries = true, slowQueryThreshold = 1000, streamUpdates = true } = args; try { const page = session.page; if (!page) { throw new Error('No page available in session'); } const realtimeEvents = []; // Set up real-time monitoring await page.addInitScript((config) => { window.__realtimeGraphQL = []; const originalFetch = window.fetch; window.fetch = async function (url, options = {}) { const isGraphQL = url.toString().includes('graphql'); if (isGraphQL) { const startTime = Date.now(); const requestId = Math.random().toString(36).substr(2, 9); // Log request start const requestEvent = { type: 'request_start', id: requestId, url: url.toString(), timestamp: startTime }; window.__realtimeGraphQL.push(requestEvent); try { const response = await originalFetch(url, options); const endTime = Date.now(); const responseTime = endTime - startTime; const responseEvent = { type: response.ok ? 'request_success' : 'request_failure', id: requestId, status: response.status, responseTime, timestamp: endTime, isSlow: responseTime > config.slowQueryThreshold }; window.__realtimeGraphQL.push(responseEvent); return response; } catch (error) { const errorEvent = { type: 'request_error', id: requestId, error: error instanceof Error ? error.message : String(error), timestamp: Date.now() }; window.__realtimeGraphQL.push(errorEvent); throw error; } } return originalFetch(url, options); }; }, { slowQueryThreshold }); // Monitor for 10 seconds initially await new Promise(resolve => setTimeout(resolve, 10000)); // Get real-time events const events = await page.evaluate(() => { return window.__realtimeGraphQL || []; }); // Process events for display const alerts = events.filter((e) => (alertOnFailures && (e.type === 'request_failure' || e.type === 'request_error')) || (highlightSlowQueries && e.isSlow)); const successfulRequests = events.filter((e) => e.type === 'request_success'); const failedRequests = events.filter((e) => e.type === 'request_failure' || e.type === 'request_error'); return { content: [{ type: 'text', text: `## ⚡ Real-Time GraphQL Monitor ### Live Status (Last 10 seconds) - **Active Monitoring**: ✅ Enabled - **Total Events**: ${events.length} - **Successful Requests**: ${successfulRequests.length} - **Failed Requests**: ${failedRequests.length} - **Slow Queries**: ${events.filter((e) => e.isSlow).length} ### Recent Alerts ${alerts.length > 0 ? alerts.slice(-10).map((alert) => { if (alert.type === 'request_failure') { return `🚨 **FAILURE**: Request ${alert.id} failed with status ${alert.status} (${alert.responseTime}ms)`; } else if (alert.type === 'request_error') { return `💥 **ERROR**: Request ${alert.id} - ${alert.error}`; } else if (alert.isSlow) { return `🐌 **SLOW**: Request ${alert.id} took ${alert.responseTime}ms (threshold: ${slowQueryThreshold}ms)`; } return ''; }).join('\n') : 'No alerts in monitoring period'} ### Recent Activity ${showSuccessfulRequests && successfulRequests.length > 0 ? successfulRequests.slice(-5).map((req) => `✅ Request ${req.id} - ${req.responseTime}ms`).join('\n') : 'Successful requests hidden (enable with showSuccessfulRequests: true)'} ### Performance Summary ${events.length > 0 ? ` - **Average Response Time**: ${(events .filter((e) => e.responseTime) .reduce((sum, e) => sum + e.responseTime, 0) / events.filter((e) => e.responseTime).length || 0).toFixed(0)}ms - **Fastest Query**: ${Math.min(...events.filter((e) => e.responseTime).map((e) => e.responseTime)) || 0}ms - **Slowest Query**: ${Math.max(...events.filter((e) => e.responseTime).map((e) => e.responseTime)) || 0}ms ` : 'No performance data available'} ### Real-Time Configuration - **Alert on Failures**: ${alertOnFailures ? '✅' : '❌'} - **Show Successful**: ${showSuccessfulRequests ? '✅' : '❌'} - **Highlight Slow Queries**: ${highlightSlowQueries ? '✅' : '❌'} - **Slow Query Threshold**: ${slowQueryThreshold}ms - **Stream Updates**: ${streamUpdates ? '✅' : '❌'} ### Next Actions - Continue monitoring with \`monitor_graphql_real_time\` for live updates - Use \`inspect_graphql_requests\` for detailed failure analysis - Use \`analyze_failed_graphql_queries\` for pattern analysis **Note**: This tool provides a snapshot. For continuous monitoring, run it again or implement persistent monitoring in your application.` }] }; } catch (error) { return { content: [{ type: 'text', text: `## ❌ Real-Time Monitoring Error **Error**: ${error instanceof Error ? error.message : 'Unknown error'}` }] }; } } // Helper methods generateGraphQLAnalysis(requests, responses, failedIds) { const responseTimes = responses.filter(r => r.responseTime).map(r => r.responseTime); const averageResponseTime = responseTimes.length > 0 ? Math.round(responseTimes.reduce((a, b) => a + b, 0) / responseTimes.length) : 0; const recommendations = []; if (failedIds.length > 0) { recommendations.push('Review failed GraphQL queries for missing mocks or schema mismatches'); } if (responseTimes.some(t => t > 2000)) { recommendations.push('Consider optimizing slow GraphQL queries (>2s response time)'); } if (requests.length === 0) { recommendations.push('No GraphQL requests detected - ensure monitoring during active app usage'); } return { averageResponseTime, fastestQuery: Math.min(...responseTimes) || 0, slowestQuery: Math.max(...responseTimes) || 0, recommendations }; } extractNumberFromText(text, pattern) { const regex = new RegExp(pattern + '(\\d+)'); const match = text.match(regex); return match ? parseInt(match[1], 10) : null; } generateMockSuggestions(content) { const suggestions = []; if (content.includes('404') || content.includes('Not Found')) { suggestions.push('Add GraphQL endpoint mock for missing operations'); } if (content.includes('500') || content.includes('Internal Server Error')) { suggestions.push('Review server-side GraphQL mock implementations'); } if (content.includes('Unnamed Operation')) { suggestions.push('Add operation names to GraphQL queries for better tracking'); } return suggestions; } analyzeFailurePatterns(content) { let analysis = ''; if (content.includes('HTTP 404')) { analysis += '- **Missing Endpoints**: GraphQL operations not found (404 errors)\n'; } if (content.includes('HTTP 500')) { analysis += '- **Server Errors**: Internal GraphQL processing issues (500 errors)\n'; } if (content.includes('Unnamed Operation')) { analysis += '- **Anonymous Queries**: Operations without names make debugging difficult\n'; } return analysis || 'No specific failure patterns detected'; } analyzeVariablePatterns(content) { // Simple analysis based on content if (content.includes('Variables')) { return 'Variable usage detected in failed queries - review variable types and required fields'; } return 'No variable patterns identified in current analysis'; } compareFailedVsSuccessful(content) { const hasSuccessful = content.includes('Successful Queries'); const hasFailed = content.includes('Failed Queries'); if (hasSuccessful && hasFailed) { return 'Both successful and failed queries detected - compare operation names and variables'; } else if (hasFailed && !hasSuccessful) { return 'Only failed queries detected - all GraphQL operations are failing'; } else if (hasSuccessful && !hasFailed) { return 'Only successful queries detected - no failures in current timeframe'; } return 'No GraphQL activity detected during analysis period'; } } //# sourceMappingURL=graphql-request-inspector-handler.js.map