type LogLevel = "info" | "success" | "warn" | "error"; interface LoggerOptions { enabled?: boolean; showTimestamp?: boolean; collapsed?: boolean; colors?: boolean; } let cachedTimestamp = ""; let lastTimestampUpdate = 0; function getTimestamp(): string { const now = Date.now(); if (now - lastTimestampUpdate > 1000) { const date = new Date(now); const month = String(date.getMonth() + 1).padStart(2, "0"); const day = String(date.getDate()).padStart(2, "0"); const hours = String(date.getHours()).padStart(2, "0"); const minutes = String(date.getMinutes()).padStart(2, "0"); const seconds = String(date.getSeconds()).padStart(2, "0"); cachedTimestamp = `${month}/${day} ${hours}:${minutes}:${seconds}`; lastTimestampUpdate = now; } return cachedTimestamp; } function getLevelStyle(level: LogLevel): { color: string; label: string } { const styles = { info: { color: "#f5f5f5", label: "INFO" }, success: { color: "#10B981", label: "SUCCESS" }, warn: { color: "#F59E0B", label: "WARN" }, error: { color: "#EF4444", label: "ERROR" }, }; return styles[level] || styles.info; } class Logger { private options: LoggerOptions; private context?: string; constructor(context?: string, options: LoggerOptions = {}) { this.context = context; this.options = { enabled: true, showTimestamp: true, collapsed: true, colors: true, ...options, }; } child(context: string, options?: LoggerOptions): Logger { const childContext = this.context ? `${this.context} > ${context}` : context; return new Logger(childContext, { ...this.options, ...options }); } private log( level: LogLevel, label: string, data?: any, ...rest: any[] ): void { if (!this.options.enabled) return; const style = getLevelStyle(level); const timestamp = this.options.showTimestamp ? `${getTimestamp()} │ ` : ""; const context = this.context ? ` │ ${this.context}` : ""; const groupLabel = `${timestamp}${style.label}${context} │ ${label}`; // In server environment (no window), use simple console.log instead of groups const isServer = typeof window === "undefined"; if (isServer) { // Server-side: Simple formatted output (no console.group in Node.js) console.log(groupLabel); if (data !== undefined) { console.log(JSON.stringify(data, null, 2)); } if (rest.length > 0) { for (const item of rest) { console.log(JSON.stringify(item, null, 2)); } } } else { // Browser: Use console.group with colors const group = this.options.collapsed ? console.groupCollapsed : console.group; if (this.options.colors) { group(`%c${groupLabel}`, `color: ${style.color}; font-weight: bold;`); } else { group(groupLabel); } if (data !== undefined) { console.log(data); } if (rest.length > 0) { for (const item of rest) { console.log(item); } } console.groupEnd(); } } info(label: string, data?: any, ...rest: any[]): void { this.log("info", label, data, ...rest); } success(label: string, data?: any, ...rest: any[]): void { this.log("success", label, data, ...rest); } warn(label: string, data?: any, ...rest: any[]): void { this.log("warn", label, data, ...rest); } error(label: string, data?: any, ...rest: any[]): void { this.log("error", label, data, ...rest); } simple(message: string): void { if (!this.options.enabled) return; const style = getLevelStyle("info"); const timestamp = this.options.showTimestamp ? `${getTimestamp()} │ ` : ""; const context = this.context ? ` │ ${this.context}` : ""; const logMessage = `${timestamp}${style.label}${context} │ ${message}`; if (this.options.colors && typeof window !== "undefined") { console.log(`%c${logMessage}`, `color: ${style.color};`); } else { console.log(logMessage); } } async measure(label: string, fn: () => Promise): Promise { const start = performance.now(); try { const result = await fn(); const duration = (performance.now() - start).toFixed(2); this.success(`${label} completed`, { duration: `${duration}ms`, result, }); return result; } catch (error) { const duration = (performance.now() - start).toFixed(2); this.error(`${label} failed`, { duration: `${duration}ms`, error, }); throw error; } } } export const logger = new Logger(); export { Logger };