@adaas/a-utils 0.1.17 → 0.1.19

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 (47) hide show
  1. package/dist/index.d.mts +964 -354
  2. package/dist/index.d.ts +964 -354
  3. package/dist/index.js +1426 -714
  4. package/dist/index.js.map +1 -1
  5. package/dist/index.mjs +1426 -714
  6. package/dist/index.mjs.map +1 -1
  7. package/examples/A-Channel-examples.ts +13 -11
  8. package/examples/A-Command-examples-2.ts +429 -0
  9. package/examples/A-Command-examples.ts +487 -202
  10. package/examples/A-StateMachine-examples.ts +609 -0
  11. package/package.json +3 -2
  12. package/src/index.ts +1 -2
  13. package/src/lib/A-Channel/A-Channel.component.ts +14 -74
  14. package/src/lib/A-Channel/A-Channel.error.ts +5 -5
  15. package/src/lib/A-Channel/A-Channel.types.ts +2 -10
  16. package/src/lib/A-Channel/A-ChannelRequest.context.ts +25 -74
  17. package/src/lib/A-Command/A-Command.constants.ts +78 -23
  18. package/src/lib/A-Command/A-Command.entity.ts +447 -119
  19. package/src/lib/A-Command/A-Command.error.ts +11 -0
  20. package/src/lib/A-Command/A-Command.types.ts +96 -20
  21. package/src/lib/A-Command/A-CommandExecution.context.ts +0 -0
  22. package/src/lib/A-Command/README.md +164 -68
  23. package/src/lib/A-Config/A-Config.container.ts +2 -2
  24. package/src/lib/A-Config/A-Config.context.ts +19 -5
  25. package/src/lib/A-Config/components/ConfigReader.component.ts +1 -1
  26. package/src/lib/A-Logger/A-Logger.component.ts +211 -35
  27. package/src/lib/A-Logger/A-Logger.constants.ts +50 -10
  28. package/src/lib/A-Logger/A-Logger.env.ts +17 -1
  29. package/src/lib/A-Memory/A-Memory.component.ts +440 -0
  30. package/src/lib/A-Memory/A-Memory.constants.ts +49 -0
  31. package/src/lib/A-Memory/A-Memory.context.ts +14 -118
  32. package/src/lib/A-Memory/A-Memory.error.ts +21 -0
  33. package/src/lib/A-Memory/A-Memory.types.ts +21 -0
  34. package/src/lib/A-Operation/A-Operation.context.ts +58 -0
  35. package/src/lib/A-Operation/A-Operation.types.ts +47 -0
  36. package/src/lib/A-StateMachine/A-StateMachine.component.ts +258 -0
  37. package/src/lib/A-StateMachine/A-StateMachine.constants.ts +18 -0
  38. package/src/lib/A-StateMachine/A-StateMachine.error.ts +10 -0
  39. package/src/lib/A-StateMachine/A-StateMachine.types.ts +20 -0
  40. package/src/lib/A-StateMachine/A-StateMachineTransition.context.ts +41 -0
  41. package/src/lib/A-StateMachine/README.md +391 -0
  42. package/tests/A-Channel.test.ts +17 -14
  43. package/tests/A-Command.test.ts +548 -460
  44. package/tests/A-Logger.test.ts +8 -4
  45. package/tests/A-Memory.test.ts +151 -115
  46. package/tests/A-Schedule.test.ts +2 -2
  47. package/tests/A-StateMachine.test.ts +760 -0
@@ -5,122 +5,276 @@ import {
5
5
  } from "./A-Command.types";
6
6
  import {
7
7
  A_CommandFeatures,
8
- A_CONSTANTS__A_Command_Event,
9
- A_CONSTANTS__A_Command_Status
8
+ A_Command_Event,
9
+ A_Command_Status,
10
+ A_CommandTransitions
10
11
  } from "./A-Command.constants";
11
- import { A_Component, A_Context, A_Entity, A_Error, A_Feature, A_Scope } from "@adaas/a-concept";
12
- import { A_Memory } from "../A-Memory/A-Memory.context";
12
+ import { A_Caller, A_Context, A_Dependency, A_Entity, A_Error, A_Feature, A_Inject, A_Scope } from "@adaas/a-concept";
13
13
  import { A_CommandError } from "./A-Command.error";
