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

396 lines • 14 kB
/** * NextJS Advanced Features Module * * Handles security analysis, server actions, edge runtime, and PPR analysis. * This module provides insights into Next.js advanced features and security. */ export class NextJSAdvancedFeatures { page; serverActions = { invocations: [], totalInvocations: 0, avgExecutionTime: 0, largestPayload: 0 }; hmrMetrics = { updates: 0, failures: 0, avgUpdateTime: 0, statePreservation: true }; async attachToPage(page) { this.page = page; await this.setupSecurityAuditing(); await this.setupServerActionMonitoring(); await this.setupHMRMonitoring(); } async getSecurityAnalysis() { if (!this.page) { return { environmentVariables: { exposed: [], properlyPrefixed: [], warnings: [] }, headers: { csp: false, xFrameOptions: 'DENY', permissionsPolicy: false, cors: 'none', missingHeaders: [] } }; } try { const securityData = await this.page.evaluate(() => { const analysis = { environmentVariables: { exposed: [], properlyPrefixed: [], warnings: [] }, headers: { csp: false, xFrameOptions: 'DENY', permissionsPolicy: false, cors: 'none', missingHeaders: [] } }; // Check for exposed environment variables const scripts = document.querySelectorAll('script'); scripts.forEach(script => { const text = script.textContent || ''; const envVarPattern = /process\.env\.([A-Z_]+)/g; let match; while ((match = envVarPattern.exec(text)) !== null) { const varName = match[1]; if (!varName.startsWith('NEXT_PUBLIC_')) { analysis.environmentVariables.exposed.push(varName); analysis.environmentVariables.warnings.push(`${varName} is exposed in client-side code`); } else { analysis.environmentVariables.properlyPrefixed.push(varName); } } }); // Check security headers const metaTags = document.querySelectorAll('meta[http-equiv]'); metaTags.forEach(meta => { const httpEquiv = meta.getAttribute('http-equiv'); if (httpEquiv === 'Content-Security-Policy') { analysis.headers.csp = true; } if (httpEquiv === 'X-Frame-Options') { analysis.headers.xFrameOptions = meta.getAttribute('content') || 'DENY'; } }); return analysis; }); return securityData || { environmentVariables: { exposed: [], properlyPrefixed: [], warnings: [] }, headers: { csp: false, xFrameOptions: 'DENY', permissionsPolicy: false, cors: 'none', missingHeaders: [] } }; } catch (error) { return { environmentVariables: { exposed: [], properlyPrefixed: [], warnings: [] }, headers: { csp: false, xFrameOptions: 'DENY', permissionsPolicy: false, cors: 'none', missingHeaders: [] } }; } } async getServerActionMonitor() { return this.serverActions; } async getEdgeRuntimeInfo() { if (!this.page) { return { routes: [], unsupportedAPIs: [], bundleSize: '0KB', coldStart: '0ms', warnings: [] }; } try { const edgeInfo = await this.page.evaluate(() => { const nextData = window.__NEXT_DATA__; return { routes: nextData?.edgeRoutes || [], unsupportedAPIs: [], bundleSize: '0KB', coldStart: '0ms', warnings: [] }; }); return edgeInfo || { routes: [], unsupportedAPIs: [], bundleSize: '0KB', coldStart: '0ms', warnings: [] }; } catch (error) { return { routes: [], unsupportedAPIs: [], bundleSize: '0KB', coldStart: '0ms', warnings: [] }; } } async getPPRAnalysis() { if (!this.page) { return { staticShells: [], dynamicHoles: [], streamingPerformance: { ttfb: 0, chunks: 0, totalTime: 0 }, suspenseBoundaries: 0 }; } try { const pprData = await this.page.evaluate(() => { const analysis = { staticShells: [], dynamicHoles: [], streamingPerformance: { ttfb: 0, chunks: 0, totalTime: 0 }, suspenseBoundaries: 0 }; // Analyze Suspense boundaries const suspenseBoundaries = document.querySelectorAll('div[data-react-suspense-boundary]'); analysis.suspenseBoundaries = suspenseBoundaries.length; // Analyze streaming performance const navigationTiming = performance.getEntriesByType('navigation')[0]; if (navigationTiming) { analysis.streamingPerformance.ttfb = navigationTiming.responseStart - navigationTiming.requestStart; analysis.streamingPerformance.totalTime = navigationTiming.loadEventEnd - navigationTiming.fetchStart; } return analysis; }); return pprData || { staticShells: [], dynamicHoles: [], streamingPerformance: { ttfb: 0, chunks: 0, totalTime: 0 }, suspenseBoundaries: 0 }; } catch (error) { return { staticShells: [], dynamicHoles: [], streamingPerformance: { ttfb: 0, chunks: 0, totalTime: 0 }, suspenseBoundaries: 0 }; } } async getHMRAnalysis() { return { updates: this.hmrMetrics.updates, failures: this.hmrMetrics.failures, avgUpdateTime: this.hmrMetrics.avgUpdateTime, statePreservation: this.hmrMetrics.statePreservation, recentUpdates: [] }; } async debugRoute() { if (!this.page) { return { currentRoute: '/', routeType: 'page', renderingMethod: 'CSR', dataFetching: [], cacheStatus: 'none', issues: [], performance: { renderTime: 0, hydrationTime: 0, totalTime: 0 } }; } const routeInfo = await this.page.evaluate(() => { const nextData = window.__NEXT_DATA__; const pathname = window.location.pathname; return { currentRoute: pathname, routeType: 'page', renderingMethod: (nextData?.isFallback ? 'ISR' : 'SSG'), dataFetching: [], cacheStatus: 'none', issues: [], performance: { renderTime: 0, hydrationTime: 0, totalTime: 0 } }; }); return routeInfo; } async analyzeServerClientFlow() { if (!this.page) { return { serverComponents: 0, clientComponents: 0, hydrationMismatches: [], bundleSplitting: { serverChunks: [], clientChunks: [], sharedChunks: [] }, performance: { serverRenderTime: 0, clientHydrationTime: 0, totalTime: 0 } }; } const flowAnalysis = await this.page.evaluate(() => { const scripts = document.querySelectorAll('script[src*="_next/static/chunks/"]'); const clientChunks = []; scripts.forEach(script => { const src = script.getAttribute('src'); if (src) { clientChunks.push(src); } }); return { serverComponents: 0, clientComponents: document.querySelectorAll('[data-reactroot]').length, hydrationMismatches: [], bundleSplitting: { serverChunks: [], clientChunks, sharedChunks: [] }, performance: { serverRenderTime: 0, clientHydrationTime: 0, totalTime: 0 } }; }); return flowAnalysis; } async monitorRSCStream() { // Return an async iterable that yields RSC stream chunks return { [Symbol.asyncIterator]: async function* () { // Simplified implementation - would need actual RSC stream monitoring yield { timestamp: new Date(), chunk: 'initial', component: 'RootLayout', size: 1024 }; } }; } async setupSecurityAuditing() { if (!this.page) return; // Monitor for security issues await this.page.evaluate(() => { // Check for potential security issues const observer = new MutationObserver((mutations) => { mutations.forEach((mutation) => { if (mutation.type === 'childList' && mutation.addedNodes.length > 0) { for (let i = 0; i < mutation.addedNodes.length; i++) { const node = mutation.addedNodes[i]; if (node.nodeType === Node.COMMENT_NODE && node.textContent?.includes('__NEXT_DATA__')) { // Monitor for data exposure } } } }); }); observer.observe(document.body, { childList: true, subtree: true }); }); } async setupServerActionMonitoring() { if (!this.page) return; // Monitor server actions await this.page.route('**/*', async (route) => { const url = route.request().url(); const method = route.request().method(); if (method === 'POST' && url.includes('/api/')) { const startTime = Date.now(); await route.continue(); const endTime = Date.now(); const duration = endTime - startTime; this.serverActions.invocations.push({ action: url, timestamp: new Date(), duration, payloadSize: route.request().postData()?.length || 0, revalidations: [] }); this.serverActions.totalInvocations++; this.serverActions.avgExecutionTime = (this.serverActions.avgExecutionTime + duration) / this.serverActions.totalInvocations; } else { await route.continue(); } }); } async setupHMRMonitoring() { if (!this.page) return; // Monitor HMR updates await this.page.evaluate(() => { const nextData = window.__NEXT_DATA__; if (nextData?.buildId?.includes('development')) { // Monitor for HMR events const originalConsoleLog = console.log; console.log = function (...args) { if (args[0] && args[0].includes('[HMR]')) { window.__hmr_updates = (window.__hmr_updates || 0) + 1; } originalConsoleLog.apply(console, args); }; } }); } } //# sourceMappingURL=nextjs-advanced-features.js.map