@adaas/a-utils 0.1.15 → 0.1.17

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.
@@ -0,0 +1,383 @@
1
+ # A-Logger Component
2
+
3
+ A sophisticated logging component for ADAAS applications with advanced formatting, scope-based organization, and configurable output levels.
4
+
5
+ ## Overview
6
+
7
+ The A_Logger component provides comprehensive logging capabilities with:
8
+
9
+ - **Scope-based Formatting**: Consistent message alignment regardless of scope name length
10
+ - **Color-coded Output**: Terminal color support for visual distinction
11
+ - **Object Formatting**: Pretty-printing of JSON objects with proper indentation
12
+ - **Error Handling**: Special formatting for A_Error and standard Error objects
13
+ - **Log Level Filtering**: Configurable filtering based on severity levels
14
+ - **Performance Optimized**: Efficient handling of large objects and rapid logging
15
+
16
+ ## Features
17
+
18
+ ### 🎨 Visual Output
19
+ - **Terminal Colors**: Support for 9 different colors (green, blue, red, yellow, gray, magenta, cyan, white, pink)
20
+ - **Consistent Alignment**: All log messages align properly regardless of scope name length
21
+ - **Structured Formatting**: Multi-line messages with proper indentation and separators
22
+
23
+ ### 📊 Data Types Support
24
+ - **Strings**: Simple text messages with optional color coding
25
+ - **Objects**: Pretty-printed JSON with nested structure support
26
+ - **Arrays**: Formatted array output with proper indentation
27
+ - **Errors**: Special handling for both A_Error and standard Error objects
28
+ - **Multi-arguments**: Support for logging multiple values in a single call
29
+
30
+ ### 🔧 Configuration
31
+ - **Log Levels**: debug, info, warn, error, all
32
+ - **Environment Variables**: Configurable via A_LOGGER_LEVEL environment variable
33
+ - **Scope Integration**: Seamless integration with A_Scope for context
34
+
35
+ ### ⚡ Performance
36
+ - **Efficient Formatting**: Optimized for large objects and rapid logging
37
+ - **Memory Conscious**: Minimal memory overhead for formatting operations
38
+ - **Non-blocking**: Asynchronous-friendly design
39
+
40
+ ## Installation
41
+
42
+ ```bash
43
+ npm install @adaas/a-utils
44
+ ```
45
+
46
+ ## Basic Usage
47
+
48
+ ### Simple Logging
49
+
50
+ ```typescript
51
+ import { A_Scope } from "@adaas/a-concept";
52
+ import { A_Logger } from "@adaas/a-utils";
53
+
54
+ // Create scope and logger
55
+ const scope = new A_Scope({ name: 'MyService' });
56
+ const logger = new A_Logger(scope);
57
+
58
+ // Basic logging
59
+ logger.log('Application started');
60
+ logger.log('green', 'Operation successful');
61
+ logger.warning('Resource usage high');
62
+ logger.error('Database connection failed');
63
+ ```
64
+
65
+ ### Object Logging
66
+
67
+ ```typescript
68
+ // Log complex objects
69
+ const user = {
70
+ id: 123,
71
+ name: 'John Doe',
72
+ preferences: {
73
+ theme: 'dark',
74
+ notifications: true
75
+ }
76
+ };
77
+
78
+ logger.log('blue', 'User data:', user);
79
+
80
+ // Multi-argument logging
81
+ logger.log('green',
82
+ 'Processing complete:',
83
+ 'Records:', 150,
84
+ 'Errors:', 2,
85
+ 'Success rate:', '98.7%'
86
+ );
87
+ ```
88
+
89
+ ### Error Handling
90
+
91
+ ```typescript
92
+ // Standard JavaScript errors
93
+ try {
94
+ throw new Error('Database timeout');
95
+ } catch (error) {
96
+ logger.error('Database operation failed:', error);
97
+ }
98
+
99
+ // A_Error instances
100
+ const validationError = new A_Error('Validation failed');
101
+ logger.error('Request validation:', validationError);
102
+
103
+ // Error with context
104
+ logger.error(
105
+ 'Operation failed:',
106
+ error,
107
+ 'Context:', { userId: '123', operation: 'update' },
108
+ 'Additional info:', 'User may need admin privileges'
109
+ );
110
+ ```
111
+
112
+ ## Advanced Usage
113
+
114
+ ### Dependency Injection
115
+
116
+ ```typescript
117
+ import { A_Inject, A_Component } from "@adaas/a-concept";
118
+
119
+ class UserService extends A_Component {
120
+ constructor(
121
+ @A_Inject(A_Logger) private logger: A_Logger
122
+ ) {
123
+ super();
124
+ }
125
+
126
+ async createUser(userData: any) {
127
+ this.logger.log('green', 'Creating user:', userData);
128
+
129
+ try {
130
+ // User creation logic
131
+ this.logger.log('User created successfully');
132
+ } catch (error) {
133
+ this.logger.error('User creation failed:', error);
134
+ throw error;
135
+ }
136
+ }
137
+ }
138
+ ```
139
+
140
+ ### Log Level Configuration
141
+
142
+ ```typescript
143
+ import { A_Config } from "@adaas/a-utils";
144
+
145
+ // Configure log level via environment variable
146
+ // A_LOGGER_LEVEL=warn
147
+
148
+ // Or via config injection
149
+ const config = new A_Config();
150
+ const logger = new A_Logger(scope, config);
151
+
152
+ // Different log levels:
153
+ // - 'debug': Shows all messages
154
+ // - 'info': Shows info, warning, and error messages
155
+ // - 'warn': Shows warning and error messages only
156
+ // - 'error': Shows error messages only
157
+ // - 'all': Shows all messages (default)
158
+ ```
159
+
160
+ ### Scope Alignment Demonstration
161
+
162
+ ```typescript
163
+ // Different scope lengths - all align consistently
164
+ const services = [
165
+ new A_Logger(new A_Scope({ name: 'API' })),
166
+ new A_Logger(new A_Scope({ name: 'Authentication' })),
167
+ new A_Logger(new A_Scope({ name: 'DatabaseConnectionPool' })),
168
+ new A_Logger(new A_Scope({ name: 'A' }))
169
+ ];
170
+
171
+ services.forEach(logger => {
172
+ logger.log('green', 'Service initialized');
173
+ logger.warning('Configuration check needed');
174
+ });
175
+
176
+ // Output shows consistent alignment:
177
+ // [API ] |15:42:123| Service initialized
178
+ // [Authentication ] |15:42:124| Service initialized
179
+ // [DatabaseConnectionPool] |15:42:125| Service initialized
180
+ // [A ] |15:42:126| Service initialized
181
+ ```
182
+
183
+ ## Color Reference
184
+
185
+ | Color | Use Case | Code |
186
+ |---------|----------|------|
187
+ | `green` | Success, completion | 32 |
188
+ | `blue` | Info, general messages | 34 |
189
+ | `red` | Errors, critical issues | 31 |
190
+ | `yellow` | Warnings, caution | 33 |
191
+ | `gray` | Debug, less important | 90 |
192
+ | `magenta` | Special highlighting | 35 |
193
+ | `cyan` | Headers, titles | 36 |
194
+ | `white` | Default text | 37 |
195
+ | `pink` | Custom highlighting | 95 |
196
+
197
+ ## Log Level Hierarchy
198
+
199
+ | Level | Shows |
200
+ |-------|-------|
201
+ | `debug` | All messages (debug, info, warning, error) |
202
+ | `info` | Info, warning, and error messages |
203
+ | `warn` | Warning and error messages only |
204
+ | `error` | Error messages only |
205
+ | `all` | All messages (alias for debug) |
206
+
207
+ ## Output Format
208
+
209
+ ### Standard Message Format
210
+ ```
211
+ [ScopeName ] |MM:SS:mmm| Message content
212
+ ```
213
+
214
+ ### Multi-line Object Format
215
+ ```
216
+ [ScopeName ] |MM:SS:mmm|
217
+ |-------------------------------
218
+ | {
219
+ | "key": "value",
220
+ | "nested": {
221
+ | "data": true
222
+ | }
223
+ | }
224
+ |-------------------------------
225
+ ```
226
+
227
+ ### Error Format
228
+ ```
229
+ [ScopeName ] |MM:SS:mmm|
230
+ |-------------------------------
231
+ | Error: | ERROR_CODE
232
+ |-------------------------------
233
+ | | Error message
234
+ | | Error description
235
+ |-------------------------------
236
+ | Stack trace...
237
+ |-------------------------------
238
+ ```
239
+
240
+ ## Performance Considerations
241
+
242
+ - **Large Objects**: Efficiently handles objects with 100+ properties
243
+ - **Rapid Logging**: Supports 100+ log calls per second without performance degradation
244
+ - **Memory Usage**: Minimal memory overhead for formatting operations
245
+ - **String Processing**: Optimized string concatenation and replacement
246
+
247
+ ## Testing
248
+
249
+ The component includes comprehensive tests covering:
250
+
251
+ - Basic logging functionality
252
+ - Scope formatting and alignment
253
+ - Object and data type handling
254
+ - Error formatting
255
+ - Color support
256
+ - Log level filtering
257
+ - Performance characteristics
258
+ - Edge cases and error conditions
259
+
260
+ Run tests:
261
+ ```bash
262
+ npm test A-Logger.test.ts
263
+ ```
264
+
265
+ ## Examples
266
+
267
+ See `examples/A-Logger-examples.ts` for comprehensive usage examples including:
268
+
269
+ - Basic logging patterns
270
+ - Scope alignment demonstration
271
+ - Object and data logging
272
+ - Error handling scenarios
273
+ - Color usage examples
274
+ - Performance logging simulation
275
+ - Real-world service logging
276
+
277
+ ## API Reference
278
+
279
+ ### Constructor
280
+
281
+ ```typescript
282
+ constructor(
283
+ scope: A_Scope,
284
+ config?: A_Config<A_LoggerEnvVariablesType>
285
+ )
286
+ ```
287
+
288
+ ### Methods
289
+
290
+ #### `log(message: any, ...args: any[]): void`
291
+ #### `log(color: ColorKey, message: any, ...args: any[]): void`
292
+ Log general messages with optional color specification.
293
+
294
+ #### `warning(...args: any[]): void`
295
+ Log warning messages with yellow color coding.
296
+
297
+ #### `error(...args: any[]): void`
298
+ Log error messages with red color coding.
299
+
300
+ ### Properties
301
+
302
+ #### `colors: readonly object`
303
+ Available color codes for terminal output.
304
+
305
+ #### `scopeLength: number`
306
+ Calculated scope length for consistent formatting.
307
+
308
+ #### `formattedScope: string`
309
+ Padded scope name for consistent alignment.
310
+
311
+ ## Environment Variables
312
+
313
+ | Variable | Type | Default | Description |
314
+ |----------|------|---------|-------------|
315
+ | `A_LOGGER_LEVEL` | string | 'all' | Log level filter (debug, info, warn, error, all) |
316
+
317
+ ## Best Practices
318
+
319
+ ### 1. Use Appropriate Colors
320
+ ```typescript
321
+ logger.log('green', 'Operation successful'); // Success
322
+ logger.log('blue', 'Processing data...'); // Info
323
+ logger.warning('Resource usage high'); // Warning
324
+ logger.error('Operation failed'); // Error
325
+ ```
326
+
327
+ ### 2. Provide Context
328
+ ```typescript
329
+ logger.log('blue', 'User operation:', {
330
+ userId: user.id,
331
+ operation: 'profile-update',
332
+ timestamp: new Date().toISOString()
333
+ });
334
+ ```
335
+
336
+ ### 3. Use Appropriate Log Levels
337
+ ```typescript
338
+ // Development
339
+ A_LOGGER_LEVEL=debug
340
+
341
+ // Production
342
+ A_LOGGER_LEVEL=warn
343
+ ```
344
+
345
+ ### 4. Structure Multi-argument Logs
346
+ ```typescript
347
+ logger.log('green',
348
+ 'Operation completed:',
349
+ 'Duration:', `${duration}ms`,
350
+ 'Records processed:', count,
351
+ 'Status:', 'success'
352
+ );
353
+ ```
354
+
355
+ ## Troubleshooting
356
+
357
+ ### Common Issues
358
+
359
+ **Q: Log messages not appearing**
360
+ A: Check the `A_LOGGER_LEVEL` environment variable. Set to 'debug' or 'all' to see all messages.
361
+
362
+ **Q: Scope names not aligning**
363
+ A: This is handled automatically. The component uses a standard scope length (20 characters) for consistent alignment.
364
+
365
+ **Q: Colors not showing in terminal**
366
+ A: Ensure your terminal supports ANSI color codes. Most modern terminals do.
367
+
368
+ **Q: Performance issues with large objects**
369
+ A: The component is optimized for large objects. If issues persist, consider logging object summaries instead of full objects.
370
+
371
+ ## Contributing
372
+
373
+ When contributing to the A-Logger component:
374
+
375
+ 1. Add comprehensive tests for new features
376
+ 2. Update documentation and examples
377
+ 3. Ensure consistent formatting and alignment
378
+ 4. Test with various scope name lengths
379
+ 5. Verify color support across different terminals
380
+
381
+ ## License
382
+
383
+ Part of the ADAAS ecosystem. See main project license for details.
@@ -1,5 +1,4 @@
1
- import { A_Component, A_Container, A_TYPES__Component_Constructor, A_TYPES__Entity_Constructor, A_TYPES__Fragment_Constructor } from "@adaas/a-concept"
2
- import { A_TYPES__Container_Constructor } from "@adaas/a-concept/dist/src/global/A-Container/A-Container.types"
1
+ import { A_Component, A_TYPES__Component_Constructor, A_TYPES__Container_Constructor, A_TYPES__Entity_Constructor, A_TYPES__Fragment_Constructor } from "@adaas/a-concept"
3
2
 
