@adaas/a-concept 0.2.5 → 0.2.7
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.cjs +2 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.mts +19 -14
- package/dist/index.d.ts +19 -14
- package/dist/index.mjs +2 -2
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
- package/src/global/A-Component/A-Component.class.ts +3 -3
- package/src/global/A-Feature/A-Feature.class.ts +92 -21
- package/src/global/A-Stage/A-Stage.class.ts +54 -52
- package/src/helpers/A_TypeGuards.helper.ts +5 -0
- package/tests/A-Abstraction.test.ts +0 -2
- package/tests/A-Feature.test.ts +268 -17
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@adaas/a-concept",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.7",
|
|
4
4
|
"description": "A-Concept is a framework of the new generation that is tailored to use AI, enabling developers to create AI-powered applications with ease. It provides a structured approach to building, managing, and deploying AI-driven solutions.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"main": "./dist/index.cjs",
|
|
@@ -29,7 +29,7 @@ export class A_Component {
|
|
|
29
29
|
* @param scope - the scope in which to call the feature
|
|
30
30
|
* @returns - void
|
|
31
31
|
*/
|
|
32
|
-
|
|
32
|
+
call(
|
|
33
33
|
/**
|
|
34
34
|
* Name of the feature to call
|
|
35
35
|
*/
|
|
@@ -38,13 +38,13 @@ export class A_Component {
|
|
|
38
38
|
* Scope in which the feature will be executed
|
|
39
39
|
*/
|
|
40
40
|
scope?: A_Scope
|
|
41
|
-
) {
|
|
41
|
+
): Promise<any> | void {
|
|
42
42
|
const newFeature = new A_Feature({
|
|
43
43
|
name: feature,
|
|
44
44
|
component: this
|
|
45
45
|
});
|
|
46
46
|
|
|
47
|
-
return
|
|
47
|
+
return newFeature.process(scope);
|
|
48
48
|
}
|
|
49
49
|
}
|
|
50
50
|
|
|
@@ -355,13 +355,13 @@ export class A_Feature<T extends A_TYPES__FeatureAvailableComponents = A_TYPES__
|
|
|
355
355
|
* This method processes the feature by executing all the stages
|
|
356
356
|
*
|
|
357
357
|
*/
|
|
358
|
-
|
|
358
|
+
process(
|
|
359
359
|
/**
|
|
360
360
|
* Optional scope to be used to resolve the steps dependencies
|
|
361
361
|
* If not provided, the scope of the caller component will be used
|
|
362
362
|
*/
|
|
363
363
|
scope?: A_Scope,
|
|
364
|
-
) {
|
|
364
|
+
): Promise<void> | void {
|
|
365
365
|
try {
|
|
366
366
|
// It seems like this is a bad idea to enforce scope inheritance here
|
|
367
367
|
// ---------------------------------------------------------------
|
|
@@ -373,20 +373,74 @@ export class A_Feature<T extends A_TYPES__FeatureAvailableComponents = A_TYPES__
|
|
|
373
373
|
|
|
374
374
|
this._state = A_TYPES__FeatureState.PROCESSING;
|
|
375
375
|
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
376
|
+
// Convert iterator to array to get all stages
|
|
377
|
+
const stages = Array.from(this);
|
|
378
|
+
|
|
379
|
+
return this.processStagesSequentially(stages, scope, 0);
|
|
379
380
|
|
|
380
|
-
return await this.completed();
|
|
381
381
|
} catch (error) {
|
|
382
|
-
|
|
382
|
+
throw this.failed(new A_FeatureError({
|
|
383
383
|
title: A_FeatureError.FeatureProcessingError,
|
|
384
384
|
description: `An error occurred while processing the A-Feature: ${this.name}. Failed at stage: ${this.stage?.name || 'N/A'}.`,
|
|
385
385
|
stage: this.stage,
|
|
386
386
|
originalError: error
|
|
387
387
|
}));
|
|
388
388
|
}
|
|
389
|
+
}
|
|
389
390
|
|
|
391
|
+
/**
|
|
392
|
+
* Process stages one by one, ensuring each stage completes before starting the next
|
|
393
|
+
*/
|
|
394
|
+
private processStagesSequentially(
|
|
395
|
+
stages: A_Stage[],
|
|
396
|
+
scope: A_Scope | undefined,
|
|
397
|
+
index: number
|
|
398
|
+
): Promise<void> | void {
|
|
399
|
+
try {
|
|
400
|
+
// Check if feature has been interrupted before processing next stage
|
|
401
|
+
if (this.state === A_TYPES__FeatureState.INTERRUPTED) {
|
|
402
|
+
return;
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
// If we've processed all stages, complete the feature
|
|
406
|
+
if (index >= stages.length) {
|
|
407
|
+
this.completed();
|
|
408
|
+
return;
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
const stage = stages[index];
|
|
412
|
+
const result = stage.process(scope);
|
|
413
|
+
|
|
414
|
+
if (A_TypeGuards.isPromiseInstance(result)) {
|
|
415
|
+
// Async stage - return promise that processes remaining stages
|
|
416
|
+
return result
|
|
417
|
+
.then(() => {
|
|
418
|
+
// Check for interruption after async stage completes
|
|
419
|
+
if (this.state === A_TYPES__FeatureState.INTERRUPTED) {
|
|
420
|
+
return;
|
|
421
|
+
}
|
|
422
|
+
return this.processStagesSequentially(stages, scope, index + 1);
|
|
423
|
+
})
|
|
424
|
+
.catch(error => {
|
|
425
|
+
throw this.failed(new A_FeatureError({
|
|
426
|
+
title: A_FeatureError.FeatureProcessingError,
|
|
427
|
+
description: `An error occurred while processing the A-Feature: ${this.name}. Failed at stage: ${stage.name}.`,
|
|
428
|
+
stage: stage,
|
|
429
|
+
originalError: error
|
|
430
|
+
}));
|
|
431
|
+
});
|
|
432
|
+
} else {
|
|
433
|
+
// Sync stage - continue to next stage immediately
|
|
434
|
+
return this.processStagesSequentially(stages, scope, index + 1);
|
|
435
|
+
}
|
|
436
|
+
} catch (error) {
|
|
437
|
+
throw this.failed(new A_FeatureError({
|
|
438
|
+
title: A_FeatureError.FeatureProcessingError,
|
|
439
|
+
description: `An error occurred while processing the A-Feature: ${this.name}. Failed at stage: ${this.stage?.name || 'N/A'}.`,
|
|
440
|
+
stage: this.stage,
|
|
441
|
+
originalError: error
|
|
442
|
+
}));
|
|
443
|
+
}
|
|
390
444
|
}
|
|
391
445
|
/**
|
|
392
446
|
* This method moves the feature to the next stage
|
|
@@ -409,22 +463,27 @@ export class A_Feature<T extends A_TYPES__FeatureAvailableComponents = A_TYPES__
|
|
|
409
463
|
* @param result
|
|
410
464
|
* @returns
|
|
411
465
|
*/
|
|
412
|
-
|
|
466
|
+
completed(): void {
|
|
413
467
|
if (this.isProcessed) return;
|
|
414
468
|
|
|
469
|
+
// Don't complete if interrupted
|
|
470
|
+
if (this.state === A_TYPES__FeatureState.INTERRUPTED) {
|
|
471
|
+
return;
|
|
472
|
+
}
|
|
415
473
|
|
|
416
474
|
this._state = A_TYPES__FeatureState.COMPLETED;
|
|
417
475
|
|
|
418
476
|
this.scope.destroy();
|
|
419
477
|
}
|
|
420
478
|
/**
|
|
421
|
-
* This method marks the feature as failed and
|
|
479
|
+
* This method marks the feature as failed and returns the error
|
|
422
480
|
* Uses to mark the feature as failed
|
|
423
481
|
*
|
|
424
482
|
* @param error
|
|
483
|
+
* @returns The error that caused the failure
|
|
425
484
|
*/
|
|
426
|
-
|
|
427
|
-
if (this.isProcessed) return
|
|
485
|
+
failed(error: A_FeatureError): A_FeatureError {
|
|
486
|
+
if (this.isProcessed) return this._error!;
|
|
428
487
|
|
|
429
488
|
this._state = A_TYPES__FeatureState.FAILED;
|
|
430
489
|
|
|
@@ -432,45 +491,47 @@ export class A_Feature<T extends A_TYPES__FeatureAvailableComponents = A_TYPES__
|
|
|
432
491
|
|
|
433
492
|
this.scope.destroy();
|
|
434
493
|
|
|
435
|
-
|
|
494
|
+
return this._error;
|
|
436
495
|
}
|
|
437
496
|
/**
|
|
438
|
-
* This method marks the feature as
|
|
497
|
+
* This method marks the feature as interrupted and throws an error
|
|
439
498
|
* Uses to interrupt or end the feature processing
|
|
440
499
|
*
|
|
441
500
|
* @param error
|
|
442
501
|
*/
|
|
443
|
-
|
|
502
|
+
interrupt(
|
|
444
503
|
/**
|
|
445
504
|
* The reason of feature interruption
|
|
446
505
|
*/
|
|
447
506
|
reason?: string | A_StageError | Error
|
|
448
|
-
) {
|
|
449
|
-
if (this.isProcessed) return
|
|
507
|
+
): A_FeatureError {
|
|
508
|
+
if (this.isProcessed) return this._error!;
|
|
450
509
|
|
|
451
510
|
this._state = A_TYPES__FeatureState.INTERRUPTED;
|
|
452
511
|
|
|
453
512
|
switch (true) {
|
|
454
513
|
case A_TypeGuards.isString(reason):
|
|
455
|
-
this._error = new A_FeatureError(A_FeatureError.Interruption, reason);
|
|
514
|
+
this._error = new A_FeatureError(A_FeatureError.Interruption, reason as string);
|
|
456
515
|
break;
|
|
457
516
|
|
|
458
517
|
case A_TypeGuards.isErrorInstance(reason):
|
|
459
518
|
this._error = new A_FeatureError({
|
|
460
519
|
code: A_FeatureError.Interruption,
|
|
461
|
-
title: reason.title,
|
|
462
|
-
description: reason.description,
|
|
520
|
+
title: (reason as any).title || 'Feature Interrupted',
|
|
521
|
+
description: (reason as any).description || (reason as Error).message,
|
|
463
522
|
stage: this.stage,
|
|
464
523
|
originalError: reason
|
|
465
524
|
});
|
|
466
525
|
break;
|
|
467
526
|
|
|
468
527
|
default:
|
|
528
|
+
this._error = new A_FeatureError(A_FeatureError.Interruption, 'Feature was interrupted');
|
|
469
529
|
break;
|
|
470
530
|
}
|
|
471
531
|
|
|
472
|
-
|
|
473
532
|
this.scope.destroy();
|
|
533
|
+
|
|
534
|
+
return this._error;
|
|
474
535
|
}
|
|
475
536
|
|
|
476
537
|
|
|
@@ -530,7 +591,17 @@ export class A_Feature<T extends A_TYPES__FeatureAvailableComponents = A_TYPES__
|
|
|
530
591
|
// create new caller for the chained feature
|
|
531
592
|
feature._caller = this._caller;
|
|
532
593
|
|
|
533
|
-
|
|
594
|
+
const result = feature.process(featureScope);
|
|
595
|
+
|
|
596
|
+
// If the chained feature processing returns a promise, ensure errors are propagated
|
|
597
|
+
if (A_TypeGuards.isPromiseInstance(result)) {
|
|
598
|
+
return result.catch(error => {
|
|
599
|
+
// Re-throw to ensure chained feature errors propagate to caller
|
|
600
|
+
throw error;
|
|
601
|
+
});
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
return result;
|
|
534
605
|
}
|
|
535
606
|
|
|
536
607
|
|
|
@@ -35,11 +35,6 @@ export class A_Stage {
|
|
|
35
35
|
*/
|
|
36
36
|
private _status: A_TYPES__A_Stage_Status = A_TYPES__A_Stage_Status.INITIALIZED;
|
|
37
37
|
|
|
38
|
-
/**
|
|
39
|
-
* Promise that will be resolved when the stage is Processed
|
|
40
|
-
*/
|
|
41
|
-
private _processed: Promise<void> | undefined;
|
|
42
|
-
|
|
43
38
|
|
|
44
39
|
/**
|
|
45
40
|
* A_Stage is a callable A_Function within A_Feature that should be run with specific parameters.
|
|
@@ -106,7 +101,7 @@ export class A_Stage {
|
|
|
106
101
|
* @param step
|
|
107
102
|
* @returns
|
|
108
103
|
*/
|
|
109
|
-
protected
|
|
104
|
+
protected getStepArgs(
|
|
110
105
|
scope: A_Scope,
|
|
111
106
|
step: A_TYPES__A_StageStep
|
|
112
107
|
) {
|
|
@@ -114,24 +109,22 @@ export class A_Stage {
|
|
|
114
109
|
(step.dependency.target as A_TYPES__Container_Constructor | A_TYPES__Component_Constructor)
|
|
115
110
|
|| scope.resolveConstructor(step.dependency.name);
|
|
116
111
|
|
|
117
|
-
return
|
|
118
|
-
.
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
return this._feature.caller.component;
|
|
112
|
+
return A_Context
|
|
113
|
+
.meta(resolverConstructor)
|
|
114
|
+
.injections(step.handler)
|
|
115
|
+
.map(dependency => {
|
|
116
|
+
switch (true) {
|
|
117
|
+
case A_TypeGuards.isCallerConstructor(dependency.target):
|
|
118
|
+
return this._feature.caller.component;
|
|
125
119
|
|
|
126
|
-
|
|
127
|
-
|
|
120
|
+
case A_TypeGuards.isFeatureConstructor(dependency.target):
|
|
121
|
+
return this._feature;
|
|
128
122
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
}
|
|
123
|
+
default: {
|
|
124
|
+
return scope.resolve(dependency);
|
|
132
125
|
}
|
|
133
|
-
}
|
|
134
|
-
)
|
|
126
|
+
}
|
|
127
|
+
})
|
|
135
128
|
}
|
|
136
129
|
|
|
137
130
|
|
|
@@ -174,17 +167,23 @@ export class A_Stage {
|
|
|
174
167
|
* @param step
|
|
175
168
|
* @returns
|
|
176
169
|
*/
|
|
177
|
-
protected
|
|
170
|
+
protected callStepHandler(
|
|
178
171
|
step: A_TYPES__A_StageStep,
|
|
179
172
|
scope: A_Scope
|
|
180
|
-
) {
|
|
173
|
+
): {
|
|
174
|
+
handler: Function,
|
|
175
|
+
params: any[]
|
|
176
|
+
} {
|
|
181
177
|
// 1) Resolve component
|
|
182
|
-
const component =
|
|
178
|
+
const component = this.getStepComponent(scope, step);
|
|
183
179
|
// 2) Resolve arguments
|
|
184
|
-
const callArgs =
|
|
180
|
+
const callArgs = this.getStepArgs(scope, step);
|
|
185
181
|
|
|
186
182
|
// 3) Call handler
|
|
187
|
-
return
|
|
183
|
+
return {
|
|
184
|
+
handler: component[step.handler].bind(component),
|
|
185
|
+
params: callArgs
|
|
186
|
+
}
|
|
188
187
|
}
|
|
189
188
|
|
|
190
189
|
|
|
@@ -198,48 +197,51 @@ export class A_Stage {
|
|
|
198
197
|
*
|
|
199
198
|
* @param scope - Scope to be used to resolve the steps dependencies
|
|
200
199
|
*/
|
|
201
|
-
|
|
200
|
+
process(
|
|
202
201
|
/**
|
|
203
202
|
* Scope to be used to resolve the steps dependencies
|
|
204
203
|
*/
|
|
205
204
|
scope?: A_Scope,
|
|
206
|
-
): Promise<void> {
|
|
205
|
+
): Promise<void> | void {
|
|
207
206
|
|
|
208
207
|
const targetScope = A_TypeGuards.isScopeInstance(scope)
|
|
209
208
|
? scope
|
|
210
209
|
: this._feature.scope;
|
|
211
210
|
|
|
212
|
-
if (!this.
|
|
213
|
-
this.
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
this._status = A_TYPES__A_Stage_Status.PROCESSING;
|
|
217
|
-
|
|
218
|
-
if (this._definition.behavior === 'sync') {
|
|
219
|
-
// in case we have to wait for the result
|
|
220
|
-
await this.callStepHandler(this._definition, targetScope);
|
|
221
|
-
} else {
|
|
222
|
-
// in case we don't have to wait for the result
|
|
223
|
-
this.callStepHandler(this._definition, targetScope);
|
|
224
|
-
}
|
|
211
|
+
if (!this.isProcessed) {
|
|
212
|
+
this._status = A_TYPES__A_Stage_Status.PROCESSING;
|
|
213
|
+
|
|
214
|
+
const { handler, params } = this.callStepHandler(this._definition, targetScope);
|
|
225
215
|
|
|
226
|
-
|
|
216
|
+
const result = handler(...params);
|
|
227
217
|
|
|
228
|
-
|
|
229
|
-
} catch (error) {
|
|
230
|
-
const wrappedError = new A_Error(error as any);
|
|
218
|
+
if (A_TypeGuards.isPromiseInstance(result)) {
|
|
231
219
|
|
|
232
|
-
|
|
220
|
+
return new Promise<void>(
|
|
221
|
+
async (resolve, reject) => {
|
|
222
|
+
try {
|
|
223
|
+
await result;
|
|
224
|
+
|
|
225
|
+
this.completed();
|
|
233
226
|
|
|
234
|
-
if (this._definition.throwOnError) {
|
|
235
227
|
return resolve();
|
|
236
|
-
}
|
|
237
|
-
|
|
228
|
+
} catch (error) {
|
|
229
|
+
const wrappedError = new A_Error(error as any);
|
|
230
|
+
|
|
231
|
+
this.failed(wrappedError);
|
|
232
|
+
|
|
233
|
+
if (this._definition.throwOnError) {
|
|
234
|
+
return resolve();
|
|
235
|
+
} else {
|
|
236
|
+
return reject(wrappedError);
|
|
237
|
+
}
|
|
238
238
|
}
|
|
239
|
-
}
|
|
240
|
-
|
|
239
|
+
});
|
|
240
|
+
} else {
|
|
241
|
+
this.completed();
|
|
242
|
+
}
|
|
243
|
+
}
|
|
241
244
|
|
|
242
|
-
return this._processed;
|
|
243
245
|
}
|
|
244
246
|
|
|
245
247
|
|
|
@@ -367,4 +367,9 @@ export class A_TypeGuards {
|
|
|
367
367
|
static isErrorSerializedType<T extends A_TYPES__Error_Serialized>(param: any): param is T {
|
|
368
368
|
return !!param && A_TypeGuards.isObject(param) && !(param instanceof Error) && "aseid" in param && ASEID.isASEID(param.aseid);
|
|
369
369
|
}
|
|
370
|
+
|
|
371
|
+
|
|
372
|
+
static isPromiseInstance<T>(value: any): value is Promise<T> {
|
|
373
|
+
return value instanceof Promise;
|
|
374
|
+
}
|
|
370
375
|
}
|