@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.
- package/dist/index.d.mts +964 -354
- package/dist/index.d.ts +964 -354
- package/dist/index.js +1426 -714
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1426 -714
- package/dist/index.mjs.map +1 -1
- package/examples/A-Channel-examples.ts +13 -11
- package/examples/A-Command-examples-2.ts +429 -0
- package/examples/A-Command-examples.ts +487 -202
- package/examples/A-StateMachine-examples.ts +609 -0
- package/package.json +3 -2
- package/src/index.ts +1 -2
- package/src/lib/A-Channel/A-Channel.component.ts +14 -74
- package/src/lib/A-Channel/A-Channel.error.ts +5 -5
- package/src/lib/A-Channel/A-Channel.types.ts +2 -10
- package/src/lib/A-Channel/A-ChannelRequest.context.ts +25 -74
- package/src/lib/A-Command/A-Command.constants.ts +78 -23
- package/src/lib/A-Command/A-Command.entity.ts +447 -119
- package/src/lib/A-Command/A-Command.error.ts +11 -0
- package/src/lib/A-Command/A-Command.types.ts +96 -20
- package/src/lib/A-Command/A-CommandExecution.context.ts +0 -0
- package/src/lib/A-Command/README.md +164 -68
- package/src/lib/A-Config/A-Config.container.ts +2 -2
- package/src/lib/A-Config/A-Config.context.ts +19 -5
- package/src/lib/A-Config/components/ConfigReader.component.ts +1 -1
- package/src/lib/A-Logger/A-Logger.component.ts +211 -35
- package/src/lib/A-Logger/A-Logger.constants.ts +50 -10
- package/src/lib/A-Logger/A-Logger.env.ts +17 -1
- package/src/lib/A-Memory/A-Memory.component.ts +440 -0
- package/src/lib/A-Memory/A-Memory.constants.ts +49 -0
- package/src/lib/A-Memory/A-Memory.context.ts +14 -118
- package/src/lib/A-Memory/A-Memory.error.ts +21 -0
- package/src/lib/A-Memory/A-Memory.types.ts +21 -0
- package/src/lib/A-Operation/A-Operation.context.ts +58 -0
- package/src/lib/A-Operation/A-Operation.types.ts +47 -0
- package/src/lib/A-StateMachine/A-StateMachine.component.ts +258 -0
- package/src/lib/A-StateMachine/A-StateMachine.constants.ts +18 -0
- package/src/lib/A-StateMachine/A-StateMachine.error.ts +10 -0
- package/src/lib/A-StateMachine/A-StateMachine.types.ts +20 -0
- package/src/lib/A-StateMachine/A-StateMachineTransition.context.ts +41 -0
- package/src/lib/A-StateMachine/README.md +391 -0
- package/tests/A-Channel.test.ts +17 -14
- package/tests/A-Command.test.ts +548 -460
- package/tests/A-Logger.test.ts +8 -4
- package/tests/A-Memory.test.ts +151 -115
- package/tests/A-Schedule.test.ts +2 -2
- 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
|
-
|
|
9
|
-
|
|
8
|
+
A_Command_Event,
|
|
9
|
+
A_Command_Status,
|
|
10
|
+
A_CommandTransitions
|
|
10
11
|
} from "./A-Command.constants";
|
|
11
|
-
import {
|
|
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 |
|
|
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
|
|
86
|
+
// ================== Static Command Information ======================
|
|
24
87
|
// ====================================================================
|
|
25
88
|
|
|
26
89
|
/**
|
|
27
|
-
*
|
|
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
|
-
//
|
|
98
|
+
// ================== Instance Properties =============================
|
|
35
99
|
// ====================================================================
|
|
36
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
*
|
|
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
|
-
*
|
|
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
|
|
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():
|
|
196
|
+
get status(): A_Command_Status {
|
|
81
197
|
return this._status;
|
|
82
198
|
}
|
|
199
|
+
|
|
83
200
|
/**
|
|
84
|
-
*
|
|
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
|
-
*
|
|
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
|
|
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
|
-
*
|
|
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
|
|
105
|
-
return this.
|
|
246
|
+
get error(): A_Error | undefined {
|
|
247
|
+
return this._error;
|
|
106
248
|
}
|
|
249
|
+
|
|
107
250
|
/**
|
|
108
|
-
*
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
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
|
-
*
|
|
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
|
|
117
|
-
return this.
|
|
266
|
+
get params(): InvokeType {
|
|
267
|
+
return this._params;
|
|
118
268
|
}
|
|
269
|
+
|
|
119
270
|
/**
|
|
120
|
-
* Indicates if the command has completed
|
|
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
|
|
123
|
-
return this._status ===
|
|
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-
|
|
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
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
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.
|
|
159
|
-
this.
|
|
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
|
-
|
|
168
|
-
async
|
|
169
|
-
|
|
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.
|
|
342
|
+
this._startTime = new Date();
|
|
343
|
+
this._status = A_Command_Status.EXECUTING;
|
|
174
344
|
|
|
175
|
-
this.
|
|
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
|
-
*
|
|
350
|
+
* Handles command completion after successful execution
|
|
183
351
|
*
|
|
184
|
-
*
|
|
352
|
+
* EXECUTION -> COMPLETED transition
|
|
185
353
|
*/
|
|
186
|
-
async
|
|
187
|
-
|
|
188
|
-
|
|
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.
|
|
361
|
+
this.emit(A_CommandFeatures.onComplete);
|
|
362
|
+
}
|
|
191
363
|
|
|
192
|
-
|
|
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.
|
|
377
|
+
this._status = A_Command_Status.FAILED;
|
|
195
378
|
|
|
196
|
-
|
|
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
|
-
|
|
461
|
+
|
|
462
|
+
if (this.isProcessed) return;
|
|
204
463
|
|
|
205
464
|
try {
|
|
206
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
540
|
+
async complete(result?: ResultType) {
|
|
541
|
+
if (this.isProcessed) return;
|
|
227
542
|
|
|
228
|
-
this._status =
|
|
229
|
-
|
|
230
|
-
this._result =
|
|
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.
|
|
557
|
+
async fail(error?: A_Error) {
|
|
558
|
+
if (this.isProcessed) return;
|
|
243
559
|
|
|
244
|
-
this._status =
|
|
245
|
-
|
|
246
|
-
|
|
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 |
|
|
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 |
|
|
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 |
|
|
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.
|
|
624
|
+
this._origin = 'invoked';
|
|
306
625
|
|
|
307
|
-
this._executionScope
|
|
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 =
|
|
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.
|
|
327
|
-
|
|
328
|
-
const memory = new A_Memory<ResultType>();
|
|
650
|
+
this._origin = 'serialized';
|
|
329
651
|
|
|
330
|
-
this._executionScope
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
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
|
+
|