@mks2508/better-logger 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/.claude/settings.local.json +13 -0
  2. package/CLAUDE.md +113 -0
  3. package/dist/assets/index-DxvJByYN.js +183 -0
  4. package/dist/index.html +334 -0
  5. package/dist/vite.svg +1 -0
  6. package/index.html +334 -0
  7. package/package.json +35 -0
  8. package/public/vite.svg +1 -0
  9. package/src/Logger.ts +577 -0
  10. package/src/Logger.ts.backup +1684 -0
  11. package/src/cli/CommandProcessor.ts +77 -0
  12. package/src/cli/commands/ConfigCommand.ts +93 -0
  13. package/src/cli/commands/ExportCommand.ts +271 -0
  14. package/src/cli/commands/StatusCommand.ts +111 -0
  15. package/src/cli/commands/ThemeCommand.ts +88 -0
  16. package/src/cli/help.ts +127 -0
  17. package/src/cli/index.ts +59 -0
  18. package/src/constants.ts +89 -0
  19. package/src/example.ts +88 -0
  20. package/src/handlers/AnalyticsLogHandler.ts +22 -0
  21. package/src/handlers/ExportLogHandler.ts +447 -0
  22. package/src/handlers/FileLogHandler.ts +30 -0
  23. package/src/handlers/RemoteLogHandler.ts +42 -0
  24. package/src/handlers/index.ts +8 -0
  25. package/src/index.ts +122 -0
  26. package/src/main.ts +126 -0
  27. package/src/style.css +96 -0
  28. package/src/styling/StyleBuilder.ts +305 -0
  29. package/src/styling/banners.ts +168 -0
  30. package/src/styling/index.ts +12 -0
  31. package/src/styling/themes.ts +235 -0
  32. package/src/types/core.ts +80 -0
  33. package/src/types/handlers.ts +95 -0
  34. package/src/types/index.ts +29 -0
  35. package/src/typescript.svg +1 -0
  36. package/src/utils/index.ts +18 -0
  37. package/src/utils/output.ts +127 -0
  38. package/src/utils/stackTrace.ts +66 -0
  39. package/src/utils/timestamps.ts +80 -0
  40. package/src/vite-env.d.ts +1 -0
  41. package/tsconfig.json +24 -0