4
3
 
5
4
  export type A_UTILS_TYPES__Manifest_Init = Array<A_UTILS_TYPES__Manifest_ComponentLevelConfig>
@@ -41,7 +41,7 @@ export class A_Memory<
41
41
  * @param requiredKeys
42
42
  * @returns
43
43
  */
44
- async verifyPrerequisites(
44
+ async prerequisites(
45
45
  requiredKeys: Array<keyof _MemoryType>
46
46
  ): Promise<boolean> {
47
47
  return requiredKeys.every(key => this._memory.has(key));
@@ -1,5 +1,5 @@
1
1
  import { A_Command } from '@adaas/a-utils/lib/A-Command/A-Command.entity';
2
- import { A_CONSTANTS__A_Command_Status, A_CONSTANTS_A_Command_Features } from '@adaas/a-utils/lib/A-Command/A-Command.constants';
2
+ import { A_CONSTANTS__A_Command_Status, A_CommandFeatures } from '@adaas/a-utils/lib/A-Command/A-Command.constants';
3
3
  import { A_Component, A_Context, A_Error, A_Feature, A_Inject, A_Scope } from '@adaas/a-concept';
4
4
  import { A_Memory } from '@adaas/a-utils/lib/A-Memory/A-Memory.context';
5
5
 
@@ -44,7 +44,7 @@ describe('A-Command tests', () => {
44
44
  A_Context.root.register(command);
45
45
 
46
46
  command.emit('A_CUSTOM_EVENT_1');
47
- command.emit('compile');
47
+ command.emit('onCompile');
48
48
 
49
49
  expect(command).toBeInstanceOf(A_Command);
50
50
  expect(command.code).toBe('my-command');
@@ -94,7 +94,7 @@ describe('A-Command tests', () => {
94
94
  class MyComponent extends A_Component {
95
95
 
96
96
  @A_Feature.Extend({ scope: [MyCommand] })
97
- async [A_CONSTANTS_A_Command_Features.EXECUTE](
97
+ async [A_CommandFeatures.onExecute](
98
98
  @A_Inject(A_Memory) context: A_Memory<resultType>
99
99
  ) {
100
100
  context.set('bar', 'baz');
@@ -131,7 +131,7 @@ describe('A-Command tests', () => {
131
131
  class MyComponent extends A_Component {
132
132
 
133
133
  @A_Feature.Extend({ scope: [MyCommand] })
134
- async [A_CONSTANTS_A_Command_Features.EXECUTE](
134
+ async [A_CommandFeatures.onExecute](
135
135
  @A_Inject(A_Memory) context: A_Memory<resultType>
136
136
  ) {
137
137
  context.error(new A_Error({ title: 'Test error' }));
@@ -170,11 +170,11 @@ describe('A-Command tests', () => {
170
170
  A_Context.root.register(command);
171
171
 
172
172
  // Track lifecycle events
173
- command.on('init', () => lifecycleOrder.push('init'));
174
- command.on('compile', () => lifecycleOrder.push('compile'));
175
- command.on('execute', () => lifecycleOrder.push('execute'));
176
- command.on('complete', () => lifecycleOrder.push('complete'));
177
- command.on('fail', () => lifecycleOrder.push('fail'));
173
+ command.on(A_CommandFeatures.onInit, () => lifecycleOrder.push('init'));
174
+ command.on(A_CommandFeatures.onCompile, () => lifecycleOrder.push('compile'));
175
+ command.on(A_CommandFeatures.onExecute, () => lifecycleOrder.push('execute'));
176
+ command.on(A_CommandFeatures.onComplete, () => lifecycleOrder.push('complete'));
177
+ command.on(A_CommandFeatures.onFail, () => lifecycleOrder.push('fail'));
178
178
 
179
179
  await command.execute();
180
180
 
@@ -221,7 +221,7 @@ describe('A-Command tests', () => {
221
221
 
222
222
  class FailingComponent extends A_Component {
223
223
  @A_Feature.Extend({ scope: [FailingCommand] })
224
- async [A_CONSTANTS_A_Command_Features.EXECUTE]() {
224
+ async [A_CommandFeatures.onExecute]() {
225
225
  throw new A_Error({ title: 'Execution failed' });
226
226
  }
227
227
  }
@@ -232,11 +232,11 @@ describe('A-Command tests', () => {
232
232
  A_Context.root.register(command);
233
233
 
234
234
  const lifecycleOrder: string[] = [];
235
- command.on('init', () => lifecycleOrder.push('init'));
236
- command.on('compile', () => lifecycleOrder.push('compile'));
237
- command.on('execute', () => lifecycleOrder.push('execute'));
238
- command.on('complete', () => lifecycleOrder.push('complete'));
239
- command.on('fail', () => lifecycleOrder.push('fail'));
235
+ command.on(A_CommandFeatures.onInit, () => lifecycleOrder.push('init'));
236
+ command.on(A_CommandFeatures.onCompile, () => lifecycleOrder.push('compile'));
237
+ command.on(A_CommandFeatures.onExecute, () => lifecycleOrder.push('execute'));
238
+ command.on(A_CommandFeatures.onComplete, () => lifecycleOrder.push('complete'));
239
+ command.on(A_CommandFeatures.onFail, () => lifecycleOrder.push('fail'));
240
240
 
241
241
  await command.execute();
242
242
 
@@ -279,8 +279,8 @@ describe('A-Command tests', () => {
279
279
  const listener1Calls: number[] = [];
280
280
  const listener2Calls: number[] = [];
281
281
 
282
- command.on('init', () => listener1Calls.push(1));
283
- command.on('init', () => listener2Calls.push(2));
282
+ command.on(A_CommandFeatures.onInit, () => listener1Calls.push(1));
283
+ command.on(A_CommandFeatures.onInit, () => listener2Calls.push(2));
284
284
 
285
285
  await command.init();
286
286
 
@@ -317,12 +317,12 @@ describe('A-Command tests', () => {
317
317
  const listenerCalls: number[] = [];
318
318
  const listener = () => listenerCalls.push(1);
319
319
 
320
- command.on('init', listener);
320
+ command.on(A_CommandFeatures.onInit, listener);
321
321
  await command.init();
322
322
  expect(listenerCalls).toEqual([1]);
323
323
 
324
324
  // Remove listener and verify it's not called again
325
- command.off('init', listener);
325
+ command.off(A_CommandFeatures.onInit, listener);
326
326
 
327
327
  // Reset to call init again
328
328
  (command as any)._status = A_CONSTANTS__A_Command_Status.CREATED;
@@ -336,7 +336,7 @@ describe('A-Command tests', () => {
336
336
 
337
337
  let receivedCommand: A_Command<any, any, any> | undefined = undefined;
338
338
 
339
- command.on('init', (cmd) => {
339
+ command.on(A_CommandFeatures.onInit, (cmd) => {
340
340
  receivedCommand = cmd;
341
341
  });
342
342
 
@@ -352,10 +352,10 @@ describe('A-Command tests', () => {
352
352
 
353
353
  const successfulCalls: number[] = [];
354
354
 
355
- command.on('init', () => {
355
+ command.on(A_CommandFeatures.onInit, () => {
356
356
  throw new Error('Listener error');
357
357
  });
358
- command.on('init', () => successfulCalls.push(1));
358
+ command.on(A_CommandFeatures.onInit, () => successfulCalls.push(1));
359
359
 
360
360
  // Listener errors are propagated and will cause the command to fail
361
361
  await expect(command.init()).rejects.toThrow('Listener error');
@@ -429,7 +429,7 @@ describe('A-Command tests', () => {
429
429
 
430
430
  class ResultProcessor extends A_Component {
431
431
  @A_Feature.Extend({ scope: [ResultCommand] })
432
- async [A_CONSTANTS_A_Command_Features.EXECUTE](
432
+ async [A_CommandFeatures.onExecute](
433
433
  @A_Inject(A_Memory) memory: A_Memory<TestResult>
434
434
  ) {
435
435
  memory.set('processedData', 'processed-input');
@@ -457,7 +457,7 @@ describe('A-Command tests', () => {
457
457
 
458
458
  // Test deserialization - result is restored to memory and accessible through get method
459
459
  const deserializedCommand = new ResultCommand(serialized);
460
- const deserializedMemory = deserializedCommand.scope.resolve(A_Memory);
460
+ const deserializedMemory = deserializedCommand.scope.resolve(A_Memory)!;
461
461
  expect(deserializedMemory.get('processedData')).toBe('processed-input');
462
462
  expect(deserializedMemory.get('count')).toBe(100);
463
463
  expect(deserializedMemory.get('metadata')).toEqual(serialized.result?.metadata);
@@ -470,7 +470,7 @@ describe('A-Command tests', () => {
470
470
 
471
471
  class ErrorComponent extends A_Component {
472
472
  @A_Feature.Extend({ scope: [ErrorCommand] })
473
- async [A_CONSTANTS_A_Command_Features.EXECUTE](
473
+ async [A_CommandFeatures.onExecute](
474
474
  @A_Inject(A_Memory) memory: A_Memory<{ output: string }>
475
475
  ) {
476
476
  memory.error(new A_Error({
@@ -503,7 +503,7 @@ describe('A-Command tests', () => {
503
503
 
504
504
  // Test deserialization - errors are restored to memory
505
505
  const deserializedCommand = new ErrorCommand(serialized);
506
- const deserializedMemory = deserializedCommand.scope.resolve(A_Memory);
506
+ const deserializedMemory = deserializedCommand.scope.resolve(A_Memory)!;
507
507
  expect(deserializedMemory.Errors?.size).toBe(2);
508
508
  const errorArray = Array.from(deserializedMemory.Errors?.values() || []);
509
509
  expect(errorArray[0].title).toBe('First error');