@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
|
@@ -20,6 +20,51 @@ import { A_Caller } from "@adaas/a-concept/a-caller";
|
|
|
20
20
|
import { A_Entity } from "@adaas/a-concept/a-entity";
|
|
21
21
|
import { A_Context } from "@adaas/a-concept/a-context";
|
|
22
22
|
import { createSuite, BenchResult, printSummary } from './helpers';
|
|
23
|
+
import Table from 'cli-table3';
|
|
24
|
+
|
|
25
|
+
// ──────────────────────────────────────────────────────────────
|
|
26
|
+
// Heap measurement helpers
|
|
27
|
+
// ──────────────────────────────────────────────────────────────
|
|
28
|
+
|
|
29
|
+
interface HeapSnapshot {
|
|
30
|
+
benchName: string;
|
|
31
|
+
heapBefore: number; // bytes
|
|
32
|
+
heapAfter: number; // bytes
|
|
33
|
+
heapDelta: number; // bytes
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function takeHeapSnapshot(): number {
|
|
37
|
+
try { global.gc!(); } catch (_) { /* --expose-gc not set */ }
|
|
38
|
+
return process.memoryUsage().heapUsed;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function formatBytes(bytes: number): string {
|
|
42
|
+
const mb = bytes / (1024 * 1024);
|
|
43
|
+
return `${mb >= 0 ? '+' : ''}${mb.toFixed(2)} MB`;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function printHeapTable(title: string, snapshots: HeapSnapshot[]) {
|
|
47
|
+
console.log(`\n${'─'.repeat(70)}`);
|
|
48
|
+
console.log(` 🧠 ${title} — Heap Usage`);
|
|
49
|
+
console.log(`${'─'.repeat(70)}`);
|
|
50
|
+
|
|
51
|
+
const table = new Table({
|
|
52
|
+
head: ['Benchmark', 'Heap Before', 'Heap After', 'Δ Heap'],
|
|
53
|
+
colWidths: [40, 14, 14, 14],
|
|
54
|
+
style: { head: ['yellow'] },
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
for (const s of snapshots) {
|
|
58
|
+
table.push([
|
|
59
|
+
s.benchName,
|
|
60
|
+
(s.heapBefore / 1024 / 1024).toFixed(2) + ' MB',
|
|
61
|
+
(s.heapAfter / 1024 / 1024).toFixed(2) + ' MB',
|
|
62
|
+
formatBytes(s.heapDelta),
|
|
63
|
+
]);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
console.log(table.toString());
|
|
67
|
+
}
|
|
23
68
|
|
|
24
69
|
|
|
25
70
|
|
|
@@ -93,6 +138,7 @@ export class ChainingComponent extends A_Component {
|
|
|
93
138
|
|
|
94
139
|
export async function runFeatureChainingBenchmarks(): Promise<any> {
|
|
95
140
|
const allResults: BenchResult[] = [];
|
|
141
|
+
const heapSnapshots: HeapSnapshot[] = [];
|
|
96
142
|
|
|
97
143
|
|
|
98
144
|
|
|
@@ -111,81 +157,110 @@ export async function runFeatureChainingBenchmarks(): Promise<any> {
|
|
|
111
157
|
|
|
112
158
|
entity.scope.inherit(childScope);
|
|
113
159
|
|
|
114
|
-
|
|
115
|
-
|
|
160
|
+
// Heap tracking state — shared across benchmarks in this suite
|
|
161
|
+
let currentHeapBefore = 0;
|
|
116
162
|
|
|
117
163
|
suite
|
|
118
|
-
.
|
|
119
|
-
const
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
component: entity,
|
|
126
|
-
scope: entity.scope
|
|
164
|
+
.on('cycle', (event: any) => {
|
|
165
|
+
const heapAfter = takeHeapSnapshot();
|
|
166
|
+
heapSnapshots.push({
|
|
167
|
+
benchName: event.target.name,
|
|
168
|
+
heapBefore: currentHeapBefore,
|
|
169
|
+
heapAfter,
|
|
170
|
+
heapDelta: heapAfter - currentHeapBefore,
|
|
127
171
|
});
|
|
128
172
|
})
|
|
129
|
-
.add('new Feature execution (direct)', () => {
|
|
130
|
-
const feature = new A_Feature({
|
|
131
|
-
name: 'entityFeature',
|
|
132
|
-
component: entity,
|
|
133
|
-
scope: entity.scope
|
|
134
|
-
});
|
|
135
173
|
|
|
136
|
-
|
|
137
|
-
|
|
174
|
+
suite
|
|
175
|
+
.add('new Feature instantiation (A_Context.featureTemplate)', {
|
|
176
|
+
onStart: () => { currentHeapBefore = takeHeapSnapshot(); },
|
|
177
|
+
fn: () => {
|
|
178
|
+
const definition = A_Context.featureTemplate('entityFeature', entity);
|
|
138
179
|
}
|
|
139
180
|
})
|
|
140
|
-
.add('new Feature
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
181
|
+
.add('new Feature instantiation (new A_Feature)', {
|
|
182
|
+
onStart: () => { currentHeapBefore = takeHeapSnapshot(); },
|
|
183
|
+
fn: () => {
|
|
184
|
+
const feature = new A_Feature({
|
|
185
|
+
name: 'entityFeature',
|
|
186
|
+
component: entity,
|
|
187
|
+
scope: entity.scope
|
|
188
|
+
});
|
|
189
|
+
}
|
|
146
190
|
})
|
|
147
|
-
.add('new Feature execution (
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
191
|
+
.add('new Feature execution (direct)', {
|
|
192
|
+
onStart: () => { currentHeapBefore = takeHeapSnapshot(); },
|
|
193
|
+
fn: () => {
|
|
194
|
+
const feature = new A_Feature({
|
|
195
|
+
name: 'entityFeature',
|
|
196
|
+
component: entity,
|
|
197
|
+
scope: entity.scope
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
for (const stage of feature) {
|
|
201
|
+
stage.process(entity.scope)
|
|
202
|
+
}
|
|
203
|
+
}
|
|
154
204
|
})
|
|
155
|
-
.add('
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
if (res instanceof Promise)
|
|
160
|
-
throw new Error('Expected synchronous execution for baseline feature call');
|
|
205
|
+
.add('new Feature execution (direct entity.call)', {
|
|
206
|
+
onStart: () => { currentHeapBefore = takeHeapSnapshot(); },
|
|
207
|
+
fn: () => {
|
|
208
|
+
const res = entity.call('entityFeature', entity.scope);
|
|
161
209
|
|
|
210
|
+
if (res instanceof Promise)
|
|
211
|
+
throw new Error('Expected synchronous execution for baseline feature call');
|
|
212
|
+
}
|
|
162
213
|
})
|
|
163
|
-
.add('
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
if (res instanceof Promise)
|
|
168
|
-
throw new Error('Expected synchronous execution for baseline feature call');
|
|
214
|
+
.add('new Feature execution (wrapped entity.call)', {
|
|
215
|
+
onStart: () => { currentHeapBefore = takeHeapSnapshot(); },
|
|
216
|
+
fn: () => {
|
|
217
|
+
const res = entity.entityFeature();
|
|
169
218
|
|
|
219
|
+
if (res instanceof Promise)
|
|
220
|
+
throw new Error('Expected synchronous execution for baseline feature call');
|
|
221
|
+
}
|
|
170
222
|
})
|
|
171
|
-
.add('[duplicated
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
throw new Error('Expected synchronous execution for baseline feature call');
|
|
223
|
+
.add('[duplicated 1] new Feature execution (wrapped entity.call)', {
|
|
224
|
+
onStart: () => { currentHeapBefore = takeHeapSnapshot(); },
|
|
225
|
+
fn: () => {
|
|
226
|
+
const res = entity.entityFeature();
|
|
176
227
|
|
|
228
|
+
if (res instanceof Promise)
|
|
229
|
+
throw new Error('Expected synchronous execution for baseline feature call');
|
|
230
|
+
}
|
|
177
231
|
})
|
|
178
|
-
.add('[duplicated
|
|
179
|
-
|
|
180
|
-
|
|
232
|
+
.add('[duplicated 2] new Feature execution (wrapped entity.call)', {
|
|
233
|
+
onStart: () => { currentHeapBefore = takeHeapSnapshot(); },
|
|
234
|
+
fn: () => {
|
|
235
|
+
const res = entity.entityFeature();
|
|
181
236
|
|
|
182
|
-
|
|
183
|
-
|
|
237
|
+
if (res instanceof Promise)
|
|
238
|
+
throw new Error('Expected synchronous execution for baseline feature call');
|
|
239
|
+
}
|
|
240
|
+
})
|
|
241
|
+
.add('[duplicated 3] new Feature execution (wrapped entity.call)', {
|
|
242
|
+
onStart: () => { currentHeapBefore = takeHeapSnapshot(); },
|
|
243
|
+
fn: () => {
|
|
244
|
+
const res = entity.entityFeature();
|
|
245
|
+
if (res instanceof Promise)
|
|
246
|
+
throw new Error('Expected synchronous execution for baseline feature call');
|
|
247
|
+
}
|
|
248
|
+
})
|
|
249
|
+
.add('[duplicated 4] new Feature execution (wrapped entity.call)', {
|
|
250
|
+
onStart: () => { currentHeapBefore = takeHeapSnapshot(); },
|
|
251
|
+
fn: () => {
|
|
252
|
+
const res = entity.entityFeature();
|
|
184
253
|
|
|
254
|
+
if (res instanceof Promise)
|
|
255
|
+
throw new Error('Expected synchronous execution for baseline feature call');
|
|
256
|
+
}
|
|
185
257
|
})
|
|
186
258
|
});
|
|
187
259
|
allResults.push(...baselineResults);
|
|
188
260
|
|
|
261
|
+
// Print heap summary
|
|
262
|
+
printHeapTable('Feature Call — Baseline (no chain)', heapSnapshots);
|
|
263
|
+
|
|
189
264
|
return allResults;
|
|
190
265
|
}
|
|
191
266
|
|
package/dist/browser/index.d.mts
CHANGED
|
@@ -2010,9 +2010,10 @@ declare class A_Feature<T extends A_TYPES__FeatureAvailableComponents = A_TYPES_
|
|
|
2010
2010
|
*/
|
|
2011
2011
|
scope?: A_Scope): Promise<void> | void;
|
|
2012
2012
|
/**
|
|
2013
|
-
*
|
|
2013
|
+
* Async continuation — processes remaining stages after the first async pivot.
|
|
2014
|
+
* Resumes from the current iterator position (this._index).
|
|
2014
2015
|
*/
|
|
2015
|
-
private
|
|
2016
|
+
private processRemainingStagesAsync;
|
|
2016
2017
|
private createStageError;
|
|
2017
2018
|
/**
|
|
2018
2019
|
* This method moves the feature to the next stage
|
|
@@ -2988,15 +2989,6 @@ type A_TYPES_ScopeDependentComponents = A_Component | A_Entity | A_Fragment | A_
|
|
|
2988
2989
|
type A_TYPES_ScopeIndependentComponents = A_Error | A_Scope | A_Caller;
|
|
2989
2990
|
|
|
2990
2991
|
declare class A_Scope<_MetaItems extends Record<string, any> = any, _ComponentType extends A_TYPES__Component_Constructor[] = A_TYPES__Component_Constructor[], _ErrorType extends A_TYPES__Error_Constructor[] = A_TYPES__Error_Constructor[], _EntityType extends A_TYPES__Entity_Constructor[] = A_TYPES__Entity_Constructor[], _FragmentType extends A_Fragment[] = A_Fragment[]> {
|
|
2991
|
-
/**
|
|
2992
|
-
* Auto-incrementing counter for generating unique scope IDs.
|
|
2993
|
-
*/
|
|
2994
|
-
private static _nextUid;
|
|
2995
|
-
/**
|
|
2996
|
-
* Unique numeric ID for this scope instance. Used as a cache key discriminator
|
|
2997
|
-
* to prevent collisions between scopes with the same name or version.
|
|
2998
|
-
*/
|
|
2999
|
-
readonly uid: number;
|
|
3000
2992
|
/**
|
|
3001
2993
|
* Scope Name uses for identification and logging purposes
|
|
3002
2994
|
*/
|
|
@@ -3025,6 +3017,11 @@ declare class A_Scope<_MetaItems extends Record<string, any> = any, _ComponentTy
|
|
|
3025
3017
|
* Invalidated by incrementing _version (cache is cleared on bump).
|
|
3026
3018
|
*/
|
|
3027
3019
|
protected _resolveConstructorCache: Map<string | Function, Function | null>;
|
|
3020
|
+
/**
|
|
3021
|
+
* Cached fingerprint string. Invalidated on every bumpVersion() call.
|
|
3022
|
+
*/
|
|
3023
|
+
private _cachedFingerprint;
|
|
3024
|
+
private _cachedFingerprintVersion;
|
|
3028
3025
|
/**
|
|
3029
3026
|
* A set of allowed components, A set of constructors that are allowed in the scope
|
|
3030
3027
|
*
|
|
@@ -3091,6 +3088,12 @@ declare class A_Scope<_MetaItems extends Record<string, any> = any, _ComponentTy
|
|
|
3091
3088
|
* allowing external caches to detect staleness via numeric comparison.
|
|
3092
3089
|
*/
|
|
3093
3090
|
get version(): number;
|
|
3091
|
+
/**
|
|
3092
|
+
* Returns a content-addressable fingerprint of the scope.
|
|
3093
|
+
* Two scopes with identical content (components, entities, fragments, errors, imports, parent)
|
|
3094
|
+
* will produce the same fingerprint. Dynamically recomputed when scope content changes.
|
|
3095
|
+
*/
|
|
3096
|
+
get fingerprint(): string;
|
|
3094
3097
|
/**
|
|
3095
3098
|
* Returns an Array of entities registered in the scope
|
|
3096
3099
|
*
|
|
@@ -3132,6 +3135,16 @@ declare class A_Scope<_MetaItems extends Record<string, any> = any, _ComponentTy
|
|
|
3132
3135
|
* Must be called on every scope mutation (register, deregister, import, deimport, inherit, destroy).
|
|
3133
3136
|
*/
|
|
3134
3137
|
protected bumpVersion(): void;
|
|
3138
|
+
/**
|
|
3139
|
+
* Computes the aggregate version of this scope and all reachable scopes (parent + imports).
|
|
3140
|
+
* Used to detect when any transitive dependency has changed, so the fingerprint cache can be invalidated.
|
|
3141
|
+
*/
|
|
3142
|
+
private aggregateVersion;
|
|
3143
|
+
/**
|
|
3144
|
+
* Computes a deterministic content-addressable fingerprint string.
|
|
3145
|
+
* Includes components, entities, fragments, errors, parent, and imports.
|
|
3146
|
+
*/
|
|
3147
|
+
private computeFingerprint;
|
|
3135
3148
|
/**
|
|
3136
3149
|
* 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.
|
|
3137
3150
|
* It refers to the visibility and accessibility of :
|
|
@@ -4210,12 +4223,22 @@ declare class A_Context {
|
|
|
4210
4223
|
* Key format: `${featureName}::${componentConstructorName}::${scopeVersion}::${metaVersion}`
|
|
4211
4224
|
* Automatically invalidated when scope version or meta version changes.
|
|
4212
4225
|
*/
|
|
4213
|
-
protected
|
|
4226
|
+
protected _featureCache: Map<string, Array<A_TYPES__A_StageStep>>;
|
|
4214
4227
|
/**
|
|
4215
4228
|
* Maximum number of entries in the featureExtensions cache.
|
|
4216
4229
|
* When exceeded, the entire cache is cleared to prevent unbounded growth.
|
|
4217
4230
|
*/
|
|
4218
4231
|
protected static readonly FEATURE_EXTENSIONS_CACHE_MAX_SIZE = 1024;
|
|
4232
|
+
/**
|
|
4233
|
+
* For each indexed constructor, stores the set of all its ancestor constructors
|
|
4234
|
+
* (walking up the prototype chain). Enables O(1) isInheritedFrom checks.
|
|
4235
|
+
*/
|
|
4236
|
+
protected _ancestors: Map<Function, Set<Function>>;
|
|
4237
|
+
/**
|
|
4238
|
+
* For each constructor, stores the set of all known descendant constructors.
|
|
4239
|
+
* Enables O(1) "find a descendant in a Set" lookups.
|
|
4240
|
+
*/
|
|
4241
|
+
protected _descendants: Map<Function, Set<Function>>;
|
|
4219
4242
|
protected _globals: Map<string, any>;
|
|
4220
4243
|
/**
|
|
4221
4244
|
* Private constructor to enforce singleton pattern.
|
|
@@ -4531,6 +4554,33 @@ declare class A_Context {
|
|
|
4531
4554
|
* Resets the Context to its initial state.
|
|
4532
4555
|
*/
|
|
4533
4556
|
static reset(): void;
|
|
4557
|
+
/**
|
|
4558
|
+
* Index a constructor's full prototype chain into the inheritance graph.
|
|
4559
|
+
* Safe to call multiple times for the same constructor — it's a no-op if already indexed.
|
|
4560
|
+
*
|
|
4561
|
+
* After indexing, `A_Context.isIndexedInheritedFrom(child, parent)` becomes O(1).
|
|
4562
|
+
*/
|
|
4563
|
+
static indexConstructor(ctor: Function): void;
|
|
4564
|
+
/**
|
|
4565
|
+
* O(1) check whether `child` inherits from `parent` using the pre-built index.
|
|
4566
|
+
* Falls back to prototype chain walking if either is not yet indexed.
|
|
4567
|
+
*
|
|
4568
|
+
* [!] Handles the same-class case: returns true if child === parent.
|
|
4569
|
+
*/
|
|
4570
|
+
static isIndexedInheritedFrom(child: Function, parent: Function): boolean;
|
|
4571
|
+
/**
|
|
4572
|
+
* Find the first constructor in `candidates` that is a descendant of (or equal to) `parent`.
|
|
4573
|
+
* Returns undefined if none found.
|
|
4574
|
+
*
|
|
4575
|
+
* Uses the optimal strategy based on set sizes:
|
|
4576
|
+
* - If candidates is small, iterates candidates and checks ancestry (O(c))
|
|
4577
|
+
* - If descendants is small, iterates descendants and checks membership (O(d))
|
|
4578
|
+
*/
|
|
4579
|
+
static findDescendantIn<T extends Function>(parent: Function, candidates: Set<T> | Array<T>): T | undefined;
|
|
4580
|
+
/**
|
|
4581
|
+
* Returns the set of indexed ancestors for a constructor, or undefined if not indexed.
|
|
4582
|
+
*/
|
|
4583
|
+
static getAncestors(ctor: Function): Set<Function> | undefined;
|
|
4534
4584
|
/**
|
|
4535
4585
|
* Type guard to check if the param is allowed for scope allocation.
|
|
4536
4586
|
*
|