@morojs/moro 1.5.17 → 1.6.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 (138) hide show
  1. package/README.md +48 -65
  2. package/dist/core/auth/morojs-adapter.js +12 -16
  3. package/dist/core/auth/morojs-adapter.js.map +1 -1
  4. package/dist/core/config/file-loader.d.ts +5 -0
  5. package/dist/core/config/file-loader.js +171 -0
  6. package/dist/core/config/file-loader.js.map +1 -1
  7. package/dist/core/config/index.d.ts +10 -39
  8. package/dist/core/config/index.js +29 -66
  9. package/dist/core/config/index.js.map +1 -1
  10. package/dist/core/config/loader.d.ts +7 -0
  11. package/dist/core/config/loader.js +269 -0
  12. package/dist/core/config/loader.js.map +1 -0
  13. package/dist/core/config/schema.js +31 -41
  14. package/dist/core/config/schema.js.map +1 -1
  15. package/dist/core/config/utils.d.ts +2 -9
  16. package/dist/core/config/utils.js +32 -19
  17. package/dist/core/config/utils.js.map +1 -1
  18. package/dist/core/config/validation.d.ts +17 -0
  19. package/dist/core/config/validation.js +131 -0
  20. package/dist/core/config/validation.js.map +1 -0
  21. package/dist/core/database/adapters/mongodb.d.ts +0 -10
  22. package/dist/core/database/adapters/mongodb.js +2 -23
  23. package/dist/core/database/adapters/mongodb.js.map +1 -1
  24. package/dist/core/database/adapters/mysql.d.ts +0 -11
  25. package/dist/core/database/adapters/mysql.js +0 -1
  26. package/dist/core/database/adapters/mysql.js.map +1 -1
  27. package/dist/core/database/adapters/postgresql.d.ts +1 -9
  28. package/dist/core/database/adapters/postgresql.js +1 -1
  29. package/dist/core/database/adapters/postgresql.js.map +1 -1
  30. package/dist/core/database/adapters/redis.d.ts +0 -9
  31. package/dist/core/database/adapters/redis.js +4 -14
  32. package/dist/core/database/adapters/redis.js.map +1 -1
  33. package/dist/core/framework.d.ts +7 -6
  34. package/dist/core/framework.js +16 -131
  35. package/dist/core/framework.js.map +1 -1
  36. package/dist/core/http/http-server.d.ts +0 -12
  37. package/dist/core/http/http-server.js +23 -151
  38. package/dist/core/http/http-server.js.map +1 -1
  39. package/dist/core/http/router.d.ts +0 -12
  40. package/dist/core/http/router.js +36 -114
  41. package/dist/core/http/router.js.map +1 -1
  42. package/dist/core/logger/filters.js +4 -12
  43. package/dist/core/logger/filters.js.map +1 -1
  44. package/dist/core/logger/index.d.ts +1 -1
  45. package/dist/core/logger/index.js +1 -2
  46. package/dist/core/logger/index.js.map +1 -1
  47. package/dist/core/logger/logger.d.ts +13 -29
  48. package/dist/core/logger/logger.js +203 -380
  49. package/dist/core/logger/logger.js.map +1 -1
  50. package/dist/core/logger/outputs.js +2 -0
  51. package/dist/core/logger/outputs.js.map +1 -1
  52. package/dist/core/middleware/built-in/auth.js +17 -88
  53. package/dist/core/middleware/built-in/auth.js.map +1 -1
  54. package/dist/core/middleware/built-in/cache.js +1 -3
  55. package/dist/core/middleware/built-in/cache.js.map +1 -1
  56. package/dist/core/middleware/built-in/index.d.ts +0 -1
  57. package/dist/core/middleware/built-in/index.js +1 -6
  58. package/dist/core/middleware/built-in/index.js.map +1 -1
  59. package/dist/core/middleware/built-in/request-logger.js +2 -3
  60. package/dist/core/middleware/built-in/request-logger.js.map +1 -1
  61. package/dist/core/middleware/built-in/sse.js +7 -9
  62. package/dist/core/middleware/built-in/sse.js.map +1 -1
  63. package/dist/core/modules/auto-discovery.d.ts +0 -17
  64. package/dist/core/modules/auto-discovery.js +12 -367
  65. package/dist/core/modules/auto-discovery.js.map +1 -1
  66. package/dist/core/modules/modules.js +2 -12
  67. package/dist/core/modules/modules.js.map +1 -1
  68. package/dist/core/networking/adapters/ws-adapter.d.ts +1 -1
  69. package/dist/core/networking/adapters/ws-adapter.js +2 -2
  70. package/dist/core/networking/adapters/ws-adapter.js.map +1 -1
  71. package/dist/core/networking/service-discovery.js +7 -7
  72. package/dist/core/networking/service-discovery.js.map +1 -1
  73. package/dist/core/routing/index.d.ts +0 -20
  74. package/dist/core/routing/index.js +13 -178
  75. package/dist/core/routing/index.js.map +1 -1
  76. package/dist/core/runtime/node-adapter.js +6 -12
  77. package/dist/core/runtime/node-adapter.js.map +1 -1
  78. package/dist/moro.d.ts +0 -48
  79. package/dist/moro.js +148 -456
  80. package/dist/moro.js.map +1 -1
  81. package/dist/types/config.d.ts +2 -58
  82. package/dist/types/core.d.ts +40 -34
  83. package/dist/types/http.d.ts +1 -16
  84. package/dist/types/logger.d.ts +0 -7
  85. package/dist/types/module.d.ts +0 -11
  86. package/package.json +2 -2
  87. package/src/core/auth/morojs-adapter.ts +13 -18
  88. package/src/core/config/file-loader.ts +233 -0
  89. package/src/core/config/index.ts +32 -77
  90. package/src/core/config/loader.ts +633 -0
  91. package/src/core/config/schema.ts +31 -41
  92. package/src/core/config/utils.ts +29 -22
  93. package/src/core/config/validation.ts +140 -0
  94. package/src/core/database/README.md +16 -26
  95. package/src/core/database/adapters/mongodb.ts +2 -30
  96. package/src/core/database/adapters/mysql.ts +0 -14
  97. package/src/core/database/adapters/postgresql.ts +2 -12
  98. package/src/core/database/adapters/redis.ts +4 -27
  99. package/src/core/framework.ts +23 -163
  100. package/src/core/http/http-server.ts +36 -176
  101. package/src/core/http/router.ts +38 -127
  102. package/src/core/logger/filters.ts +4 -12
  103. package/src/core/logger/index.ts +0 -1
  104. package/src/core/logger/logger.ts +216 -427
  105. package/src/core/logger/outputs.ts +2 -0
  106. package/src/core/middleware/built-in/auth.ts +17 -98
  107. package/src/core/middleware/built-in/cache.ts +1 -3
  108. package/src/core/middleware/built-in/index.ts +0 -8
  109. package/src/core/middleware/built-in/request-logger.ts +1 -3
  110. package/src/core/middleware/built-in/sse.ts +7 -9
  111. package/src/core/modules/auto-discovery.ts +13 -476
  112. package/src/core/modules/modules.ts +9 -20
  113. package/src/core/networking/adapters/ws-adapter.ts +5 -2
  114. package/src/core/networking/service-discovery.ts +7 -6
  115. package/src/core/routing/index.ts +14 -198
  116. package/src/core/runtime/node-adapter.ts +6 -12
  117. package/src/moro.ts +166 -554
  118. package/src/types/config.ts +2 -59
  119. package/src/types/core.ts +45 -47
  120. package/src/types/http.ts +1 -23
  121. package/src/types/logger.ts +0 -9
  122. package/src/types/module.ts +0 -12
  123. package/dist/core/config/config-manager.d.ts +0 -44
  124. package/dist/core/config/config-manager.js +0 -114
  125. package/dist/core/config/config-manager.js.map +0 -1
  126. package/dist/core/config/config-sources.d.ts +0 -21
  127. package/dist/core/config/config-sources.js +0 -502
  128. package/dist/core/config/config-sources.js.map +0 -1
  129. package/dist/core/config/config-validator.d.ts +0 -21
  130. package/dist/core/config/config-validator.js +0 -765
  131. package/dist/core/config/config-validator.js.map +0 -1
  132. package/dist/core/middleware/built-in/jwt-helpers.d.ts +0 -118
  133. package/dist/core/middleware/built-in/jwt-helpers.js +0 -221
  134. package/dist/core/middleware/built-in/jwt-helpers.js.map +0 -1
  135. package/src/core/config/config-manager.ts +0 -133
  136. package/src/core/config/config-sources.ts +0 -596
  137. package/src/core/config/config-validator.ts +0 -1078
  138. package/src/core/middleware/built-in/jwt-helpers.ts +0 -240
