UNPKG

@nanocollective/nanocoder

Version:

A local-first CLI coding agent that brings the power of agentic coding tools like Claude Code and Gemini CLI to local models or controlled APIs like OpenRouter

295 lines 11 kB
/** * Console API facade for backward compatibility * Provides drop-in replacement for console methods that routes through structured logging * * This allows gradual migration from console.log to structured logging * without breaking existing code patterns */ import { createErrorInfo } from '../../utils/error-formatter.js'; import { generateCorrelationId, getLogger, withNewCorrelationContext, } from '../../utils/logging/index.js'; // Get logger instance directly to avoid circular dependencies const logger = getLogger(); /** * Create a console method with specific logging level and special handling * This factory reduces code duplication across different console methods */ function createConsoleMethod(level, options) { const { specialErrorHandling = false, isInfoMethod = false } = options || {}; return (...args) => { const _correlationId = generateCorrelationId(); withNewCorrelationContext(context => { if (args.length === 0) { logger[level](`Empty console.${level} call`, { correlationId: context.id, source: 'console-facade', }); return; } // Special handling for Error objects in console.error if (specialErrorHandling && args.length === 1 && args[0] instanceof Error) { const errorInfo = createErrorInfo(args[0], undefined, _correlationId); logger.error('Error logged via console.error', { errorInfo, correlationId: context.id, source: 'console-facade', }); return; } // Handle single argument case if (args.length === 1) { const arg = args[0]; if (typeof arg === 'string') { logger[level](arg, { correlationId: context.id, source: 'console-facade', }); } else if (typeof arg === 'object' && arg !== null) { const message = `${level === 'error' ? 'Error' : 'Object'} logged via console.${level}`; logger[level](message, { object: arg, correlationId: context.id, source: 'console-facade', }); } else { logger[level](String(arg), { correlationId: context.id, source: 'console-facade', }); } return; } // Multiple arguments - join strings and log objects separately const strings = args.filter(arg => typeof arg === 'string'); const objects = args.filter(arg => typeof arg === 'object' && arg !== null); const primitives = args.filter(arg => typeof arg !== 'string' && typeof arg !== 'object'); let message = strings.join(' '); if (primitives.length > 0) { message += ' ' + primitives.map(String).join(' '); } const logData = { correlationId: context.id, source: 'console-facade', argumentCount: args.length, stringArgs: strings.length, objectArgs: objects.length, primitiveArgs: primitives.length, }; if (objects.length > 0) { logData.objects = objects; } // Check for error-like objects that aren't Error instances (only for error level) if (level === 'error') { const errorLikeObjects = objects.filter(obj => obj && typeof obj === 'object' && ('message' in obj || 'error' in obj || 'err' in obj)); if (errorLikeObjects.length > 0) { logData.errorLikeObjects = errorLikeObjects; } } // Use appropriate logger level (info for both log and info methods) const loggerLevel = isInfoMethod ? 'info' : level; logger[loggerLevel](message || `console.${level} called`, logData); }); }; } /** * Console replacement that routes to structured logging * Maintains the same API as global console but adds structured logging benefits */ const StructuredConsole = { /** * Replacement for console.log - routes to logger.info * Accepts any number of arguments and formats them appropriately */ log: createConsoleMethod('info', { isInfoMethod: true }), /** * Replacement for console.error - routes to logger.error with error analysis */ error: createConsoleMethod('error', { specialErrorHandling: true }), /** * Replacement for console.warn - routes to logger.warn */ warn: createConsoleMethod('warn'), /** * Replacement for console.info - routes to logger.info */ info: createConsoleMethod('info', { isInfoMethod: true }), /** * Replacement for console.debug - routes to logger.debug */ debug: createConsoleMethod('debug'), }; /** * Global console replacement hook * Can be used to temporarily replace global console for testing or specific modules */ export class ConsoleInterceptor { originalConsole; isActive = false; constructor() { this.originalConsole = { ...console }; } /** * Replace global console with structured logging version */ activate() { if (this.isActive) { return; } console.log = StructuredConsole.log; console.error = StructuredConsole.error; console.warn = StructuredConsole.warn; console.info = StructuredConsole.info; console.debug = StructuredConsole.debug; this.isActive = true; } /** * Restore original global console */ deactivate = () => { if (!this.isActive) { return; } // eslint-disable-next-line @typescript-eslint/unbound-method console.log = this.originalConsole.log; // eslint-disable-next-line @typescript-eslint/unbound-method console.error = this.originalConsole.error; // eslint-disable-next-line @typescript-eslint/unbound-method console.warn = this.originalConsole.warn; // eslint-disable-next-line @typescript-eslint/unbound-method console.info = this.originalConsole.info; // eslint-disable-next-line @typescript-eslint/unbound-method console.debug = this.originalConsole.debug; this.isActive = false; }; /** * Check if interceptor is active */ isInterceptorActive() { return this.isActive; } /** * Execute a function with console temporarily replaced */ withStructuredConsole(fn) { this.activate(); try { return fn(); } finally { this.deactivate(); } } } /** * Global console interceptor instance */ export const globalConsoleInterceptor = new ConsoleInterceptor(); /** * Decorator to automatically route console calls in a function to structured logging * @internal */ export function useStructuredConsoleDecorator(_target, _propertyName, descriptor) { const originalMethod = descriptor.value; if (typeof originalMethod === 'function') { descriptor.value = function (...args) { return globalConsoleInterceptor.withStructuredConsole(() => { return Reflect.apply(originalMethod, this, args); }); }; } return descriptor; } /** * Convenience function to create a module-scoped console * Useful for gradual migration of individual modules */ export function createModuleConsole(moduleName) { return { log: (...args) => { logger.info(`[${moduleName}] ${args.filter(a => typeof a === 'string').join(' ')}`, { moduleName, allArgs: args, correlationId: generateCorrelationId(), source: 'module-console', }); }, error: (...args) => { logger.error(`[${moduleName}] ${args.filter(a => typeof a === 'string').join(' ')}`, { moduleName, allArgs: args, correlationId: generateCorrelationId(), source: 'module-console', }); }, warn: (...args) => { logger.warn(`[${moduleName}] ${args.filter(a => typeof a === 'string').join(' ')}`, { moduleName, allArgs: args, correlationId: generateCorrelationId(), source: 'module-console', }); }, info: (...args) => { logger.info(`[${moduleName}] ${args.filter(a => typeof a === 'string').join(' ')}`, { moduleName, allArgs: args, correlationId: generateCorrelationId(), source: 'module-console', }); }, debug: (...args) => { logger.debug(`[${moduleName}] ${args.filter(a => typeof a === 'string').join(' ')}`, { moduleName, allArgs: args, correlationId: generateCorrelationId(), source: 'module-console', }); }, }; } /** * Migration helper to identify console usage patterns * Returns statistics about console method usage in the current execution */ export class ConsoleUsageTracker { usage = new Map(); constructor() { // Track console method calls by wrapping them const methods = ['log', 'error', 'warn', 'info', 'debug']; methods.forEach(method => { const original = console[method]; this.usage.set(method, 0); console[method] = (...args) => { this.usage.set(method, (this.usage.get(method) || 0) + 1); return original.apply(console, args); }; }); } getUsageStats() { return Object.fromEntries(this.usage); } reportUsage() { const stats = this.getUsageStats(); const total = Object.values(stats).reduce((sum, count) => sum + count, 0); logger.info('Console usage statistics', { total, methodBreakdown: stats, source: 'console-usage-tracker', }); } restore() { // This would need to restore the original console methods // Implementation depends on how the original methods are stored logger.info('Console usage tracker deactivated', { source: 'console-usage-tracker', }); } } // Export for testing purposes only export { StructuredConsole, useStructuredConsoleDecorator as useStructuredConsole, }; //# sourceMappingURL=console-facade.js.map