@adaas/a-concept 0.3.8 → 0.3.9
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/benchmarks/feature-optimize.bench.ts +129 -54
- package/dist/browser/index.d.mts +62 -12
- package/dist/browser/index.mjs +2 -2
- package/dist/browser/index.mjs.map +1 -1
- package/dist/node/index.cjs +301 -102
- package/dist/node/index.cjs.map +1 -1
- package/dist/node/index.d.mts +62 -12
- package/dist/node/index.d.ts +62 -12
- package/dist/node/index.mjs +301 -102
- package/dist/node/index.mjs.map +1 -1
- package/package.json +2 -2
- package/src/lib/A-Context/A-Context.class.ts +214 -41
- package/src/lib/A-Feature/A-Feature.class.ts +34 -31
- package/src/lib/A-Scope/A-Scope.class.ts +143 -72
- package/tests/A-Meta.test.ts +46 -2
- package/tests/A-Scope.test.ts +188 -0
- package/tsconfig.json +1 -0
|
@@ -45,18 +45,6 @@ export class A_Scope<
|
|
|
45
45
|
_EntityType extends A_TYPES__Entity_Constructor[] = A_TYPES__Entity_Constructor[],
|
|
46
46
|
_FragmentType extends A_Fragment[] = A_Fragment[],
|
|
47
47
|
> {
|
|
48
|
-
|
|
49
|
-
/**
|
|
50
|
-
* Auto-incrementing counter for generating unique scope IDs.
|
|
51
|
-
*/
|
|
52
|
-
private static _nextUid: number = 0;
|
|
53
|
-
|
|
54
|
-
/**
|
|
55
|
-
* Unique numeric ID for this scope instance. Used as a cache key discriminator
|
|
56
|
-
* to prevent collisions between scopes with the same name or version.
|
|
57
|
-
*/
|
|
58
|
-
readonly uid: number = A_Scope._nextUid++;
|
|
59
|
-
|
|
60
48
|
/**
|
|
61
49
|
* Scope Name uses for identification and logging purposes
|
|
62
50
|
*/
|
|
@@ -91,6 +79,12 @@ export class A_Scope<
|
|
|
91
79
|
*/
|
|
92
80
|
protected _resolveConstructorCache: Map<string | Function, Function | null> = new Map();
|
|
93
81
|
|
|
82
|
+
/**
|
|
83
|
+
* Cached fingerprint string. Invalidated on every bumpVersion() call.
|
|
84
|
+
*/
|
|
85
|
+
private _cachedFingerprint: string | undefined;
|
|
86
|
+
private _cachedFingerprintVersion: number = -1;
|
|
87
|
+
|
|
94
88
|
// ===========================================================================
|
|
95
89
|
// --------------------ALLowed Constructors--------------------------------
|
|
96
90
|
// ===========================================================================
|
|
@@ -172,6 +166,20 @@ export class A_Scope<
|
|
|
172
166
|
* allowing external caches to detect staleness via numeric comparison.
|
|
173
167
|
*/
|
|
174
168
|
get version(): number { return this._version }
|
|
169
|
+
/**
|
|
170
|
+
* Returns a content-addressable fingerprint of the scope.
|
|
171
|
+
* Two scopes with identical content (components, entities, fragments, errors, imports, parent)
|
|
172
|
+
* will produce the same fingerprint. Dynamically recomputed when scope content changes.
|
|
173
|
+
*/
|
|
174
|
+
get fingerprint(): string {
|
|
175
|
+
const aggregateVersion = this.aggregateVersion(new Set());
|
|
176
|
+
if (this._cachedFingerprint !== undefined && this._cachedFingerprintVersion === aggregateVersion) {
|
|
177
|
+
return this._cachedFingerprint;
|
|
178
|
+
}
|
|
179
|
+
this._cachedFingerprint = this.computeFingerprint(new Set());
|
|
180
|
+
this._cachedFingerprintVersion = aggregateVersion;
|
|
181
|
+
return this._cachedFingerprint;
|
|
182
|
+
}
|
|
175
183
|
// ===========================================================================
|
|
176
184
|
// --------------------Readonly Registered Properties--------------------------
|
|
177
185
|
// ===========================================================================
|
|
@@ -222,6 +230,60 @@ export class A_Scope<
|
|
|
222
230
|
protected bumpVersion(): void {
|
|
223
231
|
this._version++;
|
|
224
232
|
this._resolveConstructorCache.clear();
|
|
233
|
+
this._cachedFingerprint = undefined;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* Computes the aggregate version of this scope and all reachable scopes (parent + imports).
|
|
238
|
+
* Used to detect when any transitive dependency has changed, so the fingerprint cache can be invalidated.
|
|
239
|
+
*/
|
|
240
|
+
private aggregateVersion(visited: Set<A_Scope>): number {
|
|
241
|
+
if (visited.has(this)) return 0;
|
|
242
|
+
visited.add(this);
|
|
243
|
+
let v = this._version;
|
|
244
|
+
if (this._parent) v += this._parent.aggregateVersion(visited);
|
|
245
|
+
for (const imp of this._imports) v += imp.aggregateVersion(visited);
|
|
246
|
+
return v;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
/**
|
|
250
|
+
* Computes a deterministic content-addressable fingerprint string.
|
|
251
|
+
* Includes components, entities, fragments, errors, parent, and imports.
|
|
252
|
+
*/
|
|
253
|
+
private computeFingerprint(visited: Set<A_Scope>): string {
|
|
254
|
+
if (visited.has(this)) return '~circular~';
|
|
255
|
+
visited.add(this);
|
|
256
|
+
|
|
257
|
+
const parts: string[] = [];
|
|
258
|
+
|
|
259
|
+
// Parent
|
|
260
|
+
parts.push('P:' + (this._parent ? this._parent.computeFingerprint(visited) : '-'));
|
|
261
|
+
|
|
262
|
+
// Allowed constructors (sorted by name for determinism)
|
|
263
|
+
const allowedComponentNames = Array.from(this._allowedComponents).map(c => A_CommonHelper.getComponentName(c.name)).sort();
|
|
264
|
+
parts.push('AC:' + allowedComponentNames.join(','));
|
|
265
|
+
|
|
266
|
+
const allowedEntityNames = Array.from(this._allowedEntities).map(e => A_CommonHelper.getComponentName(e.name)).sort();
|
|
267
|
+
parts.push('AE:' + allowedEntityNames.join(','));
|
|
268
|
+
|
|
269
|
+
const allowedFragmentNames = Array.from(this._allowedFragments).map(f => A_CommonHelper.getComponentName(f.name)).sort();
|
|
270
|
+
parts.push('AF:' + allowedFragmentNames.join(','));
|
|
271
|
+
|
|
272
|
+
const allowedErrorNames = Array.from(this._allowedErrors).map(e => A_CommonHelper.getComponentName(e.name)).sort();
|
|
273
|
+
parts.push('AR:' + allowedErrorNames.join(','));
|
|
274
|
+
|
|
275
|
+
// Imports (sorted by fingerprint for determinism)
|
|
276
|
+
const importFingerprints = Array.from(this._imports).map(s => s.computeFingerprint(visited)).sort();
|
|
277
|
+
parts.push('I:' + importFingerprints.join(','));
|
|
278
|
+
|
|
279
|
+
const raw = parts.join('|');
|
|
280
|
+
|
|
281
|
+
// djb2 hash
|
|
282
|
+
let hash = 5381;
|
|
283
|
+
for (let i = 0; i < raw.length; i++) {
|
|
284
|
+
hash = ((hash << 5) + hash + raw.charCodeAt(i)) | 0;
|
|
285
|
+
}
|
|
286
|
+
return (hash >>> 0).toString(16);
|
|
225
287
|
}
|
|
226
288
|
|
|
227
289
|
/**
|
|
@@ -420,9 +482,15 @@ export class A_Scope<
|
|
|
420
482
|
this._entities.clear();
|
|
421
483
|
this._imports.clear();
|
|
422
484
|
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
485
|
+
/**
|
|
486
|
+
* Since the scope is being destroyed, we can assume that all components, fragments and entities registered in the scope are no longer needed and can be garbage collected.
|
|
487
|
+
* And scope lives in WeakMap in the A_Context, so it will be garbage collected as well, so we don't need to manually deallocate it from the A_Context.
|
|
488
|
+
*
|
|
489
|
+
Verdict: The comment is accurate. Keeping deallocate() commented out is safe. The WeakMap design handles cleanup correctly in connection to A_Feature's lifecycle (as long as features properly destroy their scopes).
|
|
490
|
+
*/
|
|
491
|
+
// if (this.issuer()) {
|
|
492
|
+
// A_Context.deallocate(this);
|
|
493
|
+
// }
|
|
426
494
|
|
|
427
495
|
this.bumpVersion();
|
|
428
496
|
}
|
|
@@ -701,24 +769,21 @@ export class A_Scope<
|
|
|
701
769
|
// 3) Check if it's a Component
|
|
702
770
|
case A_TypeGuards.isComponentConstructor(ctor): {
|
|
703
771
|
found = this.isAllowedComponent(ctor)
|
|
704
|
-
|| !!
|
|
705
|
-
.find(c => A_CommonHelper.isInheritedFrom(c, ctor));
|
|
772
|
+
|| !!A_Context.findDescendantIn(ctor, this.allowedComponents);
|
|
706
773
|
|
|
707
774
|
break;
|
|
708
775
|
}
|
|
709
776
|
// 4) Check if it's an Entity
|
|
710
777
|
case A_TypeGuards.isEntityConstructor(ctor): {
|
|
711
778
|
found = this.isAllowedEntity(ctor)
|
|
712
|
-
|| !!
|
|
713
|
-
.find(e => A_CommonHelper.isInheritedFrom(e, ctor));
|
|
779
|
+
|| !!A_Context.findDescendantIn(ctor, this.allowedEntities);
|
|
714
780
|
|
|
715
781
|
break;
|
|
716
782
|
}
|
|
717
783
|
// 5) Check if it's a Fragment
|
|
718
784
|
case A_TypeGuards.isFragmentConstructor(ctor): {
|
|
719
785
|
found = this.isAllowedFragment(ctor)
|
|
720
|
-
|| !!
|
|
721
|
-
.find(f => A_CommonHelper.isInheritedFrom(f, ctor));
|
|
786
|
+
|| !!A_Context.findDescendantIn(ctor, this.allowedFragments);
|
|
722
787
|
|
|
723
788
|
break;
|
|
724
789
|
}
|
|
@@ -726,8 +791,7 @@ export class A_Scope<
|
|
|
726
791
|
// 6) Check if it's an Error
|
|
727
792
|
case A_TypeGuards.isErrorConstructor(ctor): {
|
|
728
793
|
found = this.isAllowedError(ctor)
|
|
729
|
-
|| !!
|
|
730
|
-
.find(e => A_CommonHelper.isInheritedFrom(e, ctor));
|
|
794
|
+
|| !!A_Context.findDescendantIn(ctor, this.allowedErrors);
|
|
731
795
|
|
|
732
796
|
break;
|
|
733
797
|
}
|
|
@@ -735,8 +799,7 @@ export class A_Scope<
|
|
|
735
799
|
// 7) Check scope issuer
|
|
736
800
|
case this.issuer()
|
|
737
801
|
&& (this.issuer()!.constructor === ctor
|
|
738
|
-
||
|
|
739
|
-
)
|
|
802
|
+
|| A_Context.isIndexedInheritedFrom(this.issuer()!.constructor, ctor as Function)
|
|
740
803
|
): {
|
|
741
804
|
found = true;
|
|
742
805
|
break;
|
|
@@ -922,20 +985,16 @@ export class A_Scope<
|
|
|
922
985
|
// If it's a constructor, find any extension from the allowed constructors and return it
|
|
923
986
|
switch (true) {
|
|
924
987
|
case A_TypeGuards.isComponentConstructor(name):
|
|
925
|
-
return
|
|
926
|
-
.find((c) => A_CommonHelper.isInheritedFrom(c, name)) as A_TYPES__Component_Constructor<T> | undefined;
|
|
988
|
+
return A_Context.findDescendantIn(name, this.allowedComponents) as A_TYPES__Component_Constructor<T> | undefined;
|
|
927
989
|
|
|
928
990
|
case A_TypeGuards.isEntityConstructor(name):
|
|
929
|
-
return
|
|
930
|
-
.find((e) => A_CommonHelper.isInheritedFrom(e, name)) as A_TYPES__Entity_Constructor<T> | undefined;
|
|
991
|
+
return A_Context.findDescendantIn(name, this.allowedEntities) as A_TYPES__Entity_Constructor<T> | undefined;
|
|
931
992
|
|
|
932
993
|
case A_TypeGuards.isFragmentConstructor(name):
|
|
933
|
-
return
|
|
934
|
-
.find((f) => A_CommonHelper.isInheritedFrom(f, name)) as A_TYPES__Fragment_Constructor<T> | undefined;
|
|
994
|
+
return A_Context.findDescendantIn(name, this.allowedFragments) as A_TYPES__Fragment_Constructor<T> | undefined;
|
|
935
995
|
|
|
936
996
|
case A_TypeGuards.isErrorConstructor(name):
|
|
937
|
-
return
|
|
938
|
-
.find((e) => A_CommonHelper.isInheritedFrom(e, name)) as A_TYPES__Error_Constructor<T> | undefined;
|
|
997
|
+
return A_Context.findDescendantIn(name, this.allowedErrors) as A_TYPES__Error_Constructor<T> | undefined;
|
|
939
998
|
}
|
|
940
999
|
|
|
941
1000
|
if (!A_TypeGuards.isString(name))
|
|
@@ -975,27 +1034,17 @@ export class A_Scope<
|
|
|
975
1034
|
|
|
976
1035
|
if (component) return component as A_TYPES__Component_Constructor<T>;
|
|
977
1036
|
else
|
|
978
|
-
// 1.2) Check
|
|
1037
|
+
// 1.2) Check component ancestor names using inheritance index
|
|
979
1038
|
{
|
|
980
|
-
const
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
while (current) {
|
|
987
|
-
if (current.name === name
|
|
988
|
-
|| current.name === A_FormatterHelper.toPascalCase(name)
|
|
989
|
-
) {
|
|
990
|
-
return true;
|
|
991
|
-
}
|
|
992
|
-
current = Object.getPrototypeOf(current);
|
|
993
|
-
}
|
|
994
|
-
|
|
995
|
-
return false;
|
|
996
|
-
|
|
1039
|
+
const pascalName = A_FormatterHelper.toPascalCase(name);
|
|
1040
|
+
const protoComponent = Array.from(this.allowedComponents).find(c => {
|
|
1041
|
+
const ancestors = A_Context.getAncestors(c);
|
|
1042
|
+
if (!ancestors) return false;
|
|
1043
|
+
for (const ancestor of ancestors) {
|
|
1044
|
+
if (ancestor.name === name || ancestor.name === pascalName) return true;
|
|
997
1045
|
}
|
|
998
|
-
|
|
1046
|
+
return false;
|
|
1047
|
+
});
|
|
999
1048
|
if (protoComponent) return protoComponent as A_TYPES__Component_Constructor<T>;
|
|
1000
1049
|
}
|
|
1001
1050
|
|
|
@@ -1008,11 +1057,17 @@ export class A_Scope<
|
|
|
1008
1057
|
);
|
|
1009
1058
|
if (entity) return entity as A_TYPES__Entity_Constructor<T>;
|
|
1010
1059
|
else
|
|
1011
|
-
// 2.2) Check
|
|
1060
|
+
// 2.2) Check entity ancestor names using inheritance index
|
|
1012
1061
|
{
|
|
1013
|
-
const
|
|
1014
|
-
|
|
1015
|
-
|
|
1062
|
+
const pascalName = A_FormatterHelper.toPascalCase(name);
|
|
1063
|
+
const protoEntity = Array.from(this.allowedEntities).find(e => {
|
|
1064
|
+
const ancestors = A_Context.getAncestors(e);
|
|
1065
|
+
if (!ancestors) return false;
|
|
1066
|
+
for (const ancestor of ancestors) {
|
|
1067
|
+
if (ancestor.name === name || ancestor.name === pascalName) return true;
|
|
1068
|
+
}
|
|
1069
|
+
return false;
|
|
1070
|
+
});
|
|
1016
1071
|
if (protoEntity) return protoEntity as A_TYPES__Entity_Constructor<T>;
|
|
1017
1072
|
}
|
|
1018
1073
|
|
|
@@ -1022,11 +1077,17 @@ export class A_Scope<
|
|
|
1022
1077
|
);
|
|
1023
1078
|
if (fragment) return fragment as A_TYPES__Fragment_Constructor<T>;
|
|
1024
1079
|
else
|
|
1025
|
-
// 3.2) Check
|
|
1080
|
+
// 3.2) Check fragment ancestor names using inheritance index
|
|
1026
1081
|
{
|
|
1027
|
-
const
|
|
1028
|
-
|
|
1029
|
-
|
|
1082
|
+
const pascalName = A_FormatterHelper.toPascalCase(name);
|
|
1083
|
+
const protoFragment = Array.from(this.allowedFragments).find(f => {
|
|
1084
|
+
const ancestors = A_Context.getAncestors(f);
|
|
1085
|
+
if (!ancestors) return false;
|
|
1086
|
+
for (const ancestor of ancestors) {
|
|
1087
|
+
if (ancestor.name === name || ancestor.name === pascalName) return true;
|
|
1088
|
+
}
|
|
1089
|
+
return false;
|
|
1090
|
+
});
|
|
1030
1091
|
if (protoFragment) return protoFragment as A_TYPES__Fragment_Constructor<T>;
|
|
1031
1092
|
}
|
|
1032
1093
|
|
|
@@ -1183,7 +1244,7 @@ export class A_Scope<
|
|
|
1183
1244
|
case A_TypeGuards.isComponentConstructor(param1): {
|
|
1184
1245
|
// 1) Check components
|
|
1185
1246
|
this.allowedComponents.forEach(ctor => {
|
|
1186
|
-
if (
|
|
1247
|
+
if (A_Context.isIndexedInheritedFrom(ctor, param1)) {
|
|
1187
1248
|
const instance = this.resolveOnce<T>(ctor);
|
|
1188
1249
|
if (instance) results.push(instance as T);
|
|
1189
1250
|
}
|
|
@@ -1194,7 +1255,7 @@ export class A_Scope<
|
|
|
1194
1255
|
case A_TypeGuards.isFragmentConstructor(param1): {
|
|
1195
1256
|
// 2) Check fragments
|
|
1196
1257
|
this.allowedFragments.forEach(ctor => {
|
|
1197
|
-
if (
|
|
1258
|
+
if (A_Context.isIndexedInheritedFrom(ctor, param1)) {
|
|
1198
1259
|
const instance = this.resolveOnce<T>(ctor);
|
|
1199
1260
|
if (instance) results.push(instance as T);
|
|
1200
1261
|
}
|
|
@@ -1206,7 +1267,7 @@ export class A_Scope<
|
|
|
1206
1267
|
// 3) Check entities
|
|
1207
1268
|
this.entities.forEach(entity => {
|
|
1208
1269
|
|
|
1209
|
-
if (
|
|
1270
|
+
if (A_Context.isIndexedInheritedFrom(entity.constructor, param1)) {
|
|
1210
1271
|
results.push(entity as T);
|
|
1211
1272
|
}
|
|
1212
1273
|
});
|
|
@@ -1578,7 +1639,7 @@ export class A_Scope<
|
|
|
1578
1639
|
if (issuer
|
|
1579
1640
|
&& (
|
|
1580
1641
|
issuer.constructor === ctor
|
|
1581
|
-
||
|
|
1642
|
+
|| A_Context.isIndexedInheritedFrom(issuer?.constructor, ctor)
|
|
1582
1643
|
)) {
|
|
1583
1644
|
return issuer!;
|
|
1584
1645
|
}
|
|
@@ -1639,9 +1700,10 @@ export class A_Scope<
|
|
|
1639
1700
|
case fragmentInstancePresented && this._fragments.has(fragment):
|
|
1640
1701
|
return fragmentInstancePresented;
|
|
1641
1702
|
|
|
1642
|
-
// 3) In case when there's a
|
|
1643
|
-
case !fragmentInstancePresented
|
|
1644
|
-
const found =
|
|
1703
|
+
// 3) In case when there's a fragment that is inherited from the required fragment
|
|
1704
|
+
case !fragmentInstancePresented: {
|
|
1705
|
+
const found = A_Context.findDescendantIn(fragment, this._allowedFragments);
|
|
1706
|
+
if (!found) return undefined;
|
|
1645
1707
|
|
|
1646
1708
|
return this.resolveFragment(found);
|
|
1647
1709
|
}
|
|
@@ -1695,8 +1757,9 @@ export class A_Scope<
|
|
|
1695
1757
|
}
|
|
1696
1758
|
|
|
1697
1759
|
// 3) In case when there's a component that is inherited from the required component
|
|
1698
|
-
case !this.allowedComponents.has(component)
|
|
1699
|
-
const found =
|
|
1760
|
+
case !this.allowedComponents.has(component): {
|
|
1761
|
+
const found = A_Context.findDescendantIn(component, this.allowedComponents);
|
|
1762
|
+
if (!found) return undefined;
|
|
1700
1763
|
|
|
1701
1764
|
return this.resolveComponent(found);
|
|
1702
1765
|
}
|
|
@@ -1787,6 +1850,7 @@ export class A_Scope<
|
|
|
1787
1850
|
param1 as InstanceType<_ComponentType[number]>
|
|
1788
1851
|
);
|
|
1789
1852
|
|
|
1853
|
+
A_Context.indexConstructor(param1.constructor);
|
|
1790
1854
|
A_Context.register(this, param1);
|
|
1791
1855
|
this.bumpVersion();
|
|
1792
1856
|
|
|
@@ -1799,6 +1863,7 @@ export class A_Scope<
|
|
|
1799
1863
|
this.allowedEntities.add(param1.constructor as _EntityType[number]);
|
|
1800
1864
|
|
|
1801
1865
|
this._entities.set(param1.aseid.toString(), param1 as InstanceType<_EntityType[number]>);
|
|
1866
|
+
A_Context.indexConstructor(param1.constructor);
|
|
1802
1867
|
A_Context.register(this, param1);
|
|
1803
1868
|
this.bumpVersion();
|
|
1804
1869
|
break;
|
|
@@ -1814,6 +1879,7 @@ export class A_Scope<
|
|
|
1814
1879
|
param1 as _FragmentType[number]
|
|
1815
1880
|
);
|
|
1816
1881
|
|
|
1882
|
+
A_Context.indexConstructor(param1.constructor);
|
|
1817
1883
|
A_Context.register(this, param1);
|
|
1818
1884
|
this.bumpVersion();
|
|
1819
1885
|
|
|
@@ -1829,6 +1895,7 @@ export class A_Scope<
|
|
|
1829
1895
|
param1 as InstanceType<_ErrorType[number]>
|
|
1830
1896
|
);
|
|
1831
1897
|
|
|
1898
|
+
A_Context.indexConstructor(param1.constructor);
|
|
1832
1899
|
A_Context.register(this, (param1 as any));
|
|
1833
1900
|
this.bumpVersion();
|
|
1834
1901
|
break;
|
|
@@ -1841,6 +1908,7 @@ export class A_Scope<
|
|
|
1841
1908
|
case A_TypeGuards.isComponentConstructor(param1): {
|
|
1842
1909
|
if (!this.allowedComponents.has(param1)) {
|
|
1843
1910
|
this.allowedComponents.add(param1 as _ComponentType[number]);
|
|
1911
|
+
A_Context.indexConstructor(param1);
|
|
1844
1912
|
this.bumpVersion();
|
|
1845
1913
|
}
|
|
1846
1914
|
break;
|
|
@@ -1849,6 +1917,7 @@ export class A_Scope<
|
|
|
1849
1917
|
case A_TypeGuards.isFragmentConstructor(param1): {
|
|
1850
1918
|
if (!this.allowedFragments.has(param1)) {
|
|
1851
1919
|
this.allowedFragments.add(param1 as A_TYPES__Fragment_Constructor<_FragmentType[number]>);
|
|
1920
|
+
A_Context.indexConstructor(param1);
|
|
1852
1921
|
this.bumpVersion();
|
|
1853
1922
|
}
|
|
1854
1923
|
break;
|
|
@@ -1857,6 +1926,7 @@ export class A_Scope<
|
|
|
1857
1926
|
case A_TypeGuards.isEntityConstructor(param1): {
|
|
1858
1927
|
if (!this.allowedEntities.has(param1)) {
|
|
1859
1928
|
this.allowedEntities.add(param1 as _EntityType[number]);
|
|
1929
|
+
A_Context.indexConstructor(param1);
|
|
1860
1930
|
this.bumpVersion();
|
|
1861
1931
|
}
|
|
1862
1932
|
break;
|
|
@@ -1865,6 +1935,7 @@ export class A_Scope<
|
|
|
1865
1935
|
case A_TypeGuards.isErrorConstructor(param1): {
|
|
1866
1936
|
if (!this.allowedErrors.has(param1)) {
|
|
1867
1937
|
this.allowedErrors.add(param1 as _ErrorType[number]);
|
|
1938
|
+
A_Context.indexConstructor(param1);
|
|
1868
1939
|
this.bumpVersion();
|
|
1869
1940
|
}
|
|
1870
1941
|
break;
|
|
@@ -2041,7 +2112,7 @@ export class A_Scope<
|
|
|
2041
2112
|
this.allowedFragments.delete(param1 as A_TYPES__Fragment_Constructor<_FragmentType[number]>);
|
|
2042
2113
|
// and then deregister all instances of this fragment
|
|
2043
2114
|
Array.from(this._fragments.entries()).forEach(([ctor, instance]) => {
|
|
2044
|
-
if (
|
|
2115
|
+
if (A_Context.isIndexedInheritedFrom(ctor, param1)) {
|
|
2045
2116
|
this._fragments.delete(ctor);
|
|
2046
2117
|
A_Context.deregister(instance);
|
|
2047
2118
|
}
|
|
@@ -2055,7 +2126,7 @@ export class A_Scope<
|
|
|
2055
2126
|
this.allowedEntities.delete(param1 as _EntityType[number]);
|
|
2056
2127
|
// and then deregister all instances of this entity
|
|
2057
2128
|
Array.from(this._entities.entries()).forEach(([aseid, instance]) => {
|
|
2058
|
-
if (
|
|
2129
|
+
if (A_Context.isIndexedInheritedFrom(instance.constructor, param1)) {
|
|
2059
2130
|
this._entities.delete(aseid);
|
|
2060
2131
|
A_Context.deregister(instance);
|
|
2061
2132
|
}
|
|
@@ -2069,7 +2140,7 @@ export class A_Scope<
|
|
|
2069
2140
|
this.allowedErrors.delete(param1 as _ErrorType[number]);
|
|
2070
2141
|
// and then deregister all instances of this error
|
|
2071
2142
|
Array.from(this._errors.entries()).forEach(([code, instance]) => {
|
|
2072
|
-
if (
|
|
2143
|
+
if (A_Context.isIndexedInheritedFrom(instance.constructor, param1)) {
|
|
2073
2144
|
this._errors.delete(code);
|
|
2074
2145
|
A_Context.deregister(instance);
|
|
2075
2146
|
}
|
package/tests/A-Meta.test.ts
CHANGED
|
@@ -9,6 +9,8 @@ import { A_Context } from "@adaas/a-concept/a-context";
|
|
|
9
9
|
import { A_Feature } from "@adaas/a-concept/a-feature";
|
|
10
10
|
import { A_Meta } from "@adaas/a-concept/a-meta";
|
|
11
11
|
import { A_Inject } from "@adaas/a-concept/a-inject";
|
|
12
|
+
import { A_Entity } from "@adaas/a-concept/a-entity";
|
|
13
|
+
import { A_Scope } from "@adaas/a-concept/a-scope";
|
|
12
14
|
|
|
13
15
|
jest.retryTimes(0);
|
|
14
16
|
|
|
@@ -173,7 +175,7 @@ describe('A-Meta tests', () => {
|
|
|
173
175
|
expect(meta.get(A_TYPES__ComponentMetaKey.EXTENSIONS)?.size()).toBe(1);
|
|
174
176
|
expect(meta.get(A_TYPES__ComponentMetaKey.EXTENSIONS)?.has('.*\\.testFeature$')).toBe(true);
|
|
175
177
|
})
|
|
176
|
-
it('Should propagate a custom
|
|
178
|
+
it('Should propagate a custom meta', async () => {
|
|
177
179
|
|
|
178
180
|
class InjectableComponent extends A_Component { }
|
|
179
181
|
|
|
@@ -205,7 +207,7 @@ describe('A-Meta tests', () => {
|
|
|
205
207
|
}
|
|
206
208
|
}
|
|
207
209
|
|
|
208
|
-
class SubCustomComponent extends CustomComponent {}
|
|
210
|
+
class SubCustomComponent extends CustomComponent { }
|
|
209
211
|
|
|
210
212
|
|
|
211
213
|
const meta = A_Context.meta<CustomComponentMeta>(SubCustomComponent);
|
|
@@ -216,4 +218,46 @@ describe('A-Meta tests', () => {
|
|
|
216
218
|
expect(meta.get(A_TYPES__ComponentMetaKey.EXTENSIONS)?.size()).toBe(1);
|
|
217
219
|
expect(meta.get(A_TYPES__ComponentMetaKey.EXTENSIONS)?.has('.*\\.testFeature$')).toBe(true);
|
|
218
220
|
})
|
|
221
|
+
it('Should inherit properly extends methods when they are bind to entity', async () => {
|
|
222
|
+
|
|
223
|
+
|
|
224
|
+
const results: string[] = [];
|
|
225
|
+
|
|
226
|
+
class CustomComponentMeta extends A_ComponentMeta<{ customField: string } & A_TYPES__ComponentMeta> {
|
|
227
|
+
|
|
228
|
+
get customField(): string | undefined {
|
|
229
|
+
return this.get('customField');
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
class MyEntity extends A_Entity {
|
|
235
|
+
feature() {
|
|
236
|
+
this.call('testFeature');
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
|
|
241
|
+
@A_Meta.Define(CustomComponentMeta)
|
|
242
|
+
class CustomComponent extends A_Component {
|
|
243
|
+
|
|
244
|
+
@A_Feature.Extend({
|
|
245
|
+
name: 'testFeature',
|
|
246
|
+
scope: [MyEntity]
|
|
247
|
+
})
|
|
248
|
+
async feature() {
|
|
249
|
+
results.push('feature called');
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
const scope = new A_Scope({ name: 'testScope', components: [CustomComponent] });
|
|
254
|
+
|
|
255
|
+
const entity = new MyEntity();
|
|
256
|
+
|
|
257
|
+
scope.register(entity);
|
|
258
|
+
|
|
259
|
+
entity.feature();
|
|
260
|
+
|
|
261
|
+
expect(results).toContain('feature called');
|
|
262
|
+
})
|
|
219
263
|
})
|