@adaas/a-concept 0.3.6 → 0.3.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/README.md +116 -78
- package/benchmarks/feature-chaining.bench.ts +465 -0
- package/benchmarks/feature-lifecycle.bench.ts +245 -0
- package/benchmarks/feature-optimize.bench.ts +205 -0
- package/benchmarks/feature-profiling.bench.ts +415 -0
- package/benchmarks/feature-template.bench.ts +211 -0
- package/benchmarks/helpers.ts +97 -0
- package/benchmarks/run-all.ts +58 -0
- package/benchmarks/scope-resolve.bench.ts +245 -0
- package/benchmarks/step-manager.bench.ts +146 -0
- package/dist/browser/index.d.mts +72 -3
- package/dist/browser/index.mjs +2 -2
- package/dist/browser/index.mjs.map +1 -1
- package/dist/node/index.cjs +244 -87
- package/dist/node/index.cjs.map +1 -1
- package/dist/node/index.d.mts +72 -3
- package/dist/node/index.d.ts +72 -3
- package/dist/node/index.mjs +244 -87
- package/dist/node/index.mjs.map +1 -1
- package/package.json +11 -1
- package/src/lib/A-Context/A-Context.class.ts +155 -60
- package/src/lib/A-Entity/A-Entity.class.ts +3 -3
- package/src/lib/A-Feature/A-Feature.class.ts +46 -41
- package/src/lib/A-Feature/A-Feature.types.ts +6 -10
- package/src/lib/A-Meta/A-Meta.class.ts +18 -3
- package/src/lib/A-Scope/A-Scope.class.ts +98 -5
- package/tests/A-Feature.test.ts +101 -0
- package/tsconfig.json +1 -0
|
@@ -46,6 +46,17 @@ export class A_Scope<
|
|
|
46
46
|
_FragmentType extends A_Fragment[] = A_Fragment[],
|
|
47
47
|
> {
|
|
48
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
|
+
|
|
49
60
|
/**
|
|
50
61
|
* Scope Name uses for identification and logging purposes
|
|
51
62
|
*/
|
|
@@ -61,6 +72,25 @@ export class A_Scope<
|
|
|
61
72
|
*/
|
|
62
73
|
protected _meta: A_Meta<_MetaItems> = new A_Meta<_MetaItems>();
|
|
63
74
|
|
|
75
|
+
// ===========================================================================
|
|
76
|
+
// --------------------Cache & Versioning--------------------------------------
|
|
77
|
+
// ===========================================================================
|
|
78
|
+
/**
|
|
79
|
+
* Monotonically increasing version counter. Incremented on every mutation
|
|
80
|
+
* (register, deregister, import, deimport, inherit, destroy) so that
|
|
81
|
+
* external caches (e.g. A_Context feature-extension cache) can detect
|
|
82
|
+
* staleness cheaply via numeric comparison.
|
|
83
|
+
*/
|
|
84
|
+
protected _version: number = 0;
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Cache for resolveConstructor results (both positive and negative).
|
|
88
|
+
* Key = constructor name (string) or constructor reference toString.
|
|
89
|
+
* Value = resolved constructor or `null` for negative results.
|
|
90
|
+
* Invalidated by incrementing _version (cache is cleared on bump).
|
|
91
|
+
*/
|
|
92
|
+
protected _resolveConstructorCache: Map<string | Function, Function | null> = new Map();
|
|
93
|
+
|
|
64
94
|
// ===========================================================================
|
|
65
95
|
// --------------------ALLowed Constructors--------------------------------
|
|
66
96
|
// ===========================================================================
|
|
@@ -137,6 +167,11 @@ export class A_Scope<
|
|
|
137
167
|
* Returns a list of Constructors for A-Errors that are available in the scope
|
|
138
168
|
*/
|
|
139
169
|
get allowedErrors() { return this._allowedErrors }
|
|
170
|
+
/**
|
|
171
|
+
* Returns the current version of the scope. Each mutation increments the version,
|
|
172
|
+
* allowing external caches to detect staleness via numeric comparison.
|
|
173
|
+
*/
|
|
174
|
+
get version(): number { return this._version }
|
|
140
175
|
// ===========================================================================
|
|
141
176
|
// --------------------Readonly Registered Properties--------------------------
|
|
142
177
|
// ===========================================================================
|
|
@@ -180,6 +215,15 @@ export class A_Scope<
|
|
|
180
215
|
return this._parent;
|
|
181
216
|
}
|
|
182
217
|
|
|
218
|
+
/**
|
|
219
|
+
* Increments the scope version and clears internal caches.
|
|
220
|
+
* Must be called on every scope mutation (register, deregister, import, deimport, inherit, destroy).
|
|
221
|
+
*/
|
|
222
|
+
protected bumpVersion(): void {
|
|
223
|
+
this._version++;
|
|
224
|
+
this._resolveConstructorCache.clear();
|
|
225
|
+
}
|
|
226
|
+
|
|
183
227
|
/**
|
|
184
228
|
* A_Scope is a unique A-Concept Structure that allows to operate with A-Concept Primitives and Models in a specific context and with specific rules.
|
|
185
229
|
* It refers to the visibility and accessibility of :
|
|
@@ -379,6 +423,8 @@ export class A_Scope<
|
|
|
379
423
|
if (this.issuer()) {
|
|
380
424
|
A_Context.deallocate(this);
|
|
381
425
|
}
|
|
426
|
+
|
|
427
|
+
this.bumpVersion();
|
|
382
428
|
}
|
|
383
429
|
|
|
384
430
|
|
|
@@ -469,6 +515,7 @@ export class A_Scope<
|
|
|
469
515
|
|
|
470
516
|
|
|
471
517
|
this._parent = parent;
|
|
518
|
+
this.bumpVersion();
|
|
472
519
|
return this;
|
|
473
520
|
}
|
|
474
521
|
|
|
@@ -495,6 +542,7 @@ export class A_Scope<
|
|
|
495
542
|
return;
|
|
496
543
|
|
|
497
544
|
this._imports.add(scope);
|
|
545
|
+
this.bumpVersion();
|
|
498
546
|
});
|
|
499
547
|
|
|
500
548
|
return this;
|
|
@@ -510,8 +558,10 @@ export class A_Scope<
|
|
|
510
558
|
deimport(...scopes: A_Scope[]): A_Scope {
|
|
511
559
|
|
|
512
560
|
scopes.forEach(scope => {
|
|
513
|
-
if (this._imports.has(scope))
|
|
561
|
+
if (this._imports.has(scope)) {
|
|
514
562
|
this._imports.delete(scope);
|
|
563
|
+
this.bumpVersion();
|
|
564
|
+
}
|
|
515
565
|
});
|
|
516
566
|
return this;
|
|
517
567
|
}
|
|
@@ -894,6 +944,29 @@ export class A_Scope<
|
|
|
894
944
|
`Invalid constructor name provided: ${name}`
|
|
895
945
|
);
|
|
896
946
|
|
|
947
|
+
// ---- Optimization: check resolveConstructor cache for string lookups ----
|
|
948
|
+
const cacheKey = name;
|
|
949
|
+
if (this._resolveConstructorCache.has(cacheKey)) {
|
|
950
|
+
const cached = this._resolveConstructorCache.get(cacheKey);
|
|
951
|
+
return (cached === null ? undefined : cached) as any;
|
|
952
|
+
}
|
|
953
|
+
|
|
954
|
+
const resolved = this._resolveConstructorUncached<T>(name);
|
|
955
|
+
|
|
956
|
+
// Store in cache (null for negative/miss results)
|
|
957
|
+
this._resolveConstructorCache.set(cacheKey, resolved ?? null);
|
|
958
|
+
|
|
959
|
+
return resolved;
|
|
960
|
+
}
|
|
961
|
+
|
|
962
|
+
/**
|
|
963
|
+
* Internal uncached implementation of resolveConstructor for string names.
|
|
964
|
+
* Separated to allow the public method to wrap with caching.
|
|
965
|
+
*/
|
|
966
|
+
private _resolveConstructorUncached<T extends A_TYPES__A_DependencyInjectable>(
|
|
967
|
+
name: string
|
|
968
|
+
): A_TYPES__Entity_Constructor<T> | A_TYPES__Component_Constructor<T> | A_TYPES__Fragment_Constructor<T> | undefined {
|
|
969
|
+
|
|
897
970
|
// 1) Check components
|
|
898
971
|
const component = Array.from(this.allowedComponents).find(
|
|
899
972
|
c => c.name === name
|
|
@@ -1715,6 +1788,7 @@ export class A_Scope<
|
|
|
1715
1788
|
);
|
|
1716
1789
|
|
|
1717
1790
|
A_Context.register(this, param1);
|
|
1791
|
+
this.bumpVersion();
|
|
1718
1792
|
|
|
1719
1793
|
break;
|
|
1720
1794
|
}
|
|
@@ -1726,6 +1800,7 @@ export class A_Scope<
|
|
|
1726
1800
|
|
|
1727
1801
|
this._entities.set(param1.aseid.toString(), param1 as InstanceType<_EntityType[number]>);
|
|
1728
1802
|
A_Context.register(this, param1);
|
|
1803
|
+
this.bumpVersion();
|
|
1729
1804
|
break;
|
|
1730
1805
|
}
|
|
1731
1806
|
// 4) In case when it's a A-Fragment instance
|
|
@@ -1740,6 +1815,7 @@ export class A_Scope<
|
|
|
1740
1815
|
);
|
|
1741
1816
|
|
|
1742
1817
|
A_Context.register(this, param1);
|
|
1818
|
+
this.bumpVersion();
|
|
1743
1819
|
|
|
1744
1820
|
break;
|
|
1745
1821
|
}
|
|
@@ -1754,6 +1830,7 @@ export class A_Scope<
|
|
|
1754
1830
|
);
|
|
1755
1831
|
|
|
1756
1832
|
A_Context.register(this, (param1 as any));
|
|
1833
|
+
this.bumpVersion();
|
|
1757
1834
|
break;
|
|
1758
1835
|
}
|
|
1759
1836
|
|
|
@@ -1762,26 +1839,34 @@ export class A_Scope<
|
|
|
1762
1839
|
// ------------------------------------------
|
|
1763
1840
|
// 6) In case when it's a A-Component constructor
|
|
1764
1841
|
case A_TypeGuards.isComponentConstructor(param1): {
|
|
1765
|
-
if (!this.allowedComponents.has(param1))
|
|
1842
|
+
if (!this.allowedComponents.has(param1)) {
|
|
1766
1843
|
this.allowedComponents.add(param1 as _ComponentType[number]);
|
|
1844
|
+
this.bumpVersion();
|
|
1845
|
+
}
|
|
1767
1846
|
break;
|
|
1768
1847
|
}
|
|
1769
1848
|
// 8) In case when it's a A-Fragment constructor
|
|
1770
1849
|
case A_TypeGuards.isFragmentConstructor(param1): {
|
|
1771
|
-
if (!this.allowedFragments.has(param1))
|
|
1850
|
+
if (!this.allowedFragments.has(param1)) {
|
|
1772
1851
|
this.allowedFragments.add(param1 as A_TYPES__Fragment_Constructor<_FragmentType[number]>);
|
|
1852
|
+
this.bumpVersion();
|
|
1853
|
+
}
|
|
1773
1854
|
break;
|
|
1774
1855
|
}
|
|
1775
1856
|
// 9) In case when it's a A-Entity constructor
|
|
1776
1857
|
case A_TypeGuards.isEntityConstructor(param1): {
|
|
1777
|
-
if (!this.allowedEntities.has(param1))
|
|
1858
|
+
if (!this.allowedEntities.has(param1)) {
|
|
1778
1859
|
this.allowedEntities.add(param1 as _EntityType[number]);
|
|
1860
|
+
this.bumpVersion();
|
|
1861
|
+
}
|
|
1779
1862
|
break;
|
|
1780
1863
|
}
|
|
1781
1864
|
// 10) In case when it's a A-Error constructor
|
|
1782
1865
|
case A_TypeGuards.isErrorConstructor(param1): {
|
|
1783
|
-
if (!this.allowedErrors.has(param1))
|
|
1866
|
+
if (!this.allowedErrors.has(param1)) {
|
|
1784
1867
|
this.allowedErrors.add(param1 as _ErrorType[number]);
|
|
1868
|
+
this.bumpVersion();
|
|
1869
|
+
}
|
|
1785
1870
|
break;
|
|
1786
1871
|
}
|
|
1787
1872
|
|
|
@@ -1891,6 +1976,7 @@ export class A_Scope<
|
|
|
1891
1976
|
this.allowedComponents.delete(ctor);
|
|
1892
1977
|
}
|
|
1893
1978
|
|
|
1979
|
+
this.bumpVersion();
|
|
1894
1980
|
break;
|
|
1895
1981
|
}
|
|
1896
1982
|
// 3) In case when it's a A-Entity instance
|
|
@@ -1906,6 +1992,7 @@ export class A_Scope<
|
|
|
1906
1992
|
this.allowedEntities.delete(ctor);
|
|
1907
1993
|
}
|
|
1908
1994
|
|
|
1995
|
+
this.bumpVersion();
|
|
1909
1996
|
break;
|
|
1910
1997
|
}
|
|
1911
1998
|
// 4) In case when it's a A-Fragment instance
|
|
@@ -1920,6 +2007,7 @@ export class A_Scope<
|
|
|
1920
2007
|
this.allowedFragments.delete(ctor);
|
|
1921
2008
|
}
|
|
1922
2009
|
|
|
2010
|
+
this.bumpVersion();
|
|
1923
2011
|
break;
|
|
1924
2012
|
}
|
|
1925
2013
|
// 5) In case when it's a A-Error instance
|
|
@@ -1935,6 +2023,7 @@ export class A_Scope<
|
|
|
1935
2023
|
this.allowedErrors.delete(ctor);
|
|
1936
2024
|
}
|
|
1937
2025
|
|
|
2026
|
+
this.bumpVersion();
|
|
1938
2027
|
break;
|
|
1939
2028
|
}
|
|
1940
2029
|
|
|
@@ -1944,6 +2033,7 @@ export class A_Scope<
|
|
|
1944
2033
|
// 6) In case when it's a A-Component constructor
|
|
1945
2034
|
case A_TypeGuards.isComponentConstructor(param1): {
|
|
1946
2035
|
this.allowedComponents.delete(param1 as _ComponentType[number]);
|
|
2036
|
+
this.bumpVersion();
|
|
1947
2037
|
break;
|
|
1948
2038
|
}
|
|
1949
2039
|
// 8) In case when it's a A-Fragment constructor
|
|
@@ -1957,6 +2047,7 @@ export class A_Scope<
|
|
|
1957
2047
|
}
|
|
1958
2048
|
});
|
|
1959
2049
|
|
|
2050
|
+
this.bumpVersion();
|
|
1960
2051
|
break;
|
|
1961
2052
|
}
|
|
1962
2053
|
// 9) In case when it's a A-Entity constructor
|
|
@@ -1970,6 +2061,7 @@ export class A_Scope<
|
|
|
1970
2061
|
}
|
|
1971
2062
|
});
|
|
1972
2063
|
|
|
2064
|
+
this.bumpVersion();
|
|
1973
2065
|
break;
|
|
1974
2066
|
}
|
|
1975
2067
|
// 10) In case when it's a A-Error constructor
|
|
@@ -1983,6 +2075,7 @@ export class A_Scope<
|
|
|
1983
2075
|
}
|
|
1984
2076
|
});
|
|
1985
2077
|
|
|
2078
|
+
this.bumpVersion();
|
|
1986
2079
|
break;
|
|
1987
2080
|
}
|
|
1988
2081
|
|
package/tests/A-Feature.test.ts
CHANGED
|
@@ -1103,6 +1103,107 @@ describe('A-Feature tests', () => {
|
|
|
1103
1103
|
'ComponentA.feature1'
|
|
1104
1104
|
]);
|
|
1105
1105
|
})
|
|
1106
|
+
it('Should return promise if at least one step is async', async () => {
|
|
1107
|
+
|
|
1108
|
+
const resultChain: string[] = [];
|
|
1109
|
+
|
|
1110
|
+
class ComponentA extends A_Component {
|
|
1111
|
+
@A_Feature.Extend({
|
|
1112
|
+
name: 'testFeature',
|
|
1113
|
+
})
|
|
1114
|
+
async step1() {
|
|
1115
|
+
resultChain.push('ComponentA.step1');
|
|
1116
|
+
}
|
|
1117
|
+
|
|
1118
|
+
@A_Feature.Extend({
|
|
1119
|
+
name: 'testFeature',
|
|
1120
|
+
})
|
|
1121
|
+
step2() {
|
|
1122
|
+
resultChain.push('ComponentA.step2');
|
|
1123
|
+
}
|
|
1124
|
+
}
|
|
1125
|
+
|
|
1126
|
+
|
|
1127
|
+
class MyEntity extends A_Entity {
|
|
1128
|
+
async test() {
|
|
1129
|
+
return await this.call('testFeature');
|
|
1130
|
+
}
|
|
1131
|
+
}
|
|
1132
|
+
|
|
1133
|
+
|
|
1134
|
+
const testScope = new A_Scope({ name: 'TestScope', components: [ComponentA] });
|
|
1135
|
+
|
|
1136
|
+
const component = testScope.resolve(ComponentA)!;
|
|
1137
|
+
|
|
1138
|
+
const myEntity = new MyEntity();
|
|
1139
|
+
|
|
1140
|
+
testScope.register(myEntity);
|
|
1141
|
+
|
|
1142
|
+
const res = component.call('testFeature');
|
|
1143
|
+
const res2 = myEntity.test();
|
|
1144
|
+
|
|
1145
|
+
expect(res).toBeInstanceOf(Promise);
|
|
1146
|
+
expect(res2).toBeInstanceOf(Promise);
|
|
1147
|
+
|
|
1148
|
+
await res;
|
|
1149
|
+
await res2;
|
|
1150
|
+
|
|
1151
|
+
|
|
1152
|
+
expect(resultChain).toEqual([
|
|
1153
|
+
'ComponentA.step1',
|
|
1154
|
+
'ComponentA.step1',
|
|
1155
|
+
'ComponentA.step2',
|
|
1156
|
+
'ComponentA.step2'
|
|
1157
|
+
]);
|
|
1158
|
+
})
|
|
1159
|
+
it('Should execute sync if all steps are synchronous', async () => {
|
|
1160
|
+
|
|
1161
|
+
const resultChain: string[] = [];
|
|
1162
|
+
|
|
1163
|
+
class ComponentA extends A_Component {
|
|
1164
|
+
@A_Feature.Extend({
|
|
1165
|
+
name: 'testFeature',
|
|
1166
|
+
})
|
|
1167
|
+
step1() {
|
|
1168
|
+
resultChain.push('ComponentA.step1');
|
|
1169
|
+
}
|
|
1170
|
+
|
|
1171
|
+
@A_Feature.Extend({
|
|
1172
|
+
name: 'testFeature',
|
|
1173
|
+
})
|
|
1174
|
+
step2() {
|
|
1175
|
+
resultChain.push('ComponentA.step2');
|
|
1176
|
+
}
|
|
1177
|
+
}
|
|
1178
|
+
|
|
1179
|
+
|
|
1180
|
+
class MyEntity extends A_Entity {
|
|
1181
|
+
test() {
|
|
1182
|
+
return this.call('testFeature');
|
|
1183
|
+
}
|
|
1184
|
+
}
|
|
1185
|
+
|
|
1186
|
+
const testScope = new A_Scope({ name: 'TestScope', components: [ComponentA] });
|
|
1187
|
+
|
|
1188
|
+
const component = testScope.resolve(ComponentA)!;
|
|
1189
|
+
|
|
1190
|
+
const myEntity = new MyEntity();
|
|
1191
|
+
|
|
1192
|
+
testScope.register(myEntity);
|
|
1193
|
+
|
|
1194
|
+
const res = component.call('testFeature');
|
|
1195
|
+
const res2 = myEntity.test();
|
|
1196
|
+
|
|
1197
|
+
expect(res).not.toBeInstanceOf(Promise);
|
|
1198
|
+
expect(res2).not.toBeInstanceOf(Promise);
|
|
1199
|
+
|
|
1200
|
+
expect(resultChain).toEqual([
|
|
1201
|
+
'ComponentA.step1',
|
|
1202
|
+
'ComponentA.step2',
|
|
1203
|
+
'ComponentA.step1',
|
|
1204
|
+
'ComponentA.step2'
|
|
1205
|
+
]);
|
|
1206
|
+
})
|
|
1106
1207
|
|
|
1107
1208
|
|
|
1108
1209
|
});
|