14
-
15
-
14
+ import { A_OperationContext } from "../A-Operation/A-Operation.context";
15
+ import { A_StateMachine } from "../A-StateMachine/A-StateMachine.component";
16
+ import { A_StateMachineFeatures } from "../A-StateMachine/A-StateMachine.constants";
17
+ import { A_Logger } from "../A-Logger/A-Logger.component";
18
+ import { A_StateMachineTransition } from "../A-StateMachine/A-StateMachineTransition.context";
19
+
20
+ /**
21
+ * A_Command - Advanced Command Pattern Implementation
22
+ *
23
+ * A comprehensive command pattern implementation that encapsulates business logic
24
+ * as executable commands with full lifecycle management, event handling, and
25
+ * state persistence capabilities.
26
+ *
27
+ * ## Key Features
28
+ * - **Structured Lifecycle**: Automatic progression through init → compile → execute → complete/fail
29
+ * - **Event-Driven**: Subscribe to lifecycle events and emit custom events
30
+ * - **State Persistence**: Full serialization/deserialization for cross-service communication
31
+ * - **Type Safety**: Generic types for parameters, results, and custom events
32
+ * - **Dependency Injection**: Integrated with A-Concept's DI system
33
+ * - **Error Management**: Comprehensive error capture and handling
34
+ * - **Execution Tracking**: Built-in timing and performance metrics
35
+ *
36
+ * ## Lifecycle Phases
37
+ * 1. **CREATED** - Command instantiated but not initialized
38
+ * 2. **INITIALIZED** - Execution scope and dependencies set up
39
+ * 3. **COMPILED** - Ready for execution with all resources prepared
40
+ * 4. **EXECUTING** - Currently running business logic
41
+ * 5. **COMPLETED** - Successfully finished execution
42
+ * 6. **FAILED** - Execution failed with errors captured
43
+ *
44
+ * @template InvokeType - Type definition for command parameters
45
+ * @template ResultType - Type definition for command execution result
46
+ * @template LifecycleEvents - Union type of custom lifecycle event names
47
+ *
48
+ * @example
49
+ * ```typescript
50
+ * // Define parameter and result types
51
+ * interface UserCreateParams {
52
+ * name: string;
53
+ * email: string;
54
+ * role: 'admin' | 'user';
55
+ * }
56
+ *
57
+ * interface UserCreateResult {
58
+ * userId: string;
59
+ * success: boolean;
60
+ * }
61
+ *
62
+ * // Create custom command
63
+ * class CreateUserCommand extends A_Command<UserCreateParams, UserCreateResult> {}
64
+ *
65
+ * // Execute command
66
+ * const command = new CreateUserCommand({
67
+ * name: 'John Doe',
68
+ * email: 'john@example.com',
69
+ * role: 'user'
70
+ * });
71
+ *
72
+ * scope.register(command);
73
+ * await command.execute();
74
+ *
75
+ * console.log('Result:', command.result);
76
+ * console.log('Status:', command.status);
77
+ * ```
78
+ */
16
79
  export class A_Command<
17
80
  InvokeType extends A_TYPES__Command_Init = A_TYPES__Command_Init,
18
81
  ResultType extends Record<string, any> = Record<string, any>,
