@re-shell/cli
Version:
Full-stack development platform uniting microservices and microfrontends. Build complete applications with .NET (ASP.NET Core Web API, Minimal API), Java (Spring Boot, Quarkus, Micronaut, Vert.x), Rust (Actix-Web, Warp, Rocket, Axum), Python (FastAPI, Dja
506 lines (505 loc) • 19.1 kB
JavaScript
"use strict";
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;
};
})();
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.DebugSystem = exports.TraceLevel = void 0;
exports.createDebugSystem = createDebugSystem;
exports.getGlobalDebugSystem = getGlobalDebugSystem;
exports.setGlobalDebugSystem = setGlobalDebugSystem;
const fs = __importStar(require("fs-extra"));
const path = __importStar(require("path"));
const events_1 = require("events");
const chalk_1 = __importDefault(require("chalk"));
var TraceLevel;
(function (TraceLevel) {
TraceLevel[TraceLevel["TRACE"] = 0] = "TRACE";
TraceLevel[TraceLevel["DEBUG"] = 1] = "DEBUG";
TraceLevel[TraceLevel["INFO"] = 2] = "INFO";
TraceLevel[TraceLevel["WARN"] = 3] = "WARN";
TraceLevel[TraceLevel["ERROR"] = 4] = "ERROR";
})(TraceLevel || (exports.TraceLevel = TraceLevel = {}));
class DebugSystem extends events_1.EventEmitter {
constructor(options = {}) {
super();
this.sessions = [];
this.activeOperations = new Map();
this.traceCounter = 0;
this.sessionCounter = 0;
this.options = {
enabled: false,
level: TraceLevel.DEBUG,
includeStack: false,
includeMemory: true,
includePerformance: true,
maxTraces: 10000,
maxSessions: 100,
categories: [],
excludeCategories: [],
realtime: false,
format: 'structured',
...options
};
if (this.options.outputFile) {
this.initializeOutputStream();
}
if (this.options.realtime) {
this.setupRealtimeOutput();
}
}
initializeOutputStream() {
if (!this.options.outputFile)
return;
try {
const dir = path.dirname(this.options.outputFile);
fs.ensureDirSync(dir);
this.outputStream = fs.createWriteStream(this.options.outputFile, { flags: 'a' });
this.outputStream.on('error', (error) => {
console.error('Debug output stream error:', error);
});
}
catch (error) {
console.error('Failed to initialize debug output stream:', error);
}
}
setupRealtimeOutput() {
this.on('trace', (trace) => {
if (this.shouldOutputTrace(trace)) {
this.outputTrace(trace);
}
});
}
shouldOutputTrace(trace) {
if (!this.options.enabled)
return false;
if (trace.level < this.options.level)
return false;
if (this.options.categories.length > 0 &&
!this.options.categories.includes(trace.category)) {
return false;
}
if (this.options.excludeCategories.includes(trace.category)) {
return false;
}
return true;
}
outputTrace(trace) {
const output = this.formatTrace(trace);
if (this.outputStream) {
this.outputStream.write(output + '\n');
}
else if (this.options.realtime) {
console.log(output);
}
}
formatTrace(trace) {
switch (this.options.format) {
case 'json':
return JSON.stringify(trace);
case 'text':
return this.formatTraceAsText(trace);
case 'structured':
default:
return this.formatTraceStructured(trace);
}
}
formatTraceAsText(trace) {
const timestamp = trace.timestamp.toISOString();
const level = TraceLevel[trace.level].padEnd(5);
const duration = trace.duration ? ` (${trace.duration}ms)` : '';
const memory = this.options.includeMemory ?
` [${Math.round(trace.metadata.memory.heapUsed / 1024 / 1024)}MB]` : '';
return `${timestamp} ${level} ${trace.category}:${trace.operation}${duration}${memory}`;
}
formatTraceStructured(trace) {
const parts = [
chalk_1.default.gray(trace.timestamp.toLocaleTimeString()),
this.getLevelColor(trace.level)(TraceLevel[trace.level].padEnd(5)),
chalk_1.default.cyan(`${trace.category}:`),
chalk_1.default.white(trace.operation)
];
if (trace.duration !== undefined) {
const color = trace.duration > 1000 ? chalk_1.default.red :
trace.duration > 100 ? chalk_1.default.yellow : chalk_1.default.green;
parts.push(color(`(${trace.duration}ms)`));
}
if (this.options.includeMemory) {
const memMB = Math.round(trace.metadata.memory.heapUsed / 1024 / 1024);
parts.push(chalk_1.default.gray(`[${memMB}MB]`));
}
if (trace.error) {
parts.push(chalk_1.default.red(`ERROR: ${trace.error.message}`));
}
return parts.join(' ');
}
getLevelColor(level) {
switch (level) {
case TraceLevel.TRACE: return chalk_1.default.gray;
case TraceLevel.DEBUG: return chalk_1.default.blue;
case TraceLevel.INFO: return chalk_1.default.green;
case TraceLevel.WARN: return chalk_1.default.yellow;
case TraceLevel.ERROR: return chalk_1.default.red;
default: return chalk_1.default.white;
}
}
startSession(command, args, options) {
const sessionId = `session_${++this.sessionCounter}_${Date.now()}`;
this.currentSession = {
id: sessionId,
startTime: new Date(),
command,
args,
options,
traces: [],
performance: {
operations: new Map()
}
};
this.sessions.push(this.currentSession);
// Trim old sessions
if (this.sessions.length > this.options.maxSessions) {
this.sessions = this.sessions.slice(-this.options.maxSessions);
}
this.trace(TraceLevel.INFO, 'session', 'start', {
sessionId,
command,
args,
options
});
this.emit('session:start', this.currentSession);
return sessionId;
}
endSession() {
if (!this.currentSession)
return;
this.currentSession.endTime = new Date();
this.currentSession.performance.totalDuration =
this.currentSession.endTime.getTime() - this.currentSession.startTime.getTime();
this.trace(TraceLevel.INFO, 'session', 'end', {
sessionId: this.currentSession.id,
duration: this.currentSession.performance.totalDuration,
traceCount: this.currentSession.traces.length
});
this.emit('session:end', this.currentSession);
this.currentSession = undefined;
}
trace(level, category, operation, data, error) {
if (!this.options.enabled || !this.currentSession) {
return '';
}
const traceId = `trace_${++this.traceCounter}`;
const caller = this.options.includeStack ? this.getCaller() : undefined;
const trace = {
id: traceId,
timestamp: new Date(),
level,
category,
operation,
data,
error,
stack: this.options.includeStack && error ? error.stack : undefined,
metadata: {
pid: process.pid,
memory: process.memoryUsage(),
thread: 'main',
caller
}
};
this.currentSession.traces.push(trace);
// Trim traces if needed
if (this.currentSession.traces.length > this.options.maxTraces) {
this.currentSession.traces = this.currentSession.traces.slice(-this.options.maxTraces);
}
this.emit('trace', trace);
return traceId;
}
startOperation(category, operation, data) {
const operationId = `op_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
const trace = this.trace(TraceLevel.DEBUG, category, `${operation}:start`, {
operationId,
...data
});
this.activeOperations.set(operationId, {
start: Date.now(),
trace: this.currentSession?.traces.find(t => t.id === trace)
});
return operationId;
}
endOperation(operationId, data, error) {
const operation = this.activeOperations.get(operationId);
if (!operation)
return;
const duration = Date.now() - operation.start;
operation.trace.duration = duration;
this.trace(error ? TraceLevel.ERROR : TraceLevel.DEBUG, operation.trace.category, `${operation.trace.operation.replace(':start', ':end')}`, { operationId, duration, ...data }, error);
// Update performance stats
if (this.currentSession && this.options.includePerformance) {
this.updatePerformanceStats(operation.trace.category, operation.trace.operation, duration, !!error);
}
this.activeOperations.delete(operationId);
}
updatePerformanceStats(category, operation, duration, isError) {
if (!this.currentSession)
return;
const key = `${category}:${operation}`;
const stats = this.currentSession.performance.operations.get(key) || {
count: 0,
totalDuration: 0,
averageDuration: 0,
minDuration: Infinity,
maxDuration: 0,
errors: 0
};
stats.count++;
stats.totalDuration += duration;
stats.averageDuration = stats.totalDuration / stats.count;
stats.minDuration = Math.min(stats.minDuration, duration);
stats.maxDuration = Math.max(stats.maxDuration, duration);
if (isError) {
stats.errors++;
}
this.currentSession.performance.operations.set(key, stats);
}
getCaller() {
const stack = new Error().stack;
if (!stack)
return undefined;
const lines = stack.split('\n');
// Skip this function and the trace function
const callerLine = lines[4];
if (callerLine) {
const match = callerLine.match(/at\s+([^(]+)\s*\(([^)]+)\)/);
if (match) {
return `${match[1].trim()} (${path.basename(match[2])})`;
}
}
return undefined;
}
// Convenience methods
traceInfo(category, operation, data) {
return this.trace(TraceLevel.INFO, category, operation, data);
}
traceDebug(category, operation, data) {
return this.trace(TraceLevel.DEBUG, category, operation, data);
}
traceWarn(category, operation, data) {
return this.trace(TraceLevel.WARN, category, operation, data);
}
traceError(category, operation, error, data) {
return this.trace(TraceLevel.ERROR, category, operation, data, error);
}
// Timing helpers
time(category, operation) {
const operationId = this.startOperation(category, operation);
return (data, error) => {
this.endOperation(operationId, data, error);
};
}
async timeAsync(category, operation, fn, data) {
const operationId = this.startOperation(category, operation, data);
try {
const result = await fn();
this.endOperation(operationId, { success: true });
return result;
}
catch (error) {
this.endOperation(operationId, { success: false }, error);
throw error;
}
}
// Query and analysis
getCurrentSession() {
return this.currentSession;
}
getAllSessions() {
return [...this.sessions];
}
getSession(sessionId) {
return this.sessions.find(s => s.id === sessionId);
}
searchTraces(query) {
let sessions = this.sessions;
if (query.sessionId) {
const session = this.getSession(query.sessionId);
sessions = session ? [session] : [];
}
const traces = [];
for (const session of sessions) {
for (const trace of session.traces) {
if (query.category && trace.category !== query.category)
continue;
if (query.operation && !trace.operation.includes(query.operation))
continue;
if (query.level !== undefined && trace.level !== query.level)
continue;
if (query.startTime && trace.timestamp < query.startTime)
continue;
if (query.endTime && trace.timestamp > query.endTime)
continue;
traces.push(trace);
}
}
return traces;
}
generateReport(sessionId) {
const session = sessionId ? this.getSession(sessionId) : this.currentSession;
if (!session)
return 'No session found';
const report = [];
// Session info
report.push(chalk_1.default.bold('Debug Session Report'));
report.push(chalk_1.default.gray('='.repeat(50)));
report.push(`Session ID: ${session.id}`);
report.push(`Command: ${session.command} ${session.args.join(' ')}`);
report.push(`Start Time: ${session.startTime.toISOString()}`);
if (session.endTime) {
report.push(`End Time: ${session.endTime.toISOString()}`);
report.push(`Duration: ${session.performance.totalDuration}ms`);
}
report.push(`Traces: ${session.traces.length}`);
report.push('');
// Performance stats
if (session.performance.operations.size > 0) {
report.push(chalk_1.default.bold('Performance Statistics'));
report.push(chalk_1.default.gray('-'.repeat(30)));
for (const [operation, stats] of session.performance.operations) {
report.push(`${operation}:`);
report.push(` Count: ${stats.count}`);
report.push(` Total: ${stats.totalDuration}ms`);
report.push(` Average: ${Math.round(stats.averageDuration)}ms`);
report.push(` Min: ${stats.minDuration}ms`);
report.push(` Max: ${stats.maxDuration}ms`);
if (stats.errors > 0) {
report.push(chalk_1.default.red(` Errors: ${stats.errors}`));
}
report.push('');
}
}
// Error summary
const errors = session.traces.filter(t => t.level === TraceLevel.ERROR);
if (errors.length > 0) {
report.push(chalk_1.default.bold.red('Errors'));
report.push(chalk_1.default.gray('-'.repeat(20)));
for (const error of errors) {
report.push(`${error.timestamp.toLocaleTimeString()} - ${error.category}:${error.operation}`);
if (error.error) {
report.push(` ${error.error.message}`);
}
report.push('');
}
}
return report.join('\n');
}
exportSession(sessionId, format = 'json') {
const session = this.getSession(sessionId);
if (!session)
throw new Error('Session not found');
if (format === 'json') {
return JSON.stringify(session, null, 2);
}
// CSV format
const headers = ['timestamp', 'level', 'category', 'operation', 'duration', 'memory', 'error'];
const rows = [headers.join(',')];
for (const trace of session.traces) {
const row = [
trace.timestamp.toISOString(),
TraceLevel[trace.level],
trace.category,
trace.operation,
trace.duration || '',
Math.round(trace.metadata.memory.heapUsed / 1024 / 1024),
trace.error ? trace.error.message.replace(/,/g, ';') : ''
];
rows.push(row.join(','));
}
return rows.join('\n');
}
// Configuration
setOptions(options) {
this.options = { ...this.options, ...options };
if (options.outputFile && !this.outputStream) {
this.initializeOutputStream();
}
if (options.realtime && !this.listenerCount('trace')) {
this.setupRealtimeOutput();
}
}
getOptions() {
return { ...this.options };
}
enable() {
this.options.enabled = true;
}
disable() {
this.options.enabled = false;
}
isEnabled() {
return this.options.enabled;
}
close() {
if (this.outputStream) {
this.outputStream.end();
this.outputStream = undefined;
}
this.removeAllListeners();
}
}
exports.DebugSystem = DebugSystem;
// Global debug system
let globalDebugSystem = null;
function createDebugSystem(options) {
return new DebugSystem(options);
}
function getGlobalDebugSystem() {
if (!globalDebugSystem) {
const debugEnabled = process.env.DEBUG === 'true' ||
process.argv.includes('--debug') ||
process.argv.includes('--verbose');
globalDebugSystem = new DebugSystem({
enabled: debugEnabled,
level: debugEnabled ? TraceLevel.DEBUG : TraceLevel.INFO,
realtime: debugEnabled,
outputFile: debugEnabled ? path.join(process.cwd(), '.re-shell', 'debug.log') : undefined
});
}
return globalDebugSystem;
}
function setGlobalDebugSystem(debug) {
if (globalDebugSystem) {
globalDebugSystem.close();
}
globalDebugSystem = debug;
}