@adaas/a-concept 0.2.6 → 0.2.8
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 +5 -4
- package/dist/index.d.ts +5 -4
- package/dist/index.mjs +2 -2
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
- package/src/global/A-Feature/A-Feature.class.ts +56 -28
- package/src/global/A-Scope/A-Scope.class.ts +55 -1
- package/tests/A-Feature.test.ts +182 -16
- package/tests/A-Scope.test.ts +105 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@adaas/a-concept",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.8",
|
|
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",
|
|
@@ -375,11 +375,11 @@ export class A_Feature<T extends A_TYPES__FeatureAvailableComponents = A_TYPES__
|
|
|
375
375
|
|
|
376
376
|
// Convert iterator to array to get all stages
|
|
377
377
|
const stages = Array.from(this);
|
|
378
|
-
|
|
378
|
+
|
|
379
379
|
return this.processStagesSequentially(stages, scope, 0);
|
|
380
380
|
|
|
381
381
|
} catch (error) {
|
|
382
|
-
this.failed(new A_FeatureError({
|
|
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,
|
|
@@ -392,11 +392,16 @@ export class A_Feature<T extends A_TYPES__FeatureAvailableComponents = A_TYPES__
|
|
|
392
392
|
* Process stages one by one, ensuring each stage completes before starting the next
|
|
393
393
|
*/
|
|
394
394
|
private processStagesSequentially(
|
|
395
|
-
stages: A_Stage[],
|
|
396
|
-
scope: A_Scope | undefined,
|
|
395
|
+
stages: A_Stage[],
|
|
396
|
+
scope: A_Scope | undefined,
|
|
397
397
|
index: number
|
|
398
398
|
): Promise<void> | void {
|
|
399
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
|
+
|
|
400
405
|
// If we've processed all stages, complete the feature
|
|
401
406
|
if (index >= stages.length) {
|
|
402
407
|
this.completed();
|
|
@@ -408,22 +413,28 @@ export class A_Feature<T extends A_TYPES__FeatureAvailableComponents = A_TYPES__
|
|
|
408
413
|
|
|
409
414
|
if (A_TypeGuards.isPromiseInstance(result)) {
|
|
410
415
|
// Async stage - return promise that processes remaining stages
|
|
411
|
-
return result
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
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
|
+
});
|
|
421
432
|
} else {
|
|
422
433
|
// Sync stage - continue to next stage immediately
|
|
423
434
|
return this.processStagesSequentially(stages, scope, index + 1);
|
|
424
435
|
}
|
|
425
436
|
} catch (error) {
|
|
426
|
-
this.failed(new A_FeatureError({
|
|
437
|
+
throw this.failed(new A_FeatureError({
|
|
427
438
|
title: A_FeatureError.FeatureProcessingError,
|
|
428
439
|
description: `An error occurred while processing the A-Feature: ${this.name}. Failed at stage: ${this.stage?.name || 'N/A'}.`,
|
|
429
440
|
stage: this.stage,
|
|
@@ -455,19 +466,24 @@ export class A_Feature<T extends A_TYPES__FeatureAvailableComponents = A_TYPES__
|
|
|
455
466
|
completed(): void {
|
|
456
467
|
if (this.isProcessed) return;
|
|
457
468
|
|
|
469
|
+
// Don't complete if interrupted
|
|
470
|
+
if (this.state === A_TYPES__FeatureState.INTERRUPTED) {
|
|
471
|
+
return;
|
|
472
|
+
}
|
|
458
473
|
|
|
459
474
|
this._state = A_TYPES__FeatureState.COMPLETED;
|
|
460
475
|
|
|
461
476
|
this.scope.destroy();
|
|
462
477
|
}
|
|
463
478
|
/**
|
|
464
|
-
* This method marks the feature as failed and
|
|
479
|
+
* This method marks the feature as failed and returns the error
|
|
465
480
|
* Uses to mark the feature as failed
|
|
466
481
|
*
|
|
467
482
|
* @param error
|
|
483
|
+
* @returns The error that caused the failure
|
|
468
484
|
*/
|
|
469
|
-
failed(error: A_FeatureError) {
|
|
470
|
-
if (this.isProcessed) return
|
|
485
|
+
failed(error: A_FeatureError): A_FeatureError {
|
|
486
|
+
if (this.isProcessed) return this._error!;
|
|
471
487
|
|
|
472
488
|
this._state = A_TYPES__FeatureState.FAILED;
|
|
473
489
|
|
|
@@ -475,45 +491,47 @@ export class A_Feature<T extends A_TYPES__FeatureAvailableComponents = A_TYPES__
|
|
|
475
491
|
|
|
476
492
|
this.scope.destroy();
|
|
477
493
|
|
|
478
|
-
|
|
494
|
+
return this._error;
|
|
479
495
|
}
|
|
480
496
|
/**
|
|
481
|
-
* This method marks the feature as
|
|
497
|
+
* This method marks the feature as interrupted and throws an error
|
|
482
498
|
* Uses to interrupt or end the feature processing
|
|
483
499
|
*
|
|
484
500
|
* @param error
|
|
485
501
|
*/
|
|
486
|
-
|
|
502
|
+
interrupt(
|
|
487
503
|
/**
|
|
488
504
|
* The reason of feature interruption
|
|
489
505
|
*/
|
|
490
506
|
reason?: string | A_StageError | Error
|
|
491
|
-
) {
|
|
492
|
-
if (this.isProcessed) return
|
|
507
|
+
): A_FeatureError {
|
|
508
|
+
if (this.isProcessed) return this._error!;
|
|
493
509
|
|
|
494
510
|
this._state = A_TYPES__FeatureState.INTERRUPTED;
|
|
495
511
|
|
|
496
512
|
switch (true) {
|
|
497
513
|
case A_TypeGuards.isString(reason):
|
|
498
|
-
this._error = new A_FeatureError(A_FeatureError.Interruption, reason);
|
|
514
|
+
this._error = new A_FeatureError(A_FeatureError.Interruption, reason as string);
|
|
499
515
|
break;
|
|
500
516
|
|
|
501
517
|
case A_TypeGuards.isErrorInstance(reason):
|
|
502
518
|
this._error = new A_FeatureError({
|
|
503
519
|
code: A_FeatureError.Interruption,
|
|
504
|
-
title: reason.title,
|
|
505
|
-
description: reason.description,
|
|
520
|
+
title: (reason as any).title || 'Feature Interrupted',
|
|
521
|
+
description: (reason as any).description || (reason as Error).message,
|
|
506
522
|
stage: this.stage,
|
|
507
523
|
originalError: reason
|
|
508
524
|
});
|
|
509
525
|
break;
|
|
510
526
|
|
|
511
527
|
default:
|
|
528
|
+
this._error = new A_FeatureError(A_FeatureError.Interruption, 'Feature was interrupted');
|
|
512
529
|
break;
|
|
513
530
|
}
|
|
514
531
|
|
|
515
|
-
|
|
516
532
|
this.scope.destroy();
|
|
533
|
+
|
|
534
|
+
return this._error;
|
|
517
535
|
}
|
|
518
536
|
|
|
519
537
|
|
|
@@ -573,7 +591,17 @@ export class A_Feature<T extends A_TYPES__FeatureAvailableComponents = A_TYPES__
|
|
|
573
591
|
// create new caller for the chained feature
|
|
574
592
|
feature._caller = this._caller;
|
|
575
593
|
|
|
576
|
-
|
|
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;
|
|
577
605
|
}
|
|
578
606
|
|
|
579
607
|
|
|
@@ -1814,6 +1814,13 @@ export class A_Scope<
|
|
|
1814
1814
|
this._components.delete(param1.constructor as _ComponentType[number]);
|
|
1815
1815
|
A_Context.deregister(param1);
|
|
1816
1816
|
|
|
1817
|
+
const ctor = param1.constructor as _ComponentType[number];
|
|
1818
|
+
|
|
1819
|
+
const hasComponent = this._components.has(ctor);
|
|
1820
|
+
if (!hasComponent) {
|
|
1821
|
+
this.allowedComponents.delete(ctor);
|
|
1822
|
+
}
|
|
1823
|
+
|
|
1817
1824
|
break;
|
|
1818
1825
|
}
|
|
1819
1826
|
// 3) In case when it's a A-Entity instance
|
|
@@ -1821,14 +1828,28 @@ export class A_Scope<
|
|
|
1821
1828
|
|
|
1822
1829
|
this._entities.delete(param1.aseid.toString());
|
|
1823
1830
|
A_Context.deregister(param1);
|
|
1831
|
+
|
|
1832
|
+
const ctor = param1.constructor as _EntityType[number];
|
|
1833
|
+
|
|
1834
|
+
const hasEntity = Array.from(this._entities.values()).some(entity => entity instanceof ctor);
|
|
1835
|
+
if (!hasEntity) {
|
|
1836
|
+
this.allowedEntities.delete(ctor);
|
|
1837
|
+
}
|
|
1838
|
+
|
|
1824
1839
|
break;
|
|
1825
1840
|
}
|
|
1826
1841
|
// 4) In case when it's a A-Fragment instance
|
|
1827
1842
|
case A_TypeGuards.isFragmentInstance(param1): {
|
|
1828
|
-
|
|
1829
1843
|
this._fragments.delete(param1.constructor as A_TYPES__Fragment_Constructor<_FragmentType[number]>);
|
|
1830
1844
|
A_Context.deregister(param1);
|
|
1831
1845
|
|
|
1846
|
+
const ctor = param1.constructor as A_TYPES__Fragment_Constructor<_FragmentType[number]>;
|
|
1847
|
+
|
|
1848
|
+
const hasFragment = Array.from(this._fragments.values()).some(fragment => fragment instanceof ctor);
|
|
1849
|
+
if (!hasFragment) {
|
|
1850
|
+
this.allowedFragments.delete(ctor);
|
|
1851
|
+
}
|
|
1852
|
+
|
|
1832
1853
|
break;
|
|
1833
1854
|
}
|
|
1834
1855
|
// 5) In case when it's a A-Error instance
|
|
@@ -1836,6 +1857,14 @@ export class A_Scope<
|
|
|
1836
1857
|
|
|
1837
1858
|
this._errors.delete(param1.code);
|
|
1838
1859
|
A_Context.deregister(param1);
|
|
1860
|
+
|
|
1861
|
+
const ctor = param1.constructor as _ErrorType[number];
|
|
1862
|
+
|
|
1863
|
+
const hasError = Array.from(this._errors.values()).some(error => error instanceof ctor);
|
|
1864
|
+
if (!hasError) {
|
|
1865
|
+
this.allowedErrors.delete(ctor);
|
|
1866
|
+
}
|
|
1867
|
+
|
|
1839
1868
|
break;
|
|
1840
1869
|
}
|
|
1841
1870
|
|
|
@@ -1850,16 +1879,40 @@ export class A_Scope<
|
|
|
1850
1879
|
// 8) In case when it's a A-Fragment constructor
|
|
1851
1880
|
case A_TypeGuards.isFragmentConstructor(param1): {
|
|
1852
1881
|
this.allowedFragments.delete(param1 as A_TYPES__Fragment_Constructor<_FragmentType[number]>);
|
|
1882
|
+
// and then deregister all instances of this fragment
|
|
1883
|
+
Array.from(this._fragments.entries()).forEach(([ctor, instance]) => {
|
|
1884
|
+
if (A_CommonHelper.isInheritedFrom(ctor, param1)) {
|
|
1885
|
+
this._fragments.delete(ctor);
|
|
1886
|
+
A_Context.deregister(instance);
|
|
1887
|
+
}
|
|
1888
|
+
});
|
|
1889
|
+
|
|
1853
1890
|
break;
|
|
1854
1891
|
}
|
|
1855
1892
|
// 9) In case when it's a A-Entity constructor
|
|
1856
1893
|
case A_TypeGuards.isEntityConstructor(param1): {
|
|
1857
1894
|
this.allowedEntities.delete(param1 as _EntityType[number]);
|
|
1895
|
+
// and then deregister all instances of this entity
|
|
1896
|
+
Array.from(this._entities.entries()).forEach(([aseid, instance]) => {
|
|
1897
|
+
if (A_CommonHelper.isInheritedFrom(instance.constructor, param1)) {
|
|
1898
|
+
this._entities.delete(aseid);
|
|
1899
|
+
A_Context.deregister(instance);
|
|
1900
|
+
}
|
|
1901
|
+
});
|
|
1902
|
+
|
|
1858
1903
|
break;
|
|
1859
1904
|
}
|
|
1860
1905
|
// 10) In case when it's a A-Error constructor
|
|
1861
1906
|
case A_TypeGuards.isErrorConstructor(param1): {
|
|
1862
1907
|
this.allowedErrors.delete(param1 as _ErrorType[number]);
|
|
1908
|
+
// and then deregister all instances of this error
|
|
1909
|
+
Array.from(this._errors.entries()).forEach(([code, instance]) => {
|
|
1910
|
+
if (A_CommonHelper.isInheritedFrom(instance.constructor, param1)) {
|
|
1911
|
+
this._errors.delete(code);
|
|
1912
|
+
A_Context.deregister(instance);
|
|
1913
|
+
}
|
|
1914
|
+
});
|
|
1915
|
+
|
|
1863
1916
|
break;
|
|
1864
1917
|
}
|
|
1865
1918
|
|
|
@@ -1875,6 +1928,7 @@ export class A_Scope<
|
|
|
1875
1928
|
`Cannot deregister ${componentName} from the scope ${this.name}`
|
|
1876
1929
|
);
|
|
1877
1930
|
}
|
|
1931
|
+
|
|
1878
1932
|
}
|
|
1879
1933
|
|
|
1880
1934
|
/**
|
package/tests/A-Feature.test.ts
CHANGED
|
@@ -591,22 +591,21 @@ describe('A-Feature tests', () => {
|
|
|
591
591
|
]
|
|
592
592
|
});
|
|
593
593
|
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
});
|
|
594
|
+
await Promise.all([
|
|
595
|
+
feature.process(),
|
|
596
|
+
new Promise<void>(async (resolve) => {
|
|
597
|
+
setTimeout(() => {
|
|
598
|
+
feature.interrupt();
|
|
599
|
+
resolve();
|
|
600
|
+
}, 800);
|
|
601
|
+
}),
|
|
602
|
+
new Promise<void>(async (resolve) => {
|
|
603
|
+
setTimeout(() => {
|
|
604
|
+
expect(feature.state).toBe(A_TYPES__FeatureState.INTERRUPTED);
|
|
605
|
+
resolve();
|
|
606
|
+
}, 1000);
|
|
607
|
+
})
|
|
608
|
+
]);
|
|
610
609
|
|
|
611
610
|
}, 5000);
|
|
612
611
|
it('Should allow to use extension if only parent class provided', async () => {
|
|
@@ -868,4 +867,171 @@ describe('A-Feature tests', () => {
|
|
|
868
867
|
]);
|
|
869
868
|
|
|
870
869
|
})
|
|
870
|
+
it('Should throw a Sync error when executed sync', async () => {
|
|
871
|
+
|
|
872
|
+
const resultChain: string[] = [];
|
|
873
|
+
|
|
874
|
+
|
|
875
|
+
class ChildComponent_A extends A_Component {
|
|
876
|
+
@A_Feature.Extend({
|
|
877
|
+
name: 'testFeature',
|
|
878
|
+
})
|
|
879
|
+
test1() {
|
|
880
|
+
resultChain.push('ChildComponent_A.test');
|
|
881
|
+
throw new A_Error('Deliberate Sync Error in test1');
|
|
882
|
+
}
|
|
883
|
+
}
|
|
884
|
+
|
|
885
|
+
class ChildComponent_B extends A_Component {
|
|
886
|
+
@A_Feature.Extend({
|
|
887
|
+
name: 'testFeature',
|
|
888
|
+
})
|
|
889
|
+
test2() {
|
|
890
|
+
resultChain.push('ChildComponent_B.test');
|
|
891
|
+
}
|
|
892
|
+
}
|
|
893
|
+
|
|
894
|
+
|
|
895
|
+
const testScope = new A_Scope({ name: 'TestScope', components: [ChildComponent_A, ChildComponent_B] });
|
|
896
|
+
|
|
897
|
+
try {
|
|
898
|
+
testScope.resolve(ChildComponent_A)!.call('testFeature');
|
|
899
|
+
} catch (error) {
|
|
900
|
+
expect(error).toBeInstanceOf(A_Error);
|
|
901
|
+
expect((error as A_Error).originalError).toBeInstanceOf(A_Error)
|
|
902
|
+
expect((error as A_Error).originalError.message).toBe('Deliberate Sync Error in test1');
|
|
903
|
+
}
|
|
904
|
+
|
|
905
|
+
expect(resultChain).toEqual([
|
|
906
|
+
'ChildComponent_A.test'
|
|
907
|
+
]);
|
|
908
|
+
const feature = new A_Feature({
|
|
909
|
+
name: 'testFeature',
|
|
910
|
+
component: testScope.resolve(ChildComponent_A)!,
|
|
911
|
+
})
|
|
912
|
+
|
|
913
|
+
try {
|
|
914
|
+
feature.process();
|
|
915
|
+
} catch (error) {
|
|
916
|
+
expect(error).toBeInstanceOf(A_Error);
|
|
917
|
+
expect((error as A_Error).originalError).toBeInstanceOf(A_Error)
|
|
918
|
+
expect((error as A_Error).originalError.message).toBe('Deliberate Sync Error in test1');
|
|
919
|
+
}
|
|
920
|
+
|
|
921
|
+
expect(feature.state).toBe(A_TYPES__FeatureState.FAILED);
|
|
922
|
+
|
|
923
|
+
expect(resultChain).toEqual([
|
|
924
|
+
'ChildComponent_A.test',
|
|
925
|
+
'ChildComponent_A.test'
|
|
926
|
+
]);
|
|
927
|
+
})
|
|
928
|
+
it('Should throw an Async error when executed async', async () => {
|
|
929
|
+
|
|
930
|
+
const resultChain: string[] = [];
|
|
931
|
+
|
|
932
|
+
class ChildComponent_A extends A_Component {
|
|
933
|
+
@A_Feature.Extend({
|
|
934
|
+
name: 'testFeature',
|
|
935
|
+
})
|
|
936
|
+
async test1() {
|
|
937
|
+
resultChain.push('ChildComponent_A.test');
|
|
938
|
+
|
|
939
|
+
await new Promise<void>(async (resolve, reject) => {
|
|
940
|
+
setTimeout(() => {
|
|
941
|
+
reject(new A_Error('Deliberate Async Error in test1'));
|
|
942
|
+
}, 2000);
|
|
943
|
+
});
|
|
944
|
+
}
|
|
945
|
+
}
|
|
946
|
+
|
|
947
|
+
class ChildComponent_B extends A_Component {
|
|
948
|
+
@A_Feature.Extend({
|
|
949
|
+
name: 'testFeature',
|
|
950
|
+
})
|
|
951
|
+
async test2() {
|
|
952
|
+
resultChain.push('ChildComponent_B.test');
|
|
953
|
+
}
|
|
954
|
+
}
|
|
955
|
+
|
|
956
|
+
const testScope = new A_Scope({ name: 'TestScope', components: [ChildComponent_A, ChildComponent_B] });
|
|
957
|
+
try {
|
|
958
|
+
await Promise.all([
|
|
959
|
+
new Promise<void>(async (resolve) => {
|
|
960
|
+
setTimeout(() => {
|
|
961
|
+
resultChain.push('feature3');
|
|
962
|
+
|
|
963
|
+
resolve();
|
|
964
|
+
}, 1000);
|
|
965
|
+
}),
|
|
966
|
+
testScope.resolve(ChildComponent_A)!.call('testFeature')
|
|
967
|
+
]);
|
|
968
|
+
|
|
969
|
+
} catch (error) {
|
|
970
|
+
expect(error).toBeInstanceOf(A_Error);
|
|
971
|
+
expect((error as A_Error).originalError).toBeInstanceOf(A_Error)
|
|
972
|
+
expect((error as A_Error).originalError.message).toBe('Deliberate Async Error in test1');
|
|
973
|
+
}
|
|
974
|
+
expect(resultChain).toEqual([
|
|
975
|
+
'ChildComponent_A.test',
|
|
976
|
+
'feature3'
|
|
977
|
+
]);
|
|
978
|
+
|
|
979
|
+
const feature = new A_Feature({
|
|
980
|
+
name: 'testFeature',
|
|
981
|
+
component: testScope.resolve(ChildComponent_A)!,
|
|
982
|
+
})
|
|
983
|
+
|
|
984
|
+
try {
|
|
985
|
+
await feature.process();
|
|
986
|
+
} catch (error) {
|
|
987
|
+
expect(error).toBeInstanceOf(A_Error);
|
|
988
|
+
expect((error as A_Error).originalError).toBeInstanceOf(A_Error)
|
|
989
|
+
expect((error as A_Error).originalError.message).toBe('Deliberate Async Error in test1');
|
|
990
|
+
}
|
|
991
|
+
|
|
992
|
+
expect(feature.state).toBe(A_TYPES__FeatureState.FAILED);
|
|
993
|
+
|
|
994
|
+
expect(resultChain).toEqual([
|
|
995
|
+
'ChildComponent_A.test',
|
|
996
|
+
'feature3',
|
|
997
|
+
'ChildComponent_A.test'
|
|
998
|
+
]);
|
|
999
|
+
})
|
|
1000
|
+
|
|
1001
|
+
it('Should throw an Async error when executed async and error in method', async () => {
|
|
1002
|
+
|
|
1003
|
+
const resultChain: string[] = [];
|
|
1004
|
+
|
|
1005
|
+
class ChildComponent_A extends A_Component {
|
|
1006
|
+
@A_Feature.Extend({
|
|
1007
|
+
name: 'testFeature',
|
|
1008
|
+
})
|
|
1009
|
+
async test1() {
|
|
1010
|
+
resultChain.push('ChildComponent_A.test');
|
|
1011
|
+
|
|
1012
|
+
throw new A_Error('Deliberate Async Error in test1');
|
|
1013
|
+
}
|
|
1014
|
+
}
|
|
1015
|
+
|
|
1016
|
+
const testScope = new A_Scope({ name: 'TestScope', components: [ChildComponent_A] });
|
|
1017
|
+
|
|
1018
|
+
const feature = new A_Feature({
|
|
1019
|
+
name: 'testFeature',
|
|
1020
|
+
component: testScope.resolve(ChildComponent_A)!,
|
|
1021
|
+
})
|
|
1022
|
+
|
|
1023
|
+
try {
|
|
1024
|
+
await feature.process();
|
|
1025
|
+
} catch (error) {
|
|
1026
|
+
expect(error).toBeInstanceOf(A_Error);
|
|
1027
|
+
expect((error as A_Error).originalError).toBeInstanceOf(A_Error)
|
|
1028
|
+
expect((error as A_Error).originalError.message).toBe('Deliberate Async Error in test1');
|
|
1029
|
+
}
|
|
1030
|
+
|
|
1031
|
+
expect(feature.state).toBe(A_TYPES__FeatureState.FAILED);
|
|
1032
|
+
|
|
1033
|
+
expect(resultChain).toEqual([
|
|
1034
|
+
'ChildComponent_A.test'
|
|
1035
|
+
]);
|
|
1036
|
+
})
|
|
871
1037
|
});
|
package/tests/A-Scope.test.ts
CHANGED
|
@@ -705,4 +705,109 @@ describe('A-Scope tests', () => {
|
|
|
705
705
|
expect(resolvedA).toBeInstanceOf(MyEntity_B);
|
|
706
706
|
expect(resolvedA?.name).toBe('Entity1');
|
|
707
707
|
});
|
|
708
|
+
|
|
709
|
+
it('Should deregister entities properly', async () => {
|
|
710
|
+
|
|
711
|
+
class MyEntity extends A_Entity { }
|
|
712
|
+
|
|
713
|
+
class MyFragment extends A_Fragment { }
|
|
714
|
+
|
|
715
|
+
class MyComponent extends A_Component { }
|
|
716
|
+
|
|
717
|
+
const scope = new A_Scope({ name: 'TestScope' });
|
|
718
|
+
|
|
719
|
+
|
|
720
|
+
|
|
721
|
+
const entity = new MyEntity();
|
|
722
|
+
const fragment = new MyFragment();
|
|
723
|
+
|
|
724
|
+
scope.register(entity);
|
|
725
|
+
scope.register(fragment);
|
|
726
|
+
scope.register(MyComponent);
|
|
727
|
+
|
|
728
|
+
expect(scope.has(MyEntity)).toBe(true);
|
|
729
|
+
expect(scope.has(MyFragment)).toBe(true);
|
|
730
|
+
expect(scope.has(MyComponent)).toBe(true);
|
|
731
|
+
|
|
732
|
+
scope.deregister(entity);
|
|
733
|
+
scope.deregister(fragment);
|
|
734
|
+
scope.deregister(MyComponent);
|
|
735
|
+
|
|
736
|
+
expect(scope.has(MyEntity)).toBe(false);
|
|
737
|
+
expect(scope.has(MyFragment)).toBe(false);
|
|
738
|
+
expect(scope.has(MyComponent)).toBe(false);
|
|
739
|
+
});
|
|
740
|
+
|
|
741
|
+
it('Should deregister all entities by class', async () => {
|
|
742
|
+
|
|
743
|
+
class BaseEntity extends A_Entity { }
|
|
744
|
+
|
|
745
|
+
class MyEntityA extends A_Entity { }
|
|
746
|
+
class MyEntityB extends BaseEntity { }
|
|
747
|
+
|
|
748
|
+
|
|
749
|
+
const scope = new A_Scope({ name: 'TestScope' });
|
|
750
|
+
|
|
751
|
+
|
|
752
|
+
|
|
753
|
+
const entityA1 = new MyEntityA();
|
|
754
|
+
const entityA2 = new MyEntityA();
|
|
755
|
+
const entityB1 = new MyEntityB();
|
|
756
|
+
|
|
757
|
+
scope.register(entityA1);
|
|
758
|
+
scope.register(entityA2);
|
|
759
|
+
scope.register(entityB1);
|
|
760
|
+
|
|
761
|
+
expect(scope.resolveAll(MyEntityA).length).toBe(2);
|
|
762
|
+
expect(scope.resolveAll(BaseEntity).length).toBe(1);
|
|
763
|
+
|
|
764
|
+
scope.deregister(MyEntityA);
|
|
765
|
+
|
|
766
|
+
expect(scope.resolveAll(MyEntityA).length).toBe(0);
|
|
767
|
+
expect(scope.resolveAll(BaseEntity).length).toBe(1);
|
|
768
|
+
|
|
769
|
+
const entityB2 = new MyEntityB();
|
|
770
|
+
scope.register(entityB2);
|
|
771
|
+
|
|
772
|
+
expect(scope.resolveAll(BaseEntity).length).toBe(2);
|
|
773
|
+
|
|
774
|
+
scope.deregister(BaseEntity);
|
|
775
|
+
|
|
776
|
+
expect(scope.resolveAll(BaseEntity).length).toBe(0);
|
|
777
|
+
});
|
|
778
|
+
it('Should register/deregister fragments with the same names', async () => {
|
|
779
|
+
|
|
780
|
+
class MyFragment extends A_Fragment {
|
|
781
|
+
|
|
782
|
+
array: string[] = [];
|
|
783
|
+
|
|
784
|
+
constructor(name: string) {
|
|
785
|
+
super({ name });
|
|
786
|
+
}
|
|
787
|
+
}
|
|
788
|
+
|
|
789
|
+
const scope = new A_Scope({ name: 'TestScope' });
|
|
790
|
+
|
|
791
|
+
const fragmentA1 = new MyFragment('fragmentA');
|
|
792
|
+
|
|
793
|
+
scope.register(fragmentA1);
|
|
794
|
+
|
|
795
|
+
expect(scope.has(MyFragment)).toBe(true);
|
|
796
|
+
expect(scope.resolve(MyFragment)).toBe(fragmentA1);
|
|
797
|
+
|
|
798
|
+
scope.deregister(fragmentA1);
|
|
799
|
+
|
|
800
|
+
fragmentA1.array.push('newData');
|
|
801
|
+
|
|
802
|
+
|
|
803
|
+
expect(scope.has(MyFragment)).toBe(false);
|
|
804
|
+
|
|
805
|
+
const fragmentA2 = new MyFragment('fragmentA');
|
|
806
|
+
|
|
807
|
+
scope.register(fragmentA2);
|
|
808
|
+
|
|
809
|
+
expect(scope.has(MyFragment)).toBe(true);
|
|
810
|
+
expect(scope.resolve(MyFragment)).toBe(fragmentA2);
|
|
811
|
+
expect(fragmentA2.array.length).toBe(0);
|
|
812
|
+
});
|
|
708
813
|
});
|