@@ -0,0 +1,1684 @@
1
+ /**
2
+ * @fileoverview State-of-the-art Logger with advanced console styling and modern TypeScript patterns
3
+ * @version 2.0.0
4
+ * @author Bolt AI
5
+ * @date 2025
6
+ */
7
+
8
+ /**
9
+ * Supported log levels in hierarchical order (debug < info < warn < error < critical)
10
+ */
11
+ export const LOG_LEVELS = {
12
+ debug: 0,
13
+ info: 1,
14
+ warn: 2,
15
+ error: 3,
16
+ critical: 4,
17
+ } as const;
18
+
19
+ /**
20
+ * Log level type derived from LOG_LEVELS keys
21
+ */
22
+ export type LogLevel = keyof typeof LOG_LEVELS;
23
+
24
+ /**
25
+ * Verbosity level type for filtering logs
26
+ */
27
+ export type Verbosity = LogLevel | 'silent';
28
+
29
+ /**
30
+ * Theme variants for different visual styles
31
+ */
32
+ export type ThemeVariant = 'default' | 'dark' | 'light' | 'neon' | 'minimal' | 'cyberpunk';
33
+
34
+ /**
35
+ * Banner types for different visual approaches
36
+ */
37
+ export type BannerType = 'simple' | 'ascii' | 'unicode' | 'svg' | 'animated';
38
+
39
+ /**
40
+ * Configuration interface for logger instances
41
+ */
42
+ interface LoggerConfig {
43
+ globalPrefix?: string;
44
+ verbosity: Verbosity;
45
+ enableColors: boolean;
46
+ enableTimestamps: boolean;
47
+ enableStackTrace: boolean;
48
+ theme?: ThemeVariant;
49
+ bannerType?: BannerType;
50
+ }
51
+
52
+ /**
53
+ * Interface for custom log handlers (extensibility)
54
+ */
55
+ export interface ILogHandler {
56
+ handle(level: LogLevel, message: string, args: any[], metadata: LogMetadata): void;
57
+ }
58
+
59
+ /**
60
+ * Metadata associated with each log entry
61
+ */
62
+ interface LogMetadata {
63
+ timestamp: string;
64
+ level: LogLevel;
65
+ prefix?: string;
66
+ stackInfo?: StackInfo;
67
+ group?: string;
68
+ }
69
+
70
+ /**
71
+ * Parsed stack trace information
72
+ */
73
+ interface StackInfo {
74
+ file: string;
75
+ line: number;
76
+ column: number;
77
+ function?: string;
78
+ }
79
+
80
+ /**
81
+ * Timer entry for performance measurement
82
+ */
83
+ interface TimerEntry {
84
+ label: string;
85
+ startTime: number;
86
+ }
87
+
88
+ /**
89
+ * Theme configurations for different visual styles
90
+ */
91
+ const THEME_PRESETS = {
92
+ default: {
93
+ debug: {
94
+ emoji: 'šŸž', label: 'DEBUG',
95
+ background: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)',
96
+ color: '#ffffff', border: '1px solid #667eea',
97
+ shadow: '0 2px 4px rgba(102, 126, 234, 0.3)',
98
+ },
99
+ info: {
100
+ emoji: 'ā„¹ļø', label: 'INFO',
101
+ background: 'linear-gradient(135deg, #74b9ff 0%, #0984e3 100%)',
102
+ color: '#ffffff', border: '1px solid #74b9ff',
103
+ shadow: '0 2px 4px rgba(116, 185, 255, 0.3)',
104
+ },
105
+ warn: {
106
+ emoji: 'āš ļø', label: 'WARN',
107
+ background: 'linear-gradient(135deg, #fdcb6e 0%, #e17055 100%)',
108
+ color: '#2d3436', border: '1px solid #fdcb6e',
109
+ shadow: '0 2px 4px rgba(253, 203, 110, 0.3)',
110
+ },
111
+ error: {
112
+ emoji: 'āŒ', label: 'ERROR',
113
+ background: 'linear-gradient(135deg, #e84393 0%, #d63031 100%)',
114
+ color: '#ffffff', border: '1px solid #e84393',
115
+ shadow: '0 2px 4px rgba(232, 67, 147, 0.3)',
116
+ },
117
+ success: {
118
+ emoji: 'āœ…', label: 'SUCCESS',
119
+ background: 'linear-gradient(135deg, #00b894 0%, #00a085 100%)',
120
+ color: '#ffffff', border: '1px solid #00b894',
121
+ shadow: '0 2px 4px rgba(0, 184, 148, 0.3)',
122
+ },
123
+ critical: {
124
+ emoji: 'šŸ”„', label: 'CRITICAL',
125
+ background: 'linear-gradient(135deg, #ff3838 0%, #ff1744 100%)',
126
+ color: '#ffffff', border: '2px solid #ff3838',
127
+ shadow: '0 4px 8px rgba(255, 56, 56, 0.5)',
128
+ },
129
+ },
130
+ dark: {
131
+ debug: {
132
+ emoji: 'šŸŒ™', label: 'DEBUG',
133
+ background: 'linear-gradient(135deg, #2d3748 0%, #4a5568 100%)',
134
+ color: '#e2e8f0', border: '1px solid #4a5568',
135
+ shadow: '0 2px 4px rgba(45, 55, 72, 0.8)',
136
+ },
137
+ info: {
138
+ emoji: 'šŸ’”', label: 'INFO',
139
+ background: 'linear-gradient(135deg, #1a202c 0%, #2d3748 100%)',
140
+ color: '#90cdf4', border: '1px solid #3182ce',
141
+ shadow: '0 2px 4px rgba(26, 32, 44, 0.8)',
142
+ },
143
+ warn: {
144
+ emoji: '⚔', label: 'WARN',
145
+ background: 'linear-gradient(135deg, #744210 0%, #975a16 100%)',
146
+ color: '#faf089', border: '1px solid #d69e2e',
147
+ shadow: '0 2px 4px rgba(116, 66, 16, 0.8)',
148
+ },
149
+ error: {
150
+ emoji: 'šŸ’€', label: 'ERROR',
151
+ background: 'linear-gradient(135deg, #742a2a 0%, #9b2c2c 100%)',
152
+ color: '#feb2b2', border: '1px solid #e53e3e',
153
+ shadow: '0 2px 4px rgba(116, 42, 42, 0.8)',
154
+ },
155
+ success: {
156
+ emoji: 'šŸŽÆ', label: 'SUCCESS',
157
+ background: 'linear-gradient(135deg, #276749 0%, #2f855a 100%)',
158
+ color: '#9ae6b4', border: '1px solid #38a169',
159
+ shadow: '0 2px 4px rgba(39, 103, 73, 0.8)',
160
+ },
161
+ critical: {
162
+ emoji: 'šŸ’„', label: 'CRITICAL',
163
+ background: 'linear-gradient(135deg, #1a1a1a 0%, #ff0000 100%)',
164
+ color: '#ffffff', border: '2px solid #ff0000',
165
+ shadow: '0 4px 8px rgba(255, 0, 0, 0.9)',
166
+ },
167
+ },
168
+ neon: {
169
+ debug: {
170
+ emoji: '⚔', label: 'DEBUG',
171
+ background: 'linear-gradient(135deg, #0f3460 0%, #e94560 100%)',
172
+ color: '#00ffff', border: '1px solid #00ffff',
173
+ shadow: '0 0 10px rgba(0, 255, 255, 0.5)',
174
+ },
175
+ info: {
176
+ emoji: 'šŸ”®', label: 'INFO',
177
+ background: 'linear-gradient(135deg, #16213e 0%, #0f3460 100%)',
178
+ color: '#00ff41', border: '1px solid #00ff41',
179
+ shadow: '0 0 10px rgba(0, 255, 65, 0.5)',
180
+ },
181
+ warn: {
182
+ emoji: 'āš ļø', label: 'WARN',
183
+ background: 'linear-gradient(135deg, #533a03 0%, #e94560 100%)',
184
+ color: '#ffff00', border: '1px solid #ffff00',
185
+ shadow: '0 0 10px rgba(255, 255, 0, 0.5)',
186
+ },
187
+ error: {
188
+ emoji: 'šŸ’„', label: 'ERROR',
189
+ background: 'linear-gradient(135deg, #5c0a0a 0%, #ff073a 100%)',
190
+ color: '#ff073a', border: '1px solid #ff073a',
191
+ shadow: '0 0 10px rgba(255, 7, 58, 0.8)',
192
+ },
193
+ success: {
194
+ emoji: '✨', label: 'SUCCESS',
195
+ background: 'linear-gradient(135deg, #0a5c0a 0%, #39ff14 100%)',
196
+ color: '#39ff14', border: '1px solid #39ff14',
197
+ shadow: '0 0 10px rgba(57, 255, 20, 0.8)',
198
+ },
199
+ critical: {
200
+ emoji: '🌟', label: 'CRITICAL',
201
+ background: 'linear-gradient(135deg, #000000 0%, #ff0080 100%)',
202
+ color: '#ff0080', border: '2px solid #ff0080',
203
+ shadow: '0 0 20px rgba(255, 0, 128, 1)',
204
+ },
205
+ },
206
+ minimal: {
207
+ debug: {
208
+ emoji: '', label: 'DEBUG',
209
+ background: '#f7fafc', color: '#4a5568',
210
+ border: '1px solid #e2e8f0', shadow: 'none',
211
+ },
212
+ info: {
213
+ emoji: '', label: 'INFO',
214
+ background: '#ebf8ff', color: '#2b6cb0',
215
+ border: '1px solid #bee3f8', shadow: 'none',
216
+ },
217
+ warn: {
218
+ emoji: '', label: 'WARN',
219
+ background: '#fffbf0', color: '#c05621',
220
+ border: '1px solid #fed7aa', shadow: 'none',
221
+ },
222
+ error: {
223
+ emoji: '', label: 'ERROR',
224
+ background: '#fef5f5', color: '#c53030',
225
+ border: '1px solid #fca5a5', shadow: 'none',
226
+ },
227
+ success: {
228
+ emoji: '', label: 'SUCCESS',
229
+ background: '#f0fff4', color: '#2f855a',
230
+ border: '1px solid #9ae6b4', shadow: 'none',
231
+ },
232
+ critical: {
233
+ emoji: '', label: 'CRITICAL',
234
+ background: '#fef5f5', color: '#e53e3e',
235
+ border: '2px solid #f56565', shadow: 'none',
236
+ },
237
+ }
238
+ };
239
+
240
+ /**
241
+ * Current active theme styles
242
+ */
243
+ let LEVEL_STYLES = THEME_PRESETS.default;
244
+
245
+ /**
246
+ * Theme-specific banners for enhanced visual theming
247
+ */
248
+ const THEME_BANNERS = {
249
+ default: {
250
+ simple: 'šŸš€ ADVANCED LOGGER v2.0.0 - State-of-the-art Console Styling šŸš€',
251
+ style: 'background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; padding: 12px 20px; border-radius: 8px; font-weight: bold;'
252
+ },
253
+ dark: {
254
+ simple: 'šŸŒ™ ADVANCED LOGGER v2.0.0 - Dark Mode Console Excellence šŸŒ™',
255
+ style: 'background: linear-gradient(135deg, #1a202c 0%, #2d3748 100%); color: #e2e8f0; padding: 12px 20px; border-radius: 8px; font-weight: bold; border: 1px solid #4a5568;'
256
+ },
257
+ neon: {
258
+ simple: '⚔ ADVANCED LOGGER v2.0.0 - Cyberpunk Console Experience ⚔',
259
+ style: 'background: linear-gradient(135deg, #0f3460 0%, #e94560 100%); color: #00ffff; padding: 12px 20px; border-radius: 8px; font-weight: bold; text-shadow: 0 0 10px #00ffff;'
260
+ },
261
+ minimal: {
262
+ simple: 'ADVANCED LOGGER v2.0.0 - Clean Console Styling',
263
+ style: 'background: #f7fafc; color: #2d3748; padding: 8px 16px; border: 1px solid #e2e8f0; border-radius: 4px; font-weight: 500;'
264
+ }
265
+ };
266
+
267
+ /**
268
+ * Utility class for creating dynamic console styles with method chaining
269
+ */
270
+ class StyleBuilder {
271
+ private styles: string[] = [];
272
+
273
+ constructor(baseStyle = '') {
274
+ if (baseStyle) this.styles.push(baseStyle);
275
+ }
276
+
277
+ /**
278
+ * Add background color or gradient
279
+ */
280
+ bg(background: string): StyleBuilder {
281
+ this.styles.push(`background: ${background}`);
282
+ return this;
283
+ }
284
+
285
+ /**
286
+ * Add text color
287
+ */
288
+ color(color: string): StyleBuilder {
289
+ this.styles.push(`color: ${color}`);
290
+ return this;
291
+ }
292
+
293
+ /**
294
+ * Add border styling
295
+ */
296
+ border(border: string): StyleBuilder {
297
+ this.styles.push(`border: ${border}`);
298
+ return this;
299
+ }
300
+
301
+ /**
302
+ * Add box shadow
303
+ */
304
+ shadow(shadow: string): StyleBuilder {
305
+ this.styles.push(`box-shadow: ${shadow}`);
306
+ return this;
307
+ }
308
+
309
+ /**
310
+ * Add padding
311
+ */
312
+ padding(padding: string): StyleBuilder {
313
+ this.styles.push(`padding: ${padding}`);
314
+ return this;
315
+ }
316
+
317
+ /**
318
+ * Add border radius
319
+ */
320
+ rounded(radius: string = '4px'): StyleBuilder {
321
+ this.styles.push(`border-radius: ${radius}`);
322
+ return this;
323
+ }
324
+
325
+ /**
326
+ * Add font weight
327
+ */
328
+ bold(): StyleBuilder {
329
+ this.styles.push('font-weight: bold');
330
+ return this;
331
+ }
332
+
333
+ /**
334
+ * Add font styling
335
+ */
336
+ font(font: string): StyleBuilder {
337
+ this.styles.push(`font-family: ${font}`);
338
+ return this;
339
+ }
340
+
341
+ /**
342
+ * Add font size
343
+ */
344
+ size(size: string): StyleBuilder {
345
+ this.styles.push(`font-size: ${size}`);
346
+ return this;
347
+ }
348
+
349
+ /**
350
+ * Build the final CSS string
351
+ */
352
+ build(): string {
353
+ return this.styles.join('; ');
354
+ }
355
+ }
356
+
357
+ /**
358
+ * Proxy-based dynamic styler for chainable console styling
359
+ */
360
+ const createStyler = (): any => {
361
+ const builder = new StyleBuilder();
362
+ return new Proxy(builder, {
363
+ get(target: StyleBuilder, prop: string) {
364
+ if (prop in target) {
365
+ const method = (target as any)[prop];
366
+ if (typeof method === 'function') {
367
+ return method.bind(target);
368
+ }
369
+ return method;
370
+ }
371
+ return undefined;
372
+ }
373
+ });
374
+ };
375
+
376
+ /**
377
+ * Dynamic styler instance for external use
378
+ */
379
+ export const $ = createStyler();
380
+
381
+ /**
382
+ * Parses the current stack trace to extract caller information
383
+ */
384
+ function parseStackTrace(): StackInfo | null {
385
+ try {
386
+ const stack = new Error().stack;
387
+ if (!stack) return null;
388
+
389
+ const lines = stack.split('\n').filter(line => line.trim());
390
+
391
+ // Find the first caller that's not from Logger methods
392
+ for (let i = 1; i < lines.length; i++) {
393
+ const line = lines[i];
394
+
395
+ // Skip if line contains Logger methods or parseStackTrace
396
+ if (line.includes('parseStackTrace') ||
397
+ line.includes('Logger.') ||
398
+ line.includes('.log(') ||
399
+ line.includes('createStyledOutput')) {
400
+ continue;
401
+ }
402
+
403
+ // Parse different stack trace formats
404
+ let match;
405
+
406
+ // Chrome format: "at functionName (file:line:column)" or "at file:line:column"
407
+ const chromeMatch = line.match(/at\s+(?:(.+?)\s+\()?(.+?):(\d+):(\d+)\)?$/);
408
+ if (chromeMatch) {
409
+ match = chromeMatch;
410
+ } else {
411
+ // Firefox format: "functionName@file:line:column"
412
+ const firefoxMatch = line.match(/(.+?)@(.+?):(\d+):(\d+)$/);
413
+ if (firefoxMatch) {
414
+ match = firefoxMatch;
415
+ } else {
416
+ // Safari/other formats
417
+ const safariMatch = line.match(/(\S+)?@(.+?):(\d+):(\d+)$/);
418
+ if (safariMatch) {
419
+ match = safariMatch;
420
+ }
421
+ }
422
+ }
423
+
424
+ if (match) {
425
+ const [, functionName, file, line, column] = match;
426
+
427
+ return {
428
+ file: file?.split('/').pop()?.split('?')[0] || 'unknown',
429
+ line: parseInt(line, 10) || 0,
430
+ column: parseInt(column, 10) || 0,
431
+ function: functionName?.trim() || undefined,
432
+ };
433
+ }
434
+ }
435
+
436
+ return null;
437
+ } catch {
438
+ return null;
439
+ }
440
+ }
441
+
442
+ /**
443
+ * Formats the timestamp using modern Date API
444
+ */
445
+ function formatTimestamp(): string {
446
+ try {
447
+ // Use modern Temporal API if available, fallback to Date
448
+ const now = new Date();
449
+ return now.toISOString();
450
+ } catch {
451
+ return new Date().toISOString();
452
+ }
453
+ }
454
+
455
+ /**
456
+ * Creates styled console output with multiple %c formatters
457
+ */
458
+ function createStyledOutput(
459
+ level: LogLevel,
460
+ prefix: string | undefined,
461
+ message: string,
462
+ stackInfo: StackInfo | null
463
+ ): [string, ...string[]] {
464
+ const levelConfig = LEVEL_STYLES[level];
465
+ const timestamp = formatTimestamp();
466
+
467
+ // Base styles
468
+ const timestampStyle = new StyleBuilder()
469
+ .color('#666')
470
+ .size('11px')
471
+ .font('Monaco, Consolas, monospace')
472
+ .build();
473
+
474
+ const levelStyle = new StyleBuilder()
475
+ .bg(levelConfig.background)
476
+ .color(levelConfig.color)
477
+ .border(levelConfig.border)
478
+ .shadow(levelConfig.shadow)
479
+ .padding('2px 8px')
480
+ .rounded('4px')
481
+ .bold()
482
+ .font('Monaco, Consolas, monospace')
483
+ .size('12px')
484
+ .build();
485
+
486
+ const prefixStyle = new StyleBuilder()
487
+ .bg('#2d3748')
488
+ .color('#e2e8f0')
489
+ .padding('2px 6px')
490
+ .rounded('3px')
491
+ .bold()
492
+ .font('Monaco, Consolas, monospace')
493
+ .size('11px')
494
+ .build();
495
+
496
+ const messageStyle = new StyleBuilder()
497
+ .color('#2d3748')
498
+ .font('system-ui, -apple-system, sans-serif')
499
+ .size('14px')
500
+ .build();
501
+
502
+ const locationStyle = new StyleBuilder()
503
+ .color('#718096')
504
+ .size('11px')
505
+ .font('Monaco, Consolas, monospace')
506
+ .build();
507
+
508
+ // Build format string and styles
509
+ let format = `%c${timestamp.slice(11, 23)} %c${levelConfig.emoji} ${levelConfig.label}`;
510
+ const styles = [timestampStyle, levelStyle];
511
+
512
+ if (prefix) {
513
+ format += ` %c${prefix}`;
514
+ styles.push(prefixStyle);
515
+ }
516
+
517
+ format += ` %c${message}`;
518
+ styles.push(messageStyle);
519
+
520
+ if (stackInfo) {
521
+ format += ` %c(${stackInfo.file}:${stackInfo.line}:${stackInfo.column})`;
522
+ styles.push(locationStyle);
523
+ }
524
+
525
+ return [format, ...styles];
526
+ }
527
+
528
+ /**
529
+ * Main Logger class implementing state-of-the-art logging capabilities
530
+ */
531
+ export class Logger {
532
+ private config: LoggerConfig;
533
+ private scopedPrefix?: string;
534
+ private handlers: ILogHandler[] = [];
535
+ private timers: Map<string, TimerEntry> = new Map();
536
+ private groupDepth: number = 0;
537
+
538
+ /**
539
+ * Creates a new Logger instance
540
+ * @param config - Configuration options for the logger
541
+ */
542
+ constructor(config: Partial<LoggerConfig> = {}) {
543
+ this.config = {
544
+ verbosity: 'info',
545
+ enableColors: true,
546
+ enableTimestamps: true,
547
+ enableStackTrace: true,
548
+ theme: 'default',
549
+ bannerType: 'simple',
550
+ ...config,
551
+ };
552
+ }
553
+
554
+ /**
555
+ * Sets the global prefix for all log messages
556
+ * @param prefix - The prefix to add to all log messages
557
+ */
558
+ setGlobalPrefix(prefix: string): void {
559
+ this.config.globalPrefix = prefix;
560
+ }
561
+
562
+ /**
563
+ * Creates a scoped logger with a specific prefix
564
+ * @param prefix - The prefix for the scoped logger
565
+ * @returns A new Logger instance with the specified prefix
566
+ */
567
+ createScopedLogger(prefix: string): Logger {
568
+ const scopedLogger = new Logger(this.config);
569
+ scopedLogger.scopedPrefix = prefix;
570
+ scopedLogger.handlers = [...this.handlers];
571
+ return scopedLogger;
572
+ }
573
+
574
+ /**
575
+ * Sets the verbosity level for filtering log output
576
+ * @param level - The minimum log level to output
577
+ */
578
+ setVerbosity(level: Verbosity): void {
579
+ this.config.verbosity = level;
580
+ }
581
+
582
+ /**
583
+ * Adds a custom log handler for extensibility
584
+ * @param handler - The log handler to add
585
+ */
586
+ addHandler(handler: ILogHandler): void {
587
+ this.handlers.push(handler);
588
+ }
589
+
590
+ /**
591
+ * Checks if a log level should be output based on current verbosity
592
+ * @private
593
+ */
594
+ private shouldLog(level: LogLevel): boolean {
595
+ if (this.config.verbosity === 'silent') return false;
596
+ return LOG_LEVELS[level] >= LOG_LEVELS[this.config.verbosity];
597
+ }
598
+
599
+ /**
600
+ * Gets the effective prefix (global + scoped)
601
+ * @private
602
+ */
603
+ private getEffectivePrefix(): string | undefined {
604
+ const parts = [this.config.globalPrefix, this.scopedPrefix].filter(Boolean);
605
+ return parts.length > 0 ? parts.join(':') : undefined;
606
+ }
607
+
608
+ /**
609
+ * Core logging method that handles styling and formatting
610
+ * @private
611
+ */
612
+ private log(level: LogLevel, ...args: any[]): void {
613
+ if (!this.shouldLog(level)) return;
614
+
615
+ const stackInfo = this.config.enableStackTrace ? parseStackTrace() : null;
616
+ const prefix = this.getEffectivePrefix();
617
+ const message = args.length > 0 ? String(args[0]) : '';
618
+ const additionalArgs = args.slice(1);
619
+
620
+ // Create styled output
621
+ const [format, ...styles] = createStyledOutput(level, prefix, message, stackInfo);
622
+
623
+ // Add group indentation
624
+ const groupIndent = ' '.repeat(this.groupDepth);
625
+ const finalFormat = groupIndent + format;
626
+
627
+ // Output to console
628
+ if (additionalArgs.length > 0) {
629
+ console.log(finalFormat, ...styles, ...additionalArgs);
630
+ } else {
631
+ console.log(finalFormat, ...styles);
632
+ }
633
+
634
+ // Call custom handlers
635
+ const metadata: LogMetadata = {
636
+ timestamp: formatTimestamp(),
637
+ level,
638
+ prefix,
639
+ stackInfo: stackInfo || undefined,
640
+ };
641
+
642
+ this.handlers.forEach(handler => {
643
+ try {
644
+ handler.handle(level, message, args, metadata);
645
+ } catch (error) {
646
+ console.error('Log handler failed:', error);
647
+ }
648
+ });
649
+ }
650
+
651
+ /**
652
+ * Logs debug information (lowest priority)
653
+ * @param args - Arguments to log
654
+ */
655
+ debug(...args: any[]): void {
656
+ this.log('debug', ...args);
657
+ }
658
+
659
+ /**
660
+ * Logs informational messages
661
+ * @param args - Arguments to log
662
+ */
663
+ info(...args: any[]): void {
664
+ this.log('info', ...args);
665
+ }
666
+
667
+ /**
668
+ * Logs warning messages
669
+ * @param args - Arguments to log
670
+ */
671
+ warn(...args: any[]): void {
672
+ this.log('warn', ...args);
673
+ }
674
+
675
+ /**
676
+ * Logs error messages
677
+ * @param args - Arguments to log
678
+ */
679
+ error(...args: any[]): void {
680
+ this.log('error', ...args);
681
+ }
682
+
683
+ /**
684
+ * Logs success messages (special info level)
685
+ * @param args - Arguments to log
686
+ */
687
+ success(...args: any[]): void {
688
+ if (!this.shouldLog('info')) return;
689
+
690
+ const stackInfo = this.config.enableStackTrace ? parseStackTrace() : null;
691
+ const prefix = this.getEffectivePrefix();
692
+ const message = args.length > 0 ? String(args[0]) : '';
693
+ const additionalArgs = args.slice(1);
694
+
695
+ const successConfig = LEVEL_STYLES.success;
696
+ const timestamp = formatTimestamp();
697
+
698
+ const timestampStyle = new StyleBuilder()
699
+ .color('#666')
700
+ .size('11px')
701
+ .font('Monaco, Consolas, monospace')
702
+ .build();
703
+
704
+ const levelStyle = new StyleBuilder()
705
+ .bg(successConfig.background)
706
+ .color(successConfig.color)
707
+ .border(successConfig.border)
708
+ .shadow(successConfig.shadow)
709
+ .padding('2px 8px')
710
+ .rounded('4px')
711
+ .bold()
712
+ .font('Monaco, Consolas, monospace')
713
+ .size('12px')
714
+ .build();
715
+
716
+ const prefixStyle = new StyleBuilder()
717
+ .bg('#2d3748')
718
+ .color('#e2e8f0')
719
+ .padding('2px 6px')
720
+ .rounded('3px')
721
+ .bold()
722
+ .font('Monaco, Consolas, monospace')
723
+ .size('11px')
724
+ .build();
725
+
726
+ const messageStyle = new StyleBuilder()
727
+ .color('#2d3748')
728
+ .font('system-ui, -apple-system, sans-serif')
729
+ .size('14px')
730
+ .build();
731
+
732
+ const locationStyle = new StyleBuilder()
733
+ .color('#718096')
734
+ .size('11px')
735
+ .font('Monaco, Consolas, monospace')
736
+ .build();
737
+
738
+ let format = `%c${timestamp.slice(11, 23)} %c${successConfig.emoji} ${successConfig.label}`;
739
+ const styles = [timestampStyle, levelStyle];
740
+
741
+ if (prefix) {
742
+ format += ` %c${prefix}`;
743
+ styles.push(prefixStyle);
744
+ }
745
+
746
+ format += ` %c${message}`;
747
+ styles.push(messageStyle);
748
+
749
+ if (stackInfo) {
750
+ format += ` %c(${stackInfo.file}:${stackInfo.line}:${stackInfo.column})`;
751
+ styles.push(locationStyle);
752
+ }
753
+
754
+ const groupIndent = ' '.repeat(this.groupDepth);
755
+ const finalFormat = groupIndent + format;
756
+
757
+ if (additionalArgs.length > 0) {
758
+ console.log(finalFormat, ...styles, ...additionalArgs);
759
+ } else {
760
+ console.log(finalFormat, ...styles);
761
+ }
762
+
763
+ const metadata: LogMetadata = {
764
+ timestamp: formatTimestamp(),
765
+ level: 'info',
766
+ prefix,
767
+ stackInfo: stackInfo || undefined,
768
+ };
769
+
770
+ this.handlers.forEach(handler => {
771
+ try {
772
+ handler.handle('info', message, args, metadata);
773
+ } catch (error) {
774
+ console.error('Log handler failed:', error);
775
+ }
776
+ });
777
+ }
778
+
779
+ /**
780
+ * Logs trace information (detailed debugging)
781
+ * @param args - Arguments to log
782
+ */
783
+ trace(...args: any[]): void {
784
+ this.log('debug', ...args);
785
+ if (this.shouldLog('debug')) {
786
+ console.trace(...args);
787
+ }
788
+ }
789
+
790
+ /**
791
+ * Logs critical errors (highest priority)
792
+ * @param args - Arguments to log
793
+ */
794
+ critical(...args: any[]): void {
795
+ this.log('critical', ...args);
796
+ }
797
+
798
+ /**
799
+ * Displays data in a table format
800
+ * @param data - The data to display in table format
801
+ * @param columns - Optional column names to display
802
+ */
803
+ table(data: any, columns?: string[]): void {
804
+ if (!this.shouldLog('info')) return;
805
+
806
+ const prefix = this.getEffectivePrefix();
807
+ const tableStyle = new StyleBuilder()
808
+ .bg('linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%)')
809
+ .color('#495057')
810
+ .border('1px solid #dee2e6')
811
+ .padding('4px 8px')
812
+ .rounded('4px')
813
+ .bold()
814
+ .build();
815
+
816
+ const format = `%cšŸ“Š TABLE${prefix ? ` [${prefix}]` : ''}`;
817
+ console.log(format, tableStyle);
818
+
819
+ if (columns) {
820
+ console.table(data, columns);
821
+ } else {
822
+ console.table(data);
823
+ }
824
+ }
825
+
826
+ /**
827
+ * Starts a collapsible group in the console
828
+ * @param label - The label for the group
829
+ * @param collapsed - Whether the group should start collapsed
830
+ */
831
+ group(label: string, collapsed: boolean = false): void {
832
+ const groupStyle = new StyleBuilder()
833
+ .bg('linear-gradient(135deg, #e3f2fd 0%, #bbdefb 100%)')
834
+ .color('#1565c0')
835
+ .border('1px solid #90caf9')
836
+ .padding('4px 12px')
837
+ .rounded('6px')
838
+ .bold()
839
+ .build();
840
+
841
+ const format = `%cšŸ“ ${label}`;
842
+
843
+ if (collapsed) {
844
+ console.groupCollapsed(format, groupStyle);
845
+ } else {
846
+ console.group(format, groupStyle);
847
+ }
848
+
849
+ this.groupDepth++;
850
+ }
851
+
852
+ /**
853
+ * Ends the current console group
854
+ */
855
+ groupEnd(): void {
856
+ if (this.groupDepth > 0) {
857
+ console.groupEnd();
858
+ this.groupDepth--;
859
+ }
860
+ }
861
+
862
+ /**
863
+ * Starts a timer with the given label
864
+ * @param label - The timer label
865
+ */
866
+ time(label: string): void {
867
+ const timer: TimerEntry = {
868
+ label,
869
+ startTime: performance.now(),
870
+ };
871
+ this.timers.set(label, timer);
872
+
873
+ const timerStyle = new StyleBuilder()
874
+ .bg('linear-gradient(135deg, #fff3cd 0%, #ffeaa7 100%)')
875
+ .color('#856404')
876
+ .border('1px solid #ffeaa7')
877
+ .padding('2px 6px')
878
+ .rounded('3px')
879
+ .font('Monaco, Consolas, monospace')
880
+ .size('12px')
881
+ .build();
882
+
883
+ console.log(`%cā±ļø Timer started: ${label}`, timerStyle);
884
+ }
885
+
886
+ /**
887
+ * Ends a timer and logs the elapsed time
888
+ * @param label - The timer label to end
889
+ */
890
+ timeEnd(label: string): void {
891
+ const timer = this.timers.get(label);
892
+ if (!timer) {
893
+ this.warn(`Timer '${label}' does not exist`);
894
+ return;
895
+ }
896
+
897
+ const elapsed = performance.now() - timer.startTime;
898
+ this.timers.delete(label);
899
+
900
+ const timerStyle = new StyleBuilder()
901
+ .bg('linear-gradient(135deg, #d4edda 0%, #c3e6cb 100%)')
902
+ .color('#155724')
903
+ .border('1px solid #c3e6cb')
904
+ .padding('2px 6px')
905
+ .rounded('3px')
906
+ .font('Monaco, Consolas, monospace')
907
+ .size('12px')
908
+ .bold()
909
+ .build();
910
+
911
+ console.log(`%cā±ļø Timer ended: ${label} - ${elapsed.toFixed(2)}ms`, timerStyle);
912
+ }
913
+
914
+ /**
915
+ * Logs with custom SVG animation (experimental feature)
916
+ * @param message - The message to log
917
+ * @param svgAnimation - SVG with animation as data URI
918
+ */
919
+ logWithAnimation(message: string, svgAnimation?: string): void {
920
+ if (svgAnimation) {
921
+ const animatedStyle = new StyleBuilder()
922
+ .bg(`url("${svgAnimation}") no-repeat left center`)
923
+ .padding('20px 20px 20px 40px')
924
+ .border('2px solid #ff6b6b')
925
+ .rounded('8px')
926
+ .color('#2d3748')
927
+ .bold()
928
+ .build();
929
+
930
+ console.log(`%c${message}`, animatedStyle);
931
+ } else {
932
+ this.info(message);
933
+ }
934
+ }
935
+
936
+ /**
937
+ * Groups logs by a specific property using modern Object.groupBy (when available)
938
+ * @param items - Items to group and log
939
+ * @param groupBy - Function to extract grouping key
940
+ */
941
+ logGrouped<T>(items: T[], groupBy: (item: T) => string): void {
942
+ try {
943
+ // Use Object.groupBy if available (ES2024)
944
+ const grouped = (Object as any).groupBy?.(items, groupBy) ||
945
+ items.reduce((acc, item) => {
946
+ const key = groupBy(item);
947
+ if (!acc[key]) acc[key] = [];
948
+ acc[key].push(item);
949
+ return acc;
950
+ }, {} as Record<string, T[]>);
951
+
952
+ Object.entries(grouped).forEach(([group, groupItems]) => {
953
+ this.group(`Group: ${group}`);
954
+ this.table(groupItems);
955
+ this.groupEnd();
956
+ });
957
+ } catch {
958
+ // Fallback to simple logging
959
+ this.info('Grouped data:', items);
960
+ }
961
+ }
962
+
963
+ /**
964
+ * Sets the logger theme
965
+ * @param theme - Theme variant to apply
966
+ */
967
+ setTheme(theme: ThemeVariant): void {
968
+ if (theme in THEME_PRESETS) {
969
+ LEVEL_STYLES = (THEME_PRESETS as any)[theme];
970
+ this.config.theme = theme;
971
+
972
+ // Show theme-specific banner
973
+ if (theme in THEME_BANNERS) {
974
+ const themeBanner = (THEME_BANNERS as any)[theme];
975
+ console.log(`%c${themeBanner.simple}`, themeBanner.style);
976
+ }
977
+
978
+ this.success(`Theme changed to: ${theme}`);
979
+ } else {
980
+ this.error(`Invalid theme: ${theme}. Available themes:`, Object.keys(THEME_PRESETS));
981
+ }
982
+ }
983
+
984
+ /**
985
+ * CLI command processor for logger configuration
986
+ * @param command - Command string starting with /
987
+ */
988
+ cli(command: string): void {
989
+ if (!command.startsWith('/')) {
990
+ this.error('Invalid command. Commands must start with /');
991
+ return;
992
+ }
993
+
994
+ const parts = command.slice(1).split(' ');
995
+ const cmd = parts[0];
996
+ const args = parts.slice(1).join(' ');
997
+
998
+ switch (cmd) {
999
+ case 'config':
1000
+ this.handleConfigCommand(args);
1001
+ break;
1002
+ case 'help':
1003
+ this.showHelp();
1004
+ break;
1005
+ case 'themes':
1006
+ this.showThemes();
1007
+ break;
1008
+ case 'banners':
1009
+ this.showBanners();
1010
+ break;
1011
+ case 'banner':
1012
+ this.handleBannerCommand(args);
1013
+ break;
1014
+ case 'status':
1015
+ this.showStatus();
1016
+ break;
1017
+ case 'reset':
1018
+ this.resetConfig();
1019
+ break;
1020
+ case 'demo':
1021
+ this.showDemo();
1022
+ break;
1023
+ default:
1024
+ this.error(`Unknown command: ${cmd}. Type /help for available commands.`);
1025
+ }
1026
+ }
1027
+
1028
+ /**
1029
+ * Handle /config command with JSON or key-value pairs
1030
+ * @private
1031
+ */
1032
+ private handleConfigCommand(args: string): void {
1033
+ if (!args) {
1034
+ this.showStatus();
1035
+ return;
1036
+ }
1037
+
1038
+ try {
1039
+ // Try to parse as JSON first
1040
+ if (args.startsWith('{')) {
1041
+ const config = JSON.parse(args);
1042
+ this.applyConfig(config);
1043
+ } else {
1044
+ // Parse key=value pairs
1045
+ const pairs = args.split(',').map(pair => pair.trim().split('='));
1046
+ const config: any = {};
1047
+ pairs.forEach(([key, value]) => {
1048
+ if (key && value) {
1049
+ config[key.trim()] = value.trim().replace(/["']/g, '');
1050
+ }
1051
+ });
1052
+ this.applyConfig(config);
1053
+ }
1054
+ } catch (error) {
1055
+ this.error('Invalid config format. Use JSON or key=value pairs:', error);
1056
+ this.info('Examples: /config {"theme":"dark"} or /config theme=neon,verbosity=debug');
1057
+ }
1058
+ }
1059
+
1060
+ /**
1061
+ * Apply configuration object to logger
1062
+ * @private
1063
+ */
1064
+ private applyConfig(config: any): void {
1065
+ const validKeys = ['theme', 'verbosity', 'enableColors', 'enableTimestamps', 'enableStackTrace', 'globalPrefix', 'bannerType'];
1066
+ const applied: string[] = [];
1067
+
1068
+ Object.entries(config).forEach(([key, value]) => {
1069
+ if (validKeys.includes(key)) {
1070
+ if (key === 'theme' && typeof value === 'string') {
1071
+ this.setTheme(value as ThemeVariant);
1072
+ applied.push(`${key}=${value}`);
1073
+ } else if (key === 'bannerType' && typeof value === 'string') {
1074
+ this.setBannerType(value as BannerType);
1075
+ applied.push(`${key}=${value}`);
1076
+ } else if (key === 'verbosity') {
1077
+ this.setVerbosity(value as Verbosity);
1078
+ applied.push(`${key}=${value}`);
1079
+ } else if (key === 'globalPrefix') {
1080
+ this.setGlobalPrefix(value as string);
1081
+ applied.push(`${key}=${value}`);
1082
+ } else {
1083
+ (this.config as any)[key] = value;
1084
+ applied.push(`${key}=${value}`);
1085
+ }
1086
+ } else {
1087
+ this.warn(`Invalid config key: ${key}`);
1088
+ }
1089
+ });
1090
+
1091
+ if (applied.length > 0) {
1092
+ this.success(`Configuration updated: ${applied.join(', ')}`);
1093
+ }
1094
+ }
1095
+
1096
+ /**
1097
+ * Show CLI help information
1098
+ * @private
1099
+ */
1100
+ private showHelp(): void {
1101
+ const helpStyle = new StyleBuilder()
1102
+ .bg('linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%)')
1103
+ .color('#495057')
1104
+ .padding('15px 20px')
1105
+ .rounded('8px')
1106
+ .border('1px solid #dee2e6')
1107
+ .font('Monaco, Consolas, monospace')
1108
+ .size('13px')
1109
+ .build();
1110
+
1111
+ const helpText = `
1112
+ ╭─────────────── LOGGER CLI COMMANDS ───────────────╮
1113
+ │ │
1114
+ │ /help Show this help message │
1115
+ │ /config Show current configuration │
1116
+ │ /config {json} Apply JSON configuration │
1117
+ │ /config key=val Apply key-value config │
1118
+ │ /themes Show available themes │
1119
+ │ /banners Show available banner types │
1120
+ │ /banner [type] Change/show banner type │
1121
+ │ /status Show logger status │
1122
+ │ /demo Show feature demonstration │
1123
+ │ /reset Reset to default config │
1124
+ │ │
1125
+ ā”œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ CONFIGURATION OPTIONS ─────────────┤
1126
+ │ │
1127
+ │ theme: default | dark | neon | minimal │
1128
+ │ bannerType: simple | ascii | unicode | svg │
1129
+ │ verbosity: debug | info | warn | error | silent │
1130
+ │ enableColors: true | false │
1131
+ │ enableTimestamps: true | false │
1132
+ │ enableStackTrace: true | false │
1133
+ │ globalPrefix: "string" │
1134
+ │ │
1135
+ ā”œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ EXAMPLES ─────────────────────┤
1136
+ │ │
1137
+ │ /config {"theme":"dark","bannerType":"animated"} │
1138
+ │ /config theme=neon,verbosity=debug │
1139
+ │ /banner svg Change to SVG banner │
1140
+ │ /demo Show all features │
1141
+ │ │
1142
+ ╰───────────────────────────────────────────────────╯`;
1143
+
1144
+ console.log(`%c${helpText}`, helpStyle);
1145
+ }
1146
+
1147
+ /**
1148
+ * Show available themes with previews
1149
+ * @private
1150
+ */
1151
+ private showThemes(): void {
1152
+ this.group('šŸŽØ Available Themes');
1153
+ Object.keys(THEME_PRESETS).forEach(themeName => {
1154
+ const preview = (THEME_PRESETS as any)[themeName];
1155
+ const previewStyle = new StyleBuilder()
1156
+ .bg(preview.info.background)
1157
+ .color(preview.info.color)
1158
+ .padding('4px 8px')
1159
+ .rounded('4px')
1160
+ .border(preview.info.border)
1161
+ .build();
1162
+
1163
+ console.log(`%c${themeName}`, previewStyle, `- ${themeName} theme preview`);
1164
+ });
1165
+ this.groupEnd();
1166
+ }
1167
+
1168
+ /**
1169
+ * Show available banner types with previews
1170
+ * @private
1171
+ */
1172
+ private showBanners(): void {
1173
+ this.group('šŸ–¼ļø Available Banner Types');
1174
+ Object.keys(BANNER_VARIANTS).forEach(bannerName => {
1175
+ const banner = (BANNER_VARIANTS as any)[bannerName];
1176
+ console.log(`%c${bannerName}`, 'font-weight: bold; color: #667eea;');
1177
+ console.log(`%cPreview:`, 'color: #666; font-size: 12px;');
1178
+
1179
+ // Show a mini preview
1180
+ if (bannerName === 'simple') {
1181
+ console.log(`%c${banner.text}`, banner.style);
1182
+ } else if (bannerName === 'ascii') {
1183
+ console.log(`%c${banner.text.split('\n').slice(1, 4).join('\n')}...`, 'font-family: monospace; color: #667eea; font-size: 10px;');
1184
+ } else if (bannerName === 'unicode') {
1185
+ console.log(`%c${banner.text}`, banner.style);
1186
+ } else {
1187
+ console.log(`%c${bannerName} banner`, 'color: #666; font-style: italic;');
1188
+ }
1189
+ });
1190
+ this.groupEnd();
1191
+ }
1192
+
1193
+ /**
1194
+ * Handle banner command
1195
+ * @private
1196
+ */
1197
+ private handleBannerCommand(args: string): void {
1198
+ if (!args) {
1199
+ this.showBanner();
1200
+ return;
1201
+ }
1202
+
1203
+ if (args in BANNER_VARIANTS) {
1204
+ this.setBannerType(args as BannerType);
1205
+ this.showBanner();
1206
+ } else {
1207
+ this.error(`Invalid banner type: ${args}. Available: ${Object.keys(BANNER_VARIANTS).join(', ')}`);
1208
+ }
1209
+ }
1210
+
1211
+ /**
1212
+ * Show demonstration of all logger features
1213
+ * @private
1214
+ */
1215
+ private showDemo(): void {
1216
+ this.group('šŸŽŖ Advanced Logger Demo');
1217
+
1218
+ // Basic logging demo
1219
+ this.debug('Debug message with detailed information');
1220
+ this.info('Informational message about system state');
1221
+ this.warn('Warning about deprecated feature');
1222
+ this.error('Error processing user request');
1223
+ this.success('Operation completed successfully');
1224
+ this.critical('Critical system failure detected');
1225
+
1226
+ // Advanced features demo
1227
+ this.group('šŸ“Š Advanced Features Demo');
1228
+
1229
+ // Table demo
1230
+ this.table([
1231
+ { feature: 'Styled Console', status: 'āœ… Active', performance: 'Excellent' },
1232
+ { feature: 'Theme System', status: 'āœ… Active', performance: 'Great' },
1233
+ { feature: 'CLI Interface', status: 'āœ… Active', performance: 'Good' }
1234
+ ]);
1235
+
1236
+ // Timer demo
1237
+ this.time('demo-operation');
1238
+ setTimeout(() => {
1239
+ this.timeEnd('demo-operation');
1240
+ }, 100);
1241
+
1242
+ // SVG demo
1243
+ this.logWithSVG('SVG Demo');
1244
+
1245
+ // Animated demo
1246
+ this.logAnimated('🌟 Animated Logger Demo 🌟', 2);
1247
+
1248
+ this.groupEnd();
1249
+ this.groupEnd();
1250
+ }
1251
+
1252
+ /**
1253
+ * Show current logger status and configuration
1254
+ * @private
1255
+ */
1256
+ private showStatus(): void {
1257
+ const statusData = {
1258
+ theme: this.config.theme || 'default',
1259
+ verbosity: this.config.verbosity,
1260
+ colors: this.config.enableColors,
1261
+ timestamps: this.config.enableTimestamps,
1262
+ stackTrace: this.config.enableStackTrace,
1263
+ globalPrefix: this.config.globalPrefix || 'none',
1264
+ handlers: this.handlers.length
1265
+ };
1266
+
1267
+ this.group('āš™ļø Logger Configuration');
1268
+ this.table(statusData);
1269
+ this.groupEnd();
1270
+ }
1271
+
1272
+ /**
1273
+ * Reset logger to default configuration
1274
+ * @private
1275
+ */
1276
+ private resetConfig(): void {
1277
+ this.config = {
1278
+ verbosity: 'info',
1279
+ enableColors: true,
1280
+ enableTimestamps: true,
1281
+ enableStackTrace: true,
1282
+ theme: 'default',
1283
+ bannerType: 'simple',
1284
+ };
1285
+ LEVEL_STYLES = THEME_PRESETS.default;
1286
+ this.success('Logger configuration reset to defaults');
1287
+ }
1288
+
1289
+ /**
1290
+ * Set banner type for initialization display
1291
+ * @param bannerType - Type of banner to display
1292
+ */
1293
+ setBannerType(bannerType: BannerType): void {
1294
+ this.config.bannerType = bannerType;
1295
+ this.success(`Banner type changed to: ${bannerType}`);
1296
+ }
1297
+
1298
+ /**
1299
+ * Display banner with specified or configured type
1300
+ * @param bannerType - Optional banner type override
1301
+ */
1302
+ showBanner(bannerType?: BannerType): void {
1303
+ displayInitBanner(bannerType || this.config.bannerType);
1304
+ }
1305
+
1306
+ /**
1307
+ * Log with SVG background image
1308
+ * @param message - The message to log
1309
+ * @param svgContent - SVG content as string
1310
+ * @param options - Additional styling options
1311
+ */
1312
+ logWithSVG(message: string, svgContent?: string, options: { width?: number, height?: number, padding?: string } = {}): void {
1313
+ const { width = 300, height = 60, padding = '30px 150px' } = options;
1314
+
1315
+ let svgDataUri = '';
1316
+ if (svgContent) {
1317
+ // Encode SVG for data URI
1318
+ const encodedSVG = encodeURIComponent(svgContent);
1319
+ svgDataUri = `data:image/svg+xml,${encodedSVG}`;
1320
+ } else {
1321
+ // Default animated SVG
1322
+ const defaultSVG = `<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 ${width} ${height}'><defs><linearGradient id='grad' x1='0%' y1='0%' x2='100%' y2='0%'><stop offset='0%' style='stop-color:%23667eea'/><stop offset='100%' style='stop-color:%23764ba2'/></linearGradient></defs><rect width='100%' height='100%' fill='url(%23grad)' rx='4'/><text x='${width/2}' y='${height/2 + 5}' text-anchor='middle' fill='white' font-family='monospace' font-size='14' font-weight='bold'>${message}</text></svg>`;
1323
+ svgDataUri = `data:image/svg+xml,${encodeURIComponent(defaultSVG)}`;
1324
+ }
1325
+
1326
+ const svgStyle = new StyleBuilder()
1327
+ .bg(`url("${svgDataUri}") no-repeat center center`)
1328
+ .size(`${width}px ${height}px`)
1329
+ .padding(padding)
1330
+ .color('transparent')
1331
+ .rounded('4px')
1332
+ .build();
1333
+
1334
+ console.log(`%c${message}`, svgStyle);
1335
+ }
1336
+
1337
+ /**
1338
+ * Log with animated background gradient
1339
+ * @param message - The message to log
1340
+ * @param duration - Animation duration in seconds
1341
+ */
1342
+ logAnimated(message: string, duration: number = 3): void {
1343
+ // Inject CSS animation if not already present
1344
+ if (!document.getElementById('logger-animations')) {
1345
+ const style = document.createElement('style');
1346
+ style.id = 'logger-animations';
1347
+ style.textContent = `
1348
+ @keyframes loggerGradient {
1349
+ 0% { background-position: 0% 50%; }
1350
+ 50% { background-position: 100% 50%; }
1351
+ 100% { background-position: 0% 50%; }
1352
+ }
1353
+ @keyframes loggerPulse {
1354
+ 0%, 100% { opacity: 1; transform: scale(1); }
1355
+ 50% { opacity: 0.8; transform: scale(1.05); }
1356
+ }
1357
+ @keyframes loggerSlide {
1358
+ 0% { transform: translateX(-100%); opacity: 0; }
1359
+ 100% { transform: translateX(0); opacity: 1; }
1360
+ }
1361
+ `;
1362
+ document.head.appendChild(style);
1363
+ }
1364
+
1365
+ const animatedStyle = new StyleBuilder()
1366
+ .bg('linear-gradient(-45deg, #667eea, #764ba2, #667eea, #764ba2)')
1367
+ .size('400% 400%')
1368
+ .color('#ffffff')
1369
+ .padding('12px 20px')
1370
+ .rounded('8px')
1371
+ .bold()
1372
+ .font('Monaco, Consolas, monospace')
1373
+ .build() + `; animation: loggerGradient ${duration}s ease infinite; display: inline-block;`;
1374
+
1375
+ console.log(`%c${message}`, animatedStyle);
1376
+ }
1377
+ }
1378
+
1379
+ /**
1380
+ * File-based log handler for persistent logging
1381
+ */
1382
+ export class FileLogHandler implements ILogHandler {
1383
+ private filename: string;
1384
+
1385
+ constructor(filename: string = 'app.log') {
1386
+ this.filename = filename;
1387
+ }
1388
+
1389
+ handle(level: LogLevel, message: string, args: any[], metadata: LogMetadata): void {
1390
+ const logEntry = {
1391
+ filename: this.filename,
1392
+ timestamp: metadata.timestamp,
1393
+ level: level.toUpperCase(),
1394
+ prefix: metadata.prefix,
1395
+ message,
1396
+ args: args.slice(1),
1397
+ location: metadata.stackInfo,
1398
+ };
1399
+
1400
+ console.debug('File log entry:', logEntry);
1401
+ }
1402
+ }
1403
+
1404
+ /**
1405
+ * Remote log handler for sending logs to external services
1406
+ */
1407
+ export class RemoteLogHandler implements ILogHandler {
1408
+ private endpoint: string;
1409
+ private apiKey?: string;
1410
+
1411
+ constructor(endpoint: string, apiKey?: string) {
1412
+ this.endpoint = endpoint;
1413
+ this.apiKey = apiKey;
1414
+ }
1415
+
1416
+ async handle(level: LogLevel, message: string, args: any[], metadata: LogMetadata): Promise<void> {
1417
+ try {
1418
+ const payload = {
1419
+ timestamp: metadata.timestamp,
1420
+ level,
1421
+ message,
1422
+ prefix: metadata.prefix,
1423
+ location: metadata.stackInfo,
1424
+ additional: args.slice(1),
1425
+ };
1426
+
1427
+ await fetch(this.endpoint, {
1428
+ method: 'POST',
1429
+ headers: {
1430
+ 'Content-Type': 'application/json',
1431
+ ...(this.apiKey && { 'Authorization': `Bearer ${this.apiKey}` }),
1432
+ },
1433
+ body: JSON.stringify(payload),
1434
+ });
1435
+ } catch (error) {
1436
+ // Silently fail to avoid infinite logging loops
1437
+ }
1438
+ }
1439
+ }
1440
+
1441
+ /**
1442
+ * Banner variants for different display capabilities
1443
+ */
1444
+ const BANNER_VARIANTS = {
1445
+ simple: {
1446
+ text: 'šŸš€ ADVANCED LOGGER v2.0.0 - State-of-the-art Console Styling šŸš€',
1447
+ style: 'background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; padding: 12px 20px; border-radius: 8px; font-weight: bold; font-size: 14px;'
1448
+ },
1449
+ ascii: {
1450
+ text: `
1451
+ ___ ____ _ __ ___ _ __ _____ _____ ____ __ ___ _____ _____ _____ ____
1452
+ / _ \\ / __ \\| | / / / _ \\ | \\ | |/ ____| ____| _ \\ | | / _ \\ / ____| ___| _ | _ \\
1453
+ / /_\\ \\ / / _\` | |/ / / /_\\ \\ | \\| | | | |__ | | | | | | / / \\ \\| | __| |_ | |_| | |_) |
1454
+ | _ || | (_| | < | _ | | . \` | | | __| | | | | | | | | | | | |_ | _| | /| _ <
1455
+ | | | |\\ \\__,_|_|\\_\\ | | | | | |\\ | |___| |____| |_| | | |__\\ \\_/ /| |__| | |___| |\\ \\| |_) |
1456
+ \\_| |_/ \\____/ \\_| |_/ |_| \\_|\\_____|______|____/ |_____/\\___/ \\_____|_____|_| \\_|____/
1457
+
1458
+ Advanced Logger v2.0.0 - Console Excellence`,
1459
+ style: 'font-family: "Courier New", Consolas, Monaco, monospace; color: #667eea; font-size: 11px; line-height: 1.2;'
1460
+ },
1461
+ unicode: {
1462
+ text: `
1463
+ ╔══════════════════════════════════════════════════════════════════════════╗
1464
+ ā•‘ šŸš€ ADVANCED LOGGER v2.0.0 ā•‘
1465
+ ā•‘ State-of-the-art Console Styling ā•‘
1466
+ ā•šā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•`,
1467
+ style: 'font-family: "Courier New", Consolas, Monaco, monospace; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; padding: 15px; border-radius: 8px; font-size: 12px; line-height: 1.3;'
1468
+ },
1469
+ svg: {
1470
+ text: ' ',
1471
+ style: `
1472
+ background-image: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 400 80'><defs><linearGradient id='grad' x1='0%' y1='0%' x2='100%' y2='0%'><stop offset='0%' style='stop-color:%23667eea'/><stop offset='100%' style='stop-color:%23764ba2'/></linearGradient></defs><rect width='100%' height='100%' fill='url(%23grad)' rx='8'/><text x='200' y='30' text-anchor='middle' fill='white' font-family='monospace' font-size='14' font-weight='bold'>šŸš€ ADVANCED LOGGER</text><text x='200' y='50' text-anchor='middle' fill='white' font-family='monospace' font-size='12'>State-of-the-art Console Styling</text><text x='200' y='65' text-anchor='middle' fill='white' font-family='monospace' font-size='10'>v2.0.0</text></svg>");
1473
+ background-repeat: no-repeat;
1474
+ background-size: 400px 80px;
1475
+ padding: 40px 200px;
1476
+ color: transparent;
1477
+ display: inline-block;
1478
+ border-radius: 8px;
1479
+ `
1480
+ },
1481
+ animated: {
1482
+ text: ' šŸš€ ADVANCED LOGGER v2.0.0 ',
1483
+ style: `
1484
+ background: linear-gradient(-45deg, #667eea, #764ba2, #667eea, #764ba2);
1485
+ background-size: 400% 400%;
1486
+ color: white;
1487
+ padding: 15px 25px;
1488
+ border-radius: 10px;
1489
+ font-weight: bold;
1490
+ font-size: 14px;
1491
+ font-family: monospace;
1492
+ animation: gradientShift 3s ease infinite;
1493
+ display: inline-block;
1494
+ `
1495
+ }
1496
+ };
1497
+
1498
+ /**
1499
+ * Feature detection for banner capabilities
1500
+ */
1501
+ function detectBannerCapabilities(): BannerType {
1502
+ // Try to detect browser capabilities
1503
+ const userAgent = navigator.userAgent;
1504
+ const isChrome = /Chrome/.test(userAgent);
1505
+ const isFirefox = /Firefox/.test(userAgent);
1506
+ const isSafari = /Safari/.test(userAgent) && !/Chrome/.test(userAgent);
1507
+
1508
+ // Check for SVG support (most modern browsers)
1509
+ const supportsSVG = !!document.createElementNS && !!document.createElementNS('http://www.w3.org/2000/svg', 'svg').createSVGRect;
1510
+
1511
+ // Check for CSS animation support
1512
+ const supportsAnimations = typeof document !== 'undefined' &&
1513
+ 'animationName' in document.createElement('div').style;
1514
+
1515
+ // Progressive enhancement
1516
+ if (supportsAnimations && isChrome) {
1517
+ return 'animated';
1518
+ } else if (supportsSVG && (isChrome || isFirefox)) {
1519
+ return 'svg';
1520
+ } else if (isChrome || isFirefox) {
1521
+ return 'unicode';
1522
+ } else if (isSafari) {
1523
+ return 'ascii';
1524
+ }
1525
+
1526
+ return 'simple';
1527
+ }
1528
+
1529
+ /**
1530
+ * Display initialization banner with advanced styling
1531
+ */
1532
+ function displayInitBanner(bannerType?: BannerType): void {
1533
+ const selectedType = bannerType || detectBannerCapabilities();
1534
+ const banner = BANNER_VARIANTS[selectedType];
1535
+
1536
+ // Add CSS animation keyframes if needed
1537
+ if (selectedType === 'animated') {
1538
+ const style = document.createElement('style');
1539
+ style.textContent = `
1540
+ @keyframes gradientShift {
1541
+ 0% { background-position: 0% 50%; }
1542
+ 50% { background-position: 100% 50%; }
1543
+ 100% { background-position: 0% 50%; }
1544
+ }
1545
+ `;
1546
+ document.head.appendChild(style);
1547
+ }
1548
+
1549
+ console.log(`%c${banner.text}`, banner.style);
1550
+
1551
+ // Show feature highlights
1552
+ const features = [
1553
+ 'šŸŽØ Advanced CSS Console Styling',
1554
+ 'šŸ“ Automatic Stack Trace Parsing',
1555
+ 'šŸ”§ Scoped Loggers & Prefixes',
1556
+ '⚔ Performance Timers',
1557
+ 'šŸŽÆ Verbosity Filtering',
1558
+ 'šŸ”Œ Extensible Handlers',
1559
+ 'šŸ“± Modern TypeScript Patterns'
1560
+ ];
1561
+
1562
+ console.group(`%c✨ Features`, new StyleBuilder()
1563
+ .bg('#f8f9fa')
1564
+ .color('#495057')
1565
+ .padding('4px 8px')
1566
+ .rounded('4px')
1567
+ .bold()
1568
+ .build());
1569
+
1570
+ features.forEach(feature => {
1571
+ console.log(`%c${feature}`, new StyleBuilder()
1572
+ .color('#6c757d')
1573
+ .size('13px')
1574
+ .build());
1575
+ });
1576
+
1577
+ console.groupEnd();
1578
+ console.log(''); // Add spacing
1579
+ }
1580
+
1581
+ /**
1582
+ * Create and export the singleton logger instance
1583
+ */
1584
+ const defaultLogger = new Logger({
1585
+ verbosity: 'info',
1586
+ enableColors: true,
1587
+ enableTimestamps: true,
1588
+ enableStackTrace: true,
1589
+ });
1590
+
1591
+ // Display initialization banner with auto-detection
1592
+ displayInitBanner();
1593
+
1594
+ // Export the singleton instance as default
1595
+ export default defaultLogger;
1596
+
1597
+ /**
1598
+ * Export individual methods for convenience (with proper binding)
1599
+ */
1600
+ export const debug = (...args: any[]) => defaultLogger.debug(...args);
1601
+ export const info = (...args: any[]) => defaultLogger.info(...args);
1602
+ export const warn = (...args: any[]) => defaultLogger.warn(...args);
1603
+ export const error = (...args: any[]) => defaultLogger.error(...args);
1604
+ export const success = (...args: any[]) => defaultLogger.success(...args);
1605
+ export const trace = (...args: any[]) => defaultLogger.trace(...args);
1606
+ export const critical = (...args: any[]) => defaultLogger.critical(...args);
1607
+ export const table = (data: any, columns?: string[]) => defaultLogger.table(data, columns);
1608
+ export const group = (label: string, collapsed?: boolean) => defaultLogger.group(label, collapsed);
1609
+ export const groupEnd = () => defaultLogger.groupEnd();
1610
+ export const time = (label: string) => defaultLogger.time(label);
1611
+ export const timeEnd = (label: string) => defaultLogger.timeEnd(label);
1612
+ export const setGlobalPrefix = (prefix: string) => defaultLogger.setGlobalPrefix(prefix);
1613
+ export const createScopedLogger = (prefix: string) => defaultLogger.createScopedLogger(prefix);
1614
+ export const setVerbosity = (level: Verbosity) => defaultLogger.setVerbosity(level);
1615
+ export const addHandler = (handler: ILogHandler) => defaultLogger.addHandler(handler);
1616
+ export const setTheme = (theme: ThemeVariant) => defaultLogger.setTheme(theme);
1617
+ export const setBannerType = (bannerType: BannerType) => defaultLogger.setBannerType(bannerType);
1618
+ export const showBanner = (bannerType?: BannerType) => defaultLogger.showBanner(bannerType);
1619
+ export const logWithSVG = (message: string, svgContent?: string, options?: { width?: number, height?: number, padding?: string }) => defaultLogger.logWithSVG(message, svgContent, options);
1620
+ export const logAnimated = (message: string, duration?: number) => defaultLogger.logAnimated(message, duration);
1621
+ export const cli = (command: string) => defaultLogger.cli(command);
1622
+
1623
+ /**
1624
+ * Advanced styling utilities for external use
1625
+ */
1626
+ export const Styles = {
1627
+ /**
1628
+ * Creates a new StyleBuilder instance
1629
+ */
1630
+ create(): StyleBuilder {
1631
+ return new StyleBuilder();
1632
+ },
1633
+
1634
+ /**
1635
+ * Pre-defined common styles
1636
+ */
1637
+ presets: {
1638
+ success: new StyleBuilder()
1639
+ .bg('linear-gradient(135deg, #00b894 0%, #00a085 100%)')
1640
+ .color('#ffffff')
1641
+ .padding('4px 8px')
1642
+ .rounded('4px')
1643
+ .bold()
1644
+ .build(),
1645
+
1646
+ error: new StyleBuilder()
1647
+ .bg('linear-gradient(135deg, #e84393 0%, #d63031 100%)')
1648
+ .color('#ffffff')
1649
+ .padding('4px 8px')
1650
+ .rounded('4px')
1651
+ .bold()
1652
+ .build(),
1653
+
1654
+ warning: new StyleBuilder()
1655
+ .bg('linear-gradient(135deg, #fdcb6e 0%, #e17055 100%)')
1656
+ .color('#2d3436')
1657
+ .padding('4px 8px')
1658
+ .rounded('4px')
1659
+ .bold()
1660
+ .build(),
1661
+
1662
+ info: new StyleBuilder()
1663
+ .bg('linear-gradient(135deg, #74b9ff 0%, #0984e3 100%)')
1664
+ .color('#ffffff')
1665
+ .padding('4px 8px')
1666
+ .rounded('4px')
1667
+ .bold()
1668
+ .build(),
1669
+ },
1670
+ };
1671
+
1672
+
1673
+ /**
1674
+ * Example custom handler for demonstration
1675
+ */
1676
+ export class AnalyticsLogHandler implements ILogHandler {
1677
+ handle(level: LogLevel, message: string, _args: any[], metadata: LogMetadata): void {
1678
+ // In a real implementation, this could send analytics data
1679
+ if (level === 'error' || level === 'critical') {
1680
+ // Track errors for analytics
1681
+ console.debug('Analytics: Error tracked', { level, message, timestamp: metadata.timestamp });
1682
+ }
1683
+ }
1684
+ }