UNPKG

ctrlshiftleft

Version:

AI-powered toolkit for embedding QA and security testing into development workflows

319 lines 12 kB
"use strict"; /** * Performance Tracker * * Utility for tracking performance metrics across the ctrl.shift.left toolkit. * Collects timing information for test generation, execution, and analysis operations. */ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); Object.defineProperty(exports, "__esModule", { value: true }); exports.performanceTracker = exports.PerformanceTracker = void 0; const fs = __importStar(require("fs")); const path = __importStar(require("path")); class PerformanceTracker { constructor(options = {}) { this.entries = []; this.activeTimers = new Map(); this.projectName = options.projectName || path.basename(process.cwd()); this.version = options.version || '1.4.0'; this.outputDir = options.outputDir || path.join(process.cwd(), 'performance-reports'); this.enabled = options.enabled !== undefined ? options.enabled : true; // Create output directory if it doesn't exist if (this.enabled && !fs.existsSync(this.outputDir)) { fs.mkdirSync(this.outputDir, { recursive: true }); } } /** * Start tracking an operation * @param type Operation type * @param target Component or file being processed * @param id Optional custom identifier (defaults to auto-generated) * @returns Timer ID for stopping the timer later */ startTimer(type, target, id) { if (!this.enabled) return ''; const timerId = id || `${type}-${target}-${Date.now()}`; this.activeTimers.set(timerId, { startTime: Date.now(), type, target }); return timerId; } /** * Stop tracking an operation and record the performance data * @param timerId Timer ID from startTimer * @param metadata Additional data about the operation * @returns Duration in milliseconds or -1 if timer not found */ stopTimer(timerId, metadata) { if (!this.enabled || !timerId) return -1; const timer = this.activeTimers.get(timerId); if (!timer) { console.warn(`PerformanceTracker: Timer ${timerId} not found`); return -1; } const endTime = Date.now(); const duration = endTime - timer.startTime; this.activeTimers.delete(timerId); const entry = { id: timerId, type: timer.type, target: timer.target, startTime: timer.startTime, endTime, duration, metadata: metadata || { success: true }, system: { platform: process.platform, arch: process.arch, nodeVersion: process.version, cpuUsage: process.cpuUsage(), memoryUsage: process.memoryUsage() } }; this.entries.push(entry); return duration; } /** * Record a completed operation without using timers * @param type Operation type * @param target Component or file being processed * @param duration Duration in milliseconds * @param metadata Additional data about the operation */ recordOperation(type, target, duration, metadata) { if (!this.enabled) return; const now = Date.now(); const entry = { id: `${type}-${target}-${now}`, type, target, startTime: now - duration, endTime: now, duration, metadata: metadata || { success: true }, system: { platform: process.platform, arch: process.arch, nodeVersion: process.version } }; this.entries.push(entry); } /** * Get performance data for all recorded operations */ getEntries() { return [...this.entries]; } /** * Generate a comprehensive performance report * @param label Optional label for the report */ generateReport(label) { // Calculate summary statistics let totalDuration = 0; const typeStats = {}; let successCount = 0; this.entries.forEach(entry => { totalDuration += entry.duration; if (!typeStats[entry.type]) { typeStats[entry.type] = { count: 0, totalDuration: 0 }; } typeStats[entry.type].count += 1; typeStats[entry.type].totalDuration += entry.duration; if (entry.metadata?.success) { successCount += 1; } }); // Calculate averages and format the report const byType = {}; Object.entries(typeStats).forEach(([type, stats]) => { byType[type] = { count: stats.count, totalDuration: stats.totalDuration, averageDuration: stats.totalDuration / stats.count }; }); const report = { metadata: { timestamp: Date.now(), project: this.projectName, label, version: this.version }, entries: this.entries, summary: { totalDuration, averageDuration: totalDuration / this.entries.length || 0, byType, successRate: (successCount / this.entries.length) * 100 || 0, totalOperations: this.entries.length } }; return report; } /** * Save the performance report to a file * @param format Output format (json, markdown) * @param label Optional label for the report file * @returns Path to the saved report file */ saveReport(format = 'json', label) { if (!this.enabled || this.entries.length === 0) { console.warn('PerformanceTracker: No performance data to save'); return ''; } const report = this.generateReport(label); const timestamp = new Date().toISOString().replace(/:/g, '-').replace(/\..+/, ''); const filename = `perf-report-${label || this.projectName}-${timestamp}`; const filePath = path.join(this.outputDir, `${filename}.${format === 'json' ? 'json' : 'md'}`); if (format === 'json') { fs.writeFileSync(filePath, JSON.stringify(report, null, 2)); } else { const mdContent = this.generateMarkdownReport(report); fs.writeFileSync(filePath, mdContent); } return filePath; } /** * Generate a markdown representation of the performance report * @param report Performance report object * @returns Markdown string */ generateMarkdownReport(report) { const { metadata, summary, entries } = report; let md = `# Performance Report: ${metadata.project}\n\n`; md += `*Generated by ctrl.shift.left v${metadata.version} on ${new Date(metadata.timestamp).toLocaleString()}*\n\n`; if (metadata.label) { md += `**Label:** ${metadata.label}\n\n`; } // Summary section md += `## Summary\n\n`; md += `- **Total Operations:** ${summary.totalOperations}\n`; md += `- **Total Duration:** ${formatDuration(summary.totalDuration)}\n`; md += `- **Average Duration:** ${formatDuration(summary.averageDuration)}\n`; md += `- **Success Rate:** ${summary.successRate.toFixed(1)}%\n\n`; // Operations by type md += `## Operations by Type\n\n`; md += `| Type | Count | Total Duration | Average Duration |\n`; md += `|------|-------|----------------|------------------|\n`; Object.entries(summary.byType).forEach(([type, stats]) => { md += `| ${type} | ${stats.count} | ${formatDuration(stats.totalDuration)} | ${formatDuration(stats.averageDuration)} |\n`; }); md += `\n`; // Top 10 slowest operations const slowestOps = [...entries].sort((a, b) => b.duration - a.duration).slice(0, 10); md += `## Top 10 Slowest Operations\n\n`; md += `| Type | Target | Duration | Success |\n`; md += `|------|--------|----------|--------|\n`; slowestOps.forEach(entry => { const success = entry.metadata?.success ? '✅' : '❌'; md += `| ${entry.type} | ${entry.target} | ${formatDuration(entry.duration)} | ${success} |\n`; }); md += `\n`; // System info from the latest entry if (entries.length > 0 && entries[entries.length - 1].system) { const system = entries[entries.length - 1].system; md += `## System Information\n\n`; md += `- **Platform:** ${system.platform}\n`; md += `- **Architecture:** ${system.arch}\n`; md += `- **Node.js Version:** ${system.nodeVersion}\n`; if (system.memoryUsage) { md += `- **Memory Usage:**\n`; md += ` - RSS: ${formatBytes(system.memoryUsage.rss)}\n`; md += ` - Heap Total: ${formatBytes(system.memoryUsage.heapTotal)}\n`; md += ` - Heap Used: ${formatBytes(system.memoryUsage.heapUsed)}\n`; md += ` - External: ${formatBytes(system.memoryUsage.external)}\n`; } } return md; } /** * Reset the performance tracker, clearing all entries */ reset() { this.entries = []; this.activeTimers.clear(); } /** * Enable or disable the performance tracker */ setEnabled(enabled) { this.enabled = enabled; } } exports.PerformanceTracker = PerformanceTracker; /** * Format duration in milliseconds to a human-readable string */ function formatDuration(ms) { if (ms < 1000) { return `${ms.toFixed(1)}ms`; } else if (ms < 60000) { return `${(ms / 1000).toFixed(2)}s`; } else { const minutes = Math.floor(ms / 60000); const seconds = ((ms % 60000) / 1000).toFixed(1); return `${minutes}m ${seconds}s`; } } /** * Format bytes to a human-readable string */ function formatBytes(bytes) { if (bytes < 1024) { return `${bytes} bytes`; } else if (bytes < 1024 * 1024) { return `${(bytes / 1024).toFixed(2)} KB`; } else if (bytes < 1024 * 1024 * 1024) { return `${(bytes / (1024 * 1024)).toFixed(2)} MB`; } else { return `${(bytes / (1024 * 1024 * 1024)).toFixed(2)} GB`; } } // Export a singleton instance for shared usage exports.performanceTracker = new PerformanceTracker(); //# sourceMappingURL=performanceTracker.js.map