@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
@@ -1,4 +1,364 @@
1
- import { h as getAdaptiveColor, A as ADAPTIVE_COLORS, b as StyleBuilder, j as BUFFER_LIMITS, k as generateLogId, l as parseTimeInput, m as formatDisplayTime, n as safeStringify, o as escapeHtml, q as EXPORT_FORMATS, D as DEFAULT_CONFIG, r as setupThemeChangeListener, t as getEnvironment, p as parseStackTrace, c as createStyledOutput, f as formatTimestamp, S as StylePresets } from "./environment-TI2ByCPT.js";
1
+ import { C as getAdaptiveColor, F as ADAPTIVE_COLORS, c as StyleBuilder, G as BUFFER_LIMITS, H as generateLogId, I as parseTimeInput, J as formatDisplayTime, K as safeStringify, M as escapeHtml, N as EXPORT_FORMATS, D as DEFAULT_CONFIG$1, O as setupThemeChangeListener, q as getEnvironment, p as parseStackTrace, f as formatTimestamp, y as createStyledOutput, S as StylePresets } from "./environment-DXHouGD4.js";
2
+ const DEFAULT_CONFIG = {
3
+ maxDepth: 5,
4
+ circular: "placeholder",
5
+ preserveUndefined: false
6
+ };
7
+ class SerializerRegistry {
8
+ serializers = /* @__PURE__ */ new Map();
9
+ config;
10
+ constructor(config = {}) {
11
+ this.config = { ...DEFAULT_CONFIG, ...config };
12
+ this.registerDefaults();
13
+ }
14
+ registerDefaults() {
15
+ this.add(Error, (err) => ({
16
+ name: err.name,
17
+ message: err.message,
18
+ stack: err.stack?.split("\n").slice(0, 10),
19
+ ...err.cause ? { cause: this.serialize(err.cause) } : {}
20
+ }), 100);
21
+ this.add(Date, (date) => ({
22
+ iso: date.toISOString(),
23
+ timestamp: date.getTime()
24
+ }), 90);
25
+ this.add(RegExp, (regex) => ({
26
+ pattern: regex.source,
27
+ flags: regex.flags
28
+ }), 90);
29
+ this.add(Map, (map, ctx) => {
30
+ const obj = {};
31
+ map.forEach((value, key) => {
32
+ const keyStr = typeof key === "object" ? JSON.stringify(key) : String(key);
33
+ obj[keyStr] = this.serializeInternal(value, {
34
+ ...ctx,
35
+ depth: ctx.depth + 1,
36
+ path: [...ctx.path, keyStr]
37
+ });
38
+ });
39
+ return { __type: "Map", entries: obj };
40
+ }, 80);
41
+ this.add(Set, (set, ctx) => ({
42
+ __type: "Set",
43
+ values: Array.from(set).map(
44
+ (v, i) => this.serializeInternal(v, {
45
+ ...ctx,
46
+ depth: ctx.depth + 1,
47
+ path: [...ctx.path, `[${i}]`]
48
+ })
49
+ )
50
+ }), 80);
51
+ if (typeof Buffer !== "undefined") {
52
+ this.add(Buffer, (buf) => ({
53
+ __type: "Buffer",
54
+ length: buf.length,
55
+ preview: buf.slice(0, 50).toString("hex")
56
+ }), 70);
57
+ }
58
+ }
59
+ add(type, serializer, priority = 50) {
60
+ this.serializers.set(type, { type, serializer, priority });
61
+ }
62
+ remove(type) {
63
+ return this.serializers.delete(type);
64
+ }
65
+ has(type) {
66
+ return this.serializers.has(type);
67
+ }
68
+ getAll() {
69
+ return Array.from(this.serializers.values()).sort((a, b) => (b.priority ?? 50) - (a.priority ?? 50));
70
+ }
71
+ findSerializer(value) {
72
+ const sorted = this.getAll();
73
+ for (const entry of sorted) {
74
+ if (value instanceof entry.type) {
75
+ return entry;
76
+ }
77
+ }
78
+ return null;
79
+ }
80
+ serializeInternal(value, context) {
81
+ if (value === null) return null;
82
+ if (value === void 0) return this.config.preserveUndefined ? void 0 : "[undefined]";
83
+ if (typeof value === "function") return `[Function: ${value.name || "anonymous"}]`;
84
+ if (typeof value !== "object") return value;
85
+ if (context.depth >= this.config.maxDepth) {
86
+ return "[Max Depth]";
87
+ }
88
+ if (context.seen.has(value)) {
89
+ switch (this.config.circular) {
90
+ case "error":
91
+ throw new Error(`Circular reference at ${context.path.join(".")}`);
92
+ case "skip":
93
+ return void 0;
94
+ case "placeholder":
95
+ default:
96
+ return "[Circular]";
97
+ }
98
+ }
99
+ context.seen.add(value);
100
+ const serializer = this.findSerializer(value);
101
+ if (serializer) {
102
+ return serializer.serializer(value, context);
103
+ }
104
+ if (Array.isArray(value)) {
105
+ return value.map(
106
+ (item, i) => this.serializeInternal(item, {
107
+ ...context,
108
+ depth: context.depth + 1,
109
+ path: [...context.path, `[${i}]`]
110
+ })
111
+ );
112
+ }
113
+ const result = {};
114
+ for (const [key, val] of Object.entries(value)) {
115
+ result[key] = this.serializeInternal(val, {
116
+ ...context,
117
+ depth: context.depth + 1,
118
+ path: [...context.path, key]
119
+ });
120
+ }
121
+ return result;
122
+ }
123
+ serialize(value, config) {
124
+ const mergedConfig = { ...this.config, ...config };
125
+ return this.serializeInternal(value, {
126
+ depth: 0,
127
+ maxDepth: mergedConfig.maxDepth,
128
+ path: [],
129
+ seen: /* @__PURE__ */ new WeakSet()
130
+ });
131
+ }
132
+ }
133
+ function generateId() {
134
+ return `${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
135
+ }
136
+ class HookManager {
137
+ hooks = /* @__PURE__ */ new Map();
138
+ middlewares = [];
139
+ constructor() {
140
+ this.hooks.set("beforeLog", []);
141
+ this.hooks.set("afterLog", []);
142
+ this.hooks.set("onError", []);
143
+ }
144
+ on(event, callback, priority = 50) {
145
+ const registration = {
146
+ id: generateId(),
147
+ event,
148
+ callback,
149
+ priority,
150
+ once: false
151
+ };
152
+ const hooks = this.hooks.get(event);
153
+ hooks.push(registration);
154
+ hooks.sort((a, b) => b.priority - a.priority);
155
+ return () => this.removeHook(event, registration.id);
156
+ }
157
+ once(event, callback, priority = 50) {
158
+ const registration = {
159
+ id: generateId(),
160
+ event,
161
+ callback,
162
+ priority,
163
+ once: true
164
+ };
165
+ const hooks = this.hooks.get(event);
166
+ hooks.push(registration);
167
+ hooks.sort((a, b) => b.priority - a.priority);
168
+ return () => this.removeHook(event, registration.id);
169
+ }
170
+ off(event, callback) {
171
+ const hooks = this.hooks.get(event);
172
+ if (!hooks) return false;
173
+ const index = hooks.findIndex((h) => h.callback === callback);
174
+ if (index >= 0) {
175
+ hooks.splice(index, 1);
176
+ return true;
177
+ }
178
+ return false;
179
+ }
180
+ removeHook(event, id) {
181
+ const hooks = this.hooks.get(event);
182
+ if (hooks) {
183
+ const index = hooks.findIndex((h) => h.id === id);
184
+ if (index >= 0) {
185
+ hooks.splice(index, 1);
186
+ }
187
+ }
188
+ }
189
+ use(middleware, priority = 50) {
190
+ const registration = {
191
+ id: generateId(),
192
+ fn: middleware,
193
+ priority
194
+ };
195
+ this.middlewares.push(registration);
196
+ this.middlewares.sort((a, b) => b.priority - a.priority);
197
+ return () => {
198
+ const index = this.middlewares.findIndex((m) => m.id === registration.id);
199
+ if (index >= 0) {
200
+ this.middlewares.splice(index, 1);
201
+ }
202
+ };
203
+ }
204
+ async emit(event, entry) {
205
+ const hooks = this.hooks.get(event) || [];
206
+ let currentEntry = { ...entry };
207
+ const toRemove = [];
208
+ for (const hook of hooks) {
209
+ try {
210
+ const result = await hook.callback(currentEntry);
211
+ if (result) {
212
+ currentEntry = { ...currentEntry, ...result };
213
+ }
214
+ if (hook.once) {
215
+ toRemove.push(hook.id);
216
+ }
217
+ } catch (error) {
218
+ if (event !== "onError") {
219
+ await this.emit("onError", {
220
+ ...currentEntry,
221
+ error,
222
+ hookEvent: event
223
+ });
224
+ }
225
+ }
226
+ }
227
+ toRemove.forEach((id) => this.removeHook(event, id));
228
+ return currentEntry;
229
+ }
230
+ async process(entry) {
231
+ let currentEntry = { ...entry };
232
+ currentEntry = await this.emit("beforeLog", currentEntry);
233
+ if (this.middlewares.length > 0) {
234
+ let index = 0;
235
+ const executeNext = async () => {
236
+ if (index < this.middlewares.length) {
237
+ const middleware = this.middlewares[index++];
238
+ if (middleware) {
239
+ await middleware.fn(currentEntry, executeNext);
240
+ }
241
+ }
242
+ };
243
+ await executeNext();
244
+ }
245
+ return currentEntry;
246
+ }
247
+ async afterProcess(entry) {
248
+ await this.emit("afterLog", entry);
249
+ }
250
+ clear() {
251
+ this.hooks.forEach((hooks) => hooks.length = 0);
252
+ this.middlewares.length = 0;
253
+ }
254
+ getStats() {
255
+ return {
256
+ hooks: {
257
+ beforeLog: this.hooks.get("beforeLog")?.length || 0,
258
+ afterLog: this.hooks.get("afterLog")?.length || 0,
259
+ onError: this.hooks.get("onError")?.length || 0
260
+ },
261
+ middlewares: this.middlewares.length
262
+ };
263
+ }
264
+ }
265
+ const LOG_LEVELS = {
266
+ debug: 0,
267
+ info: 1,
268
+ warn: 2,
269
+ error: 3,
270
+ critical: 4
271
+ };
272
+ function generateTransportId() {
273
+ return `transport-${Date.now()}-${Math.random().toString(36).substr(2, 6)}`;
274
+ }
275
+ class TransportManager {
276
+ transports = /* @__PURE__ */ new Map();
277
+ defaultLevel = "info";
278
+ constructor(defaultLevel) {
279
+ if (defaultLevel) {
280
+ this.defaultLevel = defaultLevel;
281
+ }
282
+ }
283
+ add(target) {
284
+ const id = generateTransportId();
285
+ const level = target.level || this.defaultLevel;
286
+ let transport;
287
+ if (typeof target.target === "string") {
288
+ throw new Error(`String transport targets not supported. Use inline transport object.`);
289
+ } else {
290
+ transport = target.target;
291
+ }
292
+ const entry = {
293
+ id,
294
+ transport,
295
+ options: target.options || {},
296
+ level,
297
+ levelValue: LOG_LEVELS[level]
298
+ };
299
+ this.transports.set(id, entry);
300
+ return id;
301
+ }
302
+ remove(id) {
303
+ const entry = this.transports.get(id);
304
+ if (entry) {
305
+ entry.transport.close?.();
306
+ return this.transports.delete(id);
307
+ }
308
+ return false;
309
+ }
310
+ async write(record) {
311
+ const promises = [];
312
+ for (const entry of this.transports.values()) {
313
+ if (record.levelValue < entry.levelValue) {
314
+ continue;
315
+ }
316
+ let transformedRecord = record;
317
+ if (entry.options.transform) {
318
+ const result2 = entry.options.transform(record);
319
+ if (result2 === null) continue;
320
+ transformedRecord = result2;
321
+ }
322
+ const result = entry.transport.write(transformedRecord);
323
+ if (result instanceof Promise) {
324
+ promises.push(result);
325
+ }
326
+ }
327
+ await Promise.all(promises);
328
+ }
329
+ async flush() {
330
+ const promises = [];
331
+ for (const entry of this.transports.values()) {
332
+ if (entry.transport.flush) {
333
+ const result = entry.transport.flush();
334
+ if (result instanceof Promise) {
335
+ promises.push(result);
336
+ }
337
+ }
338
+ }
339
+ await Promise.all(promises);
340
+ }
341
+ async close() {
342
+ await this.flush();
343
+ const promises = [];
344
+ for (const entry of this.transports.values()) {
345
+ if (entry.transport.close) {
346
+ const result = entry.transport.close();
347
+ if (result instanceof Promise) {
348
+ promises.push(result);
349
+ }
350
+ }
351
+ }
352
+ await Promise.all(promises);
353
+ this.transports.clear();
354
+ }
355
+ get count() {
356
+ return this.transports.size;
357
+ }
358
+ list() {
359
+ return Array.from(this.transports.keys());
360
+ }
361
+ }
2
362
  const THEME_PRESETS = {
3
363
  default: {
4
364
  debug: {
@@ -1210,6 +1570,193 @@ function getAvailablePresets() {
1210
1570
  function hasPreset(name) {
1211
1571
  return name in SMART_PRESETS;
1212
1572
  }
1573
+ class ScopedLogger {
1574
+ parent;
1575
+ scopeName;
1576
+ badgeList = [];
1577
+ contextStack = [];
1578
+ _timers;
1579
+ constructor(parent, scopeName) {
1580
+ this.parent = parent;
1581
+ this.scopeName = scopeName;
1582
+ }
1583
+ get timers() {
1584
+ if (!this._timers) this._timers = /* @__PURE__ */ new Map();
1585
+ return this._timers;
1586
+ }
1587
+ getBindings() {
1588
+ return {
1589
+ scope: this.getScopePrefix(),
1590
+ badges: this.badgeList.length > 0 ? [...this.badgeList] : void 0
1591
+ };
1592
+ }
1593
+ getScopePrefix() {
1594
+ if (this.contextStack.length === 0) return this.scopeName;
1595
+ return [this.scopeName, ...this.contextStack].join(":");
1596
+ }
1597
+ badges(badges) {
1598
+ this.badgeList = [...badges];
1599
+ return this;
1600
+ }
1601
+ badge(badge) {
1602
+ if (!this.badgeList.includes(badge)) {
1603
+ this.badgeList.push(badge);
1604
+ }
1605
+ return this;
1606
+ }
1607
+ clearBadges() {
1608
+ this.badgeList = [];
1609
+ return this;
1610
+ }
1611
+ style(presetName) {
1612
+ this.parent.setTheme(presetName);
1613
+ return this;
1614
+ }
1615
+ context(contextName) {
1616
+ return new ContextLogger(this, contextName);
1617
+ }
1618
+ time(label) {
1619
+ this.timers.set(label, {
1620
+ label: `${this.scopeName}:${label}`,
1621
+ startTime: performance.now()
1622
+ });
1623
+ }
1624
+ timeEnd(label) {
1625
+ const timer = this.timers.get(label);
1626
+ if (!timer) {
1627
+ this.warn(`Timer '${label}' not found`);
1628
+ return void 0;
1629
+ }
1630
+ const elapsed = performance.now() - timer.startTime;
1631
+ this.timers.delete(label);
1632
+ this.success(`Timer: ${label} - ${elapsed.toFixed(2)}ms`);
1633
+ return elapsed;
1634
+ }
1635
+ debug(...args) {
1636
+ this.parent.logWithBindings(this.getBindings(), "debug", ...args);
1637
+ }
1638
+ info(...args) {
1639
+ this.parent.logWithBindings(this.getBindings(), "info", ...args);
1640
+ }
1641
+ warn(...args) {
1642
+ this.parent.logWithBindings(this.getBindings(), "warn", ...args);
1643
+ }
1644
+ error(...args) {
1645
+ this.parent.logWithBindings(this.getBindings(), "error", ...args);
1646
+ }
1647
+ success(...args) {
1648
+ const bindings = this.getBindings();
1649
+ this.parent.logWithBindings(bindings, "info", ...args);
1650
+ }
1651
+ critical(...args) {
1652
+ this.parent.logWithBindings(this.getBindings(), "critical", ...args);
1653
+ }
1654
+ trace(...args) {
1655
+ this.parent.logWithBindings(this.getBindings(), "debug", ...args);
1656
+ console.trace(`[${this.scopeName}]`);
1657
+ }
1658
+ _pushContext(context) {
1659
+ this.contextStack.push(context);
1660
+ }
1661
+ _popContext() {
1662
+ this.contextStack.pop();
1663
+ }
1664
+ }
1665
+ class APILogger extends ScopedLogger {
1666
+ constructor(parent, apiName) {
1667
+ super(parent, `API:${apiName}`);
1668
+ this.badge("API");
1669
+ }
1670
+ slow(message, duration) {
1671
+ this.badge("SLOW");
1672
+ const msg = duration ? `${message} (${duration}ms)` : message;
1673
+ this.warn(msg);
1674
+ }
1675
+ rateLimit(message) {
1676
+ this.badge("RATE_LIMIT");
1677
+ this.warn(message);
1678
+ }
1679
+ auth(message) {
1680
+ this.badge("AUTH");
1681
+ this.error(message);
1682
+ }
1683
+ deprecated(message) {
1684
+ this.badge("DEPRECATED");
1685
+ this.warn(message);
1686
+ }
1687
+ }
1688
+ class ComponentLogger extends ScopedLogger {
1689
+ constructor(parent, componentName) {
1690
+ super(parent, componentName);
1691
+ this.badge("COMPONENT");
1692
+ }
1693
+ lifecycle(event, message) {
1694
+ this.badge("LIFECYCLE");
1695
+ const msg = message ? `${event}: ${message}` : event;
1696
+ this.info(msg);
1697
+ }
1698
+ stateChange(from, to, data) {
1699
+ this.badge("STATE");
1700
+ const msg = `${from} → ${to}`;
1701
+ if (data) {
1702
+ this.info(msg, data);
1703
+ } else {
1704
+ this.info(msg);
1705
+ }
1706
+ }
1707
+ propsChange(changes) {
1708
+ this.badge("PROPS");
1709
+ this.debug("Props changed:", changes);
1710
+ }
1711
+ }
1712
+ class ContextLogger {
1713
+ parentLogger;
1714
+ contextName;
1715
+ constructor(parentLogger, contextName) {
1716
+ this.parentLogger = parentLogger;
1717
+ this.contextName = contextName;
1718
+ }
1719
+ run(fn) {
1720
+ this.parentLogger._pushContext(this.contextName);
1721
+ try {
1722
+ return fn();
1723
+ } finally {
1724
+ this.parentLogger._popContext();
1725
+ }
1726
+ }
1727
+ async runAsync(fn) {
1728
+ this.parentLogger._pushContext(this.contextName);
1729
+ try {
1730
+ return await fn();
1731
+ } finally {
1732
+ this.parentLogger._popContext();
1733
+ }
1734
+ }
1735
+ start() {
1736
+ this.parentLogger._pushContext(this.contextName);
1737
+ }
1738
+ end() {
1739
+ this.parentLogger._popContext();
1740
+ }
1741
+ debug(...args) {
1742
+ this.parentLogger.debug(...args);
1743
+ }
1744
+ info(...args) {
1745
+ this.parentLogger.info(...args);
1746
+ }
1747
+ warn(...args) {
1748
+ this.parentLogger.warn(...args);
1749
+ }
1750
+ error(...args) {
1751
+ this.parentLogger.error(...args);
1752
+ }
1753
+ success(...args) {
1754
+ this.parentLogger.success(...args);
1755
+ }
1756
+ critical(...args) {
1757
+ this.parentLogger.critical(...args);
1758
+ }
1759
+ }
1213
1760
  class ExportLogHandler {
1214
1761
  buffer = [];
1215
1762
  maxSize;
@@ -2497,11 +3044,15 @@ class Logger {
2497
3044
  exportHandler;
2498
3045
  cliProcessor;
2499
3046
  themeChangeListener;
3047
+ badgeList = [];
2500
3048
  displaySettings = {
2501
3049
  showTimestamp: true,
2502
3050
  showLocation: true,
2503
3051
  showBadges: true
2504
3052
  };
3053
+ serializerRegistry;
3054
+ hookManager;
3055
+ transportManager;
2505
3056
  /**
2506
3057
  * Crea una nueva instancia del Logger
2507
3058
  *
@@ -2518,9 +3069,11 @@ class Logger {
2518
3069
  */
2519
3070
  constructor(config = {}) {
2520
3071
  this.config = {
2521
- ...DEFAULT_CONFIG,
3072
+ ...DEFAULT_CONFIG$1,
2522
3073
  ...config
2523
3074
  };
3075
+ this.serializerRegistry = new SerializerRegistry();
3076
+ this.hookManager = new HookManager();
2524
3077
  if (this.config.bufferSize) {
2525
3078
  this.exportHandler = new ExportLogHandler(this.config.bufferSize);
2526
3079
  this.handlers.push(this.exportHandler);
@@ -2675,7 +3228,7 @@ class Logger {
2675
3228
  this.themeChangeListener();
2676
3229
  this.themeChangeListener = null;
2677
3230
  }
2678
- this.config = { ...DEFAULT_CONFIG };
3231
+ this.config = { ...DEFAULT_CONFIG$1 };
2679
3232
  LEVEL_STYLES = THEME_PRESETS.default;
2680
3233
  if (this.config.autoDetectTheme) {
2681
3234
  this.setupAutoThemeDetection();
@@ -2696,32 +3249,8 @@ class Logger {
2696
3249
  this.themeChangeListener();
2697
3250
  this.themeChangeListener = null;
2698
3251
  }
2699
- }
2700
- /**
2701
- * Factory method para crear loggers con scope evitando dependencias circulares
2702
- * @private
2703
- * @param {string} type - Tipo de logger ('scope' | 'api' | 'component')
2704
- * @param {string} name - Nombre del scope
2705
- * @returns {Promise<any>} Logger con scope
2706
- */
2707
- async createScopedLogger(type, name) {
2708
- try {
2709
- const { ScopedLogger, APILogger, ComponentLogger } = await import("./ScopedLogger-D-RbiFZn.js");
2710
- switch (type) {
2711
- case "component":
2712
- return new ComponentLogger(this, name);
2713
- case "api":
2714
- return new APILogger(this, name);
2715
- case "scope":
2716
- default:
2717
- return new ScopedLogger(this, name);
2718
- }
2719
- } catch (error2) {
2720
- this.error("Failed to load ScopedLogger, using basic logger with prefix:", error2);
2721
- const basicLogger = new Logger(this.getConfig());
2722
- basicLogger.setGlobalPrefix(`${this.config.globalPrefix || ""}:${name}`);
2723
- return basicLogger;
2724
- }
3252
+ this.transportManager?.close().catch(() => {
3253
+ });
2725
3254
  }
2726
3255
  // ===== SIMPLIFIED API =====
2727
3256
  /**
@@ -2782,7 +3311,7 @@ class Logger {
2782
3311
  */
2783
3312
  hideTimestamp() {
2784
3313
  this.displaySettings.showTimestamp = false;
2785
- this.success("Timestamp hidden");
3314
+ return this;
2786
3315
  }
2787
3316
  /**
2788
3317
  * Muestra el timestamp en los logs
@@ -2795,7 +3324,7 @@ class Logger {
2795
3324
  */
2796
3325
  showTimestamp() {
2797
3326
  this.displaySettings.showTimestamp = true;
2798
- this.success("Timestamp shown");
3327
+ return this;
2799
3328
  }
2800
3329
  /**
2801
3330
  * Oculta la información de ubicación (archivo:línea) en los logs
@@ -2808,7 +3337,7 @@ class Logger {
2808
3337
  */
2809
3338
  hideLocation() {
2810
3339
  this.displaySettings.showLocation = false;
2811
- this.success("Location info hidden");
3340
+ return this;
2812
3341
  }
2813
3342
  /**
2814
3343
  * Muestra la información de ubicación (archivo:línea) en los logs
@@ -2821,7 +3350,7 @@ class Logger {
2821
3350
  */
2822
3351
  showLocation() {
2823
3352
  this.displaySettings.showLocation = true;
2824
- this.success("Location info shown");
3353
+ return this;
2825
3354
  }
2826
3355
  /**
2827
3356
  * Oculta los badges en los logs
@@ -2835,81 +3364,78 @@ class Logger {
2835
3364
  */
2836
3365
  hideBadges() {
2837
3366
  this.displaySettings.showBadges = false;
2838
- this.success("Badges hidden");
3367
+ return this;
2839
3368
  }
2840
3369
  /**
2841
3370
  * Muestra los badges en los logs
2842
- *
3371
+ *
2843
3372
  * @example
2844
3373
  * logger.showBadges();
2845
3374
  * const api = logger.api('GraphQL');
2846
3375
  * api.info('Con badges'); // [API] [GraphQL] Con badges
2847
- *
3376
+ *
2848
3377
  * @since 0.3.0
2849
3378
  */
2850
3379
  showBadges() {
2851
3380
  this.displaySettings.showBadges = true;
2852
- this.success("Badges shown");
3381
+ return this;
2853
3382
  }
2854
- // ===== SCOPED LOGGERS =====
2855
3383
  /**
2856
- * Crea un logger para componentes con estilo automático
2857
- *
2858
- * @param {string} name - Nombre del componente
2859
- * @returns {ComponentLogger} Logger con scope de componente
2860
- *
3384
+ * Establece múltiples badges para los logs
3385
+ *
3386
+ * @param {string[]} badges - Array de badges a mostrar
3387
+ * @returns {this} Logger instance para encadenamiento
3388
+ *
2861
3389
  * @example
2862
- * const authLogger = logger.component('Autenticación');
2863
- * authLogger.info('Validando credenciales');
2864
- * // [COMPONENT] [Autenticación] Validando credenciales
2865
- *
2866
- * authLogger.success('Usuario autenticado');
2867
- * // [COMPONENT] [Autenticación] ✓ Usuario autenticado
2868
- *
2869
- * @since 0.3.0
3390
+ * logger.badges(['v3', 'stable']).info('Release publicado');
3391
+ * logger.badges(['API', 'v2']).warn('Endpoint deprecado');
3392
+ *
3393
+ * @since 3.0.0
2870
3394
  */
2871
- component(name) {
2872
- return this.createScopedLogger("component", name);
3395
+ badges(badges2) {
3396
+ this.badgeList = [...badges2];
3397
+ return this;
2873
3398
  }
2874
3399
  /**
2875
- * Crea un logger para APIs con badges automáticos
2876
- *
2877
- * @param {string} name - Nombre de la API o endpoint
2878
- * @returns {APILogger} Logger con scope de API
2879
- *
3400
+ * Añade un badge individual a la lista
3401
+ *
3402
+ * @param {string} badge - Badge a añadir
3403
+ * @returns {this} Logger instance para encadenamiento
3404
+ *
2880
3405
  * @example
2881
- * const api = logger.api('REST');
2882
- * api.info('GET /users');
2883
- * // [API] [REST] GET /users
2884
- *
2885
- * // Con badges adicionales
2886
- * api.badges(['v2', 'cached']).info('Respuesta desde caché');
2887
- * // [API] [v2] [cached] [REST] Respuesta desde caché
2888
- *
2889
- * @since 0.3.0
3406
+ * logger.badge('DEBUG').badge('AUTH').info('Token validado');
3407
+ *
3408
+ * @since 3.0.0
2890
3409
  */
2891
- api(name) {
2892
- return this.createScopedLogger("api", name);
3410
+ badge(badge2) {
3411
+ if (!this.badgeList.includes(badge2)) {
3412
+ this.badgeList.push(badge2);
3413
+ }
3414
+ return this;
2893
3415
  }
2894
3416
  /**
2895
- * Crea un logger con scope general y soporte de badges
2896
- *
2897
- * @param {string} name - Nombre del scope
2898
- * @returns {ScopedLogger} Logger con scope personalizado
2899
- *
3417
+ * Limpia todos los badges activos
3418
+ *
3419
+ * @returns {this} Logger instance para encadenamiento
3420
+ *
2900
3421
  * @example
2901
- * const dbLogger = logger.scope('Database');
2902
- * dbLogger.info('Conectando a MongoDB');
2903
- * // [Database] Conectando a MongoDB
2904
- *
2905
- * // Con badges personalizados
2906
- * dbLogger.badge('SLOW').warn('Query tardó 5s');
2907
- * // [SLOW] [Database] Query tardó 5s
2908
- *
2909
- * @since 0.3.0
3422
+ * logger.clearBadges().info('Sin badges');
3423
+ *
3424
+ * @since 3.0.0
2910
3425
  */
3426
+ clearBadges() {
3427
+ this.badgeList = [];
3428
+ return this;
3429
+ }
3430
+ // ===== SCOPED LOGGERS =====
3431
+ component(name) {
3432
+ return new ComponentLogger(this, name);
3433
+ }
3434
+ api(name) {
3435
+ return new APILogger(this, name);
3436
+ }
2911
3437
  scope(name) {
2912
- return this.createScopedLogger("scope", name);
3438
+ return new ScopedLogger(this, name);
2913
3439
  }
2914
3440
  // ===== SIMPLE CUSTOMIZATION =====
2915
3441
  /**
@@ -2981,13 +3507,199 @@ class Logger {
2981
3507
  }
2982
3508
  /**
2983
3509
  * Obtiene el handler de exportación si está disponible
2984
- *
3510
+ *
2985
3511
  * @returns {ExportLogHandler | undefined} Handler de exportación o undefined
2986
3512
  * @since 0.3.0
2987
3513
  */
2988
3514
  getExportHandler() {
2989
3515
  return this.exportHandler;
2990
3516
  }
3517
+ // ===== SERIALIZERS =====
3518
+ /**
3519
+ * Añade un serializador personalizado para un tipo específico
3520
+ *
3521
+ * @param type - Constructor del tipo a serializar
3522
+ * @param serializer - Función de serialización
3523
+ * @param priority - Prioridad (mayor = primero)
3524
+ *
3525
+ * @example
3526
+ * logger.addSerializer(Error, (err) => ({
3527
+ * name: err.name,
3528
+ * message: err.message,
3529
+ * stack: err.stack?.split('\n').slice(0, 5)
3530
+ * }));
3531
+ *
3532
+ * @since 3.0.0
3533
+ */
3534
+ addSerializer(type, serializer, priority) {
3535
+ this.serializerRegistry.add(type, serializer, priority);
3536
+ }
3537
+ /**
3538
+ * Elimina un serializador registrado
3539
+ *
3540
+ * @param type - Constructor del tipo a remover
3541
+ * @returns true si se eliminó
3542
+ *
3543
+ * @since 3.0.0
3544
+ */
3545
+ removeSerializer(type) {
3546
+ return this.serializerRegistry.remove(type);
3547
+ }
3548
+ /**
3549
+ * Obtiene el registry de serializadores
3550
+ *
3551
+ * @returns SerializerRegistry
3552
+ * @since 3.0.0
3553
+ */
3554
+ getSerializerRegistry() {
3555
+ return this.serializerRegistry;
3556
+ }
3557
+ // ===== HOOKS & MIDDLEWARE =====
3558
+ /**
3559
+ * Registra un hook para un evento
3560
+ *
3561
+ * @param event - Evento: 'beforeLog' | 'afterLog' | 'onError'
3562
+ * @param callback - Función a ejecutar
3563
+ * @param priority - Prioridad (mayor = primero)
3564
+ * @returns Función para desregistrar
3565
+ *
3566
+ * @example
3567
+ * const unsubscribe = logger.on('beforeLog', (entry) => {
3568
+ * entry.correlationId = getCorrelationId();
3569
+ * return entry;
3570
+ * });
3571
+ *
3572
+ * @since 3.0.0
3573
+ */
3574
+ on(event, callback, priority) {
3575
+ return this.hookManager.on(event, callback, priority);
3576
+ }
3577
+ /**
3578
+ * Registra un hook que se ejecuta solo una vez
3579
+ *
3580
+ * @param event - Evento: 'beforeLog' | 'afterLog' | 'onError'
3581
+ * @param callback - Función a ejecutar
3582
+ * @param priority - Prioridad (mayor = primero)
3583
+ * @returns Función para desregistrar
3584
+ *
3585
+ * @since 3.0.0
3586
+ */
3587
+ once(event, callback, priority) {
3588
+ return this.hookManager.once(event, callback, priority);
3589
+ }
3590
+ /**
3591
+ * Elimina un hook registrado
3592
+ *
3593
+ * @param event - Evento del hook
3594
+ * @param callback - Callback a remover
3595
+ * @returns true si se eliminó
3596
+ *
3597
+ * @since 3.0.0
3598
+ */
3599
+ off(event, callback) {
3600
+ return this.hookManager.off(event, callback);
3601
+ }
3602
+ /**
3603
+ * Añade un middleware al pipeline
3604
+ *
3605
+ * @param middleware - Función middleware
3606
+ * @param priority - Prioridad (mayor = primero)
3607
+ * @returns Función para desregistrar
3608
+ *
3609
+ * @example
3610
+ * logger.use((entry, next) => {
3611
+ * entry.requestId = asyncLocalStorage.getStore()?.requestId;
3612
+ * next();
3613
+ * });
3614
+ *
3615
+ * @since 3.0.0
3616
+ */
3617
+ use(middleware, priority) {
3618
+ return this.hookManager.use(middleware, priority);
3619
+ }
3620
+ /**
3621
+ * Obtiene el HookManager
3622
+ *
3623
+ * @returns HookManager
3624
+ * @since 3.0.0
3625
+ */
3626
+ getHookManager() {
3627
+ return this.hookManager;
3628
+ }
3629
+ // ===== TRANSPORTS =====
3630
+ /**
3631
+ * Añade un transport para envío de logs
3632
+ *
3633
+ * @param target - Configuración del transport
3634
+ * @returns ID único del transport
3635
+ *
3636
+ * @example
3637
+ * // File transport
3638
+ * logger.addTransport({
3639
+ * target: 'file',
3640
+ * options: { destination: '/var/log/app.log' }
3641
+ * });
3642
+ *
3643
+ * @example
3644
+ * // HTTP transport con batching
3645
+ * logger.addTransport({
3646
+ * target: 'http',
3647
+ * options: {
3648
+ * url: 'https://logs.example.com',
3649
+ * batchSize: 100,
3650
+ * flushInterval: 5000
3651
+ * },
3652
+ * level: 'warn'
3653
+ * });
3654
+ *
3655
+ * @since 3.0.0
3656
+ */
3657
+ addTransport(target) {
3658
+ if (!this.transportManager) {
3659
+ this.transportManager = new TransportManager();
3660
+ }
3661
+ return this.transportManager.add(target);
3662
+ }
3663
+ /**
3664
+ * Elimina un transport
3665
+ *
3666
+ * @param id - ID del transport a remover
3667
+ * @returns true si se eliminó
3668
+ *
3669
+ * @since 3.0.0
3670
+ */
3671
+ removeTransport(id) {
3672
+ return this.transportManager?.remove(id) ?? false;
3673
+ }
3674
+ /**
3675
+ * Fuerza el flush de todos los transports
3676
+ *
3677
+ * @returns Promise que resuelve cuando todos los buffers están vaciados
3678
+ *
3679
+ * @since 3.0.0
3680
+ */
3681
+ async flushTransports() {
3682
+ await this.transportManager?.flush();
3683
+ }
3684
+ /**
3685
+ * Cierra todos los transports
3686
+ *
3687
+ * @returns Promise que resuelve cuando todos están cerrados
3688
+ *
3689
+ * @since 3.0.0
3690
+ */
3691
+ async closeTransports() {
3692
+ await this.transportManager?.close();
3693
+ }
3694
+ /**
3695
+ * Obtiene el TransportManager
3696
+ *
3697
+ * @returns TransportManager o undefined si no hay transports
3698
+ * @since 3.0.0
3699
+ */
3700
+ getTransportManager() {
3701
+ return this.transportManager;
3702
+ }
2991
3703
  // ===== CORE LOGGING METHODS =====
2992
3704
  /**
2993
3705
  * Verifica si un nivel de log debe mostrarse según la verbosidad actual
@@ -3019,8 +3731,26 @@ class Logger {
3019
3731
  if (!this.shouldLog(level)) return;
3020
3732
  const stackInfo = this.config.enableStackTrace ? parseStackTrace() : null;
3021
3733
  const prefix = this.getEffectivePrefix();
3022
- const message = args.length > 0 ? String(args[0]) : "";
3023
- const additionalArgs = args.slice(1);
3734
+ const timestamp = formatTimestamp();
3735
+ const serializedArgs = args.map((arg) => this.serializerRegistry.serialize(arg));
3736
+ let message = serializedArgs.length > 0 ? String(serializedArgs[0]) : "";
3737
+ if (this.badgeList.length > 0 && this.displaySettings.showBadges) {
3738
+ const badgePrefix = this.badgeList.map((b) => `[${b}]`).join("");
3739
+ message = badgePrefix + " " + message;
3740
+ }
3741
+ const additionalArgs = serializedArgs.slice(1);
3742
+ let hookEntry = {
3743
+ level,
3744
+ message,
3745
+ args: serializedArgs,
3746
+ timestamp,
3747
+ prefix,
3748
+ stackInfo: stackInfo || void 0
3749
+ };
3750
+ this.hookManager.emit("beforeLog", hookEntry).then((processed) => {
3751
+ message = processed.message;
3752
+ }).catch(() => {
3753
+ });
3024
3754
  const [format, ...styles2] = createStyledOutput(
3025
3755
  level,
3026
3756
  LEVEL_STYLES,
@@ -3042,30 +3772,59 @@ class Logger {
3042
3772
  this.exportHandler.setGroupInfo(this.groupDepth);
3043
3773
  }
3044
3774
  const metadata = {
3045
- timestamp: formatTimestamp(),
3775
+ timestamp,
3046
3776
  level,
3047
3777
  prefix,
3048
3778
  stackInfo: stackInfo ? stackInfo : void 0
3049
3779
  };
3050
3780
  this.handlers.forEach((handler) => {
3051
3781
  try {
3052
- handler.handle(level, message, args, metadata);
3782
+ handler.handle(level, message, serializedArgs, metadata);
3053
3783
  } catch (error2) {
3054
3784
  console.error("Log handler failed:", error2);
3055
3785
  }
3056
3786
  });
3787
+ if (this.transportManager) {
3788
+ const levelValues = {
3789
+ debug: 0,
3790
+ info: 1,
3791
+ warn: 2,
3792
+ error: 3,
3793
+ critical: 4
3794
+ };
3795
+ const record = {
3796
+ level,
3797
+ levelValue: levelValues[level],
3798
+ time: Date.now(),
3799
+ msg: message,
3800
+ prefix,
3801
+ location: stackInfo ? {
3802
+ file: stackInfo.file,
3803
+ line: stackInfo.line,
3804
+ column: stackInfo.column,
3805
+ function: stackInfo.function
3806
+ } : void 0
3807
+ };
3808
+ this.transportManager.write(record).catch(() => {
3809
+ });
3810
+ }
3811
+ this.hookManager.emit("afterLog", hookEntry).catch(() => {
3812
+ });
3813
+ }
3814
+ logWithBindings(bindings, level, ...args) {
3815
+ if (!this.shouldLog(level)) return;
3816
+ let prefix = "";
3817
+ if (bindings.badges?.length) {
3818
+ prefix += bindings.badges.map((b) => `[${b}]`).join("");
3819
+ }
3820
+ if (bindings.scope) {
3821
+ prefix += `[${bindings.scope}] `;
3822
+ }
3823
+ if (prefix && args.length > 0) {
3824
+ args[0] = prefix + String(args[0]);
3825
+ }
3826
+ this.log(level, ...args);
3057
3827
  }
3058
- /**
3059
- * Registra información de debug (prioridad más baja)
3060
- *
3061
- * @param {...any} args - Mensajes y datos a loggear
3062
- *
3063
- * @example
3064
- * logger.debug('Variable estado:', { usuario: 'Juan', activo: true });
3065
- * logger.debug('Iniciando proceso de validación');
3066
- *
3067
- * @since 0.3.0
3068
- */
3069
3828
  debug(...args) {
3070
3829
  this.log("debug", ...args);
3071
3830
  }
@@ -3493,11 +4252,18 @@ function getDefaultLogger() {
3493
4252
  }
3494
4253
  getDefaultLogger();
3495
4254
  export {
4255
+ APILogger as A,
3496
4256
  BANNER_VARIANTS as B,
4257
+ ComponentLogger as C,
3497
4258
  ExportLogHandler as E,
4259
+ HookManager as H,
3498
4260
  Logger as L,
4261
+ SerializerRegistry as S,
3499
4262
  THEME_PRESETS as T,
3500
4263
  THEME_BANNERS as a,
3501
- displayInitBanner as d
4264
+ TransportManager as b,
4265
+ ScopedLogger as c,
4266
+ ContextLogger as d,
4267
+ displayInitBanner as e
3502
4268
  };
3503
- //# sourceMappingURL=Logger-B7L-ujY4.js.map
4269
+ //# sourceMappingURL=Logger-C086w3EM.js.map