@mks2508/better-logger 2.1.0 → 3.1.0

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 (111) hide show
  1. package/.claude/settings.local.json +8 -1
  2. package/CHANGELOG.json +168 -1
  3. package/CHANGELOG.md +127 -0
  4. package/CLAUDE.md +14 -1
  5. package/README.md +111 -20
  6. package/dist/Logger.d.ts +217 -70
  7. package/dist/Logger.d.ts.map +1 -1
  8. package/dist/ScopedLogger.d.ts +23 -276
  9. package/dist/ScopedLogger.d.ts.map +1 -1
  10. package/dist/chunks/Logger-BV0v1AyC.js +2 -0
  11. package/dist/chunks/Logger-BV0v1AyC.js.map +1 -0
  12. package/dist/chunks/{Logger-B7L-ujY4.js → Logger-C086w3EM.js} +868 -102
  13. package/dist/chunks/Logger-C086w3EM.js.map +1 -0
  14. package/dist/chunks/environment-C_8J-zQ_.js +4 -0
  15. package/dist/chunks/environment-C_8J-zQ_.js.map +1 -0
  16. package/dist/chunks/{environment-TI2ByCPT.js → environment-DXHouGD4.js} +468 -100
  17. package/dist/chunks/environment-DXHouGD4.js.map +1 -0
  18. package/dist/chunks/{formatting-CuNUqGks.js → formatting-BNnVUPcw.js} +2 -2
  19. package/dist/chunks/{formatting-CuNUqGks.js.map → formatting-BNnVUPcw.js.map} +1 -1
  20. package/dist/chunks/{formatting-Blwy-f0W.js → formatting-Cg5YhB9Y.js} +2 -2
  21. package/dist/chunks/{formatting-Blwy-f0W.js.map → formatting-Cg5YhB9Y.js.map} +1 -1
  22. package/dist/core.cjs +1 -1
  23. package/dist/core.js +2 -2
  24. package/dist/exports-module.d.ts +1 -1
  25. package/dist/exports-module.d.ts.map +1 -1
  26. package/dist/exports.cjs +1 -1
  27. package/dist/exports.js +2 -2
  28. package/dist/hooks/HookManager.d.ts +21 -0
  29. package/dist/hooks/HookManager.d.ts.map +1 -0
  30. package/dist/hooks/index.d.ts +2 -0
  31. package/dist/hooks/index.d.ts.map +1 -0
  32. package/dist/index.cjs +1 -1
  33. package/dist/index.cjs.map +1 -1
  34. package/dist/index.d.ts +30 -3
  35. package/dist/index.d.ts.map +1 -1
  36. package/dist/index.js +433 -20
  37. package/dist/index.js.map +1 -1
  38. package/dist/serializers/SerializerRegistry.d.ts +16 -0
  39. package/dist/serializers/SerializerRegistry.d.ts.map +1 -0
  40. package/dist/serializers/index.d.ts +2 -0
  41. package/dist/serializers/index.d.ts.map +1 -0
  42. package/dist/styling/StyleCache.d.ts +42 -0
  43. package/dist/styling/StyleCache.d.ts.map +1 -0
  44. package/dist/styling-module.d.ts +1 -1
  45. package/dist/styling-module.d.ts.map +1 -1
  46. package/dist/styling.cjs +1 -1
  47. package/dist/styling.js +3 -3
  48. package/dist/terminal/color-converter.d.ts +73 -0
  49. package/dist/terminal/color-converter.d.ts.map +1 -0
  50. package/dist/terminal/formatter.d.ts +20 -0
  51. package/dist/terminal/formatter.d.ts.map +1 -0
  52. package/dist/terminal/terminal-renderer.d.ts +20 -6
  53. package/dist/terminal/terminal-renderer.d.ts.map +1 -1
  54. package/dist/transports/ConsoleTransport.d.ts +9 -0
  55. package/dist/transports/ConsoleTransport.d.ts.map +1 -0
  56. package/dist/transports/FileTransport.d.ts +15 -0
  57. package/dist/transports/FileTransport.d.ts.map +1 -0
  58. package/dist/transports/HttpTransport.d.ts +16 -0
  59. package/dist/transports/HttpTransport.d.ts.map +1 -0
  60. package/dist/transports/TransportManager.d.ts +14 -0
  61. package/dist/transports/TransportManager.d.ts.map +1 -0
  62. package/dist/transports/index.d.ts +5 -0
  63. package/dist/transports/index.d.ts.map +1 -0
  64. package/dist/types/core.d.ts +55 -0
  65. package/dist/types/core.d.ts.map +1 -1
  66. package/dist/types/hooks.d.ts +36 -0
  67. package/dist/types/hooks.d.ts.map +1 -0
  68. package/dist/types/index.d.ts +5 -1
  69. package/dist/types/index.d.ts.map +1 -1
  70. package/dist/types/serializers.d.ts +25 -0
  71. package/dist/types/serializers.d.ts.map +1 -0
  72. package/dist/types/transports.d.ts +47 -0
  73. package/dist/types/transports.d.ts.map +1 -0
  74. package/dist/utils/environment-detector.d.ts +10 -0
  75. package/dist/utils/environment-detector.d.ts.map +1 -1
  76. package/docs/API.md +227 -0
  77. package/package.json +1 -1
  78. package/packages/core/package.json +1 -1
  79. package/packages/styling/package.json +2 -2
  80. package/src/Logger.ts +403 -114
  81. package/src/ScopedLogger.ts +78 -318
  82. package/src/hooks/HookManager.ts +177 -0
  83. package/src/hooks/index.ts +1 -0
  84. package/src/index.ts +133 -3
  85. package/src/serializers/SerializerRegistry.ts +173 -0
  86. package/src/serializers/index.ts +1 -0
  87. package/src/styling/StyleCache.ts +131 -0
  88. package/src/terminal/color-converter.ts +315 -0
  89. package/src/terminal/formatter.ts +236 -0
  90. package/src/terminal/terminal-renderer.ts +74 -79
  91. package/src/transports/ConsoleTransport.ts +28 -0
  92. package/src/transports/FileTransport.ts +53 -0
  93. package/src/transports/HttpTransport.ts +56 -0
  94. package/src/transports/TransportManager.ts +130 -0
  95. package/src/transports/index.ts +4 -0
  96. package/src/types/core.ts +68 -0
  97. package/src/types/hooks.ts +45 -0
  98. package/src/types/index.ts +44 -1
  99. package/src/types/serializers.ts +30 -0
  100. package/src/types/transports.ts +52 -0
  101. package/src/utils/environment-detector.ts +23 -1
  102. package/dist/chunks/Logger-B7L-ujY4.js.map +0 -1
  103. package/dist/chunks/Logger-DzU_c5sX.js +0 -2
  104. package/dist/chunks/Logger-DzU_c5sX.js.map +0 -1
  105. package/dist/chunks/ScopedLogger-BY3-E8Ov.js +0 -2
  106. package/dist/chunks/ScopedLogger-BY3-E8Ov.js.map +0 -1
  107. package/dist/chunks/ScopedLogger-D-RbiFZn.js +0 -361
  108. package/dist/chunks/ScopedLogger-D-RbiFZn.js.map +0 -1
  109. package/dist/chunks/environment-Ba5kShbx.js +0 -4
  110. package/dist/chunks/environment-Ba5kShbx.js.map +0 -1
  111. package/dist/chunks/environment-TI2ByCPT.js.map +0 -1
