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