@adaas/a-utils 0.1.11 → 0.1.13

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,645 @@
1
+ # A-Command
2
+
3
+ A powerful command pattern implementation that provides structured execution, lifecycle management, event handling, and state persistence for TypeScript applications.
4
+
5
+ ## Table of Contents
6
+
7
+ - [Overview](#overview)
8
+ - [Key Features](#key-features)
9
+ - [Installation](#installation)
10
+ - [Basic Usage](#basic-usage)
11
+ - [Advanced Usage](#advanced-usage)
12
+ - [Lifecycle Management](#lifecycle-management)
13
+ - [Event System](#event-system)
14
+ - [Serialization & Persistence](#serialization--persistence)
15
+ - [Error Handling](#error-handling)
16
+ - [API Reference](#api-reference)
17
+ - [Examples](#examples)
18
+
19
+ ## Overview
20
+
21
+ A-Command is an implementation of the Command Pattern that allows you to encapsulate requests as objects, thereby letting you parameterize clients with different requests, queue operations, and support undo operations. It provides a structured approach to handling complex business logic with full lifecycle management and event-driven architecture.
22
+
23
+ ## Key Features
24
+
25
+ - 🔄 **Complete Lifecycle Management** - Automatic progression through init → compile → execute → complete/fail phases
26
+ - 📡 **Event-Driven Architecture** - Subscribe to lifecycle events and custom events
27
+ - 💾 **State Persistence** - Full serialization/deserialization support for command state
28
+ - 🎯 **Type Safety** - Full TypeScript support with generic types for parameters and results
29
+ - 🔧 **Extensible** - Component-based architecture for custom execution logic
30
+ - 🛡️ **Error Handling** - Comprehensive error capture and management
31
+ - ⏱️ **Execution Tracking** - Built-in timing and duration tracking
32
+ - 🏗️ **Dependency Injection** - Integration with A-Context for scope management
33
+
34
+ ## Installation
35
+
36
+ ```bash
37
+ npm install @adaas/a-utils
38
+ ```
39
+
40
+ ## Basic Usage
41
+
42
+ ### Simple Command Creation and Execution
43
+
44
+ ```typescript
45
+ import { A_Command } from '@adaas/a-utils/lib/A-Command/A-Command.entity';
46
+ import { A_Context } from '@adaas/a-concept';
47
+
48
+ // Create a basic command
49
+ const command = new A_Command({
50
+ action: 'greet',
51
+ name: 'World'
52
+ });
53
+
54
+ // Register command in context
55
+ A_Context.root.register(command);
56
+
57
+ // Execute the command
58
+ await command.execute();
59
+
60
+ console.log(`Command status: ${command.status}`);
61
+ console.log(`Execution duration: ${command.duration}ms`);
62
+ ```
63
+
64
+ ### Typed Command with Custom Parameters and Result
65
+
66
+ ```typescript
67
+ interface UserCreateParams {
68
+ name: string;
69
+ email: string;
70
+ role: 'admin' | 'user';
71
+ }
72
+
73
+ interface UserCreateResult {
74
+ userId: string;
75
+ createdAt: string;
76
+ profileCreated: boolean;
77
+ }
78
+
79
+ class CreateUserCommand extends A_Command<UserCreateParams, UserCreateResult> {}
80
+
81
+ const command = new CreateUserCommand({
82
+ name: 'John Doe',
83
+ email: 'john@example.com',
84
+ role: 'user'
85
+ });
86
+
87
+ A_Context.root.register(command);
88
+ await command.execute();
89
+
90
+ // Access typed result
91
+ const result = command.result;
92
+ console.log(`Created user: ${result?.userId}`);
93
+ ```
94
+
95
+ ## Advanced Usage
96
+
97
+ ### Custom Command Logic with Components
98
+
99
+ ```typescript
100
+ import { A_Component, A_Feature, A_Inject } from '@adaas/a-concept';
101
+ import { A_Memory } from '@adaas/a-utils/lib/A-Memory/A-Memory.context';
102
+ import { A_CONSTANTS_A_Command_Features } from '@adaas/a-utils/lib/A-Command/A-Command.constants';
103
+
104
+ // Define command types
105
+ interface OrderProcessParams {
106
+ orderId: string;
107
+ items: Array<{ productId: string; quantity: number }>;
108
+ customerId: string;
109
+ }
110
+
111
+ interface OrderProcessResult {
112
+ orderNumber: string;
113
+ totalAmount: number;
114
+ estimatedDelivery: string;
115
+ paymentProcessed: boolean;
116
+ }
117
+
118
+ class ProcessOrderCommand extends A_Command<OrderProcessParams, OrderProcessResult> {}
119
+
120
+ // Create a component with custom execution logic
121
+ class OrderProcessor extends A_Component {
122
+
123
+ @A_Feature.Extend({ scope: [ProcessOrderCommand] })
124
+ async [A_CONSTANTS_A_Command_Features.EXECUTE](
125
+ @A_Inject(A_Memory) memory: A_Memory<OrderProcessResult>
126
+ ) {
127
+ // Access command parameters through the command instance
128
+ const command = A_Context.scope(this).resolve(ProcessOrderCommand);
129
+ const { orderId, items, customerId } = command.params;
130
+
131
+ // Process the order
132
+ const orderNumber = `ORD-${Date.now()}`;
133
+ const totalAmount = items.reduce((sum, item) => sum + (item.quantity * 10), 0);
134
+ const estimatedDelivery = new Date(Date.now() + 7 * 24 * 60 * 60 * 1000).toISOString();
135
+
136
+ // Store results in memory
137
+ await memory.set('orderNumber', orderNumber);
138
+ await memory.set('totalAmount', totalAmount);
139
+ await memory.set('estimatedDelivery', estimatedDelivery);
140
+ await memory.set('paymentProcessed', true);
141
+
142
+ console.log(`Processed order ${orderNumber} for customer ${customerId}`);
143
+ }
144
+ }
145
+
146
+ // Usage
147
+ A_Context.reset();
148
+ A_Context.root.register(OrderProcessor);
149
+
150
+ const command = new ProcessOrderCommand({
151
+ orderId: 'order-123',
152
+ customerId: 'customer-456',
153
+ items: [
154
+ { productId: 'prod-1', quantity: 2 },
155
+ { productId: 'prod-2', quantity: 1 }
156
+ ]
157
+ });
158
+
159
+ A_Context.root.register(command);
160
+ await command.execute();
161
+
162
+ console.log('Order processed:', command.result);
163
+ ```
164
+
165
+ ## Lifecycle Management
166
+
167
+ A-Command follows a structured lifecycle with automatic progression through defined phases:
168
+
169
+ ```typescript
170
+ class LifecycleCommand extends A_Command<{}, {}> {}
171
+
172
+ const command = new LifecycleCommand({});
173
+ A_Context.root.register(command);
174
+
175
+ // Track each lifecycle phase
176
+ console.log('Initial status:', command.status); // CREATED
177
+
178
+ await command.init();
179
+ console.log('After init:', command.status); // INITIALIZED
180
+
181
+ await command.compile();
182
+ console.log('After compile:', command.status); // COMPILED
183
+
184
+ // Execute runs all phases automatically
185
+ await command.execute();
186
+ console.log('After execute:', command.status); // COMPLETED
187
+
188
+ // Access timing information
189
+ console.log('Started at:', command.startedAt);
190
+ console.log('Ended at:', command.endedAt);
191
+ console.log('Duration:', command.duration, 'ms');
192
+ ```
193
+
194
+ ### Lifecycle Phases
195
+
196
+ 1. **CREATED** - Initial state when command is instantiated
197
+ 2. **INITIALIZATION** - Setting up execution scope and dependencies
198
+ 3. **INITIALIZED** - Ready for compilation
199
+ 4. **COMPILATION** - Preparing execution environment
200
+ 5. **COMPILED** - Ready for execution
201
+ 6. **COMPLETED** - Successfully finished execution
202
+ 7. **FAILED** - Execution failed with errors
203
+
204
+ ## Event System
205
+
206
+ ### Lifecycle Events
207
+
208
+ ```typescript
209
+ const command = new A_Command({});
210
+ A_Context.root.register(command);
211
+
212
+ // Subscribe to lifecycle events
213
+ command.on('init', (cmd) => {
214
+ console.log('Command initializing:', cmd?.code);
215
+ });
216
+
217
+ command.on('compile', (cmd) => {
218
+ console.log('Command compiling:', cmd?.code);
219
+ });
220
+
221
+ command.on('execute', (cmd) => {
222
+ console.log('Command executing:', cmd?.code);
223
+ });
224
+
225
+ command.on('complete', (cmd) => {
226
+ console.log('Command completed:', cmd?.code);
227
+ });
228
+
229
+ command.on('fail', (cmd) => {
230
+ console.log('Command failed:', cmd?.code);
231
+ });
232
+
233
+ await command.execute();
234
+ ```
235
+
236
+ ### Custom Events
237
+
238
+ ```typescript
239
+ type CustomEvents = 'validation-started' | 'data-processed' | 'notification-sent';
240
+
241
+ class CustomEventCommand extends A_Command<{}, {}, CustomEvents> {}
242
+
243
+ const command = new CustomEventCommand({});
244
+ A_Context.root.register(command);
245
+
246
+ // Subscribe to custom events
247
+ command.on('validation-started', (cmd) => {
248
+ console.log('Validation phase started');
249
+ });
250
+
251
+ command.on('data-processed', (cmd) => {
252
+ console.log('Data processing completed');
253
+ });
254
+
255
+ // Emit custom events during execution
256
+ command.emit('validation-started');
257
+ command.emit('data-processed');
258
+ ```
259
+
260
+ ### Managing Event Listeners
261
+
262
+ ```typescript
263
+ const command = new A_Command({});
264
+ A_Context.root.register(command);
265
+
266
+ // Add listener
267
+ const listener = (cmd) => console.log('Event triggered');
268
+ command.on('execute', listener);
269
+
270
+ // Remove listener
271
+ command.off('execute', listener);
272
+ ```
273
+
274
+ ## Serialization & Persistence
275
+
276
+ ### Basic Serialization
277
+
278
+ ```typescript
279
+ const command = new A_Command({
280
+ userId: '12345',
281
+ action: 'update-profile',
282
+ data: { name: 'John Doe', email: 'john@example.com' }
283
+ });
284
+
285
+ A_Context.root.register(command);
286
+ await command.execute();
287
+
288
+ // Serialize command state
289
+ const serialized = command.toJSON();
290
+ console.log('Serialized:', JSON.stringify(serialized, null, 2));
291
+
292
+ // Deserialize and restore command
293
+ const restoredCommand = new A_Command(serialized);
294
+ console.log('Restored params:', restoredCommand.params);
295
+ console.log('Restored status:', restoredCommand.status);
296
+ console.log('Restored result:', restoredCommand.result);
297
+ ```
298
+
299
+ ### Cross-Session Persistence
300
+
301
+ ```typescript
302
+ // Simulate saving command state to database/storage
303
+ async function saveCommandState(command: A_Command) {
304
+ const serialized = command.toJSON();
305
+ // In real application, save to database
306
+ localStorage.setItem('command-state', JSON.stringify(serialized));
307
+ return serialized.aseid;
308
+ }
309
+
310
+ // Simulate loading command state from database/storage
311
+ async function loadCommandState(commandId: string) {
312
+ // In real application, load from database
313
+ const serialized = JSON.parse(localStorage.getItem('command-state') || '{}');
314
+ return new A_Command(serialized);
315
+ }
316
+
317
+ // Usage
318
+ const command = new A_Command({ task: 'long-running-operation' });
319
+ A_Context.root.register(command);
320
+
321
+ await command.execute();
322
+ const commandId = await saveCommandState(command);
323
+
324
+ // Later, in different session...
325
+ const restoredCommand = await loadCommandState(commandId);
326
+ console.log('Restored command status:', restoredCommand.status);
327
+ ```
328
+
329
+ ## Error Handling
330
+
331
+ ### Capturing and Managing Errors
332
+
333
+ ```typescript
334
+ import { A_Error } from '@adaas/a-concept';
335
+
336
+ class ErrorHandlingCommand extends A_Command<{}, {}> {}
337
+
338
+ class ErrorHandler extends A_Component {
339
+
340
+ @A_Feature.Extend({ scope: [ErrorHandlingCommand] })
341
+ async [A_CONSTANTS_A_Command_Features.EXECUTE](
342
+ @A_Inject(A_Memory) memory: A_Memory<{}>
343
+ ) {
344
+ try {
345
+ // Simulate some operations that might fail
346
+ throw new Error('Simulated error');
347
+ } catch (error) {
348
+ // Add error to memory
349
+ await memory.error(new A_Error({
350
+ title: 'Operation Failed',
351
+ message: error.message,
352
+ code: 'OP_FAILED'
353
+ }));
354
+
355
+ // Throw error to trigger failure state
356
+ throw error;
357
+ }
358
+ }
359
+ }
360
+
361
+ A_Context.reset();
362
+ A_Context.root.register(ErrorHandler);
363
+
364
+ const command = new ErrorHandlingCommand({});
365
+ A_Context.root.register(command);
366
+
367
+ await command.execute();
368
+
369
+ console.log('Command failed:', command.isFailed);
370
+ console.log('Errors:', Array.from(command.errors?.values() || []));
371
+ ```
372
+
373
+ ### Error Serialization
374
+
375
+ ```typescript
376
+ // Errors are automatically included in serialization
377
+ const serialized = command.toJSON();
378
+ console.log('Serialized errors:', serialized.errors);
379
+
380
+ // Errors are restored during deserialization
381
+ const restoredCommand = new ErrorHandlingCommand(serialized);
382
+ console.log('Restored errors:', restoredCommand.errors);
383
+ ```
384
+
385
+ ## API Reference
386
+
387
+ ### A_Command Class
388
+
389
+ #### Constructor
390
+ ```typescript
391
+ constructor(params: InvokeType | A_TYPES__Command_Serialized<InvokeType, ResultType> | string)
392
+ ```
393
+
394
+ #### Properties
395
+ - `code: string` - Unique identifier for the command type
396
+ - `status: A_CONSTANTS__A_Command_Status` - Current execution status
397
+ - `params: InvokeType` - Command parameters
398
+ - `result: ResultType | undefined` - Execution result
399
+ - `errors: Set<A_Error> | undefined` - Execution errors
400
+ - `startedAt: Date | undefined` - Execution start time
401
+ - `endedAt: Date | undefined` - Execution end time
402
+ - `duration: number | undefined` - Execution duration in milliseconds
403
+ - `scope: A_Scope` - Execution scope for dependency injection
404
+ - `isFailed: boolean` - Whether command failed
405
+ - `isCompleted: boolean` - Whether command completed successfully
406
+
407
+ #### Methods
408
+ - `async execute(): Promise<any>` - Execute the complete command lifecycle
409
+ - `async init(): Promise<void>` - Initialize command
410
+ - `async compile(): Promise<void>` - Compile command
411
+ - `async complete(): Promise<void>` - Mark command as completed
412
+ - `async fail(): Promise<void>` - Mark command as failed
413
+ - `on(event, listener)` - Add event listener
414
+ - `off(event, listener)` - Remove event listener
415
+ - `emit(event)` - Emit event
416
+ - `toJSON()` - Serialize command state
417
+ - `fromJSON(serialized)` - Deserialize command state
418
+
419
+ ### Command Status Constants
420
+ ```typescript
421
+ enum A_CONSTANTS__A_Command_Status {
422
+ CREATED = 'CREATED',
423
+ INITIALIZATION = 'INITIALIZATION',
424
+ INITIALIZED = 'INITIALIZED',
425
+ COMPILATION = 'COMPILATION',
426
+ COMPILED = 'COMPILED',
427
+ IN_PROGRESS = 'IN_PROGRESS',
428
+ COMPLETED = 'COMPLETED',
429
+ FAILED = 'FAILED'
430
+ }
431
+ ```
432
+
433
+ ### Lifecycle Features for Components
434
+ ```typescript
435
+ enum A_CONSTANTS_A_Command_Features {
436
+ INIT = 'init',
437
+ COMPLIED = 'complied',
438
+ EXECUTE = 'execute',
439
+ COMPLETE = 'complete',
440
+ FAIL = 'fail'
441
+ }
442
+ ```
443
+
444
+ ## Examples
445
+
446
+ ### File Processing Command
447
+
448
+ ```typescript
449
+ interface FileProcessParams {
450
+ filePath: string;
451
+ operation: 'compress' | 'encrypt' | 'convert';
452
+ options: Record<string, any>;
453
+ }
454
+
455
+ interface FileProcessResult {
456
+ outputPath: string;
457
+ originalSize: number;
458
+ processedSize: number;
459
+ processingTime: number;
460
+ }
461
+
462
+ class FileProcessCommand extends A_Command<FileProcessParams, FileProcessResult> {}
463
+
464
+ class FileProcessor extends A_Component {
465
+
466
+ @A_Feature.Extend({ scope: [FileProcessCommand] })
467
+ async [A_CONSTANTS_A_Command_Features.EXECUTE](
468
+ @A_Inject(A_Memory) memory: A_Memory<FileProcessResult>
469
+ ) {
470
+ const command = A_Context.scope(this).resolve(FileProcessCommand);
471
+ const { filePath, operation, options } = command.params;
472
+
473
+ const startTime = Date.now();
474
+
475
+ // Simulate file processing
476
+ await new Promise(resolve => setTimeout(resolve, 1000));
477
+
478
+ const endTime = Date.now();
479
+
480
+ await memory.set('outputPath', `processed_${filePath}`);
481
+ await memory.set('originalSize', 1024);
482
+ await memory.set('processedSize', 512);
483
+ await memory.set('processingTime', endTime - startTime);
484
+ }
485
+ }
486
+
487
+ // Usage
488
+ A_Context.reset();
489
+ A_Context.root.register(FileProcessor);
490
+
491
+ const command = new FileProcessCommand({
492
+ filePath: '/path/to/file.txt',
493
+ operation: 'compress',
494
+ options: { quality: 0.8 }
495
+ });
496
+
497
+ A_Context.root.register(command);
498
+ await command.execute();
499
+
500
+ console.log('File processed:', command.result);
501
+ ```
502
+
503
+ ### Batch Processing with Progress Tracking
504
+
505
+ ```typescript
506
+ type BatchEvents = 'item-processed' | 'progress-update' | 'batch-complete';
507
+
508
+ interface BatchProcessParams {
509
+ items: Array<{ id: string; data: any }>;
510
+ batchSize: number;
511
+ }
512
+
513
+ interface BatchProcessResult {
514
+ processedCount: number;
515
+ failedCount: number;
516
+ totalTime: number;
517
+ results: Array<{ id: string; success: boolean; result?: any; error?: string }>;
518
+ }
519
+
520
+ class BatchProcessCommand extends A_Command<BatchProcessParams, BatchProcessResult, BatchEvents> {}
521
+
522
+ class BatchProcessor extends A_Component {
523
+
524
+ @A_Feature.Extend({ scope: [BatchProcessCommand] })
525
+ async [A_CONSTANTS_A_Command_Features.EXECUTE](
526
+ @A_Inject(A_Memory) memory: A_Memory<BatchProcessResult>
527
+ ) {
528
+ const command = A_Context.scope(this).resolve(BatchProcessCommand);
529
+ const { items, batchSize } = command.params;
530
+
531
+ const results: Array<{ id: string; success: boolean; result?: any; error?: string }> = [];
532
+ let processedCount = 0;
533
+ let failedCount = 0;
534
+
535
+ // Process items in batches
536
+ for (let i = 0; i < items.length; i += batchSize) {
537
+ const batch = items.slice(i, i + batchSize);
538
+
539
+ for (const item of batch) {
540
+ try {
541
+ // Simulate processing
542
+ await new Promise(resolve => setTimeout(resolve, 100));
543
+
544
+ results.push({
545
+ id: item.id,
546
+ success: true,
547
+ result: `processed-${item.id}`
548
+ });
549
+ processedCount++;
550
+
551
+ command.emit('item-processed');
552
+ } catch (error) {
553
+ results.push({
554
+ id: item.id,
555
+ success: false,
556
+ error: error.message
557
+ });
558
+ failedCount++;
559
+ }
560
+ }
561
+
562
+ command.emit('progress-update');
563
+ }
564
+
565
+ await memory.set('processedCount', processedCount);
566
+ await memory.set('failedCount', failedCount);
567
+ await memory.set('totalTime', command.duration || 0);
568
+ await memory.set('results', results);
569
+
570
+ command.emit('batch-complete');
571
+ }
572
+ }
573
+
574
+ // Usage with progress tracking
575
+ A_Context.reset();
576
+ A_Context.root.register(BatchProcessor);
577
+
578
+ const command = new BatchProcessCommand({
579
+ items: Array.from({ length: 10 }, (_, i) => ({ id: `item-${i}`, data: {} })),
580
+ batchSize: 3
581
+ });
582
+
583
+ // Track progress
584
+ command.on('item-processed', () => {
585
+ console.log('Item processed');
586
+ });
587
+
588
+ command.on('progress-update', () => {
589
+ console.log('Batch completed');
590
+ });
591
+
592
+ command.on('batch-complete', () => {
593
+ console.log('All batches completed');
594
+ });
595
+
596
+ A_Context.root.register(command);
597
+ await command.execute();
598
+
599
+ console.log('Batch results:', command.result);
600
+ ```
601
+
602
+ ## Integration with A-Memory
603
+
604
+ A-Command automatically integrates with A-Memory for result and error storage:
605
+
606
+ ```typescript
607
+ // Access memory during execution
608
+ class MemoryIntegrationCommand extends A_Command<{}, { step1: string; step2: number }> {}
609
+
610
+ class MemoryUser extends A_Component {
611
+
612
+ @A_Feature.Extend({ scope: [MemoryIntegrationCommand] })
613
+ async [A_CONSTANTS_A_Command_Features.EXECUTE](
614
+ @A_Inject(A_Memory) memory: A_Memory<{ step1: string; step2: number }>
615
+ ) {
616
+ // Store intermediate results
617
+ await memory.set('step1', 'completed');
618
+ await memory.set('step2', 42);
619
+
620
+ // Access stored values
621
+ const step1Result = memory.get('step1');
622
+ console.log('Step 1 result:', step1Result);
623
+
624
+ // Verify prerequisites
625
+ const hasRequiredData = await memory.verifyPrerequisites(['step1', 'step2']);
626
+ console.log('Has required data:', hasRequiredData);
627
+ }
628
+ }
629
+ ```
630
+
631
+ ---
632
+
633
+ ## Contributing
634
+
635
+ When extending A-Command functionality, please ensure:
636
+
637
+ 1. All new features include comprehensive tests
638
+ 2. TypeScript types are properly defined and exported
639
+ 3. Documentation is updated for new APIs
640
+ 4. Examples are provided for complex features
641
+ 5. Backward compatibility is maintained
642
+
643
+ ## License
644
+
645
+ This project is licensed under the MIT License - see the LICENSE file for details.
@@ -56,6 +56,21 @@ export class A_Memory<
56
56
  this._errors.add(error);
57
57
  }
58
58
 
59
+ /**
60
+ * Retrieves a value from the context memory
61
+ *
62
+ * @param key
63
+ * @returns
64
+ */
65
+ get<K extends keyof _MemoryType>(
66
+ /**
67
+ * Key to retrieve the value for
68
+ */
69
+ key: K
70
+ ): _MemoryType[K] | undefined {
71
+ return this._memory.get(key);
72
+ }
73
+
59
74
  /**
60
75
  * Saves a value in the context memory
61
76
  *