@@ -2,7 +2,6 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.createFrameworkLogger = exports.logger = exports.MoroLogger = void 0;
4
4
  exports.configureGlobalLogger = configureGlobalLogger;
5
- exports.destroyGlobalLogger = destroyGlobalLogger;
6
5
  exports.applyLoggingConfiguration = applyLoggingConfiguration;
7
6
  // Moro Logger - Beautiful, Fast, Feature-Rich
8
7
  const perf_hooks_1 = require("perf_hooks");
@@ -33,23 +32,12 @@ class MoroLogger {
33
32
  cachedTimestamp = '';
34
33
  lastTimestamp = 0;
35
34
  timestampCacheInterval = 100; // 100ms for better precision
36
- // Object pooling for LogEntry objects (Pino's technique)
37
- static ENTRY_POOL = [];
38
- static MAX_POOL_SIZE = 100;
39
- static poolIndex = 0;
40
- // String builder for efficient concatenation
41
- static stringBuilder = [];
42
- static stringBuilderIndex = 0;
43
- // Buffered output for performance
35
+ // Buffered output for micro-batching
44
36
  outputBuffer = [];
45
37
  bufferSize = 0;
46
- maxBufferSize = 1000;
38
+ maxBufferSize = 1024; // 1KB buffer
47
39
  flushTimeout = null;
48
40
  flushInterval = 1; // 1ms micro-batching
49
- // Buffer overflow protection
50
- bufferOverflowThreshold;
51
- emergencyFlushInProgress = false;
52
- isDestroyed = false;
53
41
  // High-performance output methods
54
42
  static LEVELS = {
55
43
  debug: 0,
@@ -58,6 +46,76 @@ class MoroLogger {
58
46
  error: 3,
59
47
  fatal: 4,
60
48
  };
49
+ // Static pre-allocated strings for maximum performance
50
+ static LEVEL_STRINGS = {
51
+ debug: 'DEBUG',
52
+ info: 'INFO',
53
+ warn: 'WARN',
54
+ error: 'ERROR',
55
+ fatal: 'FATAL',
56
+ };
57
+ // Pre-allocated ANSI color codes
58
+ static ANSI_COLORS = {
59
+ reset: '\x1b[0m',
60
+ bold: '\x1b[1m',
61
+ dim: '\x1b[2m',
62
+ red: '\x1b[31m',
63
+ green: '\x1b[32m',
64
+ yellow: '\x1b[33m',
65
+ blue: '\x1b[34m',
66
+ magenta: '\x1b[35m',
67
+ cyan: '\x1b[36m',
68
+ white: '\x1b[37m',
69
+ gray: '\x1b[90m',
70
+ };
71
+ // Object pool for LogEntry reuse
72
+ static ENTRY_POOL = [];
73
+ static MAX_POOL_SIZE = 100;
74
+ static poolIndex = 0;
75
+ // Object pool management
76
+ static getPooledEntry() {
77
+ if (MoroLogger.poolIndex > 0) {
78
+ return MoroLogger.ENTRY_POOL[--MoroLogger.poolIndex];
79
+ }
80
+ return {
81
+ timestamp: new Date(),
82
+ level: 'info',
83
+ message: '',
84
+ context: undefined,
85
+ metadata: undefined,
86
+ };
87
+ }
88
+ static returnPooledEntry(entry) {
89
+ if (MoroLogger.poolIndex < MoroLogger.MAX_POOL_SIZE) {
90
+ // Reset the entry
91
+ entry.timestamp = new Date();
92
+ entry.level = 'info';
93
+ entry.message = '';
94
+ entry.context = undefined;
95
+ entry.metadata = undefined;
96
+ MoroLogger.ENTRY_POOL[MoroLogger.poolIndex++] = entry;
97
+ }
98
+ }
99
+ // String builder for efficient concatenation
100
+ static stringBuilder = [];
101
+ static stringBuilderIndex = 0;
102
+ static resetStringBuilder() {
103
+ MoroLogger.stringBuilderIndex = 0;
104
+ }
105
+ static appendToBuilder(str) {
106
+ if (MoroLogger.stringBuilderIndex < MoroLogger.stringBuilder.length) {
107
+ MoroLogger.stringBuilder[MoroLogger.stringBuilderIndex++] = str;
108
+ }
109
+ else {
110
+ MoroLogger.stringBuilder.push(str);
111
+ MoroLogger.stringBuilderIndex++;
112
+ }
113
+ }
114
+ static buildString() {
115
+ const result = MoroLogger.stringBuilder.slice(0, MoroLogger.stringBuilderIndex).join('');
116
+ MoroLogger.resetStringBuilder();
117
+ return result;
118
+ }
61
119
  static COLORS = {
62
120
  debug: '\x1b[36m', // Cyan
63
121
  info: '\x1b[32m', // Green
@@ -68,20 +126,11 @@ class MoroLogger {
68
126
  context: '\x1b[34m', // Blue
69
127
  metadata: '\x1b[37m', // White
70
128
  performance: '\x1b[36m', // Cyan
71
- reset: '\x1b[0m', // Reset
72
129
  };
73
130
  static RESET = '\x1b[0m';
74
131
  static BOLD = '\x1b[1m';
75
- // Static pre-allocated strings for performance
76
- static LEVEL_STRINGS = {
77
- debug: 'DEBUG',
78
- info: 'INFO ',
79
- warn: 'WARN ',
80
- error: 'ERROR',
81
- fatal: 'FATAL',
82
- };
83
132
  constructor(options = {}) {
84
- this.options = this.validateOptions({
133
+ this.options = {
85
134
  level: 'info',
86
135
  enableColors: true,
87
136
  enableTimestamp: true,
@@ -92,14 +141,9 @@ class MoroLogger {
92
141
  outputs: [],
93
142
  filters: [],
94
143
  maxEntries: 1000,
95
- maxBufferSize: 1000,
96
144
  ...options,
97
- });
145
+ };
98
146
  this.level = this.options.level || 'info';
99
- // Initialize buffer size from options
100
- this.maxBufferSize = this.options.maxBufferSize || 1000;
101
- // Initialize buffer overflow protection
102
- this.bufferOverflowThreshold = this.maxBufferSize * 2;
103
147
  // Add default console output
104
148
  this.addOutput({
105
149
  name: 'console',
@@ -110,52 +154,6 @@ class MoroLogger {
110
154
  this.options.outputs?.forEach(output => this.addOutput(output));
111
155
  this.options.filters?.forEach(filter => this.addFilter(filter));
112
156
  }
113
- // Object pooling methods
114
- static getPooledEntry() {
115
- if (MoroLogger.ENTRY_POOL.length > 0) {
116
- const entry = MoroLogger.ENTRY_POOL.pop();
117
- // Properly reset ALL properties to prevent memory leaks
118
- entry.timestamp = new Date();
119
- entry.level = 'info';
120
- entry.message = '';
121
- entry.context = undefined;
122
- entry.metadata = undefined;
123
- entry.performance = undefined;
124
- entry.moduleId = undefined;
125
- return entry;
126
- }
127
- return MoroLogger.createFreshEntry();
128
- }
129
- // ADD this new method:
130
- static createFreshEntry() {
131
- return {
132
- timestamp: new Date(),
133
- level: 'info',
134
- message: '',
135
- context: undefined,
136
- metadata: undefined,
137
- performance: undefined,
138
- moduleId: undefined,
139
- };
140
- }
141
- static returnPooledEntry(entry) {
142
- if (MoroLogger.ENTRY_POOL.length < MoroLogger.MAX_POOL_SIZE) {
143
- MoroLogger.ENTRY_POOL.push(entry);
144
- }
145
- }
146
- // String builder methods
147
- static resetStringBuilder() {
148
- MoroLogger.stringBuilder.length = 0;
149
- MoroLogger.stringBuilderIndex = 0;
150
- }
151
- static appendToBuilder(str) {
152
- MoroLogger.stringBuilder[MoroLogger.stringBuilderIndex++] = str;
153
- }
154
- static buildString() {
155
- const result = MoroLogger.stringBuilder.join('');
156
- MoroLogger.resetStringBuilder();
157
- return result;
158
- }
159
157
  debug(message, context, metadata) {
160
158
  this.log('debug', message, context, metadata);
161
159
  }
@@ -190,9 +188,7 @@ class MoroLogger {
190
188
  }
191
189
  }
192
190
  child(context, metadata) {
193
- // Create child logger with current parent level (not original options level)
194
- const childOptions = { ...this.options, level: this.level };
195
- const childLogger = new MoroLogger(childOptions);
191
+ const childLogger = new MoroLogger(this.options);
196
192
  childLogger.contextPrefix = this.contextPrefix ? `${this.contextPrefix}:${context}` : context;
197
193
  childLogger.contextMetadata = { ...this.contextMetadata, ...metadata };
198
194
  childLogger.outputs = this.outputs;
@@ -204,9 +200,6 @@ class MoroLogger {
204
200
  setLevel(level) {
205
201
  this.level = level;
206
202
  }
207
- getLevel() {
208
- return this.level;
209
- }
210
203
  addOutput(output) {
211
204
  this.outputs.set(output.name, output);
212
205
  }
@@ -249,16 +242,6 @@ class MoroLogger {
249
242
  }
250
243
  return this.cachedTimestamp;
251
244
  }
252
- // Cached timestamp generation (updates once per second)
253
- getFastCachedTimestamp() {
254
- const now = Date.now();
255
- if (now - this.lastTimestamp > 1000) {
256
- // Update every second
257
- this.lastTimestamp = now;
258
- this.cachedTimestamp = new Date(now).toISOString().slice(0, 19).replace('T', ' ');
259
- }
260
- return this.cachedTimestamp;
261
- }
262
245
  getMetrics() {
263
246
  const now = Date.now();
264
247
  const uptime = (now - this.startTime) / 1000; // seconds
@@ -283,34 +266,47 @@ class MoroLogger {
283
266
  memoryUsage: 0,
284
267
  };
285
268
  }
286
- // Optimized logging method
269
+ // Optimized logging method with aggressive level checking
287
270
  log(level, message, context, metadata) {
288
- // Quick level check - use parent level if available (for child loggers)
289
- const effectiveLevel = this.parent ? this.parent.level : this.level;
290
- if (MoroLogger.LEVELS[level] < MoroLogger.LEVELS[effectiveLevel]) {
291
- return;
292
- }
293
- // Absolute minimal path for simple logs - pure speed
271
+ // AGGRESSIVE LEVEL CHECK - numeric comparison for maximum speed
272
+ const levelNum = MoroLogger.LEVELS[level];
273
+ const effectiveLevelNum = this.parent
274
+ ? MoroLogger.LEVELS[this.parent.level]
275
+ : MoroLogger.LEVELS[this.level];
276
+ if (levelNum < effectiveLevelNum) {
277
+ return; // Exit immediately if level is too low
278
+ }
279
+ // ULTRA-FAST PATH: Just message, no context, no metadata
294
280
  if (!metadata && !context && !this.contextPrefix && !this.contextMetadata) {
295
- this.writeSimpleLog(level, message);
281
+ const levelStr = MoroLogger.LEVEL_STRINGS[level];
282
+ this.output(`${levelStr} ${message}\n`, level);
296
283
  return;
297
284
  }
298
- // Minimal path for logs with context but no metadata
285
+ // FAST PATH: Message + context, no metadata
299
286
  if (!metadata && !this.contextMetadata) {
300
- this.writeSimpleLog(level, message, context);
287
+ const levelStr = MoroLogger.LEVEL_STRINGS[level];
288
+ if (context) {
289
+ this.output(`${levelStr} [${context}] ${message}\n`, level);
290
+ }
291
+ else {
292
+ this.output(`${levelStr} ${message}\n`, level);
293
+ }
301
294
  return;
302
295
  }
303
- // Path for complex logs
304
- if (metadata && Object.keys(metadata).length > 0) {
305
- this.complexLog(level, message, context, metadata);
296
+ // MEDIUM PATH: Message + context + simple metadata
297
+ if (metadata && Object.keys(metadata).length <= 3 && !this.contextMetadata) {
298
+ const levelStr = MoroLogger.LEVEL_STRINGS[level];
299
+ const contextStr = context ? `[${context}] ` : '';
300
+ const metaStr = this.stringify(metadata);
301
+ this.output(`${levelStr} ${contextStr}${message} ${metaStr}\n`, level);
306
302
  return;
307
303
  }
308
- // Full logging path for complex logs
304
+ // FULL PATH: All features enabled
309
305
  this.fullLog(level, message, context, metadata);
310
306
  }
311
- // Full logging with all features
307
+ // Full logging with all features using object pooling
312
308
  fullLog(level, message, context, metadata) {
313
- // Use object pooling for LogEntry (Pino's technique)
309
+ // Get pooled entry to avoid allocation
314
310
  const entry = MoroLogger.getPooledEntry();
315
311
  const now = Date.now();
316
312
  entry.timestamp = new Date(now);
@@ -327,7 +323,6 @@ class MoroLogger {
327
323
  if (this.filters.size > 0) {
328
324
  for (const filter of this.filters.values()) {
329
325
  if (!filter.filter(entry)) {
330
- MoroLogger.returnPooledEntry(entry);
331
326
  return;
332
327
  }
333
328
  }
@@ -338,76 +333,8 @@ class MoroLogger {
338
333
  this.addToHistory(entry);
339
334
  // Write to outputs with batched processing
340
335
  this.writeToOutputs(entry, level);
341
- // Return entry to pool
342
- MoroLogger.returnPooledEntry(entry);
343
- }
344
- // Absolute minimal logging - pure speed, no overhead
345
- complexLog(level, message, context, metadata) {
346
- // Use object pooling for LogEntry (Pino's technique)
347
- const entry = MoroLogger.getPooledEntry();
348
- const now = Date.now();
349
- entry.timestamp = new Date(now);
350
- entry.level = level;
351
- entry.message = message;
352
- entry.context = this.contextPrefix
353
- ? context
354
- ? `${this.contextPrefix}:${context}`
355
- : this.contextPrefix
356
- : context;
357
- entry.metadata = this.createMetadata(metadata);
358
- entry.performance = this.options.enablePerformance ? this.getPerformanceData(now) : undefined;
359
- // Write to outputs with batched processing
360
- this.writeToOutputs(entry, level);
361
- // Return entry to pool
362
- MoroLogger.returnPooledEntry(entry);
363
- }
364
- // Simple log writer with colors for minimal overhead cases
365
- writeSimpleLog(level, message, context) {
366
- const colors = this.options.enableColors !== false;
367
- const levelReset = colors ? MoroLogger.RESET : '';
368
- MoroLogger.resetStringBuilder();
369
- // Timestamp with caching optimization
370
- if (this.options.enableTimestamp !== false) {
371
- const timestamp = this.getFastCachedTimestamp();
372
- if (colors) {
373
- MoroLogger.appendToBuilder(MoroLogger.COLORS.timestamp);
374
- MoroLogger.appendToBuilder(timestamp);
375
- MoroLogger.appendToBuilder(levelReset);
376
- }
377
- else {
378
- MoroLogger.appendToBuilder(timestamp);
379
- }
380
- MoroLogger.appendToBuilder(' ');
381
- }
382
- // Level with pre-allocated strings
383
- const levelStr = MoroLogger.LEVEL_STRINGS[level];
384
- if (colors) {
385
- MoroLogger.appendToBuilder(MoroLogger.COLORS[level]);
386
- MoroLogger.appendToBuilder(MoroLogger.BOLD);
387
- MoroLogger.appendToBuilder(levelStr);
388
- MoroLogger.appendToBuilder(levelReset);
389
- }
390
- else {
391
- MoroLogger.appendToBuilder(levelStr);
392
- }
393
- // Context
394
- if (context && this.options.enableContext !== false) {
395
- MoroLogger.appendToBuilder(' ');
396
- if (colors) {
397
- MoroLogger.appendToBuilder(MoroLogger.COLORS.context);
398
- MoroLogger.appendToBuilder(`[${context}]`);
399
- MoroLogger.appendToBuilder(levelReset);
400
- }
401
- else {
402
- MoroLogger.appendToBuilder(`[${context}]`);
403
- }
404
- }
405
- // Message
406
- MoroLogger.appendToBuilder(' ');
407
- MoroLogger.appendToBuilder(message);
408
- // Output main log line with high-performance method
409
- const finalMessage = MoroLogger.buildString();
410
- this.output(`${finalMessage}\n`, level);
336
+ // Return entry to pool after a short delay to allow async operations
337
+ setTimeout(() => MoroLogger.returnPooledEntry(entry), 0);
411
338
  }
412
339
  updateMetrics(entry) {
413
340
  this.metrics.totalLogs++;
@@ -455,35 +382,23 @@ class MoroLogger {
455
382
  writeToOutputs(entry, level) {
456
383
  if (this.outputs.size === 0)
457
384
  return;
458
- let successCount = 0;
459
- const errors = [];
460
385
  for (const output of this.outputs.values()) {
461
386
  if (!output.level || MoroLogger.LEVELS[level] >= MoroLogger.LEVELS[output.level]) {
462
387
  try {
463
388
  output.write(entry);
464
- successCount++;
465
389
  }
466
390
  catch (error) {
467
- errors.push({ outputName: output.name, error });
468
- this.handleOutputError(output.name, error);
391
+ // Fallback to console.error for logger errors
392
+ // eslint-disable-next-line no-console
393
+ console.error('Logger output error:', error);
469
394
  }
470
395
  }
471
396
  }
472
- // If all outputs fail, use emergency console
473
- if (successCount === 0 && this.outputs.size > 0) {
474
- this.emergencyConsoleWrite(entry);
475
- }
476
- // Log output errors (but avoid infinite loops)
477
- if (errors.length > 0 && level !== 'error') {
478
- this.error(`Logger output errors: ${errors.length} failed`, 'MoroLogger', {
479
- errors: errors.map(e => e.outputName),
480
- });
481
- }
482
397
  }
483
398
  writeToConsole(entry) {
484
399
  const format = this.options.format || 'pretty';
485
400
  if (format === 'json') {
486
- this.output(`${this.safeStringify(entry)}\n`, entry.level);
401
+ this.output(JSON.stringify(entry) + '\n', entry.level);
487
402
  return;
488
403
  }
489
404
  if (format === 'compact') {
@@ -497,7 +412,6 @@ class MoroLogger {
497
412
  }
498
413
  writePrettyLog(entry) {
499
414
  const colors = this.options.enableColors !== false;
500
- const levelReset = colors ? MoroLogger.RESET : '';
501
415
  MoroLogger.resetStringBuilder();
502
416
  // Timestamp with caching optimization
503
417
  if (this.options.enableTimestamp !== false) {
@@ -505,29 +419,35 @@ class MoroLogger {
505
419
  if (colors) {
506
420
  MoroLogger.appendToBuilder(MoroLogger.COLORS.timestamp);
507
421
  MoroLogger.appendToBuilder(timestamp);
508
- MoroLogger.appendToBuilder(levelReset);
422
+ MoroLogger.appendToBuilder(MoroLogger.RESET);
509
423
  }
510
424
  else {
511
425
  MoroLogger.appendToBuilder(timestamp);
512
426
  }
427
+ }
428
+ // Level with color using pre-allocated strings
429
+ const levelColor = colors ? MoroLogger.COLORS[entry.level] : '';
430
+ const levelReset = colors ? MoroLogger.RESET : '';
431
+ const levelText = MoroLogger.LEVEL_STRINGS[entry.level];
432
+ // Add space after timestamp if present
433
+ if (this.options.enableTimestamp !== false) {
513
434
  MoroLogger.appendToBuilder(' ');
514
435
  }
515
- // Level with pre-allocated strings
516
- const levelStr = MoroLogger.LEVEL_STRINGS[entry.level];
517
436
  if (colors) {
518
- MoroLogger.appendToBuilder(MoroLogger.COLORS[entry.level]);
437
+ MoroLogger.appendToBuilder(levelColor);
519
438
  MoroLogger.appendToBuilder(MoroLogger.BOLD);
520
- MoroLogger.appendToBuilder(levelStr);
439
+ MoroLogger.appendToBuilder(levelText);
521
440
  MoroLogger.appendToBuilder(levelReset);
522
441
  }
523
442
  else {
524
- MoroLogger.appendToBuilder(levelStr);
443
+ MoroLogger.appendToBuilder(levelText);
525
444
  }
526
445
  // Context
527
446
  if (entry.context && this.options.enableContext !== false) {
528
- MoroLogger.appendToBuilder(' ');
447
+ const contextColor = colors ? MoroLogger.COLORS.context : '';
448
+ MoroLogger.appendToBuilder(' '); // Space before context
529
449
  if (colors) {
530
- MoroLogger.appendToBuilder(MoroLogger.COLORS.context);
450
+ MoroLogger.appendToBuilder(contextColor);
531
451
  MoroLogger.appendToBuilder(`[${entry.context}]`);
532
452
  MoroLogger.appendToBuilder(levelReset);
533
453
  }
@@ -536,7 +456,7 @@ class MoroLogger {
536
456
  }
537
457
  }
538
458
  // Message
539
- MoroLogger.appendToBuilder(' ');
459
+ MoroLogger.appendToBuilder(' '); // Space before message
540
460
  MoroLogger.appendToBuilder(entry.message);
541
461
  // Performance info
542
462
  if (entry.performance && this.options.enablePerformance !== false) {
@@ -549,7 +469,7 @@ class MoroLogger {
549
469
  perfParts.push(`${Math.round(entry.performance.memory)}MB`);
550
470
  }
551
471
  if (perfParts.length > 0) {
552
- MoroLogger.appendToBuilder(' ');
472
+ MoroLogger.appendToBuilder(' '); // Space before performance info
553
473
  if (colors) {
554
474
  MoroLogger.appendToBuilder(perfColor);
555
475
  MoroLogger.appendToBuilder(`(${perfParts.join(', ')})`);
@@ -567,20 +487,19 @@ class MoroLogger {
567
487
  const metaColor = colors ? MoroLogger.COLORS.metadata : '';
568
488
  const cleanMetadata = this.cleanMetadata(entry.metadata);
569
489
  if (Object.keys(cleanMetadata).length > 0) {
570
- MoroLogger.appendToBuilder(' ');
571
490
  if (colors) {
572
491
  MoroLogger.appendToBuilder(metaColor);
573
- MoroLogger.appendToBuilder(this.safeStringify(cleanMetadata));
492
+ MoroLogger.appendToBuilder(this.stringify(cleanMetadata));
574
493
  MoroLogger.appendToBuilder(levelReset);
575
494
  }
576
495
  else {
577
- MoroLogger.appendToBuilder(this.safeStringify(cleanMetadata));
496
+ MoroLogger.appendToBuilder(this.stringify(cleanMetadata));
578
497
  }
579
498
  }
580
499
  }
581
500
  // Output main log line with high-performance method
582
501
  const finalMessage = MoroLogger.buildString();
583
- this.output(`${finalMessage}\n`, entry.level);
502
+ this.output(finalMessage + '\n', entry.level);
584
503
  // Stack trace for errors
585
504
  if (entry.metadata?.stack && (entry.level === 'error' || entry.level === 'fatal')) {
586
505
  const stackColor = colors ? MoroLogger.COLORS.error : '';
@@ -597,179 +516,77 @@ class MoroLogger {
597
516
  }
598
517
  return clean;
599
518
  }
600
- // High-performance output with buffering
601
- output(message, level = 'info') {
602
- // Prevent memory exhaustion
603
- if (this.outputBuffer.length >= this.bufferOverflowThreshold &&
604
- !this.emergencyFlushInProgress) {
605
- this.emergencyFlushInProgress = true;
606
- this.forceFlushBuffer();
607
- this.emergencyFlushInProgress = false;
519
+ // Fast JSON stringify with error handling
520
+ stringify(obj) {
521
+ try {
522
+ return JSON.stringify(obj);
608
523
  }
524
+ catch {
525
+ return '[Circular Reference]';
526
+ }
527
+ }
528
+ // High-performance output with micro-batching
529
+ output(message, level = 'info') {
530
+ // Add to buffer
609
531
  this.outputBuffer.push(message);
610
- this.bufferSize++;
611
- // Immediate flush for critical levels or full buffer
612
- if (level === 'fatal' || level === 'error' || this.bufferSize >= this.maxBufferSize) {
532
+ this.bufferSize += message.length;
533
+ // Flush immediately if buffer is full or for errors
534
+ if (this.bufferSize >= this.maxBufferSize || level === 'error' || level === 'fatal') {
613
535
  this.flushBuffer();
614
536
  }
615
537
  else {
538
+ // Schedule flush with micro-batching
616
539
  this.scheduleFlush();
617
540
  }
618
541
  }
619
542
  scheduleFlush() {
620
- if (this.flushTimeout || this.isDestroyed) {
621
- return; // Already scheduled or destroyed
622
- }
543
+ if (this.flushTimeout)
544
+ return; // Already scheduled
623
545
  this.flushTimeout = setTimeout(() => {
624
546
  this.flushBuffer();
547
+ this.flushTimeout = null;
625
548
  }, this.flushInterval);
626
549
  }
627
550
  flushBuffer() {
628
- if (this.outputBuffer.length === 0) {
551
+ if (this.outputBuffer.length === 0)
629
552
  return;
630
- }
631
- // Group messages by stream type
632
- const stdoutMessages = [];
633
- const stderrMessages = [];
634
- for (const message of this.outputBuffer) {
635
- // Determine stream based on message content or level
636
- if (message.includes('ERROR') || message.includes('FATAL')) {
637
- stderrMessages.push(message);
638
- }
639
- else {
640
- stdoutMessages.push(message);
641
- }
642
- }
643
- // Write to appropriate streams with error handling
644
553
  try {
645
- if (stdoutMessages.length > 0 && process.stdout.writable) {
554
+ // Group by stream type for efficiency
555
+ const stdoutMessages = [];
556
+ const stderrMessages = [];
557
+ for (const message of this.outputBuffer) {
558
+ // Determine stream based on message content (simple heuristic)
559
+ if (message.includes('ERROR') || message.includes('FATAL')) {
560
+ stderrMessages.push(message);
561
+ }
562
+ else {
563
+ stdoutMessages.push(message);
564
+ }
565
+ }
566
+ // Write to streams
567
+ if (stdoutMessages.length > 0) {
646
568
  process.stdout.write(stdoutMessages.join(''));
647
569
  }
648
- if (stderrMessages.length > 0 && process.stderr.writable) {
570
+ if (stderrMessages.length > 0) {
649
571
  process.stderr.write(stderrMessages.join(''));
650
572
  }
651
573
  }
652
574
  catch {
653
- // Fallback to console if streams fail
654
- try {
655
- // eslint-disable-next-line no-console
656
- console.log(this.outputBuffer.join(''));
657
- }
658
- catch {
659
- // If even console.log fails, just ignore
660
- }
661
- }
662
- // Clear buffer
663
- this.outputBuffer.length = 0;
664
- this.bufferSize = 0;
665
- // Clear timeout
666
- if (this.flushTimeout) {
667
- clearTimeout(this.flushTimeout);
668
- this.flushTimeout = null;
669
- }
670
- }
671
- // Emergency flush for buffer overflow protection
672
- forceFlushBuffer() {
673
- if (this.outputBuffer.length === 0)
674
- return;
675
- try {
676
- const message = this.outputBuffer.join('');
677
- process.stdout.write(message);
678
- }
679
- catch (error) {
680
- // Emergency fallback - write individual messages
681
- for (const msg of this.outputBuffer) {
682
- try {
683
- process.stdout.write(msg);
684
- }
685
- catch {
686
- // If even this fails, give up on this batch
687
- break;
575
+ // Fallback to console methods if stream write fails
576
+ for (const message of this.outputBuffer) {
577
+ if (message.includes('ERROR') || message.includes('FATAL')) {
578
+ // eslint-disable-next-line no-console
579
+ console.error(message.trim());
688
580
  }
689
- }
690
- }
691
- finally {
692
- this.outputBuffer.length = 0;
693
- this.bufferSize = 0;
694
- }
695
- }
696
- // Safe stringify with circular reference detection
697
- safeStringify(obj, maxDepth = 3) {
698
- const seen = new WeakSet();
699
- const stringify = (value, depth) => {
700
- if (depth > maxDepth)
701
- return '[Max Depth Reached]';
702
- if (value === null || typeof value !== 'object')
703
- return value;
704
- if (seen.has(value))
705
- return '[Circular Reference]';
706
- seen.add(value);
707
- if (Array.isArray(value)) {
708
- return value.map(item => stringify(item, depth + 1));
709
- }
710
- const result = {};
711
- for (const [key, val] of Object.entries(value)) {
712
- if (typeof val !== 'function') {
713
- // Skip functions
714
- result[key] = stringify(val, depth + 1);
581
+ else {
582
+ // eslint-disable-next-line no-console
583
+ console.log(message.trim());
715
584
  }
716
585
  }
717
- return result;
718
- };
719
- try {
720
- return JSON.stringify(stringify(obj, 0));
721
- }
722
- catch (error) {
723
- return '[Stringify Error]';
724
- }
725
- }
726
- // Configuration validation
727
- validateOptions(options) {
728
- const validated = { ...options };
729
- // Validate log level
730
- const validLevels = ['debug', 'info', 'warn', 'error', 'fatal'];
731
- if (validated.level && !validLevels.includes(validated.level)) {
732
- console.warn(`[MoroLogger] Invalid log level: ${validated.level}, defaulting to 'info'`);
733
- validated.level = 'info';
734
- }
735
- // Validate max entries
736
- if (validated.maxEntries !== undefined) {
737
- if (validated.maxEntries < 1 || validated.maxEntries > 100000) {
738
- console.warn(`[MoroLogger] Invalid maxEntries: ${validated.maxEntries}, defaulting to 1000`);
739
- validated.maxEntries = 1000;
740
- }
741
- }
742
- // Validate buffer size
743
- if (validated.maxBufferSize !== undefined) {
744
- if (validated.maxBufferSize < 10 || validated.maxBufferSize > 10000) {
745
- console.warn(`[MoroLogger] Invalid maxBufferSize: ${validated.maxBufferSize}, defaulting to 1000`);
746
- validated.maxBufferSize = 1000;
747
- }
748
- }
749
- return validated;
750
- }
751
- // Error handling methods
752
- handleOutputError(outputName, error) {
753
- // Could implement output retry logic, circuit breaker, etc.
754
- // For now, just track the error
755
- if (!this.metrics.outputErrors) {
756
- this.metrics.outputErrors = {};
757
- }
758
- this.metrics.outputErrors[outputName] = (this.metrics.outputErrors[outputName] || 0) + 1;
759
- }
760
- emergencyConsoleWrite(entry) {
761
- const message = `${entry.timestamp.toISOString()} ${entry.level.toUpperCase()} ${entry.message}`;
762
- try {
763
- if (entry.level === 'error' || entry.level === 'fatal') {
764
- process.stderr.write(`[EMERGENCY] ${message}\n`);
765
- }
766
- else {
767
- process.stdout.write(`[EMERGENCY] ${message}\n`);
768
- }
769
- }
770
- catch {
771
- // If even emergency write fails, there's nothing more we can do
772
586
  }
587
+ // Reset buffer
588
+ this.outputBuffer.length = 0;
589
+ this.bufferSize = 0;
773
590
  }
774
591
  // Force flush streams (useful for shutdown)
775
592
  flush() {
@@ -781,36 +598,27 @@ class MoroLogger {
781
598
  // Flush any remaining buffer
782
599
  this.flushBuffer();
783
600
  try {
784
- // Force flush streams without ending them
601
+ // Force flush streams
785
602
  if (process.stdout.writable) {
786
- process.stdout.write(''); // Force flush without ending
603
+ process.stdout.end();
787
604
  }
788
605
  if (process.stderr.writable) {
789
- process.stderr.write(''); // Force flush without ending
606
+ process.stderr.end();
790
607
  }
791
608
  }
792
609
  catch {
793
610
  // Ignore flush errors
794
611
  }
795
612
  }
796
- // Destroy logger and clean up all resources (for testing)
797
- destroy() {
798
- // Mark as destroyed to prevent new timeouts
799
- this.isDestroyed = true;
800
- // Clear any remaining timeouts
613
+ // Cleanup method to clear all timeouts and handles
614
+ cleanup() {
615
+ // Clear any pending flush timeout
801
616
  if (this.flushTimeout) {
802
617
  clearTimeout(this.flushTimeout);
803
618
  this.flushTimeout = null;
804
619
  }
805
- // Flush any remaining buffer
620
+ // Flush any remaining output
806
621
  this.flushBuffer();
807
- // Clear outputs and filters
808
- this.outputs.clear();
809
- this.filters.clear();
810
- // Clear history
811
- this.history.length = 0;
812
- this.historyIndex = 0;
813
- this.historySize = 0;
814
622
  }
815
623
  }
816
624
  exports.MoroLogger = MoroLogger;
@@ -823,6 +631,28 @@ exports.logger = new MoroLogger({
823
631
  enableColors: !process.env.NO_COLOR,
824
632
  format: process.env.LOG_FORMAT || 'pretty',
825
633
  });
634
+ // Add cleanup handlers for Jest and other test runners
635
+ if (typeof process !== 'undefined') {
636
+ // Cleanup on process exit
637
+ process.on('beforeExit', () => {
638
+ exports.logger.cleanup();
639
+ });
640
+ process.on('SIGINT', () => {
641
+ exports.logger.cleanup();
642
+ process.exit(0);
643
+ });
644
+ process.on('SIGTERM', () => {
645
+ exports.logger.cleanup();
646
+ process.exit(0);
647
+ });
648
+ // For Jest and other test runners - cleanup on uncaught exceptions
649
+ process.on('uncaughtException', () => {
650
+ exports.logger.cleanup();
651
+ });
652
+ process.on('unhandledRejection', () => {
653
+ exports.logger.cleanup();
654
+ });
655
+ }
826
656
  /**
827
657
  * Configure the global logger with new settings
828
658
  * This allows runtime configuration of the logger
@@ -834,13 +664,6 @@ function configureGlobalLogger(options) {
834
664
  // Additional configuration options can be added here as needed
835
665
  // For now, focusing on level which is the most critical
836
666
  }
837
- /**
838
- * Destroy the global logger and clean up resources (for testing)
839
- * @internal
840
- */
841
- function destroyGlobalLogger() {
842
- exports.logger.destroy();
843
- }
844
667
  /**
845
668
  * Apply logging configuration from the config system and/or createApp options
846
669
  */