@@ -5,6 +5,7 @@
5
5
 
6
6
  import type { LogLevel } from '../types/index.js';
7
7
  import type { LogStyles } from '../types/index.js';
8
+ import { getANSIForeground, getANSIBackground, ANSI, type ColorCapability } from './color-converter.js';
8
9
 
9
10
  export type ANSIStyle = {
10
11
  text: string;
@@ -17,32 +18,19 @@ export type ANSIStyle = {
17
18
  };
18
19
 
19
20
  export class TerminalRenderer {
20
- private colorCapability: 'full' | 'basic' | 'none';
21
+ private colorCapability: ColorCapability;
21
22
 
22
- constructor(colorCapability: 'full' | 'basic' | 'none' = 'full') {
23
+ constructor(colorCapability: ColorCapability = 'full') {
23
24
  this.colorCapability = colorCapability;
24
25
  }
25
26
 
26
27
  /**
27
- * Get ANSI color codes for basic colors
28
+ * Get ANSI color codes - now supports truecolor/256-color
28
29
  */
29
30
  private getColorCode(color: string, background: boolean = false): string {
30
- const colorCodes: Record<string, { normal: string; bright: string }> = {
31
- 'black': { normal: '30', bright: '90' },
32
- 'red': { normal: '31', bright: '91' },
33
- 'green': { normal: '32', bright: '92' },
34
- 'yellow': { normal: '33', bright: '93' },
35
- 'blue': { normal: '34', bright: '94' },
36
- 'magenta': { normal: '35', bright: '95' },
37
- 'cyan': { normal: '36', bright: '96' },
38
- 'white': { normal: '37', bright: '97' },
39
- 'gray': { normal: '90', bright: '37' },
40
- 'grey': { normal: '90', bright: '37' }
41
- };
42
-
43
- const codes = colorCodes[color.toLowerCase()] || { normal: '37', bright: '97' };
44
- const prefix = background ? '4' : '3';
45
- return `\x1b[${codes.normal}m`;
31
+ return background
32
+ ? getANSIBackground(color, this.colorCapability)
33
+ : getANSIForeground(color, this.colorCapability);
46
34
  }
47
35
 
48
36
  /**
@@ -207,7 +195,7 @@ export class TerminalRenderer {
207
195
  }
208
196
 
209
197
  /**
210
- * Create cyberpunk-style ANSI rendering
198
+ * Create cyberpunk-style ANSI rendering with truecolor support
211
199
  */
212
200
  renderCyberpunk(
213
201
  level: LogLevel,
@@ -216,35 +204,36 @@ export class TerminalRenderer {
216
204
  prefix?: string,
217
205
  location?: string
218
206
  ): ANSIStyle {
219
- const reset = '\x1b[0m';
220
- const levelColors: Record<LogLevel, string> = {
221
- 'debug': '\x1b[95m', // Bright magenta
222
- 'info': '\x1b[94m', // Bright blue
223
- 'warn': '\x1b[93m', // Bright yellow
224
- 'error': '\x1b[91m', // Bright red
225
- 'critical': '\x1b[91m\x1b[1m' // Bright red + bold
207
+ const reset = ANSI.reset;
208
+
209
+ const cyberpunkColors: Record<LogLevel, string> = {
210
+ 'debug': '#ff00ff', // Neon magenta
211
+ 'info': '#00ffff', // Neon cyan
212
+ 'warn': '#ffff00', // Neon yellow
213
+ 'error': '#ff0040', // Neon red-pink
214
+ 'critical': '#ff0000' // Pure red
226
215
  };
227
216
 
228
217
  const parts: string[] = [];
229
218
 
230
219
  if (timestamp) {
231
- parts.push(`\x1b[90m${timestamp}${reset}`);
220
+ parts.push(`${ANSI.dim}${timestamp}${reset}`);
232
221
  }
233
222
 
234
- // Cyberpunk level styling
235
- const levelColor = levelColors[level] || '\x1b[97m';
236
- const bgBlack = '\x1b[40m';
237
- parts.push(`${bgBlack}${levelColor} ${this.getLevelText(level)} ${reset}`);
223
+ const levelColor = this.getColorCode(cyberpunkColors[level] || '#ffffff');
224
+ const bgBlack = ANSI.bg.black;
225
+ const bold = level === 'critical' ? ANSI.bold : '';
226
+ parts.push(`${bgBlack}${levelColor}${bold} ${this.getLevelText(level)} ${reset}`);
238
227
 
239
228
  if (prefix) {
240
- const bgBlack = '\x1b[40m';
241
- parts.push(`${bgBlack}\x1b[96m[${prefix.toUpperCase()}]${reset}`);
229
+ const cyanColor = this.getColorCode('#00ffff');
230
+ parts.push(`${bgBlack}${cyanColor}[${prefix.toUpperCase()}]${reset}`);
242
231
  }
243
232
 
244
233
  parts.push(message);
245
234
 
246
235
  if (location) {
247
- parts.push(`\x1b[90m(${location})${reset}`);
236
+ parts.push(`${ANSI.dim}(${location})${reset}`);
248
237
  }
249
238
 
250
239
  return {
@@ -254,7 +243,7 @@ export class TerminalRenderer {
254
243
  }
255
244
 
256
245
  /**
257
- * Create minimal ANSI rendering
246
+ * Create minimal ANSI rendering with truecolor support
258
247
  */
259
248
  renderMinimal(
260
249
  level: LogLevel,
@@ -262,26 +251,29 @@ export class TerminalRenderer {
262
251
  timestamp?: string,
263
252
  prefix?: string
264
253
  ): ANSIStyle {
265
- const reset = '\x1b[0m';
266
- const levelColors: Record<LogLevel, string> = {
267
- 'debug': '\x1b[95m', // Magenta
268
- 'info': '\x1b[94m', // Blue
269
- 'warn': '\x1b[93m', // Yellow
270
- 'error': '\x1b[91m', // Red
271
- 'critical': '\x1b[91m\x1b[1m' // Red + bold
254
+ const reset = ANSI.reset;
255
+
256
+ const minimalColors: Record<LogLevel, string> = {
257
+ 'debug': '#c678dd', // Soft purple
258
+ 'info': '#61afef', // Soft blue
259
+ 'warn': '#e5c07b', // Soft yellow
260
+ 'error': '#e06c75', // Soft red
261
+ 'critical': '#be5046' // Dark red
272
262
  };
273
263
 
274
264
  const parts: string[] = [];
275
265
 
276
266
  if (timestamp) {
277
- parts.push(`\x1b[90m${timestamp}${reset}`);
267
+ parts.push(`${ANSI.dim}${timestamp}${reset}`);
278
268
  }
279
269
 
280
- const levelColor = levelColors[level] || '\x1b[97m';
281
- parts.push(`${levelColor}${this.getLevelText(level)}:${reset}`);
270
+ const levelColor = this.getColorCode(minimalColors[level] || '#abb2bf');
271
+ const bold = level === 'critical' ? ANSI.bold : '';
272
+ parts.push(`${levelColor}${bold}${this.getLevelText(level)}:${reset}`);
282
273
 
283
274
  if (prefix) {
284
- parts.push(`\x1b[96m[${prefix.toUpperCase()}]${reset}`);
275
+ const cyanColor = this.getColorCode('#56b6c2');
276
+ parts.push(`${cyanColor}[${prefix.toUpperCase()}]${reset}`);
285
277
  }
286
278
 
287
279
  parts.push(message);
@@ -293,55 +285,58 @@ export class TerminalRenderer {
293
285
  }
294
286
 
295
287
  /**
296
- * Get chalk instance for log level (for compatibility with adapter)
288
+ * Get chalk-like interface for log level with truecolor support
297
289
  */
298
- public getChalkForLevel(level: LogLevel): any {
290
+ public getChalkForLevel(level: LogLevel): ChalkLikeInterface {
299
291
  const levelColors: Record<LogLevel, string> = {
300
- 'debug': 'magenta',
301
- 'info': 'blue',
302
- 'warn': 'yellow',
303
- 'error': 'red',
304
- 'critical': 'red'
292
+ 'debug': '#c678dd',
293
+ 'info': '#61afef',
294
+ 'warn': '#e5c07b',
295
+ 'error': '#e06c75',
296
+ 'critical': '#be5046'
305
297
  };
306
298
 
299
+ const reset = ANSI.reset;
300
+
307
301
  return {
308
302
  color: (text: string) => {
309
- const color = levelColors[level] || 'white';
303
+ const color = levelColors[level] || '#abb2bf';
310
304
  const colorCode = this.getColorCode(color);
311
- return `${colorCode}${text}${'\x1b[0m'}`;
312
- },
313
- bold: (text: string) => {
314
- const boldStyle = this.getStyleCode(['bold']);
315
- return `${boldStyle}${text}${'\x1b[0m'}`;
316
- },
317
- dim: (text: string) => {
318
- const dimStyle = this.getStyleCode(['dim']);
319
- return `${dimStyle}${text}${'\x1b[0m'}`;
320
- },
321
- bgGray: (text: string) => {
322
- return `\x1b[100m${text}${'\x1b[0m'}`;
323
- },
324
- bgBlue: (text: string) => {
325
- return `\x1b[104m${text}${'\x1b[0m'}`;
305
+ return `${colorCode}${text}${reset}`;
326
306
  },
307
+ bold: (text: string) => `${ANSI.bold}${text}${reset}`,
308
+ dim: (text: string) => `${ANSI.dim}${text}${reset}`,
309
+ bgGray: (text: string) => `${ANSI.bg.gray}${text}${reset}`,
310
+ bgBlue: (text: string) => `${this.getColorCode('#1e3a5f', true)}${text}${reset}`,
327
311
  reset: (text: string) => text,
328
312
  cyan: {
329
313
  bold: (text: string) => {
330
- const cyan = this.getColorCode('cyan');
331
- const bold = this.getStyleCode(['bold']);
332
- return `${cyan}${bold}${text}${'\x1b[0m'}`;
314
+ const cyan = this.getColorCode('#00ffff');
315
+ return `${cyan}${ANSI.bold}${text}${reset}`;
333
316
  },
334
317
  dim: (text: string) => {
335
- const cyan = this.getColorCode('cyan');
336
- const dim = this.getStyleCode(['dim']);
337
- return `${cyan}${dim}${text}${'\x1b[0m'}`;
318
+ const cyan = this.getColorCode('#00ffff');
319
+ return `${cyan}${ANSI.dim}${text}${reset}`;
338
320
  },
339
321
  bg: (text: string) => {
340
- const bgBlack = '\x1b[40m';
341
- const cyan = this.getColorCode('cyan');
342
- return `${bgBlack}${cyan}[${text.toUpperCase()}]${'\x1b[0m'}`;
322
+ const cyan = this.getColorCode('#00ffff');
323
+ return `${ANSI.bg.black}${cyan}[${text.toUpperCase()}]${reset}`;
343
324
  }
344
325
  }
345
326
  };
346
327
  }
328
+ }
329
+
330
+ export interface ChalkLikeInterface {
331
+ color: (text: string) => string;
332
+ bold: (text: string) => string;
333
+ dim: (text: string) => string;
334
+ bgGray: (text: string) => string;
335
+ bgBlue: (text: string) => string;
336
+ reset: (text: string) => string;
337
+ cyan: {
338
+ bold: (text: string) => string;
339
+ dim: (text: string) => string;
340
+ bg: (text: string) => string;
341
+ };
347
342
  }
@@ -0,0 +1,28 @@
1
+ import type { TransportRecord, TransportOptions, ITransport, LogLevel } from '../types/index.js';
2
+
3
+ export class ConsoleTransport implements ITransport {
4
+ readonly name = 'console';
5
+
6
+ constructor(private options?: TransportOptions) {}
7
+
8
+ write(record: TransportRecord): void {
9
+ const method = this.getConsoleMethod(record.level);
10
+ const prefix = record.prefix ? `[${record.prefix}] ` : '';
11
+ const location = record.location
12
+ ? ` (${record.location.file}:${record.location.line})`
13
+ : '';
14
+
15
+ console[method](`[${record.level.toUpperCase()}]${prefix} ${record.msg}${location}`);
16
+ }
17
+
18
+ private getConsoleMethod(level: LogLevel): 'log' | 'info' | 'warn' | 'error' {
19
+ switch (level) {
20
+ case 'debug': return 'log';
21
+ case 'info': return 'info';
22
+ case 'warn': return 'warn';
23
+ case 'error':
24
+ case 'critical': return 'error';
25
+ default: return 'log';
26
+ }
27
+ }
28
+ }
@@ -0,0 +1,53 @@
1
+ import type { TransportRecord, TransportOptions, ITransport } from '../types/index.js';
2
+
3
+ export interface FileTransportOptions extends TransportOptions {
4
+ destination?: string;
5
+ }
6
+
7
+ export class FileTransport implements ITransport {
8
+ readonly name = 'file';
9
+ private buffer: string[] = [];
10
+ private flushTimer?: ReturnType<typeof setInterval>;
11
+ private options: FileTransportOptions;
12
+
13
+ constructor(options?: FileTransportOptions) {
14
+ this.options = options || {};
15
+
16
+ if (this.options.flushInterval) {
17
+ this.flushTimer = setInterval(() => this.flush(), this.options.flushInterval);
18
+ }
19
+ }
20
+
21
+ write(record: TransportRecord): void {
22
+ const line = JSON.stringify(record) + '\n';
23
+ this.buffer.push(line);
24
+
25
+ if (this.buffer.length >= (this.options.batchSize || 100)) {
26
+ this.flush();
27
+ }
28
+ }
29
+
30
+ async flush(): Promise<void> {
31
+ if (this.buffer.length === 0) return;
32
+
33
+ const lines = this.buffer.join('');
34
+ this.buffer = [];
35
+
36
+ if (typeof globalThis.process !== 'undefined' && typeof require === 'function') {
37
+ try {
38
+ const fs = require('fs');
39
+ const dest = this.options.destination || 'app.log';
40
+ fs.appendFileSync(dest, lines);
41
+ } catch {
42
+ // Silent fail in browser or if fs not available
43
+ }
44
+ }
45
+ }
46
+
47
+ close(): void {
48
+ if (this.flushTimer) {
49
+ clearInterval(this.flushTimer);
50
+ }
51
+ this.flush();
52
+ }
53
+ }
@@ -0,0 +1,56 @@
1
+ import type { TransportRecord, TransportOptions, ITransport } from '../types/index.js';
2
+
3
+ export interface HttpTransportOptions extends TransportOptions {
4
+ url?: string;
5
+ headers?: Record<string, string>;
6
+ }
7
+
8
+ export class HttpTransport implements ITransport {
9
+ readonly name = 'http';
10
+ private buffer: TransportRecord[] = [];
11
+ private flushTimer?: ReturnType<typeof setInterval>;
12
+ private options: HttpTransportOptions;
13
+
14
+ constructor(options?: HttpTransportOptions) {
15
+ this.options = options || {};
16
+
17
+ if (this.options.flushInterval) {
18
+ this.flushTimer = setInterval(() => this.flush(), this.options.flushInterval);
19
+ }
20
+ }
21
+
22
+ write(record: TransportRecord): void {
23
+ this.buffer.push(record);
24
+
25
+ if (this.buffer.length >= (this.options.batchSize || 50)) {
26
+ this.flush();
27
+ }
28
+ }
29
+
30
+ async flush(): Promise<void> {
31
+ if (this.buffer.length === 0 || !this.options.url) return;
32
+
33
+ const records = [...this.buffer];
34
+ this.buffer = [];
35
+
36
+ try {
37
+ await fetch(this.options.url, {
38
+ method: 'POST',
39
+ headers: {
40
+ 'Content-Type': 'application/json',
41
+ ...this.options.headers
42
+ },
43
+ body: JSON.stringify({ logs: records })
44
+ });
45
+ } catch {
46
+ this.buffer.unshift(...records);
47
+ }
48
+ }
49
+
50
+ close(): void {
51
+ if (this.flushTimer) {
52
+ clearInterval(this.flushTimer);
53
+ }
54
+ this.flush();
55
+ }
56
+ }
@@ -0,0 +1,130 @@
1
+ import type {
2
+ TransportRecord,
3
+ TransportTarget,
4
+ TransportOptions,
5
+ ITransport,
6
+ ITransportManager,
7
+ LogLevel
8
+ } from '../types/index.js';
9
+ import { LOG_LEVELS } from '../types/index.js';
10
+
11
+ function generateTransportId(): string {
12
+ return `transport-${Date.now()}-${Math.random().toString(36).substr(2, 6)}`;
13
+ }
14
+
15
+ interface TransportEntry {
16
+ id: string;
17
+ transport: ITransport;
18
+ options: TransportOptions;
19
+ level: LogLevel;
20
+ levelValue: number;
21
+ }
22
+
23
+ export class TransportManager implements ITransportManager {
24
+ private transports: Map<string, TransportEntry> = new Map();
25
+ private defaultLevel: LogLevel = 'info';
26
+
27
+ constructor(defaultLevel?: LogLevel) {
28
+ if (defaultLevel) {
29
+ this.defaultLevel = defaultLevel;
30
+ }
31
+ }
32
+
33
+ add(target: TransportTarget): string {
34
+ const id = generateTransportId();
35
+ const level = target.level || this.defaultLevel;
36
+
37
+ let transport: ITransport;
38
+
39
+ if (typeof target.target === 'string') {
40
+ throw new Error(`String transport targets not supported. Use inline transport object.`);
41
+ } else {
42
+ transport = target.target;
43
+ }
44
+
45
+ const entry: TransportEntry = {
46
+ id,
47
+ transport,
48
+ options: target.options || {},
49
+ level,
50
+ levelValue: LOG_LEVELS[level]
51
+ };
52
+
53
+ this.transports.set(id, entry);
54
+ return id;
55
+ }
56
+
57
+ remove(id: string): boolean {
58
+ const entry = this.transports.get(id);
59
+ if (entry) {
60
+ entry.transport.close?.();
61
+ return this.transports.delete(id);
62
+ }
63
+ return false;
64
+ }
65
+
66
+ async write(record: TransportRecord): Promise<void> {
67
+ const promises: Promise<void>[] = [];
68
+
69
+ for (const entry of this.transports.values()) {
70
+ if (record.levelValue < entry.levelValue) {
71
+ continue;
72
+ }
73
+
74
+ let transformedRecord = record;
75
+ if (entry.options.transform) {
76
+ const result = entry.options.transform(record);
77
+ if (result === null) continue;
78
+ transformedRecord = result;
79
+ }
80
+
81
+ const result = entry.transport.write(transformedRecord);
82
+ if (result instanceof Promise) {
83
+ promises.push(result);
84
+ }
85
+ }
86
+
87
+ await Promise.all(promises);
88
+ }
89
+
90
+ async flush(): Promise<void> {
91
+ const promises: Promise<void>[] = [];
92
+
93
+ for (const entry of this.transports.values()) {
94
+ if (entry.transport.flush) {
95
+ const result = entry.transport.flush();
96
+ if (result instanceof Promise) {
97
+ promises.push(result);
98
+ }
99
+ }
100
+ }
101
+
102
+ await Promise.all(promises);
103
+ }
104
+
105
+ async close(): Promise<void> {
106
+ await this.flush();
107
+
108
+ const promises: Promise<void>[] = [];
109
+
110
+ for (const entry of this.transports.values()) {
111
+ if (entry.transport.close) {
112
+ const result = entry.transport.close();
113
+ if (result instanceof Promise) {
114
+ promises.push(result);
115
+ }
116
+ }
117
+ }
118
+
119
+ await Promise.all(promises);
120
+ this.transports.clear();
121
+ }
122
+
123
+ get count(): number {
124
+ return this.transports.size;
125
+ }
126
+
127
+ list(): string[] {
128
+ return Array.from(this.transports.keys());
129
+ }
130
+ }
@@ -0,0 +1,4 @@
1
+ export { TransportManager } from './TransportManager.js';
2
+ export { ConsoleTransport } from './ConsoleTransport.js';
3
+ export { FileTransport, type FileTransportOptions } from './FileTransport.js';
4
+ export { HttpTransport, type HttpTransportOptions } from './HttpTransport.js';
package/src/types/core.ts CHANGED
@@ -247,4 +247,72 @@ export interface LogStyles {
247
247
  location?: LogPartConfig;
248
248
  backdrop?: string;
249
249
  transparency?: number;
250
+ }
251
+
252
+ export interface TimerResult {
253
+ label: string;
254
+ duration: number;
255
+ startTime: number;
256
+ endTime: number;
257
+ }
258
+
259
+ export interface IScopedLogger {
260
+ debug(...args: any[]): void;
261
+ info(...args: any[]): void;
262
+ warn(...args: any[]): void;
263
+ error(...args: any[]): void;
264
+ success(...args: any[]): void;
265
+ critical(...args: any[]): void;
266
+ trace(...args: any[]): void;
267
+
268
+ badges(badges: string[]): this;
269
+ badge(badge: string): this;
270
+ clearBadges(): this;
271
+
272
+ time(label: string): void;
273
+ timeEnd(label: string): number | undefined;
274
+
275
+ style(presetName: string): this;
276
+ }
277
+
278
+ export interface IAPILogger extends IScopedLogger {
279
+ slow(message: string, duration?: number): void;
280
+ rateLimit(message: string): void;
281
+ auth(message: string): void;
282
+ deprecated(message: string): void;
283
+ }
284
+
285
+ export interface IComponentLogger extends IScopedLogger {
286
+ lifecycle(event: string, message?: string): void;
287
+ stateChange(from: string, to: string, data?: any): void;
288
+ propsChange(changes: Record<string, any>): void;
289
+ }
290
+
291
+ export interface Bindings {
292
+ scope?: string;
293
+ badges?: string[];
294
+ type?: 'scope' | 'api' | 'component';
295
+ context?: string[];
296
+ }
297
+
298
+ export type BadgeStyle = 'brackets' | 'rounded' | 'plain' | 'unicode' | 'pill';
299
+
300
+ export type TimestampFormat = 'iso' | 'time' | 'timeMs' | 'relative' | 'elapsed' | 'date' | 'custom';
301
+
302
+ export type ColumnAlign = 'left' | 'right' | 'center';
303
+
304
+ export interface ColumnConfig {
305
+ content: string;
306
+ width?: number;
307
+ align?: ColumnAlign;
308
+ color?: string;
309
+ }
310
+
311
+ export interface LogOptions {
312
+ rightAlign?: string;
313
+ columns?: ColumnConfig[];
314
+ maxWidth?: number;
315
+ keyValue?: boolean;
316
+ badgeStyle?: BadgeStyle;
317
+ timestampFormat?: TimestampFormat;
250
318
  }
@@ -0,0 +1,45 @@
1
+ import type { LogLevel, StackInfo } from './core.js';
2
+
3
+ export interface HookLogEntry {
4
+ level: LogLevel;
5
+ message: string;
6
+ args: any[];
7
+ timestamp: string;
8
+ prefix?: string;
9
+ stackInfo?: StackInfo;
10
+ correlationId?: string;
11
+ context?: Record<string, any>;
12
+ [key: string]: any;
13
+ }
14
+
15
+ export type HookEvent = 'beforeLog' | 'afterLog' | 'onError';
16
+
17
+ export type HookCallback = (entry: HookLogEntry) => void | HookLogEntry | Promise<void | HookLogEntry>;
18
+
19
+ export type MiddlewareFn = (
20
+ entry: HookLogEntry,
21
+ next: () => void | Promise<void>
22
+ ) => void | Promise<void>;
23
+
24
+ export interface HookRegistration {
25
+ id: string;
26
+ event: HookEvent;
27
+ callback: HookCallback;
28
+ priority: number;
29
+ once: boolean;
30
+ }
31
+
32
+ export interface MiddlewareRegistration {
33
+ id: string;
34
+ fn: MiddlewareFn;
35
+ priority: number;
36
+ }
37
+
38
+ export interface IHookManager {
39
+ on(event: HookEvent, callback: HookCallback, priority?: number): () => void;
40
+ once(event: HookEvent, callback: HookCallback, priority?: number): () => void;
41
+ off(event: HookEvent, callback: HookCallback): boolean;
42
+ use(middleware: MiddlewareFn, priority?: number): () => void;
43
+ emit(event: HookEvent, entry: HookLogEntry): Promise<HookLogEntry>;
44
+ process(entry: HookLogEntry): Promise<HookLogEntry>;
45
+ }