19
- LifecycleEvents extends string | A_CONSTANTS__A_Command_Event = A_CONSTANTS__A_Command_Event
82
+ LifecycleEvents extends string | A_Command_Event = A_Command_Event,
20
83
  > extends A_Entity<InvokeType, A_TYPES__Command_Serialized<InvokeType, ResultType>> {
21
84
 
22
85
  // ====================================================================
23
- // ================== Static A-Command Information ====================
86
+ // ================== Static Command Information ======================
24
87
  // ====================================================================
25
88
 
26
89
  /**
27
- * Command Identifier that corresponds to the class name
90
+ * Static command identifier derived from the class name
91
+ * Used for command registration and serialization
28
92
  */
29
93
  static get code(): string {
30
94
  return super.entity;
31
95
  }
32
96
 
33
97
  // ====================================================================
34
- // ================ Instance A-Command Information ====================
98
+ // ================== Instance Properties =============================
35
99
  // ====================================================================
36
- protected _result?: ResultType;
100
+
101
+ /** The origin of the command, used to know has it been created from serialization or invoked */
102
+ protected _origin!: 'invoked' | 'serialized';
103
+
104
+ /** Command execution scope for dependency injection and feature resolution */
37
105
  protected _executionScope!: A_Scope;
38
- protected _errors?: Set<A_Error>;
39
106
 
107
+ /** Result data produced by successful command execution */
108
+ protected _result?: ResultType;
109
+
110
+ /** Set of errors that occurred during command execution */
111
+ protected _error!: A_Error;
112
+
113
+ /** Command initialization parameters */
40
114
  protected _params!: InvokeType;
41
- protected _status!: A_CONSTANTS__A_Command_Status
42
115
 
116
+ /** Current lifecycle status of the command */
117
+ protected _status!: A_Command_Status
118
+
119
+ /** Map of event listeners organized by event name */
43
120
  protected _listeners: Map<
44
- // the name of the event
45
- LifecycleEvents | A_CONSTANTS__A_Command_Event,
46
- // the listeners for the event
121
+ LifecycleEvents | A_Command_Event,
47
122
  Set<A_TYPES__Command_Listener<InvokeType, ResultType, LifecycleEvents>>
48
123
  > = new Map();
49
124
 
125
+ /** Timestamp when command execution started */
50
126
  protected _startTime?: Date;
51
- protected _endTime?: Date
127
+
128
+ /** Timestamp when command execution ended */
129
+ protected _endTime?: Date;
130
+
131
+ /** Timestamp when command was created */
132
+ protected _createdAt!: Date;
133
+
134
+
135
+ // ====================================================================
136
+ // ================== Public Getter Properties =======================
137
+ // ====================================================================
52
138
 
53
139
  /**
54
- * Execution Duration in milliseconds
140
+ * Total execution duration in milliseconds
141
+ *
142
+ * - If completed/failed: Returns total time from start to end
143
+ * - If currently executing: Returns elapsed time since start
144
+ * - If not started: Returns undefined
55
145
  */
56
- get duration() {
146
+ get duration(): number | undefined {
57
147
  return this._endTime && this._startTime
58
148
  ? this._endTime.getTime() - this._startTime.getTime()
59
149
  : this._startTime
60
150
  ? new Date().getTime() - this._startTime.getTime()
61
151
  : undefined;
62
152
  }
153
+
154
+ /**
155
+ * Idle time before execution started in milliseconds
156
+ *
157
+ * Time between command creation and execution start.
158
+ * Useful for monitoring command queue performance.
159
+ */
160
+ get idleTime(): number | undefined {
161
+ return this._startTime && this._createdAt
162
+ ? this._startTime.getTime() - this._createdAt.getTime()
163
+ : undefined;
164
+ }
165
+
63
166
  /**
64
- * A shared scope between all features of the command during its execution
167
+ * Command execution scope for dependency injection
168
+ *
169
+ * Provides access to components, services, and shared resources
170
+ * during command execution. Inherits from the scope where the
171
+ * command was registered.
65
172
  */
66
173
  get scope(): A_Scope {
67
174
  return this._executionScope;
68
175
  }
176
+
69
177
  /**
70
- * Unique code identifying the command type
71
- * Example: 'user.create', 'task.complete', etc.
178
+ * Unique command type identifier
72
179
  *
180
+ * Derived from the class name and used for:
181
+ * - Command registration and resolution
182
+ * - Serialization and deserialization
183
+ * - Logging and debugging
184
+ *
185
+ * @example 'create-user-command', 'process-order-command'
73
186
  */
74
187
  get code(): string {
75
188
  return (this.constructor as typeof A_Command).code;
76
189
  }
77
190
  /**
78
- * Current status of the command
191
+ * Current lifecycle status of the command
192
+ *
193
+ * Indicates the current phase in the command execution lifecycle.
194
+ * Used to track progress and determine available operations.
79
195
  */
80
- get status(): A_CONSTANTS__A_Command_Status {
196
+ get status(): A_Command_Status {
81
197
  return this._status;
82
198
  }
199
+
83
200
  /**
84
- * Start time of the command execution
201
+ * Timestamp when the command was created
202
+ *
203
+ * Marks the initial instantiation time, useful for tracking
204
+ * command age and queue performance metrics.
205
+ */
206
+ get createdAt(): Date {
207
+ return this._createdAt!;
208
+ }
209
+
210
+ /**
211
+ * Timestamp when command execution started
212
+ *
213
+ * Undefined until execution begins. Used for calculating
214
+ * execution duration and idle time.
85
215
  */
86
216
  get startedAt(): Date | undefined {
87
217
  return this._startTime;
88
218
  }
219
+
89
220
  /**
90
- * End time of the command execution
221
+ * Timestamp when command execution ended
222
+ *
223
+ * Set when command reaches COMPLETED or FAILED status.
224
+ * Used for calculating total execution duration.
91
225
  */
92
226
  get endedAt(): Date | undefined {
93
227
  return this._endTime;
94
228
  }
229
+
95
230
  /**
96
- * Result of the command execution stored in the context
231
+ * Result data produced by command execution
232
+ *
233
+ * Contains the output data from successful command execution.
234
+ * Undefined until command completes successfully.
97
235
  */
98
236
  get result(): ResultType | undefined {
99
237
  return this._result;
100
238
  }
239
+
101
240
  /**
102
- * Errors encountered during the command execution stored in the context
241
+ * Array of errors that occurred during execution
242
+ *
243
+ * Automatically wraps native errors in A_Error instances
244
+ * for consistent error handling. Empty array if no errors occurred.
103
245
  */
104
- get errors(): Set<A_Error> | undefined {
105
- return this._errors;
246
+ get error(): A_Error | undefined {
247
+ return this._error;
106
248
  }
249
+
107
250
  /**
108
- * Parameters used to invoke the command
109
- */
110
- get params(): InvokeType {
111
- return this._params;
251
+ * Command initialization parameters
252
+ *
253
+ * Contains the input data used to create and configure the command.
254
+ * These parameters are immutable during command execution.
255
+ return new A_Error(err);
256
+ }
257
+ });
112
258
  }
259
+
113
260
  /**
114
- * Indicates if the command has failed
261
+ * Command initialization parameters
262
+ *
263
+ * Contains the input data used to create and configure the command.
264
+ * These parameters are immutable during command execution.
115
265
  */
116
- get isFailed(): boolean {
117
- return this._status === A_CONSTANTS__A_Command_Status.FAILED;
266
+ get params(): InvokeType {
267
+ return this._params;
118
268
  }
269
+
119
270
  /**
120
- * Indicates if the command has completed successfully
271
+ * Indicates if the command has been processed (completed or failed)
272
+ *
273
+ * Returns true if the command has completed or failed, false otherwise.
121
274
  */
122
- get isCompleted(): boolean {
123
- return this._status === A_CONSTANTS__A_Command_Status.COMPLETED;
275
+ get isProcessed(): boolean {
276
+ return this._status === A_Command_Status.COMPLETED
277
+ || this._status === A_Command_Status.FAILED;
124
278
  }
125
279
 
126
280
  /**
@@ -138,115 +292,280 @@ export class A_Command<
138
292
  /**
139
293
  * Command invocation parameters
140
294
  */
141
- params: InvokeType | A_TYPES__Command_Serialized<InvokeType, ResultType> | string
295
+ params: InvokeType | A_TYPES__Command_Serialized<InvokeType, ResultType> | string,
142
296
  ) {
143
- super(params as any)
297
+ super(params as any);
144
298
  }
145
299
 
146
-
147
300
  // --------------------------------------------------------------------------
148
- // A-Command Lifecycle Methods
301
+ // A-StateMachine Feature Extensions
149
302
  // --------------------------------------------------------------------------
303
+ @A_Feature.Extend()
304
+ protected async [A_StateMachineFeatures.onBeforeTransition](
305
+ @A_Inject(A_StateMachineTransition) transition: A_StateMachineTransition,
306
+ @A_Inject(A_Logger) logger?: A_Logger,
307
+ ...args: any[]
308
+ ) {
309
+ this.checkScopeInheritance();
150
310
 
151
- // should create a new Task in DB with basic records
152
- async init(): Promise<void> {
153
- // first check statuis if it passed then - skip
154
- if (this._status !== A_CONSTANTS__A_Command_Status.CREATED) {
311
+ // and register all allowed status transitions
312
+ // switch across allowed transitions, if not allowed throw an error
313
+ logger?.debug('yellow', `Command ${this.aseid.toString()} transitioning from ${transition.from} to ${transition.to}`);
314
+ }
315
+
316
+ @A_Feature.Extend()
317
+ protected async [A_CommandTransitions.CREATED_TO_INITIALIZED](
318
+ @A_Inject(A_StateMachineTransition) transition: A_StateMachineTransition,
319
+ ...args: any[]
320
+ ): Promise<void> {
321
+ if (this._status !== A_Command_Status.CREATED) {
155
322
  return;
156
323
  }
157
324
 
158
- this._status = A_CONSTANTS__A_Command_Status.INITIALIZATION;
159
- this._startTime = new Date();
160
-
325
+ this._createdAt = new Date();
326
+ this._status = A_Command_Status.INITIALIZED;
161
327
 
162
328
  this.emit(A_CommandFeatures.onInit);
163
- await this.call(A_CommandFeatures.onInit, this.scope);
164
- this._status = A_CONSTANTS__A_Command_Status.INITIALIZED;
165
329
  }
166
330
 
167
- // Should compile everything before execution
168
- async compile() {
169
- if (this._status !== A_CONSTANTS__A_Command_Status.INITIALIZED) {
331
+ @A_Feature.Extend()
332
+ protected async [A_CommandTransitions.INITIALIZED_TO_EXECUTING](
333
+ @A_Inject(A_StateMachineTransition) transition: A_StateMachineTransition,
334
+ ...args: any[]
335
+ ): Promise<void> {
336
+ if (this._status !== A_Command_Status.INITIALIZED
337
+ && this._status !== A_Command_Status.CREATED
338
+ ) {
170
339
  return;
171
340
  }
172
341
 
173
- this.checkScopeInheritance();
342
+ this._startTime = new Date();
343
+ this._status = A_Command_Status.EXECUTING;
174
344
 
175
- this._status = A_CONSTANTS__A_Command_Status.COMPILATION;
176
- this.emit(A_CommandFeatures.onCompile);
177
- await this.call(A_CommandFeatures.onCompile, this.scope);
178
- this._status = A_CONSTANTS__A_Command_Status.COMPILED;
345
+ this.emit(A_CommandFeatures.onExecute);
179
346
  }
180
347
 
348
+ @A_Feature.Extend()
181
349
  /**
182
- * Processes the command execution
350
+ * Handles command completion after successful execution
183
351
  *
184
- * @returns
352
+ * EXECUTION -> COMPLETED transition
185
353
  */
186
- async process() {
187
- if (this._status !== A_CONSTANTS__A_Command_Status.COMPILED)
188
- return;
354
+ protected async [A_CommandTransitions.EXECUTING_TO_COMPLETED](
355
+ @A_Inject(A_StateMachineTransition) transition: A_StateMachineTransition,
356
+ ...args: any[]
357
+ ): Promise<void> {
358
+ this._endTime = new Date();
359
+ this._status = A_Command_Status.COMPLETED;
189
360
 
190
- this._status = A_CONSTANTS__A_Command_Status.IN_PROGRESS;
361
+ this.emit(A_CommandFeatures.onComplete);
362
+ }
191
363
 
192
- this.checkScopeInheritance();
364
+ @A_Feature.Extend()
365
+ /**
366
+ * Handles command failure during execution
367
+ *
368
+ * EXECUTION -> FAILED transition
369
+ */
370
+ protected async [A_CommandTransitions.EXECUTING_TO_FAILED](
371
+ @A_Inject(A_StateMachineTransition) transition: A_StateMachineTransition,
372
+ @A_Inject(A_Error) error: A_Error,
373
+ ...args: any[]
374
+ ): Promise<void> {
375
+ this._endTime = new Date();
193
376
 
194
- this.emit(A_CommandFeatures.onExecute);
377
+ this._status = A_Command_Status.FAILED;
195
378
 
196
- await this.call(A_CommandFeatures.onExecute, this.scope);
379
+ this.emit(A_CommandFeatures.onFail);
380
+ }
381
+
382
+
383
+ // --------------------------------------------------------------------------
384
+ // A-Command Lifecycle Feature Extensions
385
+ // --------------------------------------------------------------------------
386
+ @A_Feature.Extend()
387
+ /**
388
+ * Default behavior for Command Initialization uses StateMachine to transition states
389
+ */
390
+ protected async [A_CommandFeatures.onInit](
391
+ @A_Inject(A_StateMachine) stateMachine: A_StateMachine,
392
+ ...args: any[]
393
+ ): Promise<void> {
394
+ await stateMachine.transition(A_Command_Status.CREATED, A_Command_Status.INITIALIZED);
395
+ }
396
+
397
+ @A_Feature.Extend({
398
+ after: /.*/
399
+ })
400
+ protected async [A_CommandFeatures.onBeforeExecute](
401
+ @A_Dependency.Required()
402
+ @A_Inject(A_StateMachine) stateMachine: A_StateMachine,
403
+ ...args: any[]
404
+ ): Promise<void> {
405
+ await stateMachine.transition(A_Command_Status.INITIALIZED, A_Command_Status.EXECUTING);
406
+ }
407
+
408
+ @A_Feature.Extend()
409
+ protected async [A_CommandFeatures.onExecute](
410
+ ...args: any[]
411
+ ): Promise<void> {
412
+ }
413
+
414
+ @A_Feature.Extend()
415
+ /**
416
+ * By Default on AfterExecute calls the Completion method to mark the command as completed
417
+ *
418
+ * [!] This can be overridden to implement custom behavior using A_Feature overrides
419
+ */
420
+ protected async [A_CommandFeatures.onAfterExecute](
421
+ ...args: any[]
422
+ ): Promise<void> {
423
+ }
424
+
425
+ @A_Feature.Extend({
426
+ after: /.*/
427
+ })
428
+ protected async [A_CommandFeatures.onComplete](
429
+ @A_Inject(A_StateMachine) stateMachine: A_StateMachine,
430
+ ...args: any[]
431
+ ): Promise<void> {
432
+ await stateMachine.transition(A_Command_Status.EXECUTING, A_Command_Status.COMPLETED);
433
+ }
434
+
435
+ @A_Feature.Extend({
436
+ after: /.*/
437
+ })
438
+ protected async [A_CommandFeatures.onFail](
439
+ @A_Dependency.Required()
440
+ @A_Inject(A_StateMachine) stateMachine: A_StateMachine,
441
+ @A_Inject(A_OperationContext) operation: A_OperationContext,
442
+ ...args: any[]
443
+ ): Promise<void> {
444
+ await stateMachine.transition(A_Command_Status.EXECUTING, A_Command_Status.FAILED);
445
+ }
446
+
447
+ // --------------------------------------------------------------------------
448
+ // A-Command Lifecycle Methods
449
+ // --------------------------------------------------------------------------
450
+ /**
451
+ * Initializes the command before execution.
452
+ */
453
+ async init(): Promise<void> {
454
+ await this.call(A_CommandFeatures.onInit, this.scope);
197
455
  }
198
456
 
199
457
  /**
200
458
  * Executes the command logic.
201
459
  */
202
460
  async execute(): Promise<any> {
203
- this.checkScopeInheritance();
461
+
462
+ if (this.isProcessed) return;
204
463
 
205
464
  try {
206
- await this.init();
465
+ this.checkScopeInheritance();
466
+
467
+ const context = new A_OperationContext('execute-command');
468
+
469
+ this.scope.register(context);
470
+
471
+ await new Promise<void>(async (resolve, reject) => {
472
+
473
+ try {
474
+
475
+
476
+ const onBeforeExecuteFeature = new A_Feature({
477
+ name: A_CommandFeatures.onBeforeExecute,
478
+ component: this,
479
+ scope: this.scope
480
+ })
481
+
482
+ const onExecuteFeature = new A_Feature({
483
+ name: A_CommandFeatures.onExecute,
484
+ component: this,
485
+ scope: this.scope
486
+ })
207
487
 
208
- await this.compile();
488
+ const onAfterExecuteFeature = new A_Feature({
489
+ name: A_CommandFeatures.onAfterExecute,
490
+ component: this,
491
+ scope: this.scope
492
+ })
209
493
 
210
- await this.process();
211
494
 
212
- await this.complete();
213
495
 
496
+ this.on(A_CommandFeatures.onComplete, () => {
497
+
498
+ onBeforeExecuteFeature.interrupt();
499
+ onExecuteFeature.interrupt();
500
+ onAfterExecuteFeature.interrupt();
501
+
502
+ resolve();
503
+ });
504
+
505
+
506
+ await onBeforeExecuteFeature.process(this.scope);
507
+
508
+ await onExecuteFeature.process(this.scope);
509
+
510
+ await onAfterExecuteFeature.process(this.scope);
511
+
512
+ /** only in case it was really invoked we automatically transit it to COMPLETED state */
513
+ if (this._origin === 'invoked') {
514
+ await this.complete();
515
+ }
516
+
517
+ resolve();
518
+
519
+ } catch (error) {
520
+ reject(error);
521
+ }
522
+ });
214
523
 
215
524
  } catch (error) {
216
- await this.fail();
525
+ let targetError = error instanceof A_Error
526
+ ? error
527
+ : new A_CommandError({
528
+ title: A_CommandError.ExecutionError,
529
+ description: `An error occurred while executing command "${this.aseid.toString()}".`,
530
+ originalError: error
531
+ });
532
+
533
+ await this.fail(targetError);
217
534
  }
218
-
219
- this._executionScope.destroy();
220
535
  }
221
536
 
222
537
  /**
223
538
  * Marks the command as completed
224
539
  */
225
- async complete() {
226
- this.checkScopeInheritance();
540
+ async complete(result?: ResultType) {
541
+ if (this.isProcessed) return;
227
542
 
228
- this._status = A_CONSTANTS__A_Command_Status.COMPLETED;
229
- this._endTime = new Date();
230
- this._result = this.scope.resolve(A_Memory)!.toJSON() as ResultType;
543
+ this._status = A_Command_Status.COMPLETED;
544
+
545
+ this._result = result;
231
546
 
232
- this.emit(A_CommandFeatures.onComplete);
233
547
  await this.call(A_CommandFeatures.onComplete, this.scope);
234
548
 
549
+ this.scope.destroy();
235
550
  }
236
551
 
237
552
 
553
+
238
554
  /**
239
555
  * Marks the command as failed
240
556
  */
241
- async fail() {
242
- this.checkScopeInheritance();
557
+ async fail(error?: A_Error) {
558
+ if (this.isProcessed) return;
243
559
 
244
- this._status = A_CONSTANTS__A_Command_Status.FAILED;
245
- this._endTime = new Date();
246
- this._errors = this.scope.resolve(A_Memory)!.Errors;
560
+ this._status = A_Command_Status.FAILED;
561
+ if (error) {
562
+ this._error = error;
563
+ this.scope.register(error);
564
+ }
247
565
 
248
- this.emit(A_CommandFeatures.onFail);
249
566
  await this.call(A_CommandFeatures.onFail, this.scope);
567
+
568
+ this.scope.destroy();
250
569
  }
251
570
 
252
571
 
@@ -260,7 +579,7 @@ export class A_Command<
260
579
  * @param event
261
580
  * @param listener
262
581
  */
263
- on(event: LifecycleEvents | A_CONSTANTS__A_Command_Event, listener: A_TYPES__Command_Listener<InvokeType, ResultType, LifecycleEvents>) {
582
+ on(event: LifecycleEvents | A_Command_Event, listener: A_TYPES__Command_Listener<InvokeType, ResultType, LifecycleEvents>) {
264
583
  if (!this._listeners.has(event)) {
265
584
  this._listeners.set(event, new Set());
266
585
  }
@@ -272,7 +591,7 @@ export class A_Command<
272
591
  * @param event
273
592
  * @param listener
274
593
  */
275
- off(event: LifecycleEvents | A_CONSTANTS__A_Command_Event, listener: A_TYPES__Command_Listener<InvokeType, ResultType, LifecycleEvents>) {
594
+ off(event: LifecycleEvents | A_Command_Event, listener: A_TYPES__Command_Listener<InvokeType, ResultType, LifecycleEvents>) {
276
595
  this._listeners.get(event)?.delete(listener);
277
596
  }
278
597
  /**
@@ -280,8 +599,8 @@ export class A_Command<
280
599
  *
281
600
  * @param event
282
601
  */
283
- emit(event: LifecycleEvents | A_CONSTANTS__A_Command_Event) {
284
- this._listeners.get(event)?.forEach(listener => {
602
+ emit(event: LifecycleEvents | A_Command_Event) {
603
+ this._listeners.get(event)?.forEach(async listener => {
285
604
  listener(this);
286
605
  });
287
606
  }
@@ -302,13 +621,18 @@ export class A_Command<
302
621
  fromNew(newEntity: InvokeType): void {
303
622
  super.fromNew(newEntity);
304
623
 
305
- this._executionScope = new A_Scope();
624
+ this._origin = 'invoked';
306
625
 
307
- this._executionScope.register(new A_Memory<ResultType>());
626
+ this._executionScope = new A_Scope({
627
+ name: `A-Command-Execution-Scope-${this.aseid.toString()}`,
628
+ components: [A_StateMachine],
629
+ });
630
+
631
+ this._createdAt = new Date();
308
632
 
309
633
  this._params = newEntity;
310
634
 
311
- this._status = A_CONSTANTS__A_Command_Status.CREATED;
635
+ this._status = A_Command_Status.CREATED;
312
636
  }
313
637
 
314
638
 
@@ -323,33 +647,25 @@ export class A_Command<
323
647
  fromJSON(serialized: A_TYPES__Command_Serialized<InvokeType, ResultType>): void {
324
648
  super.fromJSON(serialized);
325
649
 
326
- this._executionScope = new A_Scope();
327
-
328
- const memory = new A_Memory<ResultType>();
650
+ this._origin = 'serialized';
329
651
 
330
- this._executionScope.register(memory);
652
+ this._executionScope = new A_Scope({
653
+ name: `A-Command-Execution-Scope-${this.aseid.toString()}`,
654
+ components: [A_StateMachine],
655
+ });
331
656
 
657
+ if (serialized.createdAt) this._createdAt = new Date(serialized.createdAt);
332
658
  if (serialized.startedAt) this._startTime = new Date(serialized.startedAt);
333
659
  if (serialized.endedAt) this._endTime = new Date(serialized.endedAt);
334
660
 
335
-
336
- // Restore result and errors in the memory
337
- if (serialized.result) {
338
- Object.entries(serialized.result).forEach(([key, value]) => {
339
- memory.set(key, value);
340
- });
341
- }
342
-
343
- if (serialized.errors) {
344
- serialized.errors.forEach(err => {
345
- memory.error(new A_Error(err));
346
- });
347
- }
348
-
349
661
  this._params = serialized.params
662
+ this._status = serialized.status;
350
663
 
351
- this._status = serialized.status || A_CONSTANTS__A_Command_Status.CREATED;
664
+ if (serialized.error)
665
+ this._error = new A_CommandError(serialized.error)
352
666
 
667
+ if (serialized.result)
668
+ this._result = serialized.result;
353
669
  }
354
670
 
355
671
 
@@ -364,23 +680,34 @@ export class A_Command<
364
680
  code: this.code,
365
681
  status: this._status,
366
682
  params: this._params,
683
+ createdAt: this._createdAt.toISOString(),
367
684
  startedAt: this._startTime ? this._startTime.toISOString() : undefined,
368
685
  endedAt: this._endTime ? this._endTime.toISOString() : undefined,
369
686
  duration: this.duration,
687
+ idleTime: this.idleTime,
370
688
  result: this.result,
371
- errors: this.errors ? Array.from(this.errors).map(err => err.toJSON()) : undefined
689
+ error: this.error ? this.error.toJSON() : undefined,
372
690
  }
373
691
  };
374
692
 
375
693
 
694
+ //============================================================================================
695
+ // Helpers Methods
696
+ //============================================================================================
697
+ /**
698
+ * Ensures that the command's execution scope inherits from the context scope
699
+ *
700
+ * Throws an error if the command is not bound to any context scope
701
+ */
376
702
  protected checkScopeInheritance(): void {
377
703
  let attachedScope: A_Scope;
704
+
378
705
  try {
379
706
  attachedScope = A_Context.scope(this);
380
707
  } catch (error) {
381
708
  throw new A_CommandError({
382
709
  title: A_CommandError.CommandScopeBindingError,
383
- description: `Command ${this.code} is not bound to any context scope. Ensure the command is properly registered within a context before execution.`,
710
+ description: `Command ${this.aseid.toString()} is not bound to any context scope. Ensure the command is properly registered within a context before execution.`,
384
711
  originalError: error
385
712
  });
386
713
  }
@@ -389,4 +716,5 @@ export class A_Command<
389
716
  this.scope.inherit(A_Context.scope(this));
390
717
  }
391
718
  }
392
- }
719
+ }
720
+