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
270 lines • 11.2 kB
JavaScript
import { nanoid } from 'nanoid';
export var FaultType;
(function (FaultType) {
// Network Faults
FaultType["NETWORK_LATENCY"] = "network_latency";
FaultType["NETWORK_TIMEOUT"] = "network_timeout";
FaultType["NETWORK_DISCONNECTION"] = "network_disconnection";
FaultType["PACKET_LOSS"] = "packet_loss";
// Resource Faults
FaultType["MEMORY_PRESSURE"] = "memory_pressure";
FaultType["CPU_SPIKE"] = "cpu_spike";
FaultType["DISK_SPACE"] = "disk_space";
// Service Faults
FaultType["DATABASE_ERROR"] = "database_error";
FaultType["API_DOWNTIME"] = "api_downtime";
FaultType["SERVICE_DEGRADATION"] = "service_degradation";
})(FaultType || (FaultType = {}));
export class FaultInjectionEngine {
activeFaults = new Map();
sessionData = { injectedFaults: [] };
constructor() {
// No longer requires page in constructor
}
async injectFault(page, fault) {
const faultId = nanoid();
const timestamp = Date.now();
// Add to active faults first
this.activeFaults.set(faultId, { id: faultId, fault, timestamp });
this.sessionData.injectedFaults.push({ id: faultId, fault, timestamp });
switch (fault.type) {
case FaultType.NETWORK_LATENCY:
await this.injectNetworkLatency(page, fault, faultId);
break;
case FaultType.NETWORK_TIMEOUT:
await this.injectNetworkTimeout(page, fault, faultId);
break;
case FaultType.NETWORK_DISCONNECTION:
await this.injectNetworkDisconnection(page, fault, faultId);
break;
case FaultType.PACKET_LOSS:
await this.injectPacketLoss(page, fault, faultId);
break;
case FaultType.MEMORY_PRESSURE:
await this.injectMemoryPressure(page, fault, faultId);
break;
case FaultType.CPU_SPIKE:
await this.injectCPUSpike(page, fault, faultId);
break;
case FaultType.DISK_SPACE:
await this.injectDiskSpace(page, fault, faultId);
break;
case FaultType.DATABASE_ERROR:
await this.injectDatabaseError(page, fault, faultId);
break;
case FaultType.API_DOWNTIME:
await this.injectAPIDowntime(page, fault, faultId);
break;
case FaultType.SERVICE_DEGRADATION:
await this.injectServiceDegradation(page, fault, faultId);
break;
}
return faultId;
}
async injectNetworkLatency(page, fault, faultId) {
const handler = async (route) => {
await new Promise(resolve => setTimeout(resolve, fault.config.latencyMs || 0));
await route.continue();
};
await page.route(fault.config.urlPattern, handler);
this.activeFaults.get(faultId).handler = handler;
}
async injectNetworkTimeout(page, fault, faultId) {
const handler = async (route) => {
await new Promise(resolve => setTimeout(resolve, fault.config.timeoutMs || 5000));
await route.abort('timedout');
};
await page.route(fault.config.urlPattern, handler);
this.activeFaults.get(faultId).handler = handler;
}
async injectNetworkDisconnection(page, fault, faultId) {
const handler = async (route) => {
await route.abort('connectionfailed');
};
await page.route(fault.config.urlPattern, handler);
this.activeFaults.get(faultId).handler = handler;
}
async injectPacketLoss(page, fault, faultId) {
const handler = async (route) => {
const shouldDrop = Math.random() * 100 < (fault.config.lossPercentage || 0);
if (shouldDrop) {
await route.abort('connectionfailed');
}
else {
await route.continue();
}
};
await page.route(fault.config.urlPattern, handler);
this.activeFaults.get(faultId).handler = handler;
}
async injectMemoryPressure(page, fault, faultId) {
await page.evaluate(({ limitMB, id }) => {
// Simulate memory pressure by allocating arrays
const arrays = [];
const targetBytes = (limitMB || 100) * 1024 * 1024;
let allocated = 0;
const interval = setInterval(() => {
try {
// Allocate 1MB chunks
const chunk = new Array(1024 * 1024 / 8);
arrays.push(chunk);
allocated += 1024 * 1024;
if (allocated >= targetBytes) {
clearInterval(interval);
}
}
catch (e) {
clearInterval(interval);
}
}, 10);
// Store cleanup function
window.__faultCleanup = window.__faultCleanup || {};
window.__faultCleanup[id] = () => {
clearInterval(interval);
arrays.length = 0;
};
}, { limitMB: fault.config.limitMB, id: faultId });
}
async injectCPUSpike(page, fault, faultId) {
await page.evaluate((config) => {
const startTime = Date.now();
const duration = config.durationMs || 1000;
const intensity = config.intensity || 0.8;
const spike = () => {
const now = Date.now();
if (now - startTime < duration) {
// CPU intensive operation
const endTime = now + (16 * intensity); // ~16ms per frame
while (Date.now() < endTime) {
Math.sqrt(Math.random());
}
requestAnimationFrame(spike);
}
};
requestAnimationFrame(spike);
}, fault.config);
}
async injectDiskSpace(page, fault, faultId) {
await page.evaluate((availableKB) => {
// Override storage quota APIs
if ('storage' in navigator && 'estimate' in navigator.storage) {
const originalEstimate = navigator.storage.estimate;
navigator.storage.estimate = async () => {
const result = await originalEstimate.call(navigator.storage);
return {
...result,
quota: (availableKB || 1024) * 1024,
usage: Math.min(result.usage || 0, (availableKB || 1024) * 1024 * 0.9)
};
};
}
}, fault.config.availableKB);
}
async injectDatabaseError(page, fault, faultId) {
const handler = async (route) => {
const shouldError = Math.random() < (fault.config.errorRate || 0.5);
if (shouldError) {
await route.fulfill({
status: 500,
contentType: 'application/json',
body: JSON.stringify({ error: 'Database connection failed' })
});
}
else {
await route.continue();
}
};
await page.route(fault.config.urlPattern, handler);
this.activeFaults.get(faultId).handler = handler;
}
async injectAPIDowntime(page, fault, faultId) {
const handler = async (route) => {
await route.fulfill({
status: fault.config.statusCode || 503,
contentType: 'application/json',
body: JSON.stringify({ error: 'Service temporarily unavailable' })
});
};
await page.route(fault.config.urlPattern, handler);
this.activeFaults.get(faultId).handler = handler;
}
async injectServiceDegradation(page, fault, faultId) {
const handler = async (route) => {
const degradationFactor = fault.config.degradationFactor || 0.5;
const delay = Math.random() * 5000 * degradationFactor;
await new Promise(resolve => setTimeout(resolve, delay));
// Randomly drop some requests
if (Math.random() < degradationFactor * 0.1) {
await route.abort('failed');
}
else {
await route.continue();
}
};
await page.route(fault.config.urlPattern, handler);
this.activeFaults.get(faultId).handler = handler;
}
async removeFault(page, faultId) {
const activeFault = this.activeFaults.get(faultId);
if (!activeFault)
return;
// Clean up page-level changes
if (activeFault.fault.type === FaultType.MEMORY_PRESSURE) {
await page.evaluate((id) => {
if (window.__faultCleanup && window.__faultCleanup[id]) {
window.__faultCleanup[id]();
delete window.__faultCleanup[id];
}
}, faultId);
}
this.activeFaults.delete(faultId);
}
async clearAllFaults(page) {
const faultIds = Array.from(this.activeFaults.keys());
for (const faultId of faultIds) {
await this.removeFault(page, faultId);
}
}
getActiveFaults() {
return Array.from(this.activeFaults.values());
}
getSessionData() {
return this.sessionData;
}
async generateTestScenarios() {
return [
{
name: 'Network Resilience',
faults: [
{ type: FaultType.NETWORK_LATENCY, config: { latencyMs: 2000, urlPattern: '**/api/**' } },
{ type: FaultType.PACKET_LOSS, config: { lossPercentage: 10, urlPattern: '**/api/**' } }
],
expectedBehavior: 'Application should show loading states and retry failed requests'
},
{
name: 'Resource Constraints',
faults: [
{ type: FaultType.MEMORY_PRESSURE, config: { limitMB: 50 } },
{ type: FaultType.CPU_SPIKE, config: { durationMs: 3000, intensity: 0.9 } }
],
expectedBehavior: 'Application should remain responsive and not crash'
},
{
name: 'Service Degradation',
faults: [
{ type: FaultType.API_DOWNTIME, config: { urlPattern: '**/auth/**', statusCode: 503 } },
{ type: FaultType.DATABASE_ERROR, config: { urlPattern: '**/api/data/**', errorRate: 0.3 } }
],
expectedBehavior: 'Application should gracefully handle errors and show appropriate messages'
},
{
name: 'Complete Outage',
faults: [
{ type: FaultType.NETWORK_DISCONNECTION, config: { urlPattern: '**' } }
],
expectedBehavior: 'Application should work offline or show clear offline state'
}
];
}
}
//# sourceMappingURL=fault-injection-engine.js.map